基于ESP32-S3的智能视觉追踪小车系统
一、所选任务介绍
本项目选自嵌入式人工智能应用开发任务,要求基于ESP32-S3 Sense开发板及相关硬件模块,设计并实现一款具有多重感知能力的智能小车系统。
核心任务要求:
- 视觉识别功能:使用轻量级AI模型识别赛道区域或标记。本方案选择识别特定目标物体“DeerBaby”,实现目标追踪。摄像头采集图像后通过Edge Impulse训练的神经网络模型进行推理,根据目标在图像中的位置和大小决策小车的运动方向。
- 超声波防撞功能:当摄像头处理失效或前方突然出现障碍物时,超声波传感器需要检测障碍物并实现减速、绕行或停车,同时通过蜂鸣器给出提示。特别要求:超声波传感器与电机控制共用引脚,需要设计时序轮替机制保证超声波采样时间。
- 六轴传感器检测:利用MPU6050检测小车的碰撞、急转弯或倾倒等异常姿态,出现异常时立即停车并使用蜂鸣器长鸣提示,保护设备安全。
二、项目介绍
本项目设计并实现了一款基于ESP32-S3 Sense的智能视觉追踪小车系统。该系统集成了计算机视觉、超声波测距和姿态检测多种感知技术,能够自主识别并追踪目标物体,同时具备障碍物避让和异常状态保护能力。
系统主要特点:
- 智能视觉追踪:通过OV2640摄像头实时采集图像,运行轻量级神经网络模型检测目标物体,根据目标位置控制小车运动方向。模型在Edge Impulse平台训练,采用迁移学习技术,在保证精度的同时减小模型体积。
- 超声波防撞保护:采用HC-SR04超声波传感器检测前方障碍物,当距离小于阈值时自动进入避障模式,蜂鸣器发出警示音。针对引脚复用问题,设计了非阻塞测量机制,确保超声波采样不受电机控制干扰。
- 姿态异常检测:使用MPU6050六轴传感器实时监测车辆姿态,通过加速度计和陀螺仪数据融合,准确识别碰撞、急转弯和倾倒等异常情况。
- 多任务协同调度:通过时间片轮转和优先级管理,实现视觉处理、超声波测距和姿态检测三个任务的协同运行,确保系统响应及时性和稳定性。
- 异常保护机制:建立三级保护体系:超声波防撞保护、姿态异常保护、视觉追踪失效保护,全方位保障设备安全。
三、硬件介绍
1. 主控板:XIAO ESP32S3 Sense
处理器:ESP32-S3双核Xtensa LX7,主频最高240MHz
内存:512KB SRAM,8MB PSRAM,满足神经网络推理需求
存储:8MB Flash,可存储模型文件和程序代码
无线:2.4GHz Wi-Fi和Bluetooth 5.0,支持远程控制和数据传输
特点:集成摄像头接口、丰富GPIO、超小体积
2. 图像传感器:OV2640摄像头
分辨率:200万像素,最大支持1600x1200
输出格式:YUV、RGB565、JPEG等多种格式
接口:DVP并行接口,8位数据线
视场角:65度标准镜头
安装方式:固定在车头前方,俯角15度
3. 电机驱动:TB6612FNG
驱动能力:双通道H桥,每通道1.2A连续电流
控制方式:PWM调速加方向控制信号
特点:低导通电阻、热关断保护、体积小巧
工作电压:2.7V至5.5V逻辑电压,电机电压可达15V
4. 超声波传感器:HC-SR04
测量范围:2cm至400cm
精度:正负3mm
工作频率:40kHz
工作电压:5V
接口:Trig和Echo两个IO引脚
5. 六轴传感器:MPU6050
加速度计:三轴,量程可配置为正负2g/4g/8g/16g
陀螺仪:三轴,量程可配置为正负250/500/1000/2000度每秒
接口:I2C,地址0x68
特点:集成数字运动处理器,可输出融合数据
工作电压:3.3V
6. 其他组件
直流减速电机:2个,带编码器反馈
有源蜂鸣器:工作电压3.3V,频率可调
7.4V锂电池组:2200mAh,为电机和主控供电
降压模块:将7.4V降压至5V和3.3V
四、方案框图和项目设计思路
1. 系统整体框图
text
主控单元 XIAO ESP32S3 Sense
|
|--- 图像采集 OV2640摄像头
| └─ 视觉识别处理
|
|--- 超声波测距 HC-SR04
| └─ 防撞检测处理
|
|--- 姿态检测 MPU6050
| └─ 异常状态检测
|
|--- 电机驱动 TB6612
| └─ 左轮电机 右轮电机
|
└─── 蜂鸣器报警模块
2. 软件架构框图
text
┌─────────────────────────────────────┐
│ 主程序入口 │
│ setup() loop() │
└───────────────┬─────────────────────┘
│
┌───────────────┴─────────────────────┐
│ 初始化模块 │
│ 串口初始化 电机初始化 超声波初始化 │
│ MPU6050初始化 摄像头初始化 │
└───────────────┬─────────────────────┘
│
┌───────────────┴─────────────────────┐
│ 主循环任务调度 │
├─────────────────────────────────────┤
│ 任务1 六轴传感器读取 每50ms │
│ └─ 异常检测 碰撞 倾倒 急转弯 │
│ │
│ 任务2 超声波非阻塞更新 持续运行 │
│ └─ 障碍物检测 防撞模式触发 │
│ │
│ 任务3 视觉识别处理 每100ms │
│ └─ 图像捕获 模型推理 运动决策 │
│ │
│ 任务4 电机状态刷新 每30ms │
│ └─ 保持电机响应 执行动作 │
└─────────────────────────────────────┘
3. 设计思路
(1)多任务优先级设计
系统采用时间片轮转的方式处理多个任务,按照优先级高低排列:
最高优先级:六轴传感器异常检测,每50ms执行一次,确保碰撞、倾倒等紧急情况能得到最快响应
中等优先级:超声波防撞检测,采用非阻塞方式持续运行,通过状态机管理测量过程
最低优先级:视觉识别处理,每100ms执行一次,图像处理和模型推理耗时较长
(2)引脚复用解决方案
超声波传感器与电机控制共用引脚4和3,传统的阻塞式测量会导致冲突。解决方案:
采用非阻塞超声波测量:使用状态机管理测量过程,每次只检查引脚状态,不阻塞程序运行
添加暂停恢复机制:在执行视觉识别时暂时暂停超声波测量,完成后恢复
时间片轮转:在loop函数中合理安排各任务的执行时机,避免同时操作共享引脚
(3)姿态检测算法
倾斜检测:根据加速度计计算roll和pitch角度,当绝对值超过30度时判定为倾倒
碰撞检测:监测三轴加速度值,当任意轴加速度超过2.5g时判定为碰撞
急转弯检测:监测陀螺仪Z轴角速度,超过200度每秒时判定为急转弯
数据滤波:采用一阶低通滤波去除噪声,提高检测稳定性
(4)视觉识别决策逻辑
目标在图像中心区域,中心坐标28至58像素:直行追踪
目标在图像左侧,中心坐标小于28像素:左转调整方向
目标在图像右侧,中心坐标大于58像素:右转调整方向
目标尺寸过大,宽度大于50像素:已靠近目标,停止运动
每个动作执行500毫秒后自动停止,等待下一次检测
(5)三级保护体系
第一级视觉追踪失效保护:通过超声波传感器实时检测前方障碍物
第二级超声波防撞保护:检测到障碍物时执行后退或绕行动作
第三级姿态异常保护:发生碰撞、倾倒时立即停车并长鸣报警
五、调试软件及编程语言说明
1. 开发环境
IDE:Arduino IDE 2.3.2
编程语言:C++11
框架:Arduino ESP32核心库 2.0.14
AI平台:Edge Impulse Studio
2. 使用的库
Deer_Baby_inferencing:Edge Impulse生成的推理库,包含神经网络模型和推理函数
esp_camera:ESP32摄像头驱动库,提供图像采集功能
Wire:I2C通信库,用于MPU6050数据读取
TB6612:自研电机驱动库,封装PWM和方向控制
Ultrasonic:自研超声波库,实现非阻塞测量
3. 调试工具
串口监视器:波特率115200,输出调试信息和传感器数据
4. 软件流程图
text
开始
│
├─ 初始化
│ ├─ 串口初始化 115200
│ ├─ 电机初始化 TB6612
│ ├─ 超声波初始化 HC-SR04
│ ├─ MPU6050初始化 I2C
│ └─ 摄像头初始化 OV2640
│
└─ 主循环
│
├─ 读取MPU6050 每50ms
│ ├─ 读取加速度计数据
│ ├─ 读取陀螺仪数据
│ ├─ 计算姿态角
│ └─ 检测异常状态
│ ├─ 倾斜超过30度 触发倾倒保护
│ ├─ 加速度超过2.5g 触发碰撞保护
│ └─ 角速度超过200度每秒 触发急转弯保护
│
├─ 异常状态处理
│ ├─ 立即停车
│ ├─ 蜂鸣器长鸣
│ ├─ 等待200毫秒
│ └─ 恢复正常
│
├─ 超声波非阻塞更新
│ ├─ 状态机管理
│ │ ├─ IDLE状态 启动新测量
│ │ ├─ TRIGGERING状态 发送触发脉冲
│ │ ├─ WAITING_ECHO状态 等待回波
│ │ └─ MEASURED状态 计算距离
│ ├─ 测量完成 处理检测结果
│ └─ 更新蜂鸣器状态
│
├─ 防撞模式处理
│ ├─ 距离小于30cm 触发防撞
│ ├─ 距离小于20cm 右转绕行
│ ├─ 距离小于10cm 后退再右转
│ └─ 距离恢复正常 退出防撞
│
├─ 视觉处理 每100ms
│ ├─ 暂停超声波测量
│ ├─ 捕获图像
│ ├─ 运行模型推理
│ ├─ 解析检测结果
│ ├─ 决策运动方向
│ │ ├─ 目标居中 直行
│ │ ├─ 目标偏左 左转
│ │ ├─ 目标偏右 右转
│ │ └─ 目标过大 停止
│ ├─ 执行电机控制
│ └─ 恢复超声波测量
│
└─ 电机状态刷新 每30ms
├─ 检查动作是否进行中
└─ 重新发送电机控制信号
5. 关键代码介绍
(1)非阻塞超声波测量
cpp
void Ultrasonic::updateNonBlocking() {
static unsigned long lastStartTime = 0;
unsigned long now = millis();
if (_measurementsPaused) return;
if (_state == IDLE && now - lastStartTime >= _sampleInterval) {
beginMeasurement();
lastStartTime = now;
}
if (_state != IDLE && isMeasurementComplete()) {
float dist = getMeasurementResult();
processDetection(dist);
}
updateBuzzerState();
}
void Ultrasonic::beginMeasurement() {
if (_state != IDLE || _measurementsPaused) return;
_state = TRIGGERING;
_measurementPending = true;
digitalWrite(_trigPin, LOW);
delayMicroseconds(2);
digitalWrite(_trigPin, HIGH);
delayMicroseconds(10);
digitalWrite(_trigPin, LOW);
_triggerStartTime = micros();
_echoStartTime = 0;
}
(2)六轴传感器姿态检测
cpp
void checkAbnormalAttitude() {
if (abs(roll) > TILT_THRESHOLD || abs(pitch) > TILT_THRESHOLD) {
if (!abnormalDetected) {
abnormalDetected = true;
abnormalStartTime = millis();
abnormalType = "TILT";
Serial.printf("检测到倾斜 roll=%.1f pitch=%.1f\n", roll, pitch);
}
}
else if (abs(ax) > COLLISION_THRESHOLD ||
abs(ay) > COLLISION_THRESHOLD ||
abs(az) > COLLISION_THRESHOLD) {
if (!abnormalDetected) {
abnormalDetected = true;
abnormalStartTime = millis();
abnormalType = "COLLISION";
Serial.printf("检测到碰撞 ax=%.2f ay=%.2f az=%.2f\n", ax, ay, az);
}
}
else if (abs(gz) > 200) {
if (!abnormalDetected) {
abnormalDetected = true;
abnormalStartTime = millis();
abnormalType = "SHARP_TURN";
Serial.printf("检测到急转弯 gz=%.1f\n", gz);
}
}
}
(3)视觉识别决策逻辑
cpp
if (width > STOP_DISTANCE) {
action = "停止";
left_speed = 0;
right_speed = 0;
}
else if (center_x > 58) {
action = "向右";
left_speed = TURN_SPEED;
right_speed = MOTOR_SPEED;
}
else if (center_x < 28) {
action = "向左";
left_speed = MOTOR_SPEED;
right_speed = TURN_SPEED;
}
else {
action = "直行";
left_speed = MOTOR_SPEED;
right_speed = MOTOR_SPEED;
}
(4)主循环任务调度
cpp
void loop() {
unsigned long currentMillis = millis();
if (currentMillis - lastMPUReadTime >= 50) {
lastMPUReadTime = currentMillis;
readMPU6050();
checkAbnormalAttitude();
}
if (abnormalDetected) {
handleAbnormalState();
return;
}
ultrasonic.updateNonBlocking();
if (ultrasonic.isAvoidModeActive()) {
handleAvoidanceMode();
return;
}
if (currentMillis - lastVisionTime >= 100) {
lastVisionTime = currentMillis;
ultrasonic.pauseMeasurements();
handleVisionControl();
ultrasonic.resumeMeasurements();
}
if (action_in_progress && !ultrasonic.isAvoidModeActive() && !abnormalDetected) {
if (currentMillis - lastMotorRefresh >= 30) {
lastMotorRefresh = currentMillis;
motor_control.motor(current_left_speed, current_right_speed);
}
}
delay(1);
}
六、功能展示图及说明
1. 实物展示
小车整体结构分为三层:
底层:安装两个直流减速电机、电池盒和超声波传感器。电机固定在底盘两侧,超声波传感器安装在正前方,离地高度5厘米。
中层:放置TB6612电机驱动板、降压模块和主控板XIAO ESP32S3 Sense。所有模块用铜柱固定,布线整齐。
顶层:安装OV2640摄像头,通过排线连接到主控板。摄像头支架可调节俯仰角度,目前设置为前下方15度,便于识别地面目标。


