基于STM32的图形识别智能跟踪小车设计

news2024/10/25 9:30:51

引言

本项目设计了一个基于STM32的图形识别智能跟踪小车,能够通过摄像头识别特定图形或标志,并自动跟随这些图形进行移动。系统结合了摄像头模块和图像处理算法,实现了对前方物体的识别与跟踪。同时,小车具备避障功能,并通过电机驱动模块实现灵活的运动控制。此设计适用于智能机器人、自动导航以及教学实验等场景。

环境准备

1. 硬件设备
  • STM32F103C8T6 开发板(或其他 STM32 系列)
  • OV7670 摄像头模块(用于采集图像)
  • L298N 电机驱动模块(用于控制电机)
  • 直流电机和车轮(用于机器人运动)
  • 超声波传感器(如 HC-SR04,用于避障)
  • OLED 显示屏(用于显示状态)
  • 面包板和杜邦线
  • USB-TTL 串口调试工具
2. 软件工具
  • STM32CubeMX:用于初始化 STM32 外设。
  • Keil uVision 或 STM32CubeIDE:用于编写和下载代码。
  • OpenCV 或其他图像处理库(在PC端用于图像调试)。
  • ST-Link 驱动程序:用于下载程序到 STM32。

项目实现

1. 硬件连接
  • 摄像头模块连接:将 OV7670 摄像头的 SDA、SCL、数据引脚连接到 STM32 的 DCMI 接口或 GPIO 引脚,用于捕获图像信号。
  • 电机驱动连接:将 L298N 电机驱动模块的输入引脚连接到 STM32 的 GPIO(如 PA3、PA4、PA5、PA6),用于控制电机的前进、后退、左转和右转。
  • 超声波传感器连接:将 HC-SR04 的 Trig 和 Echo 引脚连接到 STM32 的 GPIO(如 PA0 和 PA1),用于测距和避障。
  • OLED 显示屏连接:将 OLED 的 SDA 和 SCL 引脚连接到 STM32 的 I2C 接口(如 PB6 和 PB7),用于显示当前小车状态。
  • 电源管理:将电池接入电机驱动模块,并为 STM32 和摄像头模块提供稳定的电源。
2. STM32CubeMX 配置
  • 打开 STM32CubeMX,选择你的开发板型号。
  • 配置系统时钟为 HSI,确保系统稳定运行。
  • 配置 GPIO 引脚用于电机控制和超声波传感器。
  • 配置 I2C 用于与 OLED 显示屏通信。
  • 配置 DCMI 接口或 GPIO 用于摄像头模块数据传输。
  • 配置 TIM1 生成 PWM 信号,用于控制电机速度。
  • 生成代码,选择 Keil 或 STM32CubeIDE 作为工具链。
3. 编写主程序

在生成的项目基础上,编写图像采集、识别处理、跟踪算法、电机控制和避障的代码。以下是图形识别智能跟踪小车的基本代码示例:

#include "stm32f1xx_hal.h"
#include "camera.h"
#include "motor.h"
#include "ultrasonic.h"
#include "oled.h"

// 定义避障阈值和运动速度
#define OBSTACLE_DISTANCE 15  // 当距离小于15cm时避障
#define PWM_SPEED 80          // 电机默认速度

// 函数声明
void System_Init(void);
void Detect_Obstacle(void);
void Capture_Image(void);
void Process_Image(void);
void Follow_Object(void);
void Control_Motor(uint8_t left_speed, uint8_t right_speed, int8_t left_dir, int8_t right_dir);
void Display_Status(const char *status);

// 全局变量
uint32_t distance = 0;
uint8_t object_detected = 0;  // 图形识别标志

void System_Init(void)
{
    HAL_Init();
    SystemClock_Config();
    MX_GPIO_Init();
    MX_I2C1_Init();
    MX_TIM1_Init();
    MX_DCMI_Init();
    
    OLED_Init();
    Ultrasonic_Init();
    Motor_Init();
    Camera_Init();
    
    OLED_ShowString(0, 0, "Tracking Car Ready");
}

