19.3-【星曈科技】openmv Hopenmv发送五个uchar Openmv+STM32F103C8T6视觉巡线小车 STM32循迹小车系列教程 使用OpenMV循迹 openMV寻迹与小车控制

功能介绍放开头, 使用便捷无需愁

这是全网最详细、性价比最高的STM32实战项目入门教程,通过合理的硬件设计和详细的视频笔记介绍,硬件使用STM32F103主控资料多方便学习,通过3万字笔记、12多个小时视频、20多章节代码手把手教会你如何开发和调试。让你更快掌握嵌入式系统开发。

V3.3.0-STM32智能小车

视频: https://www.bilibili.com/video/BV16x4y1M7EN/?spm_id_from=333.337.search-card.all.click

V3:HAL库开发、功能:PID速度控制、PID循迹、PID跟随、遥控、避障、PID角度控制、视觉控制、电磁循迹、RTOS等功能。

第19章-OpenMV视觉循迹功能

19.3-openmv发送五个uchar

openmv 通过串口发送数据格式:帧头+一个字节 +一个字节+一个字节+一个字节+一个字节+帧尾。

把上面识别的数据发送出来结合电脑串口上位机调试、显示上位机上

前面我们通过openmv 识别到线的相对位置,那么现在要openmv 把上面的信息发给STM32,然后STM32接受到信息知道线位置才能更好控制电机运动。
如何完成通信 发送什么数据
在这里插入图片描述
如何验证发送正确的数据了

把openmv 的串口连接串口上位机,通过上位机接收数据,查看数据是否正确。
在这里插入图片描述
我们看一下代码,主要看串口部分

#.....省略部分代码...
uart = UART(3,115200,bits=8, parity=None, stop=1, timeout_char = 1000)#初始化串口三、波特率115200 TXD:P4\PB10 RXD:P5\PB11

#.....省略部分代码...
def send_five_uchar(c1,c2,c3,c4,c5):#功能发送五个无符号字符(unsigned char)
    global uart;
    data = ustruct.pack("<BBBBBBBB",#使用了 ustruct.pack() 函数将这些数据打包为二进制格式。使用 "<BBBBBBBB" 作为格式字符串来指定要打包的数据的类型和顺序:
                   0xA5,
                   0xA6,
                   c1,
                   c2,
                   c3,
                   c4,
                   c5,
                   0x5B
                   )
    uart.write(data);#uart.write(data) 将打包好的二进制数据帧写入 UART 发送缓冲区,从而将数据通过串口发送出去
    print(data)#通过 print(data) 打印发送的数据到串行终端,方便调试和确认发送的内容。
 #.....省略部分代码...   
    send_five_uchar(flag[0],flag[1],flag[2],flag[3],flag[4])#把五个数据通过串口发送出去、发送五个无符号字符。

整个工程代码如下

import pyb, sensor, image, math, time
from pyb import UART
import ustruct
from image import SEARCH_EX, SEARCH_DS
import time
import sensor, lcd
#导入需要的库和模块

#使用中可能根据自己情况需要修改的值
#1. GROUND_THRESHOLD 阈值参数 通过工具->机器视觉->阈值编辑器->帧缓冲区 调整出要识别的LAB阈值。
#2.注意是否有下面两句根据自己摄像头调整
  #sensor.set_vflip(True)
  #sensor.set_hmirror(True)


#sensor.set_contrast(1)#设置相机图像对比度。-3+3
#sensor.set_gainceiling(16)#设置相机图像增益上限。2, 4, 8, 16, 32, 64, 128

uart = UART(3,115200,bits=8, parity=None, stop=1, timeout_char = 1000)#初始化串口三、波特率115200 TXD:P4\PB10 RXD:P5\PB11

roi1 =	 [( 20,   105, 10, 10),
          ( 45,   105, 10, 10),
          ( 75,   105, 10, 10),
          ( 105,  105, 10, 10),
          (130,   105, 10, 10)]#定义一个名为roi1的列表,其中包含了5个元组。每个元组代表了一个矩形感兴趣区域在图像上的位置和大小。
