分享缩略图

分享到:
链接已复制
首页> 新闻中心>

【树莓派 OpenCV STM32]智能小车巡线

2025-06-24 11:47:22

来源:新华网

字体:

一、所用材料。

  1. 树莓派4B。
  2. 官方的覆盆子摄像头。
  3. STM32F103C8T6最小系统板。

二、实现功能。

        用OpenCV处理树莓派摄像头中的图像,将图像处理后的数据串口通信给下位机STM32F103C8T6,然后下位机给出控制信号,利用pid算法控制汽车的运动轨迹。如下图所示,硬件连接实物。(本文仅介绍了树莓派与下位机之间的通信部分)

三、实现过程。

 3.1 树莓派。

  1、配置串口。

       可以参考 学习笔记1:UART通信树莓派和STM32 本博客第一章,详细介绍了如何改变串口映射和安装和使用mini串口调试助手。安装minicom后,输入终端。 minicom -D /dev/ttyAMA0。 。之后确实有提示没有权限,此时需要现在终端输入 。sudo chmod 777 /dev/ttyAMA0。再输入 。minicom -D /dev/ttyAMA0。miniocm可以正常打开。

       按照上面博客的步骤,在进行下一步操作之前,确保树莓派和计算机之间的正常通信。

2、调用OpenCV。

       使用Opencv前,可以确认树莓派的通信是否正常,您可以尝试运行以下代码。     如果串口通正常,会间接收到1-100的数字。

import serialimport timeser = serial.Serial('/dev/ttyAMA0',115200)num = 1while True:        ser.write(str(int(num)).encode() + '\r\n')    num += 1    if num > 100:        num = 1            time.sleep(0.2)。

         确定串口通信正常后,OpenCV可用于图像处理,以下是我使用的代码。

         确定串口通信正常后,OpenCV可用于图像处理,以下是我使用的代码。为了使黑色识别效果更好,我在代码中添加了高斯模糊来降低噪音,60࿰选择黑色阈值c;也可以根据具体情况适当改编代码。

import cv2import numpy as npimport serialimport time def main(): # 打开摄像头 cap = cv2.VideoCapture(0) # 检查摄像头是否成功打开 if not cap.isOpened(): print("无法打开摄像头Ř) return ser = serial.Serial('/dev/ttyAMA0',115200) while True: start_time = time.time() # 读取当前帧 ret, frame = cap.read() # 检查帧读取是否正确 if not ret: print("????????") break # 将图片转移到灰度值 gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) #高斯模糊 blurred = cv2.GaussianBlur(gray, (5, 5), 0) # 设置黑色阈值范围 _, threshold = cv2.threshold(blurred, 60, 255, cv2.THRESH_BINARY_INV) # 寻找轮廓 kernel = np.ones((5, 5), np.uint8) opening = cv2.morphologyEx(threshold, cv2.MORPH_OPEN, kernel) _, contours, hierarchy = cv2.findContours(opening, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) # 绘制轮廓 centers = [] for contour in contours: # 计算轮廓的边界值 x, y, w, h = cv2.boundingRect(contour) if w * h > 100: # 只显示较大的轮廓 cv2.rectangle(frame, (x, y), (x+w, y+h), (255, 0, 0), 2) center_x = x + w //2 centers.append(center_x) #水平中点坐标发送黑色域 if centers: message =str(centers[0]).encode() + b'\n' ser.write(message) # 显示原始图像和结果图像 cv2.imshow('Frame', frame) cv2.imshow('Threshold', threshold) # 按q退出 if cv2.waitKey(1) & 0xFF == ord('q'): break time.sleep(max(0,0.05 - (time.time() - start_time))) # 释放摄像头 cap.release() # 关闭所有窗户 cv2.destroyAllWindows()if __name__ == '__main__': main()。       其中,在调用 。       其中,在调用 。 cv2.findContours。 函数时,由于OpenCV版本的问题,返回值的数量可能会不同,会出现。

“ValueError: too many values to unpack (expected 2)”。

的报错。如果在运行中出现这个错误,可以用下一段࿰替换寻找轮廓的代码c;这样可以避免不同版本带来的问题。