// 检测障碍物并进行避障
void Detect_Obstacle(void)
{
    distance = Ultrasonic_Measure();
    if (distance < OBSTACLE_DISTANCE)
    {
        Control_Motor(0, 0, 0, 0);  // 停止电机
        Display_Status("Obstacle Detected");
        HAL_Delay(1000);  // 等待1秒钟
        Control_Motor(PWM_SPEED, PWM_SPEED, -1, -1);  // 后退
        HAL_Delay(500);  // 后退0.5秒
        Control_Motor(0, 0, 0, 0);  // 停止
    }
}

// 捕获图像
void Capture_Image(void)
{
    Camera_Capture();  // 使用 OV7670 模块捕获一帧图像
    Display_Status("Image Captured");
}

// 处理图像以检测特定对象
void Process_Image(void)
{
    object_detected = Camera_ProcessImage();  // 使用图像处理算法检测特定图形
    if (object_detected)
    {
        Display_Status("Object Detected");
    }
    else
    {
        Display_Status("No Object");
    }
}

// 跟随检测到的目标
void Follow_Object(void)
{
    if (object_detected)
    {
        // 假设检测到的物体在视野左侧,控制小车向左转
        Control_Motor(PWM_SPEED, PWM_SPEED, 1, -1);  // 左转
        Display_Status("Following Object");
    }
    else
    {
        Control_Motor(0, 0, 0, 0);  // 停止
        Display_Status("Searching Object");
    }
}

// 控制电机
void Control_Motor(uint8_t left_speed, uint8_t right_speed, int8_t left_dir, int8_t right_dir)
{
    Motor_SetSpeed(left_speed, right_speed);
    Motor_SetDirection(left_dir, right_dir);
}

// 显示状态
void Display_Status(const char *status)
{
    OLED_Clear();
    OLED_ShowString(0, 0, status);
}

int main(void)
{
    System_Init();
    
    while (1)
    {
        Detect_Obstacle();  // 避障
        Capture_Image();    // 捕获图像
        Process_Image();    // 处理图像
        Follow_Object();    // 跟随检测到的物体
        HAL_Delay(100);     // 每0.1秒更新一次状态
    }
}
4. 各模块代码
摄像头模块图像捕获与处理

通过 OV7670 摄像头模块采集图像,并将图像数据用于图形识别处理:

#include "camera.h"

// 初始化摄像头
void Camera_Init(void)
{
    // 配置摄像头的初始化参数,设置图像分辨率、帧率等
}

// 捕获图像
void Camera_Capture(void)
{
    // 调用摄像头采集一帧图像,存储到缓冲区
}

// 处理图像并检测目标
uint8_t Camera_ProcessImage(void)
{
    // 使用简单的图像处理算法(如颜色检测、形状检测等)识别特定物体
    // 如果检测到特定目标,返回 1;否则返回 0
    return 1;  // 假设检测到了目标
}
电机控制

通过 L298N 驱动模块控制小车的前进、后退、左转和右转:

#include "motor.h"
#include "tim.h"

// 初始化电机
void Motor_Init(void)
{
    // 初始化电机的 GPIO 引脚和 PWM 通道
}

// 设置电机速度
void Motor_SetSpeed(uint8_t left_speed, uint8_t right_speed)
{
    __HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, left_speed); // 左电机 PWM
    __HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_2, right_speed); // 右电机 PWM
}

// 设置电机方向,1 表示前进,-1 表示后退,0 表示停止
void Motor_SetDirection(int8_t left_dir, int8_t right_dir)
{
    if (left_dir == 1)
    {
        HAL_GPIO_WritePin(GPIOA, GPIO_PIN_3, GPIO_PIN_SET);
        HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET);
    }
    else if (left_dir == -1)
    {
        HAL_GPIO_WritePin(GPIOA, GPIO_PIN_3, GPIO_PIN_RESET);
        HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET);
    }
    else
    {
        HAL_GPIO_WritePin(GPIOA, GPIO_PIN_3, GPIO_PIN_RESET);
        HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET);
    }

    if (right_dir == 1)
    {
        HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_SET);
        HAL_GPIO_WritePin(GPIOA, GPIO_PIN_6, GPIO_PIN_RESET);
    }
    else if (right_dir == -1)
    {
        HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_RESET);
        HAL_GPIO_WritePin(GPIOA, GPIO_PIN_6, GPIO_PIN_SET);
    }
    else
    {
        HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_RESET);
        HAL_GPIO_WritePin(GPIOA, GPIO_PIN_6, GPIO_PIN_RESET);
    }
}
超声波传感器避障

