视觉巡线小车——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上将数据解析出来,以便后续控制运用。

相关推荐
yutian06068 小时前
Keil MDK下载程序后MCU自动重启设置
单片机·嵌入式硬件·keil
析木不会编程11 小时前
【小白51单片机专用教程】protues仿真独立按键控制LED
单片机·嵌入式硬件·51单片机
枯无穷肉14 小时前
stm32制作CAN适配器4--WinUsb的使用
stm32·单片机·嵌入式硬件
不过四级不改名67715 小时前
基于HAL库的stm32的can收发实验
stm32·单片机·嵌入式硬件
嵌入式科普15 小时前
十一、从0开始卷出一个新项目之瑞萨RA6M5串口DTC接收不定长
c语言·stm32·cubeide·e2studio·ra6m5·dma接收不定长
嵌入式大圣15 小时前
单片机UDP数据透传
单片机·嵌入式硬件·udp
云山工作室16 小时前
基于单片机的视力保护及身姿矫正器设计(论文+源码)
stm32·单片机·嵌入式硬件·毕业设计·毕设
嵌入式-老费16 小时前
基于海思soc的智能产品开发(mcu读保护的设置)
单片机·嵌入式硬件
qq_3975623117 小时前
MPU6050 , 设置内部低通滤波器,对于输出数据的影响。(简单实验)
单片机
liyinuo201718 小时前
嵌入式(单片机方向)面试题总结
嵌入式硬件·设计模式·面试·设计规范