前言
通过视觉巡线小车——stm32+openmv(二),已基本实现了减速电机的速度闭环控制。要使小车能够自主巡线,除了能够精准的控制速度之外,还需要得到小车偏离黑线的差值——即位置偏差。本文将通过openmv得到该偏差。
建议参考内容:
系列文章请查看:视觉巡线小车——stm32+openmv系列文章
一、openmv代码
1、初始化外设,如串口等;
2、运行主要代码,拍照,图像二值化处理,线性回归处理,得到黑线与openmv中心线之间的像素点偏差以及偏离角度。
线性回归算法的原理是寻找一条最佳的直线来拟合数据点集。在视 觉巡线中,这些数据点就是二值化图像中代表线条的像素点。算法会计算这些像素点 的平均值、方差等统计量,并通过最小二乘法等来找到一条最佳的直线。
3、将得到数据打包,并发送给stm32。
threshold = (0, 23, -128, 127, -128, 127) # grayscale threshold for dark things...
import sensor, image, time
from pyb import led
from machine import uart
import struct
sensor.reset()
sensor.set_vflip(false) # 设置openmv图像“水平方向进行翻转”
sensor.set_hmirror(false) # 设置openmv图像“竖直方向进行翻转”
sensor.set_pixformat(sensor.rgb565)
sensor.set_framesize(sensor.qqqvga) # 80x60 (4,800 pixels) - o(n^2) max = 2,3040,000.
# 线性回归算法的运算量大,越小的分辨率识别的效果越好,运算速度越快
#sensor.set_windowing([0,20,80,40])
sensor.skip_frames(time = 2000) # warning: if you use qqvga it may take seconds
clock = time.clock() # to process a frame sometimes.
myuart = uart(1, 115200)
# uart(1)是p0-rx p1-tx
myuart.init(115200, bits=8, parity=none, stop=1) #8位数据位,无校验位,1位停止位
def send_data_packet(x, y):
temp = struct.pack(">bbii", #格式为小端模式俩个字符俩个整型
0xaa, #帧头1
0xbb, #帧头2
int(x), # up sample by 4 #数据1
int(y)) # up sample by 4 #数据2
myuart.write(temp)
#串口发送
while(true):
clock.tick()
img = sensor.snapshot().binary([threshold])
''' 截取一张图片,进行 “阈值分割”
阈值分割函数image.binary()对图像进行二值化(binary:二元的;由两部分组成的)
得到的效果是:将阈值颜色变成白色,非阈值颜色变成黑色'''
line = img.get_regression([(100,100)], robust = true)#调用线性回归函数
# 对所有的阈值像素进行线性回归
# 线性回归的效果就是将我们视野中“二值化”分割后的图像回归成一条直线
if (line):
rho_err = abs(line.rho())-img.width()/2
# 计算我们的直线相对于中央位置偏移的距离(偏移的像素)
# abs()函数:返回数字的绝对值 line.rho():返回霍夫变换后的直线p值。
if line.theta()>90:
theta_err = line.theta()-180
else:
theta_err = line.theta()
# 进行坐标的变换:y轴方向为0°,x轴正方向为90°,x轴负方向为-90°
img.draw_line(line.line(), color = 127)
print(rho_err,line.magnitude(),theta_err)
#line.magnitude()返回一个表示“线性回归效果”的值,这个值越大,线性回归效果越好;
# 如果越接近于0,说明我们的线性回归效果越接近于一个圆,效果越差
if line.magnitude()>8:
send_data_packet(rho_err,theta_err)
led(1).off()
else:
led(1).on()
led(2).off()
else:
led(2).on()
pass
#print(clock.fps())
处理前后结果对比:
二、stm32端接收数据
1.配置串口
由于openmv与stm32之间采用串口通讯,所以同样需要在cubemx进行配置:
同理也需要开启中断,这里不再赘述,参考上一篇文章。
2.接收数据并解析
需要加入以下代码,进行初始化:
//全局变量
unsigned char openmv_buf;
int theta_org,rho_org;
//初始化处加入
hal_uart_receive_it(&huart2,&openmv_buf,1);
在串口2中断回调函数中处理如下:
void hal_uart_rxcpltcallback(uart_handletypedef *huart)
{
if(huart->instance == usart2 )
{
// printf("ok\n");
rec_proce(openmv_buf);
hal_uart_receive_it(&huart2,&openmv_buf,1);
}
}
void rec_proce(u8 data)
{
static u8 rxbuffer[10];
static u8 data_cnt = 0;
static u8 state = 0;
if(state==0&&data==0xaa)
{
state=1;
}
else if(state==1&&data==0xbb)
{
state=2;
data_cnt = 0;
}
else if(state==2)
{
rxbuffer[data_cnt++]=data;
if(data_cnt>=8)
{
state = 0;
rho_org = (int)((rxbuffer[0]<<24) | (rxbuffer[1]<<16) | (rxbuffer[2]<<8) | (rxbuffer[3]));
theta_org = (int)((rxbuffer[4]<<24) | (rxbuffer[5]<<16) | (rxbuffer[6]<<8) | (rxbuffer[7]));
printf("%d,%d\n",rho_org,theta_org);
// for(int i=0;i<8;i++) printf("%d",rxbuffer[i]);
// printf("\n\n\n\n");
}
}
else
state = 0;
}
如果要使用printf进行打印输出,则需要加入以下代码,这里以串口3为例,如下:
#include <stdio.h>
int fputc(int ch,file *f)
{
while((usart3->sr & 0x40) == 0);
usart3->dr = (uint8_t)ch;
return ch;
}
总结
通过本文,使用openmv得到中心线偏离黑线的像素点偏差和角度偏差,再将数据打包通过串口发送给stm32,最后在stm32上将数据解析出来,以便后续控制运用。
发表评论