# 寻找轮廓try: # OpenCV 4.x及一些3.x版本 contours, hierarchy = cv2.findContours(mask, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)except ValueError: # OpenCV 3.更早版x _, contours, hierarchy = cv2.findContours(mask, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)。

       如果代码运行正确,然后你会得到下图所示的结果 ,一般来说,识别效果还不错,黑色区域的轮廓也很清晰。

       如果代码运行正确,然后你会得到下图所示的结果 ,一般来说,识别效果还不错,黑色区域的轮廓也相当清晰。同时,如果能在电脑串口助手上收到黑色区域的水平中点值,到目前为止,树莓派的配置工作已经圆满结束。

      。

3.2 STM32。

       因为这个博客不涉及控制部分,因此,下位机的配置相对简单,࿰只需要一个简单的串口接收模块c;这里简单的就是󿀌如果您有任何具体问题,请参考我的另一个色块跟踪博客,有初始化配置和串口初始化的详细过程,虽然是F407ZGT6,但在逻辑上与F103C8T6没有太大区别c;附上连接【OpenMV+PID控制二维自由舵色块跟踪。

  1、CubeMX。

  1、CubeMX。

       由于芯片不同�因此,时钟树的配置不同于F4,具体值见下图。

       UART1和USART2都需要在串口配置上打开,UART1用于与树莓派通信,UART2用于与计算机通信,方便中间过程的调参。(USART2配置相同)。

  2、KEIL。

        ①usart.C最后添加串口重定向代码。

int fputc(int ch, FILE *f){   HAL_UART_Transmit(&huart2, (uint8__t *)&ch, 1, 0xfff);  return ch;}int fgetc(FILE *f){   uint8_t ch = 0;  HAL_UART_Receive(&huart2, &ch, 1, 0xfff);  return ch;}。

        ②Usart.加入h中仓库。

        ②usart.加入h中仓库。

#include

        ③在main.串口的初始配置添加到c中的相应位置。

#include #define RxBuffer_MaxSize 256 //最大接收字节数charar RxBuffer[RxBuffer_MaxSize],rx_buf[RxBuffer_MaxSize]; //接收数据uint8_t aRxBuffer; //接收中断缓冲uint8__t Uart1_Rx_Cnt = 0; //接收缓冲计数//* USER CODE BEGIN 2 */HAL_UART_Receive_IT(&huart1, (uint8__t *)&aRxBuffer, 1);/* USER CODE END 2 */。

        ④在main.c后加入串口回调函数。

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart){ UNUSED(huart); if(huart == &{ ////// HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_13); // 有数据可以翻转LED灯 RxBuffer[Uart1____Rx_Cnt++] = aRxBuffer; if((RxBuffer[Uart1____Rx_Cnt-1] == '\n')) { // 检测到帧尾 RxBuffer[Uart1____Rx_Cnt-1] = '\0'; // 替换帧尾的字符串结束符 strcpy(rx_buf, &RxBuffer[0]); // 将数据复制到rx__buf,跳过帧头 printf("%s\r\n", rx_buf); Uart1_Rx_Cnt = 0; // 重置计数器 memset(RxBuffer, 0, sizeof(RxBuffer)); // 清空接收缓冲区 } HAL_UART_Receive_IT(&huart1, (uint8__t *)&aRxBuffer, 1); }}。

       由于帧头࿰没有设置在树莓派的串口发送中c;并将帧尾设置为'\n',因此,串口接收的代码相对简单。如果代码运行正确,而且单片机和电脑之间的通信顺利,那么电脑端也会收到黑色区域的水平中点值,如视频所示。如果代码运行正确,并且单片机与计算机之间的通信顺畅,那么电脑端也会收到黑色区域的水平中点值,如视频所示。

黑域水平中点值传输。

四、结语。       本博客只是智能巡线车视觉的一部分,如果时间允许,将写下整个巡线功能,同时,如果以后有更好的图像处理代码,我会在这个博客中同步修改。如果您在配置过程中遇到任何问题或发现此博客有任何问题,欢迎给我发私信或直接在评论中留言。另外,如果您现在对控制模块感兴趣,不妨去看看我和实验室队友的博客,他写了一些关于驱动电机的内容,在此附上链接。[STM32ƱHAL]控制DengFOC移植的闭环速度。[STM32ƱHAL]控制DengFOC移植的闭环位置。[STM32ƱHAL】I2C+AS5600编码器读取DMA。

【责任编辑:新华网】
返回顶部