1. 项目概述与核心功能
这个基于STM32F103C8的循迹避障小车项目,是我最近完成的一个嵌入式系统综合实践案例。核心目标是实现一个能够自主循迹行驶并具备避障功能的智能小车系统。主控采用性价比极高的STM32F103C8T6,通过两路红外循迹传感器实现黑线跟踪,两路红外避障传感器防止碰撞,配合L298N电机驱动模块实现双轮差速控制。
项目亮点在于完整实现了从Proteus仿真到实际硬件部署的全流程验证。在仿真环境下,我使用Proteus 8.15版本搭建了完整的电路模型,包括STM32最小系统、传感器模块、电机驱动电路和数码管显示模块。实际测试中,小车能够稳定识别宽度≥2cm的黑色轨迹线,并在检测到前方障碍物(距离5-30cm可调)时自动停止并发出警报。
2. 硬件系统设计详解
2.1 主控芯片选型与配置
选择STM32F103C8T6作为主控主要基于以下考虑:
- 72MHz主频的Cortex-M3内核,性能足够处理多传感器数据
- 64KB Flash+20KB RAM的存储配置,满足程序存储和运行需求
- 丰富的外设接口:4个通用定时器(TIM1-TIM4),2个SPI接口,3个USART等
- 价格优势:零售价约10元,性价比极高
实际使用中需要注意:
- 系统时钟配置:使用8MHz外部晶振,通过PLL倍频到72MHz
- GPIO模式设置:传感器输入口配置为上拉输入模式,电机控制口配置为复用推挽输出
- 调试接口:保留SWD接口用于程序下载和调试
2.2 传感器模块设计与实现
红外循迹传感器
采用TCRT5000红外反射传感器,工作电压3.3-5V,检测距离1-8mm可调。电路设计要点:
- 发射端:通过100Ω限流电阻连接红外发射管
- 接收端:比较器输出直接连接STM32 GPIO
- 灵敏度调节:通过10KΩ电位器调整比较器阈值
安装注意事项:
- 传感器距地面高度建议保持在8-12mm
- 两个传感器间距应略小于轨迹线宽度(通常15-20mm)
- 避免强光直射传感器表面
红外避障传感器
采用E18-D80NK光电开关,有效检测距离3-80cm可调。关键参数:
- 工作电压:5V DC
- 输出形式:数字量(检测到障碍物时输出低电平)
- 响应时间:<2ms
- 安装角度:建议向前倾斜10-15度以减少地面反射干扰
2.3 电机驱动系统
L298N双H桥电机驱动模块配置要点:
- 供电电压:7-12V(建议使用2节18650锂电池)
- 逻辑电压:5V(可直接由STM32的3.3V IO口控制)
- 使能端:ENA和ENB必须接高电平(Proteus仿真中常被忽略)
- 电流保护:建议在电机电源线上串联自恢复保险丝
PWM调速实现:
- 使用TIM3的4个通道(CH1-CH4)分别控制两个电机的速度和方向
- PWM频率设置为1kHz(ARR=71,PSC=999)
- 占空比调节范围0-100%,对应CCRx值0-71
3. 软件架构与核心算法
3.1 系统初始化流程
完整的系统初始化包括以下步骤:
- 时钟系统配置(RCC)
- GPIO端口初始化(传感器输入、电机控制、数码管接口)
- 定时器PWM输出配置(TIM3)
- SPI接口初始化(数码管驱动)
- 中断系统配置(可选)
- 外设自检(传感器、电机、显示)
关键代码片段:
c复制void System_Init(void)
{
RCC_Configuration();
GPIO_Configuration();
TIM3_PWM_Init(71, 999); // 1kHz PWM
SPI1_Init();
Buzzer_Init();
Motor_Stop();
}
3.2 循迹避障决策算法
采用优先级状态机设计,避障优先级高于循迹:
c复制void Main_Decision_Loop(void)
{
static uint32_t last_update = 0;
if(HAL_GetTick() - last_update < 50) return; // 20Hz更新率
last_update = HAL_GetTick();
// 1. 读取所有传感器状态
Sensor_Status status = Read_Sensors();
// 2. 避障判断(最高优先级)
if(status.obstacle_left || status.obstacle_right) {
Current_Mode = MODE_ALERT;
Motor_Stop();
Buzzer_Alert();
return;
}
// 3. 循迹决策
if(!status.track_left && !status.track_right) {
Current_Mode = MODE_FORWARD;
Motor_Forward(70); // 70%速度前进
}
else if(status.track_left && !status.track_right) {
Current_Mode = MODE_TURN_LEFT;
Motor_Turn_Left(50); // 左转
}
else if(!status.track_left && status.track_right) {
Current_Mode = MODE_TURN_RIGHT;
Motor_Turn_Right(50); // 右转
}
else {
Current_Mode = MODE_STOP;
Motor_Stop(); // 异常情况
}
// 4. 更新显示
Display_Mode(Current_Mode);
}
算法优化点:
- 加入50ms的决策周期控制,避免过于频繁的状态切换
- 使用结构体封装传感器状态,提高代码可读性
- 引入Current_Mode全局变量,方便状态跟踪和显示
3.3 电机控制策略
差速转向实现原理:
- 直行:左右电机相同PWM占空比
- 左转:右电机保持速度,左电机减速
- 右转:左电机保持速度,右电机减速
- 停止:两电机PWM输出0
关键控制函数:
c复制void Motor_Control(int16_t left_speed, int16_t right_speed)
{
// 限幅处理
left_speed = constrain(left_speed, -100, 100);
right_speed = constrain(right_speed, -100, 100);
// 左电机控制
if(left_speed >= 0) {
GPIO_WriteBit(GPIOB, GPIO_Pin_6, Bit_SET);
GPIO_WriteBit(GPIOB, GPIO_Pin_7, Bit_RESET);
TIM_SetCompare3(TIM3, left_speed * 71 / 100);
} else {
GPIO_WriteBit(GPIOB, GPIO_Pin_6, Bit_RESET);
GPIO_WriteBit(GPIOB, GPIO_Pin_7, Bit_SET);
TIM_SetCompare3(TIM3, (-left_speed) * 71 / 100);
}
// 右电机控制(类似左电机)
// ...
}
4. 系统调试与优化
4.1 Proteus仿真要点
-
元件选择:
- STM32F103C8:Proteus 8.15及以上版本支持
- L298N:使用"Motor Driver"库中的模型
- 红外传感器:使用"IRLED"和"PHOTOTRANSISTOR"组合模拟
-
常见仿真问题:
- 电机不转:检查L298N使能端是否接高电平
- 传感器无反应:调整红外发射强度和接收灵敏度参数
- PWM波形异常:检查定时器配置和GPIO复用设置
-
调试技巧:
- 使用Proteus逻辑分析仪观察PWM波形
- 添加虚拟终端监控调试输出
- 利用电压探针检查关键节点信号
4.2 实际硬件调试经验
-
电源管理:
- 数字部分和电机驱动部分电源建议分开供电
- 在STM32的3.3V输出端添加100μF电容滤波
- 电机电源线使用低ESR的电解电容(220μF以上)
-
信号完整性:
- 传感器信号线长度不超过20cm
- 电机PWM控制线建议使用双绞线
- 避免将信号线与电机电源线平行走线
-
机械结构优化:
- 传感器安装板使用可调高度的支架
- 车轮与电机轴之间使用联轴器减少偏心振动
- 整体重心尽量降低并靠近驱动轮
4.3 性能优化技巧
- 传感器滤波算法:
c复制#define SAMPLE_COUNT 5
uint8_t Debounce_Input(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
{
uint8_t count = 0;
for(uint8_t i=0; i<SAMPLE_COUNT; i++) {
if(GPIO_ReadInputDataBit(GPIOx, GPIO_Pin)) count++;
Delay_us(100);
}
return (count > SAMPLE_COUNT/2) ? 1 : 0;
}
- 动态PWM调整:
- 根据电池电压自动调整PWM占空比,保持速度稳定
- 在转向时渐进式调整PWM,避免急转弯导致打滑
- 低功耗设计:
- 空闲时降低主频至8MHz
- 使用停机模式配合唤醒中断
- 关闭未使用的外设时钟
5. 扩展功能与进阶改进
5.1 无线遥控功能扩展
通过HC-05蓝牙模块增加手机遥控功能:
-
硬件连接:
- TXD接STM32的PA10(USART1_RX)
- RXD接STM32的PA9(USART1_TX)
- VCC接3.3V,GND接地
-
软件实现:
c复制void USART1_IRQHandler(void)
{
if(USART_GetITStatus(USART1, USART_IT_RXNE)) {
uint8_t cmd = USART_ReceiveData(USART1);
switch(cmd) {
case 'F': Motor_Forward(80); break;
case 'B': Motor_Backward(60); break;
// 其他控制命令...
}
}
}
5.2 路径记忆与自主导航
通过以下改进实现简单路径记忆:
- 增加EEPROM存储模块(如AT24C02)
- 定时记录位置和状态信息
- 回放模式时读取存储的路径数据
5.3 多传感器融合方案
升级传感器系统:
- 增加超声波模块实现精确测距
- 使用灰度传感器替代数字红外,实现多级循迹
- 添加MPU6050获取姿态信息,提高运动稳定性
6. 常见问题解决方案
6.1 电机运行异常排查表
| 现象 | 可能原因 | 解决方案 |
|---|---|---|
| 单侧电机不转 | L298N使能端未接通 | 检查ENA/ENB是否接高电平 |
| 电机抖动 | PWM频率不合适 | 调整PWM频率至1-5kHz |
| 转速不稳定 | 电源供电不足 | 检查电池电压,增加滤波电容 |
| 无法反转 | 控制逻辑错误 | 检查IN1/IN2信号组合 |
6.2 传感器故障处理指南
-
循迹传感器始终触发:
- 检查安装高度是否合适
- 调整电位器降低灵敏度
- 清洁传感器表面
-
避障传感器检测距离短:
- 检查发射管工作电流
- 调整传感器上的灵敏度电位器
- 避免环境光干扰
-
传感器响应延迟:
- 检查上拉电阻值(建议4.7K-10K)
- 优化软件去抖算法
- 缩短信号线长度
6.3 系统稳定性提升技巧
-
电源处理:
- 在STM32的每个电源引脚添加0.1μF去耦电容
- 电机驱动部分使用独立电源
- 添加TVS二极管防止电压尖峰
-
软件看门狗:
c复制IWDG_WriteAccessCmd(IWDG_WriteAccess_Enable);
IWDG_SetPrescaler(IWDG_Prescaler_32); // 约1s超时
IWDG_SetReload(0xFFF);
IWDG_Enable();
- 异常恢复机制:
- 关键操作增加超时判断
- 重要数据设置默认值
- 实现软件复位功能
7. 项目总结与心得
经过这个项目的完整开发周期,我总结了以下几点重要经验:
-
模块化设计至关重要:
- 将传感器、电机、显示等模块独立封装
- 定义清晰的接口规范
- 便于单独测试和替换
-
仿真与实物的差异:
- Proteus中完美的电路可能在实物中出现各种问题
- 传感器参数需要根据实际环境调整
- 机械结构的影响不可忽视
-
调试技巧:
- 使用LED指示灯辅助调试
- 分段验证各功能模块
- 保持详细的调试记录
-
性能平衡:
- 响应速度与稳定性的权衡
- 功能丰富度与系统复杂度的平衡
- 成本控制与技术指标的取舍
对于想要复现或改进这个项目的开发者,我的建议是:
- 先从Proteus仿真开始,验证基本功能
- 制作实物时优先确保电源和电机驱动稳定
- 逐步添加功能,避免一次性集成过多模块
- 保留足够的调试接口和扩展空间