#具体而言,每个元组包含了4个数值依次的含义是:ROI左上角点的x坐标、ROI左上角点的y坐标、ROI的宽度、ROI的高度

led = pyb.LED(1) # led = pyb.LED(1)表示led表示红灯。各种状态如下:Red LED = 1, Green LED = 2, Blue LED = 3, IR LEDs = 4.
led.on()         #点亮红灯 板载红灯点亮表示程序得到执行

sensor.reset()#初始化相机传感器。
sensor.set_pixformat(sensor.RGB565)#设置相机模块的像素模式:sensor.RGB565: 16 bits/像素。
sensor.set_framesize(sensor.QQVGA)#设置图像分辨率、如果改变分辨率也要调整ROI区域。摄像头不同、应用场景不同可以选择不同分辨率。这里使用QQVGA可能画质很胡,但是为了兼容不同型号摄像头我们先使用QQVGA 不影响循迹效果
sensor.skip_frames(time=2000)#跳过指定数目的帧。在这里,设置为跳过2000毫秒(即2秒)的帧。这样可以给传感器一些时间进行初始化和自适应调整。
sensor.set_auto_whitebal(True)#设置为自动白平衡模式。这使得摄像头可以根据场景中的光照条件自动调整图像的白平衡,从而保持图像色彩更加准确和自然。
sensor.set_auto_gain(False)#关闭自动增益模式。通常情况下,开启自动增益会帮助摄像头自动调整亮度,并在低亮度环境下提高图像清晰度。通过设置为False,禁用了这个功能,使用固定增益值。

# 注意是否有下面两句根据自己摄像头调整
sensor.set_vflip(True)  #垂直方向翻转 根据自己摄像头和模块安装位置调整 !!!重要不同摄像头是否需要镜像根据实际情况定,如果不需要镜像需要注释掉
sensor.set_hmirror(True) #水平方向反转 根据自己摄像头和模块安装位置调整 !!!重要不同摄像头是否需要镜像根据实际情况定,如果不需要镜像需要注释掉


#lcd.init() #初始化lcd屏幕

#最好根据自己情况设置一下!!!
#GROUND_THRESHOLD=(0, 8, -128, 23, -128, 80)#阈值参数,用于在图像处理中对标物体进行颜色识别分割。在OpenMV IDE软件 工具->机器视觉->阈值编辑器->帧缓冲区 调整出要识别的LAB阈值。
GROUND_THRESHOLD=(0, 30, -22, 23, -128, 80)#阈值参数,用于在图像处理中对标物体进行颜色识别分割。在OpenMV IDE软件 工具->机器视觉->阈值编辑器->帧缓冲区 调整出要识别的LAB阈值。
def send_five_uchar(c1,c2,c3,c4,c5):#功能发送五个无符号字符(unsigned char)
    global uart;
    data = ustruct.pack("<BBBBBBBB",#使用了 ustruct.pack() 函数将这些数据打包为二进制格式。使用 "<BBBBBBBB" 作为格式字符串来指定要打包的数据的类型和顺序:
                   0xA5,
                   0xA6,
                   c1,
                   c2,
                   c3,
                   c4,
                   c5,
                   0x5B
                   )
    uart.write(data);#uart.write(data) 将打包好的二进制数据帧写入 UART 发送缓冲区,从而将数据通过串口发送出去
    print(data)#通过 print(data) 打印发送的数据到串行终端,方便调试和确认发送的内容。

