做一辆PID调速循迹小车——从接线到调参的踩坑实录
一、为什么要做闭环:开环小车的"翻车现场"
很多新手第一台智能小车都是开环:给左右电机同样的 PWM,车就"理论上"直走。可一上地面就露馅——左边轮子先转、右边轮子打滑,走两米歪成香蕉;地毯、瓷砖、木地板换一张地面,速度全变。原因很简单:直流电机不是理想元件,同样的 PWM 占空比,因电池电压下降、电机内阻差异、地面摩擦不同,转速根本不一样。
要做"指哪走哪"的小车,必须把开环升级成速度闭环:用编码器实时测出每个轮子的转速,让 PID 算法不断微调 PWM,把实际转速拉回到目标值。这篇就带你从硬件接线、编码器读数,一路调到 PID 稳起来。
二、硬件清单与接线
模块 | 型号建议 | 作用 | 备注 |
| 主控 | Arduino UNO / DFRomeo | 逻辑运算、PWM输出 | Romeo板自带电机驱动可省TB6612 |
| 电机驱动 | TB6612FNG | PWM驱动直流电机 | 比L298N效率高、发热小 |
| 减速电机 | TT马达带霍尔编码器 | 执行+测速 | 1:30或1:45减速比,每圈64脉冲 |
| 循迹模块 | 4路红外循迹 / SEN05 | 识别黑线 | 数字输出,上拉需共地 |
| 电源 | 2S锂电(7.4V) | 供电 | 务必加共地,别用USB单独供电 |
TB6612 关键接线:PWMA/PWMB 接 Arduino 的 PWM 脚(如 D5/D6),AIN1/AIN2、BIN1/BIN2 接普通数字脚控制方向,STBY 拉高使能,VM 接电机电源,VCC 接 3.3~5V,所有 GND 必须连在一起。这一步是新手烧板重灾区:共地没接,控制信号参考电位漂移,电机动不动乱转。
三、编码器测速:M法与外部中断
测速最稳的做法是把编码器两相脉冲接到 Arduino 的外部中断脚(UNO 是 D2/D3),用 A 相触发中断、B 相判方向,每次中断计数加一。中断里只做"计数++",绝不放大段逻辑。
主循环里每 20ms 取一次增量,除以采样时间得到转速:
volatile long cntL = 0, cntR = 0;
void onEncL(){ cntL++; } // A相中断,只计数
void setup(){
attachInterrupt(digitalPinToInterrupt(2), onEncL, RISING);
}
// 主循环: 每20ms
float speedL = (cntL - lastL) * 60.0 / (PPR * 0.02); // 转/分, PPR=每圈脉冲数
新手常见坑:用了轮询读编码器(polling),转速一高就漏脉冲;或者没做软件消抖,一个脉冲触发多次中断,测出来虚高一倍。中断计数 + 合理采样周期(20~50ms)是稳妥起点。
四、PID到底在调什么
PID 的三个参数各有各的脾气,把它想成"开车调速"就不抽象了:
参数 | 直观作用 | 调大表现 | 太小表现 |
| Kp | 看当前误差就动手 | 响应快、易超调震荡 | 反应迟钝、追不上目标 |
| Ki | 记住历史欠账 | 消除稳态误差、易积分饱和 | 稳态偏差消不掉 |
| Kd | 预测趋势提前刹车 | 抑制超调、抗扰、怕噪声 | 压不住超调 |
调参口诀:先 P 后 I 再 D。先把 Ki、Kd 设 0,从小到大加 Kp 直到小车能追目标但轻微震荡;再加一点 Ki 消掉"总差一点点"的稳态误差;最后用 Kd 压住超调。速度环典型起步值(TT马达+TB6612):Kp≈2.0、Ki≈0.5、Kd≈0.1,按自己车微调。
float computePID(float target, float now, float &prevError, float &integral){
float error = target - now;
integral += error;
integral = constrain(integral, -255, 255); // 抗积分饱和
float out = Kp*error + Ki*integral + Kd*(error - prevError);
prevError = error;
return constrain(out, -255, 255); // 抗输出饱和
}
五、现场调试与踩坑记录
第一台车调 PID 时最容易"调着调着就乱",记下这几点能省一半时间:
• 上电先验编码器方向:用手转左轮,串口里 cntL 应增加;反了就把中断脚对调,否则 PID 一闭环就把轮子往反方向猛打。
• 先原地测空载再上地:空载转速稳了再放地面,负载变化要重调 Kp。
• 电池电压是关键变量:锂电从 8.2V 掉到 7.0V,同样 PWM 转速能差 15%。闭环能扛住,但别指望开环也稳。
• 循迹环与速度环要分开:循迹是"方向环"(输出左右轮速差),速度环是"执行环"(保证每个轮子达到目标速度)。把两个环混在一个 PID 里调,必崩。
六、实战案例:瓷砖地面循迹不抖头
一辆 4 路循迹小车在瓷砖上高频"左右抽搐"。排查后发现是速度环 Kp 给太高,编码器噪声被放大。把采样周期从 20ms 放到 30ms,加了一阶低通滤波 `(新值*0.3 + 旧值*0.7)`,再把 Kp 从 3.5 降到 2.2,小车过弯平滑了。结论:噪声是 Kd 的天敌,闭环前先确认编码器读数本身干净。
七、工程要点小结
• TB6612 务必共地、STBY 拉高,否则驱动不出力。
• 编码器走外部中断,中断里只计数,别做浮点运算。
• 调参顺序:P → I → D,先空载后上地。
• 速度环与循迹方向环解耦,各调各的 PID。
• 电池电压会漂移,靠闭环抗,不靠开环赌。
八、延伸阅读
• DFRobot Romeo 驱动板 + 编码器电机官方例程
• Arduino 外部中断与 `attachInterrupt` 用法
• 《PID 控制从零到工程实战》社区精华帖
(本文为 DIY 实践整理,参数为常见经验值,请结合自己的电机、电池与地面微调。欢迎在评论区贴出你的 PID 波形一起折腾。——爱控电子)


