当前位置: 首页 > news >正文

openmv与stm32通信

控制小车视觉循迹使用 OpenMV 往往是不够的。一般使用 OpenMV 对图像进行处理,将处理过后的数据使用串口发送给STM32,使用STM32控制小车行驶。本文主要讲解 OpenMV 模块与 STM32 间的串口通信以及两种循迹方案,分别是划分检测区域和线性回归。

一、OpenMV模块与STM32间的串口通信

1、硬件连接

OpenMV端:UART_RX—P5、UART_TX—P4

STM32端:USART_TX—PA9 、USART_RX—PA10

STM32的TX接OpenMV的RX、STM32的RX接OpenMV的TX。

2、OpenMV 端串口发送数据

uart = UART(3,115200,bits=8, parity=None, stop=1, timeout_char = 1000)FH = bytearray([0x2C,0x12,cx,cy,ch,ci,0x5B])
uart.write(FH)

波特率为115200

0x2C、0x12为帧头,0x5B为桢尾

bytearray([ ])函数:用于把十六进制数据以字节形式存放到字节数组中,以便以数据帧的形式发送出去进行通信。

也可打包为函数形式:

def sending_data(cx,cy,cw,ch):global uart;data = ustruct.pack("<bbhhhhb", 0x2C,                    0x12,                    int(cx),   int(cy),   int(ch),   int(ci),   0x5B)uart.write(data);   #必须要传入一个字节数组sending_data(cx,cy,ch,ci)

3、STM32串口以中断方式接收数据

static u8 Cx=0,Cy=0,Ch=0,Ci=0;
//USART1 全局中断服务函数
void USART1_IRQHandler(void)             
{u8 com_data; u8 i;static u8 RxCounter1=0;static u16 RxBuffer1[10]={0};static u8 RxState = 0;  static u8 RxFlag1 = 0;if( USART_GetITStatus(USART1,USART_IT_RXNE)!=RESET)        //接收中断  {USART_ClearITPendingBit(USART1,USART_IT_RXNE);   //清除中断标志com_data = USART_ReceiveData(USART1);if(RxState==0&&com_data==0x2C)  //0x2c帧头{RxState=1;RxBuffer1[RxCounter1++]=com_data;OLED_Refresh();}else if(RxState==1&&com_data==0x12)  //0x12帧头{RxState=2;RxBuffer1[RxCounter1++]=com_data;}else if(RxState==2){RxBuffer1[RxCounter1++]=com_data;if(RxCounter1>=10||com_data == 0x5B)  //RxBuffer1接受满了,接收数据结束{RxState=3; RxFlag1=1;Cx=RxBuffer1[RxCounter1-5];Cy=RxBuffer1[RxCounter1-4];Ch=RxBuffer1[RxCounter1-3];Ci=RxBuffer1[RxCounter1-2];}}else if(RxState==3)     //检测是否接受到结束标志{if(RxBuffer1[RxCounter1-1] == 0x5B){USART_ITConfig(USART1,USART_IT_RXNE,DISABLE);//关闭DTSABLE中断RxFlag1 = 0; RxCounter1 = 0; RxState = 0;USART_ITConfig(USART1,USART_IT_RXNE,ENABLE);}else   //接收错误{RxState = 0;  RxCounter1=0;for(i=0;i<10;i++){RxBuffer1[i]=0x00;      //将存放数据数组清零}}} else   //接收异常{RxState = 0;  RxCounter1=0;for(i=0;i<10;i++){RxBuffer1[i]=0x00;      //将存放数据数组清零}}}   
}

二、视觉循迹方案一:划分检测区域

该方法和一般循迹模块的原理相同。使用五个红框对图像进行分割,对应五路循迹模块的五个输出。

为提高识别精度,将图像二值化后再进行黑线检测。(若要识别其他颜色的线则需要使用常规的设置阈值法)

