当前位置:   article > 正文

Openmv识别Apriltag码并与stm32进行串口通信_stm32和openmv串口通信

stm32和openmv串口通信

        本文使用带有独立处理图像模块的摄像头Openmv进行Apriltag码的识别,并将Openmv与stm32进行串口通信,将Apriltag码的ID、中心位置相对于Openmv摄像头中心坐标的偏移量、以及Apriltag码相对于Openmv镜头的距离通过串口通信传输给stm32。

        接线图Openmv通过电脑USB口供电,Openmv接三根线,一根与stm32共地,一根将Openmv的P4与stm32的A10相连接,另一根将Openmv的P5管脚与stm32端的A9相连接(即两者的Rx和Tx交错链接,以实现串口通信):

        

串口通信采取的方式:

        采取串口通信发送hex数据包的方式,通讯协议为两个帧头和一个帧尾。由于整型数据较为容易发送,可以通过将Openmv端的tag_id(int)直接发送、Apriltag码相对于Openmv镜头中心的横向偏移量x_translation(float)、以及距离distance(float)乘以1000或者10000进行发送。另外,发送负数也会导致错误,因此加入一个标志位,在Openmv端只发送正整数,通过标志位的值,在stm32端判断是否需要加上负号。

        可以通过time.sleep_ms()来设置摄像头的帧数,实际上每秒发送数据的速度达不到帧数。

        采取的串口通信为使用串口com3,波特率为9600,数据位8位,无校验位,停止位1。事实上返回的两个float都是相对距离,并不是实际距离,若要实际距离只需要如下:

# f_x 是x的像素为单位的焦距。对于标准的OpenMV,应该等于2.8/3.984*656,这个值是用毫米为单位的焦距除以x方向的感光元件的长度,乘以x方向的感光元件的像素(OV7725)
# f_y 是y的像素为单位的焦距。对于标准的OpenMV,应该等于2.8/2.952*488,这个值是用毫米为单位的焦距除以y方向的感光元件的长度,乘以y方向的感光元件的像素(OV7725)

Openmv端python代码:

  1. import sensor
  2. import image
  3. import time
  4. import struct
  5. from pyb import UART
  6. # 使用openmv的串口3(com3)
  7. # 波特率要跟需要通信的设备一样 Tx 和 Rx 引脚交错连接
  8. sensor.reset()
  9. sensor.set_pixformat(sensor.RGB565)
  10. sensor.set_framesize(sensor.QQVGA) #QQVGA
  11. sensor.skip_frames(30)
  12. sensor.set_auto_gain(False) # 关闭
  13. sensor.set_auto_whitebal(False) # 关闭
  14. clock = time.clock()
  15. uart = UART (3, 9600) #初始化串口3,波特率为9600(注意:下位机stm32记得也配置成9600)
  16. uart.init(9600, bits=8, parity=None, stop=1) #设置波特率为9600,数据位8位,无校验位,停止位1位
  17. #编码模式UTF-8
  18. # 注意!与find_qrcodes不同,find_apriltags 不需要软件矫正畸变就可以工作。
  19. # f_x 是x的像素为单位的焦距。对于标准的OpenMV,应该等于2.8/3.984*656,这个值是用毫米为单位的焦距除以x方向的感光元件的长度,乘以x方向的感光元件的像素(OV7725)
  20. # f_y 是y的像素为单位的焦距。对于标准的OpenMV,应该等于2.8/2.952*488,这个值是用毫米为单位的焦距除以y方向的感光元件的长度,乘以y方向的感光元件的像素(OV7725)
  21. # c_x 是图像的x中心位置 c_y 是图像的y中心位置
  22. tag_families = 0
  23. tag_families |= image.TAG36H11 # comment out to disable this family (default family)
  24. f_x = (2.8 / 3.984) * 160 # 默认值
  25. f_y = (2.8 / 2.952) * 120 # 默认值
  26. c_x = 160 * 0.5 # 默认值(image.w * 0.5)
  27. c_y = 120 * 0.5 # 默认值(image.h * 0.5)
  28. #apriltag家族tag36h11
  29. def family_name(tag):
  30. if tag.family() == image.TAG36H11:
  31. return "TAG36H11"
  32. while(True):
  33. clock.tick()
  34. img = sensor.snapshot()
  35. for tag in img.find_apriltags(families=tag_families,fx=f_x, fy=f_y, cx=c_x, cy=c_y): # TAG36H11
  36. img.draw_rectangle(tag.rect(), color = (255, 0, 0))
  37. img.draw_cross(tag.cx(), tag.cy(), color = (0, 255, 0))
  38. print_args = (family_name(tag), tag.id(), tag.x_translation(), tag.y_translation(), tag.z_translation())
  39. print("Tag Family %s, Tag ID %d, Tx: %f, Ty %f, Tz %f" % print_args)
  40. #帧头 + 帧头 + id信息 + x坐标 + z距离 + 标志位 + 帧尾
  41. #使用struct将数据打包并发送,保证float型x坐标数据准确及其信息的完整性
  42. if tag.x_translation() >= 0:
  43. data = struct.pack("<bbiiibb",
  44. 0xAA, #帧头
  45. 0xAE, #帧头
  46. tag.id(), #数据id
  47. #x的坐标
  48. int(10000*tag.x_translation()),#数据1
  49. #z的坐标
  50. -int(10000*tag.z_translation()),#数据2
  51. 0xBF, #标志位表示大于零
  52. 0xAC) #帧尾
  53. else:
  54. data = struct.pack("<bbiiibb",
  55. 0xAA, #帧头
  56. 0xAE, #帧头
  57. tag.id(), #数据id
  58. #x的坐标
  59. -int(1000*tag.x_translation()),#数据1
  60. #z的坐标
  61. -int(10000*tag.z_translation()),#数据2
  62. 0xCF, #标志位表示小于零
  63. 0xAC) #帧尾
  64. uart.write(data) #com3串口发送
  65. time.sleep_ms(50)
  66. # uart.write("Tag Family %s, Tag ID %d, Tx: %f, Ty %f, Tz %f" % print_args+"\r\n")#与windows通信
  67. print(clock.fps())