while(True):
    data=0
    blob1=None
    blob2=None
    blob3=None
    blob4=None
    blob5=None
    flag = [0,0,0,0,0]
    img = sensor.snapshot().lens_corr(strength = 1.7 , zoom = 1.0)#对获取到的图像执行镜头校正的操作。
    blob1 = img.find_blobs([GROUND_THRESHOLD], roi=roi1[0])#在图像中通过颜色阈值 GROUND_THRESHOLD1 检测 roi1[0] 区域内的色块,并将检测结果赋值给 blob1。
    blob2 = img.find_blobs([GROUND_THRESHOLD], roi=roi1[1])#同理
    blob3 = img.find_blobs([GROUND_THRESHOLD], roi=roi1[2])
    blob4 = img.find_blobs([GROUND_THRESHOLD], roi=roi1[3])
    blob5 = img.find_blobs([GROUND_THRESHOLD], roi=roi1[4])

    if blob1:#如果roi1区域内找到阈值色块 就会赋值flag[0]1
        flag[0] = 1
    if blob2:
        flag[1] = 1
    if blob3:
        flag[2] = 1
    if blob4:
        flag[3] = 1
    if blob5:
        flag[4] = 1
 #   print(flag[0],flag[1],flag[2],flag[3],flag[4])#把数据打印在串行终端方便调试
    send_five_uchar(flag[0],flag[1],flag[2],flag[3],flag[4])#把五个数据通过串口发送出去、发送五个无符号字符。

    for rec in roi1:#遍历所有感兴趣的区域roi1 并绘制color=(255,0,0)颜色
        img.draw_rectangle(rec, color=(255,0,0))
        #lcd.display(img) # Take a picture and display the image.#将图像显示在lcd中

openmv在线运行(即板子需要连接openmv IDE 控制运行)

使用openmv连接外部供电时候**一定不能接错接反电源
在这里插入图片描述
通过SSCOM查看发送的数据 是否和摄像头应该识别的状态一致
在这里插入图片描述

如何脱机运行

然后就可以脱机运行(openmv 板子不连接 openmv IDE 上电自动运行)

  1. 用USB 把openmv 连接到电脑,点击连接

  2. 点击连接
    在这里插入图片描述
    点击否保留我们的注释内容
    在这里插入图片描述
    然后我们在openmv 对应的U盘就会看到多的main文件,打开可以可以看到是刚刚代码
    在这里插入图片描述

现在我们拔掉USB进行如下连接

然后打开串口软件查看接收到的数据。
在这里插入图片描述
这里我们openmv发送了数据,下面我们使用STM32进行接受

