视觉巡线小车——STM32+OpenMV(三)

目录

前言

一、OpenMV代码

二、STM32端接收数据

1.配置串口

2.接收数据并解析

总结



前言

通过视觉巡线小车------STM32+OpenMV(二),已基本实现了减速电机的速度闭环控制。要使小车能够自主巡线,除了能够精准的控制速度之外,还需要得到小车偏离黑线的差值------即位置偏差。本文将通过OpenMV得到该偏差。

建议参考内容:

OpenMV巡线小车 | 星瞳科技

项目实例 · OpenMV中文入门教程


一、OpenMV代码

1、初始化外设,如串口等;

2、运行主要代码,拍照,图像二值化处理,线性回归处理,得到黑线与OpenMV中心线之间的像素点偏差以及偏离角度。

线性回归算法的原理是寻找一条最佳的直线来拟合数据点集。在视 觉巡线中,这些数据点就是二值化图像中代表线条的像素点。算法会计算这些像素点 的平均值、方差等统计量,并通过最小二乘法等来找到一条最佳的直线。

3、将得到数据打包,并发送给STM32。

python 复制代码
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)
# OpenMV RT 只有串口UART(1),对应P4-TX P5-RX; OpenMV4 H7 Plus, OpenMV4 H7, OpenMV3 M7 的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.接收数据并解析

需要加入以下代码,进行初始化:

cpp 复制代码
//全局变量
unsigned char OpenMV_Buf;
int theta_org,rho_org;


//初始化处加入
HAL_UART_Receive_IT(&huart2,&OpenMV_Buf,1);

在串口2中断回调函数中处理如下:

cpp 复制代码
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;
    /* 帧头1 */
    if(state==0&&data==0xAA)
    {
        state=1;
    }
    /* 帧头2 */
    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为例,如下:

cpp 复制代码
#include <stdio.h>
int fputc(int ch,FILE *f)
{
    while((USART3->SR & 0x40) == 0);
    USART3->DR = (uint8_t)ch;
    return ch;
}

总结

通过本文,使用OpenMV得到中心线偏离黑线的像素点偏差和角度偏差,再将数据打包通过串口发送给STM32,最后在STM32上将数据解析出来,以便后续控制运用。

相关推荐
cjy_Somnr2 小时前
keil5报错显示stm32的SWDIO未连接不能烧录
stm32·单片机·嵌入式硬件
Lay_鑫辰3 小时前
西门子诊断-状态和错误位(“轴”工艺对象 V1...3)
服务器·网络·单片机·嵌入式硬件·自动化
无垠的广袤5 小时前
【工业树莓派 CM0 NANO 单板计算机】本地部署 EMQX
linux·python·嵌入式硬件·物联网·树莓派·emqx·工业物联网
雲烟7 小时前
嵌入式设备EMC安规检测参考
网络·单片机·嵌入式硬件
泽虞7 小时前
《STM32单片机开发》p7
笔记·stm32·单片机·嵌入式硬件
田甲8 小时前
【STM32】 数码管驱动
stm32·单片机·嵌入式硬件
up向上up8 小时前
基于51单片机垃圾箱自动分类加料机快递物流分拣器系统设计
单片机·嵌入式硬件·51单片机
纳祥科技17 小时前
Switch快充方案,内置GaN,集成了多个独立芯片
单片机
单片机日志18 小时前
【单片机毕业设计】【mcugc-mcu826】基于单片机的智能风扇系统设计
stm32·单片机·嵌入式硬件·毕业设计·智能家居·课程设计·电子信息
松涛和鸣19 小时前
从零开始理解 C 语言函数指针与回调机制
linux·c语言·开发语言·嵌入式硬件·排序算法