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

news2024/9/20 6:19:59

一、所用材料

  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编码器

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1957489.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

备忘录系统

目录 一、 系统简介 1.简介 2需求分析 3 编程环境与工具 二、 系统总体设计 1 系统的功能模块图。 2 各功能模块简介 3项目结构 4 三、 主要业务流程 &#xff08;1&#xff09;用户及管理员登录流程图 &#xff08;2&#xff09;信息添加流程 &#xff0…

强烈推荐这三款IOS应用,让你的生活更美好

Dino记账 Dino记账是一款结合了简洁设计和强大功能的记账应用&#xff0c;它通过多维度图表帮助用户轻松掌握金钱流向。应用界面明亮且配色突出&#xff0c;使得记录内容易于阅读&#xff0c;让记账和管理账目变得更加简单。 主要特性&#xff1a; 极简风格与易用性&#xff1…

史上最全Spring的@Transactional不生效的12大场景

一、事务不生效 1、访问权限的问题 在Spring框架中&#xff0c;AbstractFallbackTransactionAttributeSource是用于确定一个给定的方法是否应该被事务管理的一个抽象类。它的computeTransactionAttribute方法用于计算并返回一个方法的TransactionAttribute。computeTransacti…

SD NAND存储卡:小尺寸下的大容量存储

SD NAND是一种基于NAND闪存技术的存储设备&#xff0c;采用SD卡形式&#xff0c;具备高存储容量、高速度和高可靠性的特点&#xff0c;广泛应用于嵌入式系统和消费电子产品中。 在如今数据驱动的世界&#xff0c;存储技术的发展至关重要。MK米客方德作为存储芯片行业的领先者&…

C++(week14): C++提高:(一)面向对象设计:设计原则、设计模式

文章目录 一、面向对象设计的概念4.统一建模语言&#xff1a;UML语言StartUML 二、类与类之间的关系0.总结(1)类与类的五种关系(2)区别(3)面向对象 vs 基于对象 1.继承 (泛化耦合)2.组合 (Composition)3.聚合 (Aggregation)4.关联(1)双向关联(2)单向关联 5.依赖 (Dependency) 三…

JNDI注入-RMI和Reference

参考博客&#xff1a; JNDI注入与动态类加载 JNDI Java命名和接口目录为用Java编程语言编写的应用程序提供命名和目录功能。 可以通过一种通用方式访问各种服务&#xff0c;类似通过名字查找对象的功能&#xff0c;和RMI有点类似。 原生JNDI支持RMI&#xff0c;LDAP&#…

2024最新前端学习路线指南!

2024最新前端学习路线指南&#xff01; 如果你正在寻找一份全面的前端学习路线图&#xff0c;那么这份精心打造的学习大纲恰好符合您的需求。无论您是新手还是经验丰富的开发者&#xff0c;这份路线图都能够帮助您系统地掌握前端开发的关键知识点&#xff0c;并在实践中不断提…

实验2-3-7 阶梯电价

//实验2-3-7 阶梯电价 /*为了提倡居民节约用电&#xff0c;某省电力公司执行“阶梯电价”&#xff0c; 安装一户一表的居民用户电价分为两个“阶梯”&#xff1a; 月用电量50千瓦时&#xff08;含50千瓦时&#xff09;以内的&#xff0c;电价为0.53元/千瓦时&#xff1b; 超过5…

java实现加水印功能

1-Word加水印 实现原理&#xff1a; ● 通过页眉页脚生成文字水印或图片水印&#xff0c;然后设置页眉页脚的高度以及旋转角度来设置水印。 源代码&#xff1a; 1、引入pom依赖 <dependency><groupId>org.apache.poi</groupId><artifactId>poi-ooxml…

家具购物小程序的设计

管理员账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;用户管理&#xff0c;家具分类管理&#xff0c;家具新品管理&#xff0c;订单管理&#xff0c;系统管理 微信端账号功能包括&#xff1a;系统首页&#xff0c;家具新品&#xff0c;家具公告&#xff0…

给配置环境变量的小白们的一个tips