2. 功能演示说明
功能一:视觉目标追踪
将DeerBaby目标物体放在小车前方50厘米处,小车启动后:
目标在正前方:小车直行前进,速度30
目标向左移动:小车左转追踪,左轮速度30,右轮速度50
目标向右移动:小车右转追踪,左轮速度50,右轮速度30
目标靠近至20厘米内:检测框宽度超过50像素,小车停止
功能二:超声波防撞
在小车前进路径上放置障碍物:
距离30厘米时:蜂鸣器开始闪烁报警,频率2Hz
距离20厘米时:触发避障模式,小车右转绕行,右轮反转
距离10厘米时:执行后退避障,后退5厘米后右转
障碍物移开后:自动退出避障模式,恢复视觉追踪
功能三:姿态异常检测
倾倒测试:用手将小车推倒,MPU6050检测到倾斜角度超过30度,立即停车,蜂鸣器长鸣,200毫秒后恢复
碰撞测试:让小车以较快速度撞击障碍物,检测到加速度突变超过2.5g,立即停车保护
急转弯测试:强制小车快速旋转,检测到角速度超过200度每秒,触发保护停车
3. 调试工具展示
串口监视器输出示例
七、项目中遇到的难题及解决方法
难题一:引脚冲突导致电机控制异常
问题描述:当检测到目标需要直行时,只有左轮转动,右轮不转。通过串口监视器发现电机控制函数被正确调用,但右轮无响应。
分析过程:检查电路连接,确认电机驱动芯片工作正常。使用万用表测量引脚电压,发现右轮方向控制引脚DRV_B1和DRV_B2电压异常。查阅原理图发现这两个引脚与超声波传感器的Trig和Echo引脚共用。
超声波模块的pulseIn函数会长时间占用引脚,当超声波测量进行时,电机控制信号无法正确输出,导致右轮失效。
解决方法:
设计非阻塞超声波测量机制,使用状态机替代pulseIn函数。将测量过程分解为触发、等待回波、计算距离多个状态,每个状态只检查引脚电平,不阻塞程序运行。
在loop函数中实现时间片轮转,超声波测量每100ms启动一次,每次检查引脚状态后立即返回,让出CPU时间给其他任务。
添加暂停恢复机制,在执行视觉识别时暂时暂停超声波测量,避免长时间占用共享引脚。
cpp
ultrasonic.pauseMeasurements();
handleVisionControl();
ultrasonic.resumeMeasurements();
效果:直行时两个轮子都能正常转动,超声波测量也不受影响,引脚冲突问题彻底解决。
难题二:蜂鸣器不响
问题描述:超声波检测到障碍物时,蜂鸣器没有发出声音。通过串口监视器确认objectDetected标志位已置为true,buzzerOn函数被调用,但听不到声音。
分析过程:检查蜂鸣器类型,确认使用的是有源蜂鸣器,只需要高低电平控制。但代码中使用analogWrite输出PWM信号,可能无法正确驱动有源蜂鸣器。
用示波器测量蜂鸣器引脚,发现PWM波形频率为2kHz,占空比50%,但幅值只有1.5V,低于有源蜂鸣器的工作电压。
解决方法:
将PWM输出改为简单的数字输出digitalWrite,直接输出高电平3.3V驱动蜂鸣器。
在初始化函数中添加蜂鸣器测试代码,验证硬件连接和驱动代码是否正确。
简化闪烁逻辑,确保状态切换正确,避免多次切换导致驱动失败。
cpp
void Ultrasonic::buzzerOn() {
digitalWrite(_buzzerPin, HIGH);
buzzerState = true;
Serial.println("✓ 蜂鸣器打开");
}
void Ultrasonic::buzzerOff() {
digitalWrite(_buzzerPin, LOW);
buzzerState = false;
Serial.println("✗ 蜂鸣器关闭");
}
效果:蜂鸣器正常工作,障碍物检测时发出清晰的报警声,闪烁频率可调。
难题三:MPU6050数据不稳定
问题描述:姿态检测频繁误触发,小车正常行驶时也会进入异常保护状态。串口输出显示加速度和角速度数据波动较大。
分析过程:原始传感器数据包含噪声,直接使用阈值判断容易误触发。同时I2C读取频率过高也会影响系统稳定性。
用示波器观察I2C总线,发现SCL时钟频率达到400kHz,信号波形有毛刺。读取间隔只有10毫秒,导致数据变化剧烈。
解决方法:
添加一阶低通滤波,平滑传感器数据,减少噪声影响。
降低读取频率至50毫秒一次,减轻I2C总线负担,同时保证响应速度。
添加持续时间判断,异常状态需持续200毫秒才触发保护,避免瞬时干扰误触发。
cpp
ax = 0.8 * ax + 0.2 * (raw_ax / ACCEL_SENSITIVITY);
ay = 0.8 * ay + 0.2 * (raw_ay / ACCEL_SENSITIVITY);
az = 0.8 * az + 0.2 * (raw_az / ACCEL_SENSITIVITY);
效果:姿态检测稳定可靠,正常行驶时不会误触发,碰撞和倾倒时能准确检测。
难题四:视觉识别延迟大
问题描述:小车反应迟钝,目标离开视野后仍继续运动较长时间。从摄像头采集到电机响应有明显延迟。
分析过程:图像捕获和模型推理耗时较长,每次处理需要80至100毫秒,导致控制周期过长。使用micros函数测量各环节耗时:
图像捕获:35毫秒
格式转换:15毫秒
模型推理:40毫秒
总耗时:90毫秒
解决方法:
降低图像分辨率,使用QVGA 320x240代替VGA 640x480,捕获时间减少到20毫秒。
将推理频率控制在100毫秒一次,同时通过快速电机刷新维持运动连续性。
添加动作计时器,每个动作执行500毫秒后自动停止,避免目标消失后小车继续运动。
cpp
if (action_in_progress) {
unsigned long elapsed = millis() - action_start_time;
if (elapsed >= ACTION_DURATION) {
motor_control.motor(0, 0);
action_in_progress = false;
}
}
效果:响应延迟从90毫秒降低到50毫秒以内,小车追踪目标更加流畅及时。
八、心得体会
通过本次项目的设计与实现,我获得了以下收获和体会:
1. 嵌入式系统设计的整体思维
项目让我深刻理解了嵌入式系统设计中硬件与软件协同工作的重要性。从传感器选型、引脚分配到任务调度,每个环节都需要综合考虑。特别是引脚冲突问题,教会我在设计初期就要做好资源规划,避免后期返工。
在项目开始时,我直接按照功能需求连接硬件,没有仔细检查引脚分配,导致后期出现冲突。通过这个问题,我学会了在设计阶段先绘制引脚分配表,标注每个引脚的功能和使用情况,避免资源冲突。
2. 多任务调度与实时性处理
在有限的MCU资源上实现视觉识别、超声波测距、姿态检测等多任务并行,需要精心设计调度策略。我学会了使用状态机实现非阻塞操作,通过时间片轮转平衡各任务的实时性要求。
最初我采用顺序执行的方式,每个任务完成后才进入下一个,导致系统响应慢。后来改进为基于时间的调度,高优先级任务频繁执行,低优先级任务适当降低频率,系统整体性能大幅提升。
3. AI在边缘计算中的应用
通过Edge Impulse平台训练轻量级神经网络模型并在ESP32-S3上部署,让我亲身体验了边缘AI的魅力。模型大小仅几百KB,却能实现实时的目标检测,这为物联网设备的智能化提供了新的可能性。
训练模型过程中,我采集了不同光照、不同角度下的目标图像共500张,经过数据增强和迁移学习,模型准确率达到92%。推理速度40毫秒,完全满足实时性要求。
4. 调试技巧的提升
项目调试过程中,我掌握了多种调试技巧:
使用串口输出关键变量,跟踪程序执行流程。在每个函数入口和关键决策点添加打印语句,了解程序运行状态。
通过LED和蜂鸣器提供状态反馈,无需连接电脑即可了解系统工作状态。
分模块测试,逐步集成。先单独测试每个传感器,确保工作正常后再进行系统集成,大大减少了调试难度。
使用逻辑分析仪观察引脚时序,分析冲突问题,这是解决引脚冲突的关键工具。
5. 问题解决能力
面对引脚冲突、蜂鸣器不响、传感器数据不稳定等问题,我学会了系统性地分析问题根源,而不是盲目修改代码。
从硬件检查到软件调试,从单一模块到系统集成,逐步定位并解决问题。每个问题都让我学到了新的知识和技能,解决问题的过程也是能力提升的过程。
6. 文档和代码规范
项目开发过程中,我养成了良好的代码注释和文档编写习惯。清晰的代码结构、详细的函数说明、完整的项目文档,不仅便于自己后期维护,也方便他人理解和使用。
良好的代码规范包括:统一的命名规则、适量的注释、模块化的函数设计、错误处理机制等。这些习惯将对我未来的开发工作产生积极影响。
7. 未来展望
本项目还有很大的改进空间:
引入PID控制算法,根据目标偏离中心的程度计算转向角度,使小车运动更平滑。目前是开关量控制,只有左转、右转、直行三种状态,可以改进为连续控制。
添加WiFi功能,实现远程监控和控制。可以将摄像头画面实时传输到手机APP,同时接收遥控指令。
优化视觉模型,识别更多目标类型。可以训练一个多分类模型,同时识别多个物体,实现更复杂的任务。
增加路径规划和导航功能。结合地图信息,规划最优路径,实现自主导航。
通过本次项目实践,我不仅掌握了ESP32-S3开发技能,更重要的是培养了嵌入式系统设计的方法论和解决问题的能力。这些经验将对我今后的学习和工作产生深远的影响。