将python文件复制粘贴到Openmv端的main.py中,即可实现Openmv脱机自动执行Apriltag码识别

stm32端代码:

Serial.c

  1. #include "stm32f10x.h" // Device header
  2. #include <stdio.h>
  3. #include <stdarg.h>
  4. uint8_t receive_data[13];
  5. uint8_t Serial_RxFlag; //定义接收数据包标志位
  6. int tag_id; //apriltag码的id 包括 0 ,1 ,2三个数据
  7. //偏移距离数值越大与openmv镜头中心偏离越远
  8. //相对距离数值越大距离openmv镜头越远
  9. //x_translation的正负表示物体相对openmv镜头中心的右左
  10. //x_translation<0,表示apriltag码相对于openmv镜头中心在左
  11. //x_translation>0,表示apriltag码相对于openmv镜头中心在右
  12. int x_translation; //apriltag码偏离openmv镜头中心的相对位移
  13. int distance; //apriltag码距离openmv镜头中心的相对距离
  14. int Sign; //标志位
  15. void Serial_Init(void)
  16. {
  17. RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
  18. RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
  19. GPIO_InitTypeDef GPIO_InitStructure;
  20. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
  21. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
  22. GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  23. GPIO_Init(GPIOA,&GPIO_InitStructure);
  24. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
  25. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
  26. GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  27. GPIO_Init(GPIOA,&GPIO_InitStructure);
  28. USART_InitTypeDef USART_InitStructure ;
  29. USART_InitStructure.USART_BaudRate= 9600; //串口通信波特率为9600
  30. USART_InitStructure.USART_HardwareFlowControl= USART_HardwareFlowControl_None;
  31. USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;
  32. USART_InitStructure.USART_Parity = USART_Parity_No;
  33. USART_InitStructure.USART_StopBits = USART_StopBits_1;
  34. USART_InitStructure.USART_WordLength = USART_WordLength_8b;
  35. USART_Init(USART1,&USART_InitStructure);
  36. USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
  37. NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
  38. NVIC_InitTypeDef NVIC_InitStructure;
  39. NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
  40. NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  41. NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
  42. NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
  43. NVIC_Init(&NVIC_InitStructure);
  44. USART_Cmd(USART1,ENABLE);
  45. }
  46. /**
  47. * 函 数:获取串口接收数据包标志位
  48. * 参 数:无
  49. * 返 回 值:串口接收数据包标志位,范围:0~1,接收到数据包后,标志位置1,读取后标志位自动清零
  50. */
  51. uint8_t Serial_GetRxFlag(void)
  52. {
  53. if (Serial_RxFlag == 1) //如果标志位为1
  54. {
  55. Serial_RxFlag = 0;
  56. return 1; //则返回1,并自动清零标志位
  57. }
  58. return 0; //如果标志位为0,则返回0
  59. }
  60. /**
  61. * 函 数:USART1中断函数
  62. * 参 数:无
  63. * 返 回 值:无
  64. * 注意事项:此函数为中断函数,无需调用,中断触发后自动执行
  65. * 函数名为预留的指定名称,可以从启动文件复制
  66. * 请确保函数名正确,不能有任何差异,否则中断函数将不能进入
  67. */
  68. void USART1_IRQHandler(void)
  69. {
  70. static uint8_t RxState = 0; //定义表示当前状态机状态的静态变量
  71. static uint8_t pRxPacket = 0; //定义表示当前接收数据位置的静态变量
  72. if (USART_GetITStatus(USART1, USART_IT_RXNE) == SET) //判断是否是USART1的接收事件触发的中断
  73. {
  74. uint8_t RxData = USART_ReceiveData(USART1); //读取数据寄存器,存放在接收的数据变量
  75. /*使用状态机的思路,依次处理数据包的不同部分*/
  76. /*当前状态为0,接收数据包包头*/
  77. if (RxState == 0)
  78. {
  79. if (RxData == 0xAA) //如果数据确实是包头
  80. {
  81. RxState = 1; //置下一个状态 //数据包的位置归零
  82. }
  83. }
  84. /*当前状态为1,接收数据包数据*/
  85. else if (RxState == 1)
  86. {
  87. if (RxData == 0xAE)
  88. {
  89. RxState = 2;
  90. pRxPacket = 0;
  91. }
  92. }
  93. /*当前状态为2,接收数据包包尾*/
  94. else if (RxState == 2)
  95. {
  96. receive_data[pRxPacket] = RxData; //将数据存入数据包数组的指定位置
  97. pRxPacket ++; //数据包的位置自增
  98. if (pRxPacket >= 13) //如果收够12个数据
  99. {
  100. RxState = 3; //置下一个状态
  101. }
  102. }
  103. else if(RxState == 3)
  104. {
  105. if (RxData == 0xAC) //如果数据确实是包尾部
  106. {
  107. RxState = 0; //状态归0
  108. Serial_RxFlag = 1; //接收数据包标志位置1,成功接收一个数据包
  109. }
  110. }
  111. tag_id = receive_data[3] << 24 | receive_data[2] << 16 | receive_data[1] << 8 | receive_data[0];//位移运算,将hex16进制转换成int整型
  112. if(receive_data[12] == 0xBF)
  113. {
  114. x_translation = receive_data[7] << 24 | receive_data[6] << 16 | receive_data[5] << 8 | receive_data[4];
  115. }
  116. else if(receive_data[12] == 0xCF)
  117. {
  118. x_translation = -(receive_data[7] << 24 | receive_data[6] << 16 | receive_data[5] << 8 | receive_data[4]);
  119. }
  120. distance = receive_data[11] << 24 | receive_data[10] << 16 | receive_data[9] << 8 | receive_data[8];
  121. USART_ClearITPendingBit(USART1, USART_IT_RXNE); //清除标志位
  122. }
  123. }