使用 HC-SR04 超声波传感器进行障碍物检测,并根据距离数据执行避障操作:

#include "ultrasonic.h"

// 初始化超声波传感器
void Ultrasonic_Init(void)
{
    // 配置超声波传感器的 GPIO 引脚
}

// 测量距离
uint32_t Ultrasonic_Measure(void)
{
    uint32_t distance = 0;
    uint32_t local_time = 0;
    
    // 发送 Trig 脉冲
    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_0, GPIO_PIN_SET);
    HAL_Delay(10);  // 10 微秒脉冲
    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_0, GPIO_PIN_RESET);
    
    // 等待 Echo 的高电平并计算时间
    while (!HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_1));  // 等待 Echo 引脚变高
    while (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_1))    // 计算 Echo 高电平的时间
    {
        local_time++;
        HAL_Delay(1);  // 延时 1 微秒,注意:需要根据实际时钟调整
    }
    
    // 计算距离 (声音速度 34000 cm/s)
    distance = (local_time * 0.034) / 2;  // 除以 2 是因为声音往返
    return distance;
}
OLED 显示

用于显示当前状态,如小车是否检测到障碍物或正在跟踪目标:

#include "oled.h"

// 初始化 OLED 显示屏
void OLED_Init(void)
{
    // OLED 初始化代码
}

// 显示字符串
void OLED_ShowString(uint8_t x, uint8_t y, const char *str)
{
    // 在 OLED 上显示字符串
}

// 清屏
void OLED_Clear(void)
{
    // 清除 OLED 显示内容
}

⬇帮大家整理了单片机的资料

包括stm32的项目合集【源码+开发文档】

点击下方蓝字即可领取,感谢支持!⬇

点击领取更多嵌入式详细资料

问题讨论,stm32的资料领取可以私信!

 

系统工作原理

  • 图像捕获与处理:通过 OV7670 摄像头模块实时采集图像,将图像数据传输至 STM32,使用简单的图像处理算法(如颜色检测或形状识别)来识别特定的图形或目标。小车根据识别结果进行自动跟踪。

  • 障碍物检测:使用超声波传感器实时检测前方障碍物,当障碍物距离小于设定阈值时,小车会自动停止并后退,避免碰撞。

  • 电机控制:通过 L298N 电机驱动模块,小车能够实现前进、后退、左转、右转等操作,配合图像识别实现自动跟踪和避障。

  • 状态显示:OLED 显示屏实时显示小车的状态信息,如是否检测到目标、是否遇到障碍物等,便于调试和实时监控小车的工作状态。

常见问题与解决方法

1. 图像识别不准确
  • 问题原因:摄像头分辨率低,或图像处理算法不够准确。
  • 解决方法:可以提高图像分辨率,或者优化图像处理算法,如增加边缘检测或形状匹配的准确性。
2. 电机运行不平稳
  • 问题原因:PWM 信号不稳定或电机驱动模块供电不足。
  • 解决方法:调整 PWM 信号的频率和占空比,确保电机驱动模块的供电电压稳定。
3. 超声波传感器避障不灵敏
  • 问题原因:传感器安装角度不合适或环境中存在干扰。
  • 解决方法:调整超声波传感器的安装角度,确保其正对目标,并适当增加避障的阈值。

扩展功能

  • 远程控制与数据上传:可以添加 Wi-Fi 模块或蓝牙模块,实现小车的远程控制和实时数据上传,使用户可以通过手机或电脑实时查看小车的运行状态。

  • 复杂图形识别:通过扩展图像处理算法,可以识别更加复杂的图形或标志,如二维码或车牌号码,适用于更复杂的场景。

  • 自动导航与路径规划:加入 GPS 模块和路径规划算法,使小车能够根据地图信息自动规划路径,实现更复杂的导航功能。

结论

通过本项目,我们成功设计了一个基于 STM32 的图形识别智能跟踪小车,能够通过摄像头识别目标图形并自动跟随。系统集成了图像处理、避障、智能控制等功能,广泛应用于智能机器人、自动驾驶实验以及教育和竞赛项目。该项目具备较强的可扩展性,可以通过增加远程控制和路径规划等功能,实现更多智能化应用。

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

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

