当前位置: 代码网 > it编程>硬件开发>stm32 > 【树莓派+OpenCV+STM32】智能小车巡线_提取线路数据并通过串口通信传输

【树莓派+OpenCV+STM32】智能小车巡线_提取线路数据并通过串口通信传输

2024年07月28日 stm32 我要评论
本博客只是智能巡线小车中视觉的一部分,后续如果时间允许的话会将整个巡线的功能都写下来,同时之后如果有更好的图像处理代码我也会同步在此篇博客中修改。大家如果在配置过程中遇到什么问题或者发现此博客有任何问题,欢迎私信我或者直接在评论里留言言。

一、所用材料

  1. 树莓派4b
  2. 树莓派官方摄像头
  3. stm32f103c8t6最小系统板

二、实现功能

        在树莓派上用opencv对摄像头中的图像进行处理,将图像处理后的数据通过串口通信给到下位机stm32f103c8t6,再由下位机给出控制信号,利用pid算法实现对小车运动轨迹的控制。硬件连接实物如下图所示。(本文章只讲述到树莓派与下位机之间通信的部分)

三、实现过程

 3.1 树莓派

  1、配置串口

       可以参考 学习笔记一:树莓派与stm32的uart通信 这篇博客的第一章,里面详细讲述了如何改变串口的映射和mini串口调试助手的安装及使用。其中在我安装好minicom后,在终端输入 minicom -d /dev/ttyama0 后确实出现了提示没有权限的情况,这个时候需要现在终端输入 sudo chmod 777 /dev/ttyama0 再输入 minicom -d /dev/ttyama0 就可以正常打开miniocm了。

       按照上面这篇博客的步骤,确保树莓派和电脑之间可以正常通信后再进行下一步操作。

2、调用opencv

       在使用opencv前可以再确认一下树莓派的通信是否正常,可以试着运行下面的代码。     如果串口通正常,将会间接收到从1-100的数字。

import serial
import time

ser = serial.serial('/dev/ttyama0',115200)
num = 1

while true:
    
    ser.write(str(int(num)).encode() + '\r\n')
    num += 1
    if num > 100:
        num = 1
        
    time.sleep(0.2)

         在确定串口通信正常后,就可以用opencv来进行图像处理,在下面呈上我使用的代码。为了使对黑色的识别效果更好,我在代码中加入了高斯模糊来减小噪声,黑色的阈值选定的是60,大家也可以根据具体情况来适当改编代码。

import cv2
import numpy as np
import serial
import 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)” 的报错。如果在运行中出现了这个报错,可以将寻找轮廓的代码换成下面这段,这样就可以避免因版本不同而带来的问题。

# 寻找轮廓
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

       因为本博客不涉及到控制部分,所以下位机的配置就相对比较简单了,只需要简单的串口接收模块就可以,这里就简单带过,具体有问题的可以参考我的另一篇色块追踪的博客,里面有初始化配置和串口初始化的详细过程,虽然是f407zgt6的,但是逻辑上和f103c8t6没有太大区别,在此附上连接【openmv+stm32】pid控制二维自由舵机色块追踪

  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, 0xffff);
  return ch;
}
int fgetc(file *f)
{
  uint8_t ch = 0;
  hal_uart_receive(&huart2, &ch, 1, 0xffff);
  return ch;
}

        ②在usart.h中加入库

#include <stdio.h>

        ③在main.c中相应的地方加入串口初始配置

#include <string.h>
 
#define rxbuffer_maxsize  256     //最大接收字节数
char 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 == &huart1){
//      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);
    }
}

       由于在树莓派的串口发送中并没有设置帧头,并将帧尾设置成了'\n',所以串口接收的代码相对较简单。如果代码运行无误,且单片机与电脑间的通信顺利,则电脑端也将会收到黑色区域的水平中点值,正如如视频中所示。

黑色域水平中点值传输

四、结语

       本博客只是智能巡线小车中视觉的一部分,后续如果时间允许的话会将整个巡线的功能都写下来,同时之后如果有更好的图像处理代码我也会同步在此篇博客中修改。大家如果在配置过程中遇到什么问题或者发现此博客有任何问题,欢迎私信我或者直接在评论里留言。另外,如果大家现在就对控制模块感兴趣的话,不妨去看下我同实验室队友‘南极熊ii’的博客,他写过一些关于驱动电机的内容,在此附上链接。

[stm32+hal]dengfoc移植之闭环速度控制

[stm32+hal]dengfoc移植之闭环位置控制

【stm32+hal】i2c+dma读取as5600编码器

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com