跳到正文
XiaoGao 的个人博客

NO.20 · 嵌入式开发

STM32 灰度循迹小车:8 路传感器 + PID + 舵机转向

STM32F103 灰度循迹小车完整实战——CD4051 多路复用采集 8 路灰度、加权位置误差建模、抗饱和 PID 转向、弯道自动减速与舵机平滑步进,5ms 控制周期全闭环。

发布 DATE
字数 WORDS
1,050
阅读 READ
3 分钟

一条黑线、一台小车、一套完整闭环。这个项目用一颗 72MHz 的 STM32F103C8T6,把 8 路灰度传感器、加权位置误差、抗饱和 PID、舵机转向和双后轮调速串成了一条 200Hz 的控制链路,是嵌入式入门到进阶的完整实战参考。

系统架构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
               ┌──────────────────────────────────────────────┐
│ STM32F103C8T6 72MHz │
│ │
┌──────────┐ │ ┌────────┐ ┌─────────┐ ┌────────────┐ │ ┌─────────┐
│ 8 路灰度 │──▶│ │ ADC1 │──▶│ 加权 │──▶│ PID 控制 │──▶│ MG995 │
│ 传感器 │ │ │ PA1 │ │ 位置误差│ │ kp/ki/kd │ │ 舵机 │
│ (CD4051) │ │ └────────┘ └─────────┘ └─────┬──────┘ │ │ 前轮转向 │
└────┬─────┘ │ PB0/PB1/PB10 选通道 │ │ └─────────┘
│ │ ▼ │
3 位地址线 │ ┌────────────┐ │ ┌─────────┐
│ │ 弯道减速策略 │──▶│ L298N │
│ │ PWM 调制 │ │ 双后轮 │
│ └────────────┘ │ └─────────┘
└──────────────────────────────────────────────┘
控制周期: 5ms (200Hz)

硬件平台

项目 规格
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
2
3
4
5
kp = 12.0f;  ki = 0.0f;  kd = 6.0f;   /* 实测初值 */
integral_limit = 30.0f; /* 积分限幅 */
base_pwm = 400; /* 直道 PWM (~40%) */
min_pwm = 250; /* 弯道 PWM 下限 (>电机死区) */
max_servo = 60; /* 舵机限幅 ±60° */

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
2
3
4
5
6
src/stm32-firmware/
├── APP/ # 应用层:加权误差 + PID + 弯道降速(trace_task.c)
├── BSP/ # 板级层:delay / 灰度 / 电机 / 舵机 / 串口
├── CMSIS/ # Cortex-M3 内核 + 启动文件
├── FWLib/ # ST 标准外设库
└── USER/ # 工程入口:main.c + AllHeader.h

基于 ST 标准外设库(SPL)开发,无 RTOS、无中断,适合作为嵌入式入门到进阶的完整实战参考。

仓库

    ↑↓ 选择↵ 打开>theme · >random · >top · >archive