相关文章

顺序表|消失的数字|轮转数组|移除元素|合并有序数组|删除有序数组中重复项(C)

面试题 17.04. 消失的数字 思路一 排序遍历 如果下一个不等于上一个数1&#xff0c;这个下一个数就是消失的数字 时间复杂度 O ( N ⋅ log ⁡ 2 N ) O(N\cdot \log_{2}N) O(N⋅log2​N) 思路二 0N等差数列公式计算结果 - 数组中的值&#xff0c;结果就是消失的数字 int mis…

基于海市蜃楼算法(Fata Morgana Algorithm ,FATA)的多无人机协同三维路径规划(提供MATLAB代码)

一、海市蜃楼算法 海市蜃楼算法&#xff08;Fata Morgana Algorithm &#xff0c;FATA&#xff09;是2024年提出一种新型的群体智能优化算法&#xff0c;它的设计灵感来源于自然现象中的海市蜃楼形成过程。FATA算法通过模仿光线在不均匀介质中的传播方式&#xff0c;提出了两种…

日语学习者福音:4大翻译软件集锦,总有一款适合你!

现在全球化了&#xff0c;学个外语&#xff0c;尤其是日语这种很有魅力的语言&#xff0c;成了很多人的愿望。不管是为了方便旅行&#xff0c;还是喜欢日本文化&#xff0c;或者想在工作中更有竞争力&#xff0c;学日语都挺重要的。学语言不容易&#xff0c;特别是遇到难懂的句…

C#实现Punycode编码/解码