告诉什么也不懂的计算机小白包括我&#xff0c;配置环境变量的时候&#xff0c;系统变量配置好之后&#xff0c;path变量一定要去users里加&#xff0c;不要在系统变量里加&#xff0c;不然查版本号的时候你就会像我一样在命令行里傻傻的 你加到系统变量的path后&#xff0c;在…

Linux网络——深入理解传入层协议TCP

目录 一、前导知识 1.1 TCP协议段格式 1.2 TCP全双工本质 二、三次握手 2.1 标记位 2.2 三次握手 2.3 捎带应答 2.4 标记位 RST 三、四次挥手 3.1 标记位 FIN 四、确认应答(ACK)机制 五、超时重传机制 六 TCP 流量控制 6.1 16位窗口大小 6.2 标记位 PSH 6.3 标记…

昇思25天学习打卡营第10天|xiaoyushao

从今天开始做一些实践应用&#xff0c;今天分享的是FCN图像语义分割。 全卷积网络&#xff08;Fully Convolutional Networks&#xff0c;FCN&#xff09;是UC Berkeley的Jonathan Long等人于2015年在Fully Convolutional Networks for Semantic Segmentation一文中提出的用于图…

值得买科技与MiniMax达成官方合作伙伴关系,共建融合生态

7月29日&#xff0c;值得买科技与大模型公司MiniMax宣布达成官方合作伙伴关系。 MiniMax旗下大模型产品海螺AI现已接入值得买“消费大模型增强工具集”&#xff0c;基于海螺AI比价策略&#xff0c;用户可通过海螺AI“悬浮球”功能实现快速比价及跳转购买。 此次合作也标志着值…

2023河南萌新联赛第(二)场 南阳理工学院

A. 国际旅行Ⅰ 题目&#xff1a; 思路&#xff1a; 因为题意上每个国家可以相互到达&#xff0c;所以只需要排序&#xff0c;输出第k小的值就可以了。 AC代码&#xff1a; #include<bits/stdc.h> #define int long long #define IOS ios::sync_with_stdio(0);cin.tie…

springboot短视频推荐系统-计算机毕业设计源码21503

摘 要 本论文基于协同过滤算法&#xff0c;旨在设计并实现一种基于SpringBoot框架的短视频推荐系统。该系统主要分为平台用户和管理员两类角色&#xff0c;用户可以注册、登录、浏览短视频内容&#xff0c;并根据个人兴趣收藏喜爱的视频。管理员则可以管理系统数据、用户和内容…

3.1.数据增广

数据增广 ​ 以图片为例&#xff0c;在不同的灯光&#xff0c;色温&#xff0c;以及灯光反射的影响下&#xff0c;对识别可能会造成很大影响。这时候我们希望样本有更多的多样性&#xff0c;则可以在语言里面加入各种不同的背景噪音&#xff0c;或者改变图片的颜色和形状 1.常…

【2024蓝桥杯/C++/B组/进制】

题目 代码 #include <bits/stdc.h> using namespace std;// 定义一个字符串 str&#xff0c;其内容为 "8100178706957568" string str "8100178706957568";// 函数 check 用于检查传入的字符串是否全部由数字组成 bool check(const string& st…

Java(二十七)---二叉搜索树以及Map和Set

文章目录 前言1.二叉搜索树1.1.概念1.2.操作--- 插入1.3.操作---搜索1.4.操作---删除1.6.性能分析1.7 和 java 类集的关系 2.搜索2.1.概念和场景2.2.模型 3.Map的使用3.1.关于Map的说明3.2.Map.Entry<K,V>的说明3.3.Map中常用的方法3.4.TreeMap的使用案例 4.Set的使用4.1…

探索 Milvus 存储系统:如何评估和优化 Milvus 存储性能

欢迎来到探索 Milvus 系列。Milvus 是一款支持水平扩展和具备出色性能的开源向量数据库。Milvus 的核心是其强大的存储系统&#xff0c;是数据持久化和存储的关键基础。该系统包括几个关键组成部分&#xff1a;元数据存储&#xff08;meta storage&#xff09;、消息存储&#…