Serial.h

  1. #ifndef __SERIAL_H
  2. #define __SERIAL_H
  3. void Serial_Init(void);
  4. uint8_t Serial_GetRxFlag(void);
  5. void USART1_IRQHandler(void);
  6. extern uint8_t receive_data[];
  7. uint8_t Serial_GetRxFlag(void);
  8. extern int tag_id;
  9. extern int x_translation;
  10. extern int distance;
  11. #endif

main.c

  1. #include "stm32f10x.h" // Device header
  2. #include "Delay.h"
  3. #include "Buzzer.h"
  4. #include "OLED.h"
  5. #include "Serial.h"
  6. int main(void)
  7. {
  8. OLED_Init();
  9. Serial_Init();
  10. OLED_ShowString(1,1,"tag_id:");
  11. OLED_ShowString(2,1,"x_trs:");
  12. OLED_ShowString(3,1,"Diace:");
  13. while(1)
  14. {
  15. OLED_ShowNum(1,10,tag_id,2);
  16. OLED_ShowSignedNum(2,9,x_translation,5);
  17. OLED_ShowNum(3,10,distance,5);
  18. }
  19. }

本文内容由网友自发贡献,转载请注明出处:https://www.wpsshop.cn/w/知新_RL/article/detail/399496
推荐阅读
相关标签
  

闽ICP备14008679号