可以使用STM32F103C8T6的GPIO口来控制共阳极三位数码管显示数字。由于共阳极三位数码管需要同时驱动三个数码管,因此需要使用定时器中断来实现动态扫描显示。下面是一个简单的代码实现: 1.首先需要在STM32F103C8T6上配置GPIO口为输出,配置定时器中断扫描三个数码管。 ```c #include "stm32f10x.h" #define uchar unsigned char #define uint unsigned int // 定义数码管端口,共阳极三位数码管需要同时输出三个数码管的段选和位选信号 #define DIG0_ON GPIOA->BRR = GPIO_Pin_0 #define DIG1_ON GPIOA->BRR = GPIO_Pin_1 #define DIG2_ON GPIOA->BRR = GPIO_Pin_2 #define DIG0_OFF GPIOA->BSRR = GPIO_Pin_0 #define DIG1_OFF GPIOA->BSRR = GPIO_Pin_1 #define DIG2_OFF GPIOA->BSRR = GPIO_Pin_2 #define SEG_A_ON GPIOB->BRR = GPIO_Pin_1 #define SEG_B_ON GPIOC->BRR = GPIO_Pin_5 #define SEG_C_ON GPIOC->BRR = GPIO_Pin_4 #define SEG_D_ON GPIOC->BRR = GPIO_Pin_3 #define SEG_E_ON GPIOC->BRR = GPIO_Pin_2 #define SEG_F_ON GPIOC->BRR = GPIO_Pin_1 #define SEG_G_ON GPIOC->BRR = GPIO_Pin_0 #define SEG_DP_ON GPIOD->BRR = GPIO_Pin_2 #define SEG_A_OFF GPIOB->BSRR = GPIO_Pin_1 #define SEG_B_OFF GPIOC->BSRR = GPIO_Pin_5 #define SEG_C_OFF GPIOC->BSRR = GPIO_Pin_4 #define SEG_D_OFF GPIOC->BSRR = GPIO_Pin_3 #define SEG_E_OFF GPIOC->BSRR = GPIO_Pin_2 #define SEG_F_OFF GPIOC->BSRR = GPIO_Pin_1 #define SEG_G_OFF GPIOC->BSRR = GPIO_Pin_0 #define SEG_DP_OFF GPIOD->BSRR = GPIO_Pin_2 // 数码管显示数据表,共阳极三位数码管需要同时输出三个数码管的段选和位选信号 uchar dis_table[16]={{0,0,0}, {1,0,0}, {0,1,0}, {1,1,0}, {0,0,1}, {1,0,1}, {0,1,1}, {1,1,1}, {0,0,0}, {1,0,0}, {0,1,0}, {1,1,0}, {0,0,1}, {1,0,1}, {0,1,1}, {1,1,1}}; // 定义定时器中断函数 void TIM1_UP_IRQHandler(void) { static uint cnt = 0; static uint num_cnt = 0; static uint num = {0,0,0}; if(TIM_GetITStatus(TIM1,TIM_IT_Update)!=RESET) { TIM_ClearITPendingBit(TIM1,TIM_IT_Update); if(cnt == 0) { // 计算当前位数字和位选信号 num_cnt++; if(num_cnt > 2) num_cnt = 0; num[num_cnt] = num[num_cnt] + 1; if(num[num_cnt] > 9) num[num_cnt] = 0; // 输出数字和位选信号 DIG0_OFF; DIG1_OFF; DIG2_OFF; SEG_A_OFF; SEG_B_OFF; SEG_C_OFF; SEG_D_OFF; SEG_E_OFF; SEG_F_OFF; SEG_G_OFF; SEG_DP_OFF; if(num_cnt == 0) { if(num != 0) SEG_A_ON; SEG_B_ON; if(num != 1 && num != 4) SEG_C_ON; if(num != 0 && num != 1 && num != 7) SEG_D_ON; if(num == 0 || num == 2 || num == 6 || num == 8) SEG_E_ON; if(num != 1 && num != 2 && num != 3 && num != 7) SEG_F_ON; if(num != 0 && num != 1 && num != 7) SEG_G_ON; if(num == 0) SEG_DP_ON; DIG0_ON; } else if(num_cnt == 1) { if(num != 0) SEG_A_ON; if(num != 2 && num != 6 && num != 8 && num != 0) SEG_B_ON; if(num != 1 && num != 4 && num != 7) SEG_C_ON; if(num != 1 && num != 4) SEG_D_ON; if(num != 0 && num != 1 && num != 7) SEG_E_ON; if(num != 1 && num != 2 && num != 3 && num != 7 && num != 0) SEG_F_ON; if(num != 0 && num != 1 && num != 7) SEG_G_ON; DIG1_ON; } else if(num_cnt == 2) { if(num != 0) SEG_A_ON; if(num != 2 && num != 6 && num != 8 && num != 0) SEG_B_ON; if(num != 1 && num != 4 && num != 7) SEG_C_ON; if(num != 1 && num != 4) SEG_D_ON; if(num != 0 && num != 1 && num != 7) SEG_E_ON; if(num != 1 && num != 2 && num != 3 && num != 7 && num != 0) SEG_F_ON; if(num != 0 && num != 1 && num != 7) SEG_G_ON; DIG2_ON; } } cnt++; if(cnt > 100) cnt = 0; } } int main(void) { GPIO_InitTypeDef GPIO_InitStructure; TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; NVIC_InitTypeDef NVIC_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB | RCC_APB2Periph_GPIOC | RCC_APB2Periph_GPIOD, ENABLE); RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_4 | GPIO_Pin_5; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOB, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_4 | GPIO_Pin_5; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOC, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOD, &GPIO_InitStructure); // 定时器TIM2配置,设置周期为1ms TIM_TimeBaseStructure.TIM_Period = 999; TIM_TimeBaseStructure.TIM_Prescaler = 71; TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseStructure.TIM_ClockDivision = 0; TIM_TimeBaseStructure.TIM_RepetitionCounter = 0; TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure); // 定时器TIM2中断配置 NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE); TIM_Cmd(TIM2,ENABLE); while(1); } ``` 2.配置完成后,就可以通过修改num数组的值来改变三位数码管显示的数字了。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

滑水滑成滑头

金主爸爸的打赏是我前进的最大

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值