import pyb, sensor, image, math, time
from pyb import UART
import ustruct
from image import SEARCH_EX, SEARCH_DS
sensor.set_contrast(1)
sensor.set_gainceiling(16)
clock = time.clock()
uart = UART(3,115200,bits=8, parity=None, stop=1, timeout_char = 1000)
# 划分五个区域
roi1 =     [(0, 40, 20, 40),     #  x y w h 分别对应x、y坐标,框的宽度、高度(35, 40, 20, 40),(70, 40, 10, 10),(105, 40, 20, 40),(140, 40, 20, 40)]     sensor.reset() 
sensor.set_pixformat(sensor.RGB565) 
sensor.set_framesize(sensor.QQVGA)
sensor.skip_frames(time=2000) 
sensor.set_auto_whitebal(True)
sensor.set_auto_gain(False) 
sensor.set_vflip(False)
sensor.set_hmirror(False)
clock = time.clock()GRAYSCALE_THRESHOLD = [(20,100)]
low_threshold = (0, 50)  while(True):clock.tick()blob1=Noneblob2=Noneblob4=Noneblob5=Nonecx=cy=cw=i=0img = sensor.snapshot().lens_corr(strength = 1.7 , zoom = 1.0)img.mean(2)binary_img = img.binary(GRAYSCALE_THRESHOLD) # 二值化blob1 = binary_img.find_blobs([low_threshold],roi=roi1[0])blob2 = binary_img.find_blobs([low_threshold],roi=roi1[1])blob3 = binary_img.find_blobs([low_threshold],roi=roi1[2])blob4 = binary_img.find_blobs([low_threshold],roi=roi1[3])blob5 = binary_img.find_blobs([low_threshold],roi=roi1[4])if blob1:cx = 1if blob2:cy = 1if blob3:cw = 1if blob4:ch = 1if blob5:ci = 1FH = bytearray([0x2C,0x12,cx,cy,ch,ci,0x5B])uart.write(FH)for rec in roi1:img.draw_rectangle(rec, color=(255,0,0))#绘制出roi区域

实验效果:

三、视觉循迹方案二:线性回归

将控制线的偏移距离与角度偏差作为PID控制对象,分别计算其控制输出,最后相加。

下面直接附上详细注释版的代码:

THRESHOLD = (21, 0, -77, 5, -110, 127)  
#定义一个阈值元组用于图像二值化处理。这个阈值将用于将图像转换为二进制图像,用于线检测。
import sensor, image, time  
#导入了用于图像处理的sensor、image模块,以及用于时间相关操作的time模块。
from pyb import LED 
from pid import PID #从pid模块中导入了PID类,用于实现PID控制算法。
import time
from pyb import UART #从pyb模块中导入了UART类,用于串口通信。
import math rho_pid = PID(p=0.37, i=0)    #创建一个PID对象rho_pid,用于控制线的偏移距离。
theta_pid = PID(p=0.001, i=0) #创建一个PID对象theta_pid,用于控制线的角度偏差。
#rho 是从图像原点到直线的垂直距离, theta 是直线与垂直轴之间的角度。LED(1).on()
LED(2).on()
LED(3).on()
uart = UART(3,19200)        # 创建一个UART对象uart,用于串口通信,波特率为19200。
sensor.reset()              # 重置图像传感器
sensor.set_vflip(True)      # 设置图像传感器垂直翻转,即上下颠倒。
sensor.set_hmirror(True)    # 设置图像传感器水平镜像,即左右翻转。
sensor.set_pixformat(sensor.RGB565) # 设置图像传感器的像素格式为RGB565。
sensor.set_framesize(sensor.QQQVGA) # 设置图像传感器的帧大小为QQQVGA。
sensor.skip_frames(time = 2000)     # 跳过2000毫秒的图像帧,使传感器稳定。
clock = time.clock()                # 创建一个时间对象clock,用于计时。while(True):clock.tick() #记录当前时间。img = sensor.snapshot().binary([THRESHOLD]) #获取图像的快照,并将其转换为二值图像,根据之前定义的阈值进行二值化处理。line = img.get_regression([(100,100)], robust = True) #在二值图像中检测线段,返回一个线段对象line。#参数[(100,100)]表示检测线段时使用的区域,robust=True表示使用鲁棒回归算法进行线段拟合。if (line): #如果检测到了线段。rho_err = abs(line.rho())-img.width()/2  #计算线段的偏移距离,即线段的rho值减去图像宽度的一半。if line.theta()>90: #如果线段的角度大于90度。theta_err = line.theta()-180 #计算线段的角度偏差,即线段的角度减去180度。else:theta_err = line.theta() # 小于等于90度 直接将线段的角度作为角度偏差。img.draw_line(line.line(), color = 127) # 在图像上绘制检测到的线段。print(rho_err,line.magnitude(),rho_err) # 打印线段的偏移距离、线段的长度和偏移距离。if line.magnitude()>8: # 如果线段的长度大于8。rho_output = rho_pid.get_pid(rho_err,1) #使用PID控制算法计算线段偏移距离的控制输出。theta_output = theta_pid.get_pid(theta_err,1) #使用PID控制算法计算线段角度偏差的控制输出。output = rho_output + theta_output #将线段偏移距离和角度偏差的控制输出相加。if(output<0): #控制输出小于0,STM32不好处理负数,所以要将计算出的负数取绝对值+100#stm32通过判断其值是否大于100来判断其是否为负数。output = abs(output) + 100  #将控制输出取绝对值并加上100。OUTPUT = str(round(output)) #将控制输出四舍五入转换为字符串。因为UART的write()只接受字符串作为参数,不接受数值类型uart.write(OUTPUT) #将控制输出通过串口发送。uart.write('\r\n') #发送回车换行符。print(OUTPUT)   #打印控制输出。pass

pid.py 文件见OpenMV官方源码:https://book.openmv.cc/project/follow-lines.html

实验效果:


http://www.mrgr.cn/news/30235.html

相关文章:

  • 【神经科学学习笔记】基于分层嵌套谱分割(Nested Spectral Partition)模型分析大脑网络整合与分离的局部指标(二)
  • 点云论文阅读-1-pointnet++
  • 13、DHCP和FTP协议
  • go get 和go install 的区别
  • 修改msyql用户密码及更新mysql密码策略
  • PHPCUSTOM用久了占用变大,请关闭日志功能即可
  • 前端大屏自适应方案
  • 基于GIKT深度知识追踪模型的习题推荐系统源代码+数据库+使用说明,后端采用flask,前端采用vue
  • 阿里云kafka消息写入topic失败
  • jenkins 部署到tomcat
  • 2024最新的软件测试面试八股文(答案+文档)
  • Xmind软件自定义安装,如何安装在指定位置(不修改注册表),修改默认安装到c盘软件的安装位置
  • rpm 与 yum
  • JAVA客户端发送图片给服务端案例
  • JavaWeb笔记整理——Redis
  • 信息收集常用指令
  • 『功能项目』QFrameWorkBug修改器界面【65】
  • TimeSpan(一个简单的计时器)
  • 佰朔资本:pb和pe是什么?股票pb和pe怎么看?
  • apt-get install 安装的tomcat配置
  • 硬件资源从硬编码到设备树
  • 深入解析NVIDIA GH200:Grace Hopper的多样性与性能挑战
  • 基于SpringBoot+Vue的篮球馆会员信息管理系统
  • nodejs child_process 操作git 提交记录 提取git commit信息
  • Vue:加载本地视频
  • C++和OpenGL实现3D游戏编程【连载9】——纹理的镂空显示