NO.20 · 嵌入式开发
STM32 灰度循迹小车:8 路传感器 + PID + 舵机转向
STM32F103 灰度循迹小车完整实战——CD4051 多路复用采集 8 路灰度、加权位置误差建模、抗饱和 PID 转向、弯道自动减速与舵机平滑步进,5ms 控制周期全闭环。
一条黑线、一台小车、一套完整闭环。这个项目用一颗 72MHz 的 STM32F103C8T6,把 8 路灰度传感器、加权位置误差、抗饱和 PID、舵机转向和双后轮调速串成了一条 200Hz 的控制链路,是嵌入式入门到进阶的完整实战参考。
系统架构
1 | ┌──────────────────────────────────────────────┐ |
硬件平台
| 项目 | 规格 |
|---|---|
| MCU | STM32F103C8T6(Cortex-M3, 72MHz, 64KB Flash, 20KB SRAM) |
| 电机驱动 | L298N 双 H 桥,A/B 通道分别驱动两后轮 |
| 舵机 | MG995 金属齿,前轮转向,6V 独立供电 |
| 传感器 | 8 通道灰度模块(CD4051 多路复用 + 模拟输出) |
| 电源 | 12V 锂电池 + DC-DC 6V/3A(舵机独立供电) |
注意:MG995 堵转电流可达 2.5A,不能从 L298N 板载 5V 取电,否则 STM32 会复位。
核心亮点
资源利用
- CD4051 多路复用:仅用 1 个 ADC 通道(PA1)+ 3 根地址线(PB0/PB1/PB10)轮流采样 8 路灰度,引脚与 ADC 资源占用降到最低;
- TIM1 双通道共享时基:CH1/CH4 输出两路独立 2kHz PWM 分别驱动左右后轮,每轮独占通道避免并联超流;
- TIM2 舵机 PWM:72MHz/72 = 1MHz 计数,1us 分辨率 + ARR=19999 → 精确 50Hz,脉宽直接映射角度;
- 无中断、无 DMA:全部轮询实现,逻辑简单、行为可预测。
算法优化
- 8 路加权位置误差:对称权重
[-7,-5,-3,-1,1,3,5,7],对压线传感器取平均,把离散开关量变成连续位置量; - 抗饱和 PID:积分限幅(±30)+ 误差过零清零,避免大弯道积分饱和导致超调振荡;
- 弯道自动减速:误差越大 PWM 越低(最多降至直道 40%),无需额外传感器;
- 舵机平滑步进:每周期脉宽最多变化 25us 逼近目标角度;
- 丢线容错:8 路全部丢线时沿用上次误差,保持转向趋势而非急停。
关键算法
加权位置误差
| 通道 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
|---|---|---|---|---|---|---|---|---|
| 权重 | -7 | -5 | -3 | -1 | +1 | +3 | +5 | +7 |
1 | error = (Σ 权重[i]) / (压在黑线上的传感器个数) |
线居中 → error ≈ 0 → 直行;线偏右 → error > 0 → 舵机右打。误差范围 [-7, +7]。
平滑步进:抗抖动核心
误差短暂回零时,PID 输出会瞬间跳回 0°,舵机会”打一下又抽回中位”。解决方法是每控制周期最多改变 25us 脉宽,把”瞬间跳到绝对角度”变为”持续朝目标角度逼近”,±60° 全行程约 70ms 到位——想更跟手就调大 SERVO_MAX_STEP,想更柔和就调小。
调参指南
实测初值(APP/trace_task.c):
1 | kp = 12.0f; ki = 0.0f; kd = 6.0f; /* 实测初值 */ |
PID 整定顺序:先 kp(增大至小幅震荡再回退一档)→ 再加 kd(抑制震荡、改善过弯手感)→ 最后 ki(循迹场景多为 0)→ 验证(直道画 S 减小 kp,弯道冲出减小 base_pwm 或增大 kd)。
常见问题排查
| 现象 | 排查方向 |
|---|---|
| 舵机抖动 / STM32 复位 | 舵机是否独立供电、是否共地;调小 SERVO_MAX_STEP |
| 舵机转向后抽回中位 | 确认使用 Servo_Set_Angle_Smooth() |
| ADC 全 0 或全满 | PA1 须为模拟输入;检查灰度模块输出类型 |
| 直行偏向一侧 | 微调 SERVO_CENTER(1500 ±10us 标定) |
| 全亮/全灭不启动 | 安全锁机制:把车正常压在线上再上电 |
工程分层
1 | src/stm32-firmware/ |
基于 ST 标准外设库(SPL)开发,无 RTOS、无中断,适合作为嵌入式入门到进阶的完整实战参考。