1. 项目概述
这个项目让我想起了十年前第一次玩智能小车时的场景——那时候用几个红外传感器加一堆杜邦线就能让小车跑起来,现在的硬件条件真是好太多了。这次我们要做的是一台基于STM32F103的智能小车,它不仅能循着黑线跑,还能用超声波探测障碍物自主避障。听起来简单?但要让这两个功能和谐共处可不简单。
核心硬件就是那块经典的STM32F103C8T6最小系统板,加上L298N电机驱动、HC-SR04超声波模块和TCRT5000红外传感器。软件层面需要处理多传感器数据融合、电机PWM控制和简单的决策逻辑。最有趣的部分是如何让小车在循迹过程中突然遇到障碍物时,能优雅地绕过去而不是直接撞上或者丢失轨迹。
2. 硬件设计与选型
2.1 主控芯片选择
STM32F103C8T6这块"蓝色药丸"在创客圈火了十几年不是没道理的。72MHz主频、20KB RAM加上64KB Flash,对于我们的需求来说性能绰绰有余。更重要的是它的定时器资源丰富——我们至少需要4路PWM输出控制电机,2个定时器捕获超声波信号,还有余力处理红外传感器的ADC采样。
注意:市面上有些山寨版的STM32F103性能缩水严重,建议购买正版芯片或选择GD32等兼容型号
2.2 传感器配置方案
我采用了5路TCRT5000红外传感器排成一排作为循迹阵列,中间三个间距1.5cm,两侧的间距2cm。这种非等距排列能在保证中央区域检测精度的同时,扩大两侧的探测范围。超声波模块安装在可旋转的舵机上,这样通过左右摆动就能实现180°范围内的障碍物扫描。
| 传感器 | 数量 | 安装位置 | 供电电压 | 接口类型 |
|---|---|---|---|---|
| TCRT5000 | 5 | 车头底部 | 3.3V | 模拟量输出 |
| HC-SR04 | 1 | 可旋转支架 | 5V | 数字脉冲 |
| SG90舵机 | 1 | 超声波底座 | 5V | PWM控制 |
2.3 电机驱动电路
L298N虽然效率不高(典型效率约70%),但胜在皮实耐操。我用的是带光耦隔离的版本,虽然贵几块钱但能有效防止电机干扰导致单片机复位。两个直流减速电机选用的是TT马达,6V供电下空载转速约200RPM,足够小车以0.3m/s的速度平稳运行。
3. 软件架构设计
3.1 实时控制逻辑
整个系统跑在FreeRTOS上,创建了三个主要任务:
- 传感器数据采集任务(优先级3)
- 导航决策任务(优先级2)
- 电机控制任务(优先级1)
这种架构确保了传感器数据能及时更新,同时电机控制不会因为决策逻辑的复杂计算而出现延迟。我在任务间通过消息队列传递数据,避免使用全局变量带来的资源竞争问题。
3.2 传感器数据处理
红外传感器的模拟量读数需要经过滑动平均滤波:
c复制#define FILTER_SIZE 5
uint16_t ir_filter(FILTER_TYPE *filter) {
uint32_t sum = 0;
for(uint8_t i=0; i<FILTER_SIZE-1; i++){
filter->buf[i] = filter->buf[i+1];
sum += filter->buf[i];
}
filter->buf[FILTER_SIZE-1] = ADC_Read();
sum += filter->buf[FILTER_SIZE-1];
return sum/FILTER_SIZE;
}
超声波测距则采用双边沿捕获的方式提高精度:
c复制void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim) {
if(htim->Channel == HAL_TIM_ACTIVE_CHANNEL_1){
if(!ultrasonic.edge_flag){
ultrasonic.rising_time = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1);
ultrasonic.edge_flag = 1;
}else{
ultrasonic.falling_time = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1);
ultrasonic.distance = (ultrasonic.falling_time - ultrasonic.rising_time)*0.017; //cm
ultrasonic.edge_flag = 0;
}
}
}
3.3 导航状态机设计
小车的核心行为用状态机实现,包含以下几个状态:
- 循迹模式(默认状态)
- 避障模式(检测到前方障碍)
- 绕障模式(执行避障动作)
- 回轨模式(避障后重新寻找轨迹)
状态转换逻辑如下图所示(伪代码表示):
python复制def state_machine():
if current_state == LINE_FOLLOWING:
if ultrasonic.distance < 15cm:
current_state = OBSTACLE_AVOIDANCE
elif current_state == OBSTACLE_AVOIDANCE:
start_avoidance_procedure()
current_state = DETOUR
elif current_state == DETOUR:
if check_path_clear():
current_state = RETURN_TO_TRACK
elif current_state == RETURN_TO_TRACK:
if line_detected():
current_state = LINE_FOLLOWING
4. 关键算法实现
4.1 循迹PID控制
采用位置式PID算法控制小车沿黑线行驶。五个红外传感器的读数被转换为位置偏差error:
c复制int16_t calculate_error(uint16_t *adc_values) {
// 给每个传感器分配权重
const int8_t weights[5] = {-20, -10, 0, 10, 20};
uint16_t sum = 0, weighted_sum = 0;
for(uint8_t i=0; i<5; i++){
// 反相处理:黑线检测时ADC值较小
uint16_t normalized = 4095 - adc_values[i];
sum += normalized;
weighted_sum += normalized * weights[i];
}
return (sum > 500) ? (weighted_sum / sum) : 0; // 防止除零
}
PID参数经过实测调整后确定为:
- Kp = 0.8
- Ki = 0.001
- Kd = 2.5
4.2 避障路径规划
当超声波检测到前方障碍物时,小车执行以下动作序列:
- 完全停止(刹车200ms)
- 舵机向左转90度,测量左侧距离
- 舵机向右转180度,测量右侧距离
- 比较两侧空间,选择较开阔的一侧
- 执行三步绕障动作:后退→转向→前进
c复制void avoidance_sequence() {
motor_brake(200);
servo_rotate(90);
delay(100);
float left_dist = get_ultrasonic_distance();
servo_rotate(-90);
delay(100);
float right_dist = get_ultrasonic_distance();
if(left_dist > right_dist && left_dist > 20.0) {
turn_left_90();
} else if(right_dist > 20.0) {
turn_right_90();
} else {
// 两侧空间都不足,执行原地掉头
turn_around();
}
}
5. 系统调试与优化
5.1 传感器校准技巧
红外传感器容易受到环境光影响,建议按以下步骤校准:
- 将小车放置在白色背景上,记录每个传感器的ADC值(white_value)
- 移到黑色轨迹线上,记录新读数(black_value)
- 设置阈值为 (white_value + black_value)/2
- 在不同光照条件下重复3次取平均值
超声波模块需要校准距离偏移量。实测发现HC-SR04在20cm处的误差约0.5cm,可以通过线性补偿:
c复制float calibrated_distance = raw_distance * 0.98 + 0.3;
5.2 电机特性测试
通过实验测得电机PWM占空比与速度的关系曲线:
| 占空比(%) | 左轮速度(cm/s) | 右轮速度(cm/s) |
|---|---|---|
| 30 | 12.5 | 11.8 |
| 40 | 18.2 | 17.5 |
| 50 | 24.1 | 23.3 |
| 60 | 28.7 | 27.9 |
基于这个数据,我在电机控制函数中加入了非对称补偿:
c复制void set_motor_speed(uint8_t left, uint8_t right) {
// 右轮补偿系数
float compensation = 1.03;
TIM1->CCR1 = left; // 左轮PWM
TIM1->CCR2 = right * compensation; // 右轮PWM
}
5.3 典型问题排查
-
小车走直线偏移
- 检查电机供电电压是否一致
- 用示波器观察两路PWM波形是否对称
- 尝试交换电机接线确认是否为硬件差异
-
超声波偶尔测距异常
- 确保VCC电压稳定(建议并联100uF电容)
- 检查Trig信号脉冲宽度不小于10us
- 两次测量间隔建议大于60ms
-
红外传感器误触发
- 在传感器LED端串联100Ω电阻降低发射功率
- 在ADC输入端添加0.1uF滤波电容
- 避免阳光直射传感器区域
6. 进阶改进方向
这套基础系统稳定后,可以考虑以下增强功能:
- 增加IMU传感器:用MPU6050检测小车姿态,防止在高速转弯时侧翻
- 引入视觉处理:加上OV2640摄像头,通过OpenMV实现图像识别
- 无线遥控功能:添加HC-05蓝牙模块,用手机APP进行控制
- SLAM建图:升级到STM32F4系列,运行简化版的即时定位与地图构建算法
我在车体前方预留了多个扩展接口,方便后续添加新模块。电源部分也专门设计了5V和3.3V的稳压输出,最大可提供2A电流,足够驱动各种外设。