测试代码 string word "我爱你"; string idn "我爱你.中国"; string wordCode PunyCode.Encode(word); string punycode PunyCode.IDN2Punycode(idn);Console.WriteLine(word); Console.WriteLine(wordCode); Console.WriteLine(PunyCode.Decode(word…

【工欲善其事】巧用 PowerShell 自动清除复制 PDF 文本时夹杂的换行符号

文章目录 巧用 PowerShell 自动清除复制 PDF 文本时夹杂的换行符号1 问题描述2 解决方案3 具体步骤4 效果测试5 小结与复盘 巧用 PowerShell 自动清除复制 PDF 文本时夹杂的换行符号 1 问题描述 不知各位是否也为复制过来的文本中夹杂的回车换行符抓狂过&#xff1f;就是在复…

Spring Boot知识管理:提升信息检索效率

4系统概要设计 4.1概述 本系统采用B/S结构(Browser/Server,浏览器/服务器结构)和基于Web服务两种模式&#xff0c;是一个适用于Internet环境下的模型结构。只要用户能连上Internet,便可以在任何时间、任何地点使用。系统工作原理图如图4-1所示&#xff1a; 图4-1系统工作原理…

ubuntu下route命令详解

buntu下route命令详解 1、显示路由表 route -n2、临时路由设置&#xff0c;重启网卡失效#添加一条路由(发往192.168.62这个网段的全部要经过网关192.168.1.1)route add -net 192.168.62.0 netmask 255.255.255.0 gw 192.168.1.1#删除一条路由 删除的时候不用写网关route del …

extern “C“ 的作用、C++ 和 C 编译的不同、C++ 编译过程的五个主要阶段

在 C 中&#xff0c;如果需要从 C 语言导入函数或与 C 代码交互&#xff0c;需要使用 extern "C" 关键字。这是因为 C 和 C 在编译过程中的 符号命名机制&#xff08;即 "名称修饰" 或 "name mangling"&#xff09;不同。 1. extern "C&qu…

怎么把一段音频的人声和背景音乐分开?

在数字音频处理中&#xff0c;将一段音频中的人声和背景音乐分开是一个复杂但又常见的需求。这种技术广泛应用于音乐制作、影视后期、广告制作等多个领域。本文将为你详细解析如何通过不同的方法实现这一目标&#xff0c;帮助你更好地掌握音频分离技术。 一、音频分离的基本概念…

无人直播自动化回复客户咨询

我们插件是根据页面元素变动进行自动化操作的&#xff0c;想要实现网页版自动化&#xff0c;必须了解html以及dom结构&#xff0c;还有xpath定位方法。 各大直播后台页面结构不一样&#xff0c;所以要进行兼容处理&#xff0c;我们一个插件支持以下直播或客服平台 唯一客服浏…

数据结构与算法:栈与队列的高级应用

目录 3.1 栈的高级用法 3.2 队列的深度应用 3.3 栈与队列的综合应用 总结 数据结构与算法&#xff1a;栈与队列的高级应用 栈和队列是两种重要的线性数据结构&#xff0c;它们在计算机科学和工程的许多领域都有广泛的应用。从函数调用到表达式求值&#xff0c;再到任务调度…

c#的opcua客户端源码,支持用户名密码

在工作中遇到需要采集西门子机床的opcua和kepServer的opcua&#xff0c;找了一些网上的demo都不能都连接成功&#xff0c;好不容易找到一个两个都支持的c#opcua客户端源码&#xff0c;分享给大家。 软件界面如下 连接kepserver成功的界面 连接西门子opcua的界面 c#源码链接如下…

开关电源调制模式和工作模式

开关电源调制模式和工作模式 ‌‌开关电源定义开关电源分类‌单管DC/DC和、双管DC/DC和四管DC/DC的主要区别正激和反激 DCDCBUCK原理BOOST原理BUCK-BOOST原理异步整流和异步整流同步整流异步整流同步和异步整流区别同步和异步整流优缺点 DCDC调制模式PWM&#xff08;Pulse Widt…

C++ | Leetcode C++题解之第475题供暖器

题目&#xff1a; 题解&#xff1a; class Solution { public:int findRadius(vector<int>& houses, vector<int>& heaters) {sort(houses.begin(), houses.end());sort(heaters.begin(), heaters.end());int ans 0;for (int i 0, j 0; i < houses.…

轻松掌握TCP与UDP核心机制

White graces&#xff1a;个人主页 &#x1f649;专栏推荐:Java入门知识&#x1f649; &#x1f649; 内容推荐:网络编程TCP&#x1f649; &#x1f439;今日诗词:Best wishes&#x1f439; ⛳️点赞 ☀️收藏⭐️关注&#x1f4ac;卑微小博主&#x1f64f; ⛳️点赞 ☀️收…

相机曝光的两种模式

相机的曝光模式 相机帧率和曝光时间的关系 相机的图像采集包括曝光(Exposure)和读出(Readout)两部分 曝光又分为非重叠(non-overlapped)曝光和重叠(overlapped)曝光两种 在非重叠(“non-overlapped”)模式中&#xff0c;每个图像采集的周期中&#xff0c;相机在下一个图像采集…

萝卜快跑:迈向全球,挑战与机遇并存

萝卜快跑&#xff1a;迈向全球&#xff0c;挑战与机遇并存 引言 萝卜快跑的崛起 萝卜快跑的优势 萝卜快跑面临的挑战 未来展望 引言 在当今科技飞速发展的时代&#xff0c;自动驾驶技术成为了全球瞩目的焦点。众多科技巨头纷纷投入大量资源&#xff0c;试图在这个新兴领域…

文件完整性监控:如何提高企业的数据安全性

企业网络庞大而复杂&#xff0c;需要处理大量关键业务数据&#xff0c;这些敏感文件在企业网络中不断传输&#xff0c;并由多个用户和实体存储、共享和访问。FIM 工具或具有 FIM 功能的 SIEM 解决方案使企业能够跟踪未经授权的文件更改、对敏感信息的恶意访问、数据篡改尝试和内…

FreeRTOS——空闲任务和钩子函数介绍

空闲任务 在前面的学习中我们提到&#xff0c;空闲任务会负责释放一些被删除任务的内存&#xff0c;在FreeRTOS中&#xff0c;用户分配的内存通常也是在空闲任务中释放的。空闲任务是一个特殊的任务&#xff0c;当没有其他任务需要运行时&#xff0c;系统将会调度空闲任务来执行…

硬盘格式化后能恢复数据吗?好用4款工具集锦

嘿&#xff0c;硬盘格式化后能恢复数据吗&#xff1f;咱们现在的生活&#xff0c;数据可是宝贝&#xff0c;这大家都清楚。学习用的资料、工作的文件&#xff0c;还有那些宝贵的照片、视频&#xff0c;统统都存硬盘里。万一硬盘不小心被格式化了&#xff0c;那感觉就像所有东西…