做智能循迹小车绕不开PID控制算法这个词。不管是电磁三轮车跑赛道还是红外或摄像头循线只要有“根据偏差纠正方向”的需求最后都会落到闭环控制器的设计上。这篇文章把我自己从新车调试到顺畅跑完赛道的整套PID思路写出来包括电磁传感器偏差怎么算、增量式和位置式PID怎么写、转向环和速度环怎么配以及最容易被忽视的环境温度对ADC采样的影响。适合刚把机械结构装好、正在写控制逻辑的同学也适合那种车能跑但弯道老甩尾、直道画龙的人参考。1. 循迹小车为什么需要PID闭环从开关式控制说起1.1 阈值控制的天花板最直观的循迹控制方式就是阈值判断。电磁车跑起来靠近赛道中心线一侧的电感感应到的电压更高程序检测到“左边信号强”就往左打检测到“右边信号强”就往右打。红外循迹也一样左边光电管压线就左转右边压线就右转。这种控制思路叫开关控制也叫Bang-Bang控制实现简单一个if-else就能写完。但真跑起来阈值控制有两个很明显的缺陷。第一输出量只有“左满舵、回正、右满舵”几个固定档位舵机在小角度范围里来回猛打车头会不停摆动跑在直道上也是左右画龙。第二它没有办法区分“偏了1厘米”和“偏了5厘米”只要超过阈值就一视同仁地满舵修正。赛道稍微宽一点、车速快一点这种离散输出就会让小车像喝醉一样走不出一条平滑轨迹。我见过不少新手的车明明机械结构调得很正速度也不快就是控制逻辑太粗暴导致成绩上不去。开关控制还有个隐藏问题它是纯滞后的只有偏差产生之后才动作而且动作幅度是固定的没有“提前量”的概念。入弯时等到车身已经明显偏了才猛打方向出弯时又没有根据偏差减小的趋势提前回正结果就是弯道里一拱一拱地过。要解决这些问题就得把“偏差”变成一个连续量并且让控制输出和偏差的大小、变化趋势挂钩这就是PID闭环要做的事情。1.2 闭环控制到底“闭”在哪个环PID控制算法里的P、I、D三项本质都是对“目标值与实际值的偏差”做数学运算得到一个连续的控制量。在小车这个场景下三个关键量分别是目标值赛道中心线的位置通常对应偏差为0。测量值传感器读到的当前车身偏离中心线的程度比如电磁差比或像素偏移。输出值舵机PWM脉宽或者两个后轮的差速值。一开始我不太理解“闭环”是什么意思后来类比成开车就好懂了。你把车开到路中间如果方向盘只能左打满或右打满那车一定走不成直线但如果你能根据车离路边的距离连续修正方向盘角度感觉到车往左偏就稍微往右带一点这就形成了一个持续反馈、持续修正的循环。PID要做的就是把“稍微”“带一点”这种感觉量化为数学公式让单片机每秒几百次地替你做这个修正动作。闭环控制的价值在于系统不会因为一次外部扰动比如风吹、地面不平、轮胎打滑而彻底失控。只要传感器还活着控制器就会不断算偏差、不断修正理论上被控量会一直围绕目标值小幅波动而不是一路偏到赛道外面去。2. PID三个环节在小车上分别起什么作用2.1 比例项让舵机“跟”上偏差比例项是PID里最核心、最关键的一项公式里就是Kp乘以当前偏差。它做的事情很直白偏差越大输出越大。车身往左偏得多就多往右打一点方向偏差变小输出也线性变小。在小车调试中Kp太小车对偏差“没感觉”过弯时方向不够直接冲出赛道Kp太大控制过头车身在赛道中心线附近来回横摆直道变成蛇形。比例项的物理意义可以理解为一根连接车身和赛道中心线的弹簧偏差就是拉伸的距离弹簧的劲度系数就是Kp。距离越远拉回来的力越大但力太大就会拉过头于是产生振荡。有一个很常见的坑舵机角度和PID输出量纲不匹配。如果你用的舵机PWM范围是500到2500微秒中值1500而偏差范围是-1到1那么Kp直接给20到30是合理的因为输出范围大约是1500加正负20到30微秒舵机只有很轻微的角度变化。很多新手直接把网上别人的Kp抄过来但别人用的是电磁差比值你用的是归一化到-100到100的整数量纲完全不一样调出来的效果自然天差地别。这点后文调参部分会详细说。2.2 积分项消除系统性的固定偏移积分项的记忆是对偏差的累积用来消除那些“比例控制解决不了”的静态误差。什么叫静态误差假设小车因为左右轮胎气压不同、电机转速不一致、传感器焊歪了在直道上如果不做任何修正会持续往一个方向匀速偏。比例控制会根据当前偏差来回修正但最终可能会稳定在一个“偏差不为零”的状态因为需要一点残留偏差维持一个修正力。这时候积分项就起作用了它把误差一点点累计起来即使当前偏差很小累计值也会慢慢变大最终补上那个固定偏移。但积分项在赛车上是一个需要谨慎用的东西。循迹赛道变化快车在弯道里时偏差方向一直在变积分项累计的都是弯道大误差一旦出弯这个积分值还没来得及衰减会让车往反方向猛打一下这就是积分饱和现象。我的经验是电磁循迹小车大部分场景下可以不用积分项或者只给一个非常小的Ki比如0.001到0.01级别同时必须加积分限幅。如果车在直道有明显单边偏移优先检查机械结构和传感器安装而不是急着加I。2.3 微分项抑制过冲和振荡微分项对应的是偏差的变化速率也就是“偏差正在变大还是变小”。它的作用是提前感知偏差的趋势给控制输出一个阻尼效果。入弯时车身开始偏微分项检测到偏差在快速增大会提前反向增大输出让车更早、更平滑地转动出弯时偏差正在快速归零微分项又会收着点方向避免车头冲过中心线。用弹簧类比的话比例项是弹簧微分项就是阻尼器。没有阻尼的弹簧会反复弹跳加上阻尼后很快就稳定在平衡位置。在车上这部分表现为直道不再左右横摆弯道出弯回正也更干脆。Kd太小没效果Kd太大会放大传感器噪声车的舵机会高频抖动甚至发出滋滋声。有一个关键细节微分项对噪声非常敏感。电磁传感器的ADC值本身就有波动如果直接对原始偏差做微分噪声会被放大得很厉害。实际处理时要么对偏差做一阶低通滤波再微分要么对舵机输出做平滑处理。更讲究的做法是“微分先行”就是只对测量值求微分而不是对偏差求微分这样目标值跳变的时候不会产生微分尖峰。3. 电磁三轮循迹车上的PID落地3.1 传感器偏差的计算电磁信号怎么变成“偏了多少”电磁循迹的原理是赛道中心线铺设了一根通有20kHz交变电流的导线导线周围产生交变磁场。车上横排的工字电感感应到磁场后产生感应电压距离导线越近电压越高。电磁三轮车的常见传感器布置是水平左右两个电感加上中间或上方一个垂直电感形成水平方向和垂直方向的交叉布局。真正的偏差计算不是直接用左右电感电压做差因为电感电压同时受距离和电流大小影响。更稳的做法是归一化类似这样float calc_error(float LF, float RB) { float sum LF RB; if (sum 1.0f) { return 0.0f; // 信号过弱视为丢失 } return (LF - RB) / sum; }左右对称摆放时如果车身正好在导线正上方LF和RB电压相等偏差为0。偏左的时候LF大RB小偏差为正程序据此右转。除以两者之和的归一化处理把距离远近、电流大小这些共模因素消掉了偏差被限制在-1到1之间对后边调PID非常友好。这里有一个很值得说的点为什么用左右差比而不是直接用单边电压因为赛道电流可能不稳定、电池电压会随时间下降、电感离导线高度不同都会让绝对电压大幅变化。如果用绝对差值跑一圈下来同一个位置的偏差数值可能都不同PID参数就没法固定。归一化之后偏差变成一个相对量只要传感器布局对称不同场地的偏差值都比较稳定。实际工程里还可以用取对数再作差的方式压缩动态范围让远距离的弱信号变化也能被放大适合大前瞻的快速车。垂直电感的作用是判断车身在导线的左边还是右边同时辅助判断过弯方向。因为水平电感的感应电压在导线正上方时最强离开导线后衰减很快而垂直电感在导线两侧也会感应出不同极性的电压所以很多车队用它来识别元素、判断左右。对PID来说核心偏差量还是水平电感的差比值。3.2 舵机与差速两种转向方式的PID输出三轮小车常见的转向方式有两种。一种是前轮舵机转向、后轮单电机或双电机驱动类似真车的阿克曼结构PID的输出直接映射到舵机PWM。另一种是后轮差速转向前轮是万向轮通过控制左右两个后轮的转速差实现转向PID的输出加到差速值上。两种方式对PID输出量的处理有区别。舵机方式的输出很直观uint16_t servo_pwm SERVO_MIDDLE (int16_t)(Kp * error Ki * integral Kd * derivative); // 再做限幅确保在500~2500us之间 if (servo_pwm 2500) servo_pwm 2500; if (servo_pwm 500) servo_pwm 500;舵机响应速度有限一般几十毫秒才能转到目标角度所以PID输出不要变化太快输出限幅和输出平滑都很有必要。差速方式则把PID输出作为速度修正值比如左轮速度等于基础速度加修正值右轮速度等于基础速度减修正值。因为电机本身是一个惯性系统PID输出直接控制PWM占空比但实际转速不会马上跟上所以差速车的速度环和转向环通常要一起考虑。差速车有一个优势是转向力矩大原地就能转但在高速时横摆也会更明显对Kd的需求更高。阿克曼结构转向更自然、直道稳定性好但机械结构复杂舵机响应慢对前瞻和PID前馈的要求更高。选哪种方案取决于赛道类型和预算但PID算法的核心框架完全一致。实际项目中我建议把PID计算和输出执行分开。控制周期一般固定为5毫秒或10毫秒也就是200Hz到100Hz用定时器中断或者RTOS任务保证周期稳定。PWM输出到舵机或电机不需要每次都重新计算这样逻辑更清晰也方便后面加递归平均滤波、卡尔曼滤波之类的优化。3.3 速度环和转向环的配合一辆正经循迹车不止有一个PID闭环。转向环负责控制方向它的输入是传感器偏差输出是舵机角度或差速值。速度环负责控制车速它的输入是目标车速与实际车速的差输出是电机PWM。两个环单独调都能稳定工作但合在一起时问题就来了速度太快转向环来不及修正冲出赛道速度太慢积分项和微分特性的表现又会变化。我常用的配置是串级方案外环是转向环内环是速度环。转向环的输出不只是舵机角度还可以联动速度环的目标速度。比如偏差很大的时候说明车在入弯或进急弯这时候主动把目标速度降下来偏差很小的时候说明在直道允许速度环把车速拉上去。用大白话说就是“直道油门给足弯道提前减速”这就是很多人说的速度前瞻。实现上最简单的是根据偏差绝对值做分段目标速度if (fabs(error) 0.2f) { target_speed FAST_SPEED; } else if (fabs(error) 0.5f) { target_speed MID_SPEED; } else { target_speed SLOW_SPEED; }更平滑的做法是用一个映射函数比如目标速度随偏差绝对值线性下降。也可以在转向PID算完输出后根据输出大小修正目标速度输出越大说明弯越急车速就得越低。这两条路我都试过简单场景下分段就够了但跑复杂综合赛道时线性映射配合速度环的加减速限制会让车在入弯时有一个可控的减速过程不会一下子顿挫。无论如何速度环的响应需要比转向环慢一些否则速度波动会对传感器读数造成干扰两个环之间互相激励最后就是整车抖动。4. 调参实战一套在小车上可行的PID参数调试流程4.1 调参前的静态标定没有静态标定就调PID等于盲人摸象。装机之后我建议先做这几件事确认传感器方向与极性。手动把车放在导线正上方读偏差是否接近0往左偏移偏差是否为正。如果方向反了PID输出会让车越偏越远直接飞出去。检查方法是在代码里固定输出一个已知舵机角确认舵机转向和PID期望一致。检查舵机中值。断电状态下把PWM调到1500微秒附近看前轮是否真的指向正前方。如果舵机中值不对PID输出再准也会表现为单侧跑偏。用螺丝刀调整舵盘位置或代码里加一个偏移量修正。记录ADC原始值的动态范围。把车在赛道中心线左右大幅度移动记下左右电感电压的最小值和最大值。这个范围决定归一化偏差的灵敏度和是否需要做非线性映射。做一个简单的遥控或按键测试模式方便不铺赛道也能验证舵机、电机的响应。静态标定这一步很多新手直接跳过结果调PID时根本分不清是参数问题还是安装问题。比如车往左偏你以为是Kp不够实际上是因为舵机中值没有调正PID输出被中和掉了一部分。排查这类问题特别浪费时间我在第一次做电磁车的时候就在这上面耗了一个下午。4.2 先调P再调D最后动I调参顺序我基本固定先把I和D设成0只保留Kp在低速下跑直道和简单弯道看车能不能大体跟上赛道。然后逐步加大Kp直到车在直道上开始出现轻微的来回摆动这时候记下这个临界Kp值再往回调到它的60%到70%作为基础值。P调到位的标志是入弯时方向反应及时直道不至于画龙但弯道里还会有一些角度不够或过冲的现象。此时开始加Kd每次增加一点观察直道横摆是否被抑制。Kd的合适量级通常比Kp小一到两个数量级。比如Kp30时Kd大概在0.5到3之间具体看控制周期和传感器滤波强度。I是最后才考虑的而且我建议能不加就不加。前面说过循迹赛道的参考值一直在变化积分项很容易积累出一堆历史债务。只有当车在一条很长很直的直道上表现出固定单边偏离并且你确认机械安装没问题时才考虑加一个很小的Ki同时把积分限幅设到输出的5%以内。这里可以给一组我实际用过的初始参数注意量纲是归一化偏差-1到1、输出为舵机PWM微秒偏移量、控制周期10毫秒参数典型初始值调整方向Kp25~40直道画龙减小入弯迟钝增大Ki0~0.02直道固定偏向增大Kd0.5~3出弯过冲、振荡增大积分上限输出量程的5%饱和甩尾时减小调参时要一次只动一个参数改完了至少跑完整一圈再判断效果不要看到一次弯道表现好转就急着又改下一个参数。电磁车的传感器数据有滞后某一圈的偶然现象很可能不是参数变化带来的。4.3 温度漂移与动态参数问题这节要聊一个很多人忽视、我踩过不少坑的点温度对PID控制的影响。电磁传感器的核心是电感线圈线圈的电阻会随温度变化磁芯的导磁率也会随温度变化导致同样放在赛道中心线上早晨和中午读到的ADC值就可能不一样。更关键的是单片机ADC通常以供电电压作为参考电压电池从满电放到欠压参考电压会缓慢下降原始ADC读数会整体漂移。如果PID的输入偏差直接用ADC绝对差值计算参数可能上午还好好的下午就突然方向不准了。这也是我强烈建议使用归一化偏差的原因之一。归一化除法能消掉共模增益变化温度导致整体信号按比例缩放时偏差值变化不大。但温度还会引起左右传感器不对称漂移那就没办法靠归一化完全解决。实际工程处理有几种思路传感器采样时做零点校准小车启动后的前几秒采一组背景值后续读数减掉背景值再算偏差。定期把车放到中心线上自动校准左右平衡。如果赛道环境温度变化特别大可以在车上加一个温度传感器标定出温度与电感信号之间的修正曲线用查表方式做补偿。这就是很多温控PID系统里的思路本质上是把外部扰动变量引入控制器而不是让PID盲目硬扛。ADC采样参考用内部基准电压或者用电源电压监测引脚做归一化。顺带说一句PID不只是车用的。温度控制里的PID和循迹小车完全同构被控量是温度传感器是NTC或热电偶执行器是加热器的PWM占空比控制器同样是一个偏差闭环。你在小车上练会的Kp、Ki、Kd手感放到温控系统里一样能快速上手区别只是被控对象的时间常数从毫秒级变成了秒级甚至分钟级微分项和积分项的灵敏度完全不同。5. 常见问题与排查技巧实录5.1 直道画龙、左右摇摆直道上车头反复左右摆动最常见的两个原因一个是Kp太大比例修正过度另一个是Kd不足系统缺少阻尼。先试降低Kp如果降下来之后直道稳定了但弯道反应变慢那就是Kp和Kd的匹配问题需要把Kp回调一点然后增大Kd。还有一个容易被忽略的原因是传感器采样噪声。ADC值毛刺很大时偏差信号本身就在抖动PID输出自然跟着抖。我建议先看串口打印的偏差数据如果偏差波形本身就是锯齿状的先在软件上做滑动平均或者卡尔曼滤波再调PID不然怎么调都调不干净。舵机自身的响应特性也可能造成高频抖动。舵机内部有一个位置闭环输入PWM变化太快时它会产生“追不上”的现象表现为颤抖。这种情况下要在PID输出后做限幅变化率每次控制周期最多允许输出改变一定微秒数让舵机有一个平滑的追踪过程。5.2 弯道甩尾、出弯回正慢弯道甩尾往往发生在高速入弯时重心转移加上转向过猛后轮失去抓地力车尾往外甩传感器瞬间看到极大的偏差PID输出又进一步加大形成恶性循环。降低入弯速度是首要手段其次检查转向环输出限幅不要让舵机在过弯时直接打到最大角度。出弯回正慢则一般和微分项有关。如果Kd不够车出弯后偏差已经在减小但方向输出没有同步回缩车头就会冲过中心线在弯后直道上走出一个S形。这时候适当增加Kd让系统在偏差趋于零的过程中提前“踩刹车”。但Kd增加到一定程度传感器噪声会被放大所以需要先保证偏差滤波质量。一个比较实用的技巧是给转向输出加一个死区。当偏差绝对值小于一个很小的范围时舵机直接给中值不让它频繁微调。这能减少直道阶段的微小抖动但死区不能太大否则车会在中心线附近出现一个“失控”区间反而来回寻找。一般死区设在偏差范围的1%到2%以内。5.3 启动猛冲、积分饱和有些车一上电就往一个方向猛打然后跑飞大概率是积分项起始状态没有清零。程序启动时积分变量里残留着上一次运行的累计值直接参与输出。解决方法很简单上电时初始化PID状态结构体把积分值、上一次偏差全部清零。运转过程中出现积分饱和表现为出弯时方向反打过度。这是因为弯道里偏差长时间很大积分值一直在累计等到车身回正之后积分值还维持在一个很高的水平继续输出大力矩把车推向另一边。处理方法是加积分限幅同时使用“遇限消积分”策略当PID输出达到执行器极限时停止积分累加。有些控制库支持抗积分饱和没有的话自己写也就几行代码的事情。增量式PID天然不容易积分饱和因为它输出的是改变量而不是绝对值积分体现在累加的过程中天然受到输出限幅的约束。这也是一些老手在速度环上偏好增量式的原因之一。位置式PID直观、适合舵机这种直接输出角度的执行器增量式更适合电机这种需要叠加到前一个状态上的执行器。两种写法本质等价选哪种取决于执行器的物理意义没有绝对的好坏。5.4 常见问题速查表现象可能原因处理办法上电跑飞PID输出极性反、舵机中值错先静态标定再确认偏差方向与转向方向直道蛇形Kp过大、Kd不足、信号噪声大降Kp、加Kd、对偏差做低通滤波弯道冲出赛道速度过快、Kp不足、前瞻太近降速、加Kp、调整传感器前瞻距离出弯S形Kd不足或过大、速度未恢复重点调Kd检查速度切换是否太激进固定单边偏移舵机中值偏、左右传感器不对称调整中值偏移、静态标定左右增益高频转向抖动舵机跟踪不上、微分噪声大输出限幅变化率、滤波降噪参数随场地变化传感器未归一化、ADC参考不稳用归一化偏差、校准ADC参考电压电池电量影响大供电电压波动、参考电压漂移加稳压模块、使用内部参考基准每条排查路径我都实际走过。最花时间的往往不是PID参数本身而是“没想清楚问题出在控制环节还是机械/传感器环节”。所以我建议做一层简单的日志打印把偏差、P项、I项、D项、最终输出量每隔几十个控制周期发出来用无线串口观察。数据比感觉可靠得多尤其在高速段肉眼根本来不及看清车到底是哪一瞬间甩出去的。最后再分享一个习惯把一批调试用的参数固化在代码里用拨码开关或按键切换赛道调试时边跑边换参数不用每次重新编译烧录。这看起来是个笨办法实际能省下大量重复烧录的时间。等你把Kp、Ki、Kd在不同赛道元素下的表现都摸透了再去做分段PID、模糊PID或者模型预测控制都会顺手很多因为你对“偏差、输出、响应”这三个词已经有了实实在在的手感。