PID算法实战:循迹小车设计与参数整定全攻略

发布时间:2026/10/5 5:53:45
PID算法实战:循迹小车设计与参数整定全攻略 做循迹小车这事儿听起来像是大学实验室里的课程设计但真正上手之后你会发现从“能跑”到“跑得稳”之间隔着一整个PID算法。我前前后后做了好几版基于PID算法的循迹小车从最开始的51单片机加红外对管到后来的Arduino加摄像头再到帮学弟调的电磁三轮车踩过的坑比写出来的代码多得多。这篇博文就把我用PID算法调校循迹小车的完整思路、代码实现、参数整定过程以及那些光看教程学不到的排查经验一次性讲清楚。这个项目能解决什么问题其实很简单让小车沿着一条引导线稳定地高速行驶不出线、不抖动、不原地打转。网上搜“循迹小车”能搜出一堆教程但大部分止步于“三路红外开关量控制”那种方案在低速下勉强能用速度一上来就原形毕露走S形、冲线、在弯道直接飞出去。PID算法解决的就是这种“连续控制”的问题让转向量随偏离程度平滑变化。这个项目适合刚入门嵌入式、想搞明白PID到底怎么落地的人也适合准备参加智能车竞赛的团队拿来当基础框架。1. 项目整体设计思路先想清楚再动手1.1 循迹小车的核心问题与方案选型循迹小车的本质是一个“传感器-控制器-执行器”的闭环系统。传感器负责感知小车相对引导线的位置偏差控制器根据偏差计算转向控制量执行器通常是两个直流电机通过差速实现转向。听起来简单但每个环节都有多种方案可选选型直接决定后续的调试难度和性能上限。传感器方案主要有三类红外灰度传感器阵列、摄像头视觉循迹、电磁感应循迹。红外灰度阵列最便宜也最容易上手适合第一次做PID闭环摄像头方案信息量大能看到远处弯道适合高速循迹电磁循迹则是通过工字电感检测赛道导线产生的磁场比赛专用方案对环境光完全免疫。我这次博文的主要案例是红外灰度阵列版本因为它的数据最干净、最可控能把注意力集中在PID本身的调校上同时在后面的章节里也会补充摄像头和电磁方案的误差模型差异。主控我选了Arduino Nano理由很实在上手快、中断和PWM资源够用、社区资料多。电机驱动用TB6612FNG而不是常见的L298N。L298N的压降实在感人两节锂电池供电到电机端能少掉快2V转速和响应都会差一截。TB6612的内阻低发热小体积也小配合Arduino完全够用。电机选的是带霍尔编码器的N20减速电机1:30减速比实测空载转速大概300转左右。底盘直接买现成的亚克力板车架别自己打孔省下的时间足够你多调两组参数。1.2 为什么非要上PID阈值开关控制到底差在哪很多教程里的“三路循迹”逻辑是这样的中间传感器压线直行左边压线说明车身偏右左轮减速右轮加速右边压线反向操作。这是典型的开关量控制输出只有“左转、右转、直行”三个状态本质上是离散的Bang-Bang控制。这种控制在低速0.3m/s以下下问题不大但速度一提上来问题就全来了传感器检测到偏差的那一刻车身已经偏出去一截而Bang-Bang控制给的是固定幅度的修正量要么修正不足导致继续偏移要么修正过头导致向另一侧振荡。表现出来就是小车走S形弯道处来回摆头严重的时候直接冲出赛道。这就像开车时方向盘只有三挡可调要么不打要么打满正常人开不出直线。PID算法的核心价值在于把“偏了多少”这个连续量引入控制。偏差越大修正力度越大偏差变化越快阻尼越大。这样转向量是连续平滑的小车就能在高速下保持稳定。说句实在话PID不是唯一能跑的方案像模糊控制、纯P控制甚至查表法也能凑合但PID是工程上性价比最高、最通用、最容易调参的选择这也是我强烈建议直接用PID的原因。1.3 硬件选型与整体架构我的硬件清单如下Arduino Nano主控板一块五路红外灰度传感器模块一个单独买的传感器支架比集成模块好调整间距TB6612FNG电机驱动模块一个带编码器的N20电机两个7.4V 18650两串电池组一个5V稳压模块一个给主控和传感器供电亚克力底盘一套万向轮一个杜邦线和热缩管若干。整体信号流是这样的五路灰度传感器的数字输出接到Arduino的数字引脚主控读取传感器状态后计算位置偏差值经过PID计算得到差速控制量再通过TB6612驱动左右两个电机差速转动。电源方面电池直接给电机驱动供电稳压模块降压后单独给主控和传感器供电避免电机启动瞬间拉低电压导致主控复位这个问题很多人第一次做都会遇到而且是玄学故障的头号来源。2. PID算法核心细节增量式与位置式2.1 PID公式拆解与直观理解PID的三个字母分别代表比例、积分、微分。比例项看当前偏差有多大偏差大就给大修正积分项看过去偏差累积了多少用来消除长期存在的静差微分项看偏差变化的趋势偏差快速变化时给一个反向的阻尼力抑制振荡。位置式PID的公式是u(k) Kp*e(k) Ki*sum(e) Kd*(e(k)-e(k-1))输出是绝对的执行量。用在电机控制上u(k)直接就是PWM的占空比。增量式PID的公式是du(k) Kp*(e(k)-e(k-1)) Ki*e(k) Kd*(e(k)-2*e(k-1)e(k-2))输出的是执行量的增量最终PWM等于上一次的值加上这个增量。用一个生活类比来解释你开车要保持车道如果只盯着当前偏离量打方向盘这就是P如果发现方向总是微微偏右于是每次都比上一次多回正一点这就是I如果发现车正在快速向右偏你提前向左回一把抑制这个趋势这就是D。三者配合车才能稳定在车道中心。2.2 增量式PID为什么在循迹小车里更常用虽然位置式PID看起来更直观但在循迹小车这个场景下我更推荐增量式PID。原因有三个第一增量式PID天然没有积分饱和问题。位置式的积分项是对偏差的累加如果小车长时间出线积分值会累积到很大即使后来偏差归零积分项仍然会持续输出很大的修正量导致小车猛打方向。增量式是对偏差做差不存在长期累积的问题输出天然收敛。第二增量式PID的输出是增量而非全量这意味着即使PID算出的增量暂时异常最终输出的PWM也不会发生剧烈跳变对电机和驱动的冲击小得多。位置式输出突变时电机会瞬间从低速切到高速机械冲击非常大。第三增量式PID便于实现限幅和手动/自动切换。我经常在调试时先把PID断开手动给一个固定的差速比验证机械结构和传感器逻辑没问题之后再切入PID闭环。增量式的输出逻辑让这种切换非常顺滑位置式则容易在切换瞬间产生一个很大的阶跃输出。不过增量式也有个弱点它只输出增量需要一个地方保存当前的PWM基准值。如果赛道左右弯道分布不均或者电池电压变化导致基础速度漂移依赖固定基准值的增量式可能表现不如位置式。解决方案通常是给基准速度加上一个根据弯道曲率调整的前馈项或者定期校准基准速度。2.3 摄像头循迹和电磁循迹的误差模型差异摄像头循迹的误差计算和红外阵列完全不同。摄像头拿到的是整幅图像经过二值化后提取出白线的轮廓然后计算白线的中心位置和图像中心位置的像素差归一化后作为误差值。这个误差是连续且平滑的信息量大得多可以看到前方几十厘米的弯道趋势所以摄像头车可以跑出非常高的速度。代价是图像处理需要算力主控至少用STM32或者带硬件二值化的OpenMVArduino Nano那点算力跑图像处理太吃力。电磁循迹的误差计算用的是差比和算法。三个电感或更多按一定间距横向排列每个电感感应到赛道导线的磁场强度根据场的衰减特性靠近导线的电感信号最大。误差值计算公式是(A_right - A_left)/(A_right A_left)结果范围在-1到1之间。电磁方案的好处是完全不受光照影响在室外或者光线复杂的环境也能跑而且可以在赛道上通过不同频率的导线组合传递额外信息。不管哪种传感器方案PID的计算层是一样的输入是归一化后的偏差输出是差速控制量。这就是PID的通用性所在——传感器部分随便换控制算法那层代码基本不用动。很多人容易把传感器方案和PID纠缠在一起其实想清楚分层设计之后代码结构会干净很多调参也更有针对性。3. 循迹小车详细制作与程序实现3.1 底盘组装与传感器布局底盘组装这一步没什么太高深的技巧但有几个细节直接影响后续控制效果。首先是传感器支架的高度灰度传感器的发射管和接收管离地面太近光斑太小遇到不平整赛道容易误判离得太远光线发散黑白区分度下降。我用下来最合适的距离是1到1.5厘米装好之后拿张白纸测试能看到明显的红点光斑且边界清晰。传感器横向间距也要注意。五路传感器如果摆得太密转弯时可能一瞬间全部脱离黑线导致偏差计算跳变摆得太宽又起不到精细检测的作用。我建议传感器间距取8到12毫米这样总宽度大约覆盖4到5厘米刚好比常见的2厘米黑线宽两倍左右。这个宽度下直道能稳定检测急弯也能保留至少一路传感器在线内。电机的安装要注意左右对称。亚克力底盘的电机孔位如果开模精度不够两个电机装好之后轴线不平行小车会天然朝一个方向偏跑PID只能靠不停的修正来对抗这个偏置表现就是直道也在轻微S形。装好之后空转测试两个轮子转速一致的时候小车应该是完全直线走的如果发现偏优先检查电机座是否装正别急着调PID。3.2 传感器校准与基础循迹逻辑灰度传感器的输出有两种数字量高/低电平和模拟量电阻分压后的电压值。数字量通过电位器调节比较阈值简单但有隐患——环境光线变化之后阈值就不准了。我比较推荐用模拟量输出在程序里做动态阈值处理。校准方法是这样的把小车放在赛道纯白区域连续读取50次传感器模拟值取平均作为白底基准再放在黑线上方同样取50次的平均作为黑线基准。然后当前读数的判定阈值设为黑白基准的中间值。实测下来这种动态阈值比固定阈值稳定得多至少下午阳光从窗外照进来导致整体光强变化的时候不用重新手动拧电位器。基础循迹逻辑是计算偏差值。五路传感器从左到右编号为1到5每一路在检测到黑线时输出一个权重值。我常用的权重方案是左边第一路权重-4第二路-2中间0右边第二路2右边第一路4。当前偏差等于所有检测到黑线的传感器的权重之和再除以检测到的传感器数量。这样即使同时有两路压线偏差也是连续变化的不会从-4直接跳到4。把这个偏差值归一化到-1到1之间就可以送给PID了。3.3 PID程序代码实现Arduino示例我用的Arduino代码框架大概是这样的。主循环里分成四步读传感器、算偏差、算PID、输出PWM。下面是一份完整的增量式PID关键代码示例基于Arduino环境我实测跑过没问题。// PID参数后面调参就是改这三个值 float Kp 180.0, Ki 15.0, Kd 80.0; // 偏差和PID相关变量 float error 0.0, lastError 0.0, prevError 0.0; float integral 0.0, derivative 0.0, output 0.0; // 基础速度和PWM限幅 int baseSpeed 200; // 基础PWM0-255 int maxPWM 255; int maxDelta 120; // 差速最大增量防止转向过猛 // 读取传感器并计算偏差返回-1到1之间的归一化偏差 float readLinePosition() { int sensorValues[5]; float weightSum 0.0, valueSum 0.0; int weights[5] {-4, -2, 0, 2, 4}; for (int i 0; i 5; i) { sensorValues[i] analogRead(sensorPins[i]); // 判断是否压线低于动态阈值视为检测到黑线 if (sensorValues[i] threshold[i]) { weightSum weights[i]; valueSum 1.0; } } if (valueSum 0) { // 所有传感器都没有压线可能是出线了保持上一次偏差 return lastError; } else { return weightSum / valueSum / 4.0; // 归一到-1到1 } } // 增量式PID计算返回本次应增加的差速量 float calculatePID(float err) { integral err; // 累积积分项 if (integral 50.0) integral 50.0; // 积分限幅 if (integral -50.0) integral -50.0; derivative err - lastError; float delta Kp * (err - prevError) Ki * err Kd * (err - 2 * lastError prevError); prevError lastError; lastError err; return delta; } void loop() { error readLinePosition(); float delta calculatePID(error); // 限幅 if (delta maxDelta) delta maxDelta; if (delta -maxDelta) delta -maxDelta; // 差速输出左电机加delta右电机减delta int leftPWM baseSpeed (int)delta; int rightPWM baseSpeed - (int)delta; // PWM限幅 leftPWM constrain(leftPWM, 0, maxPWM); rightPWM constrain(rightPWM, 0, maxPWM); setMotorSpeed(leftPWM, rightPWM); delay(10); // 控制周期10ms相当于100Hz }这段代码里有几个地方需要特别解释一下。constrain函数限幅保证PWM不会超出0到255的范围不然Arduino的analogWrite会发生奇怪的行为。积分限幅是为了防止积分项在长时间压线时累积过大虽然增量式PID没有积分饱和问题但代码里仍然保留了积分项因为纯增量式只靠PD两个参数在低速大弯道时可能修正不足。delay(10)定了一个10毫秒的控制周期这个时间要和你实际跑的主循环计算耗时匹配理论上控制周期越短响应越快但也不能短到传感器数据还没稳定就读取。3.4 参数整定方法与实测记录调参是PID项目里最让人头大也最核心的阶段。我的调参流程分三步走每一步都有明确的判断标准。第一步先把积分项和微分项置零也就是Ki0Kd0只留Kp。从很小的值开始比如Kp20让小车以较低速度跑直线。如果小车反应迟钝、在弯道明显跟不上就加大Kp每次增加20到30直到小车能过弯但开始出现轻微S形摆动为止。这个阶段的判断标准就是“能过弯但开始抖”记下这个Kp值大概是我车上的180左右。第二步加入微分项Kd。Kd的作用是抑制过冲和振荡所以当Kp已经调到临界振荡值时逐渐加大Kd每次增加10到20跑直道观察S形是否明显收敛。如果Kd太大小车会显得僵硬直道出现高频微颤用手摸底盘能感觉到震动这就是Kd过大了回退到不震动的值。我实测合适的Kd在80左右。第三步最后加入积分项Ki。Ki的作用是消除稳态误差比如小车在长直道始终保持轻微左偏说明有个系统性的偏置这时加一点Ki可以有效补偿。Ki通常很小从5开始加见效后基本在10到20之间就够。Ki过大的表现是过弯时转向过度直道也会出现缓慢的周期性摆动。我记录过一组调好的参数Kp180Ki15Kd80baseSpeed200控制周期10ms。在这个参数下小车能以大约0.8m/s的速度稳定跑过90度弯和S弯直道几乎不摆头弯道也没有明显的冲出趋势。当然这个参数不是万能的换一块电池、换一组轮胎、甚至换个比赛场地都有可能需要微调。所以我在代码里把参数定义成全局变量调参的时候不用重新编译直接用串口修改方便太多了。提示调参的时候一定要先把小车垫起来让轮子空转确认PID输出逻辑正常之后再下地跑。不然小车冲出去撞墙的瞬间你大概率会后悔没多做这一步。4. 常见问题排查与避坑技巧实录4.1 小车走S形、抖动振荡问题走S形是最常见的现象表现为小车在直道上左右摆动幅度随时间扩大或缩小。原因一般是两个Kp过大导致过冲或者微分项不足导致阻尼不够。排查思路是先降低Kp如果S形幅度立刻变小那就是Kp的问题。另一个容易忽略的原因是传感器数据跳变。如果偏差计算在相邻两次采样之间从0.2直接跳到0.8PID微分项会产生一个巨大的突变让小车猛地打一下方向。这种问题通常出在传感器布局上传感器间距太大或者阈值判断不稳定都会导致偏差不连续。解决方案是把偏差计算改成加权平均并且对连续采样值做简单的低通滤波比如新值占0.7旧值占0.3效果立竿见影。还有一个机械层面的原因电机响应不一致。左右两个电机即使是同型号由于制造公差在同一PWM下转速也会有差异。处理方式是加一个速度闭环用编码器测量转速把PID对外输出作为目标速度再做一层内环PID。双闭环结构在比赛级小车里是标配普通DIY项目如果不想增加复杂度至少要做一次电机转速标定把两个电机的死区PWM差异补偿掉。4.2 传感器误判与光线干扰灰度传感器对光线非常敏感同一个位置早上测和下午测模拟读数可能差出两三百。最彻底的解决方案是前面说的动态阈值其次还可以给传感器模块加遮光罩用热缩管或者黑胶带把传感器周围包一圈只留底部透光能大幅降低侧向光干扰。还有一种情况是传感器压在两段黑线之间的白色间隙上尤其是转弯时赛道线变弯曲传感器可能正好落在黑线边缘读数在黑白之间来回跳变。解决办法是在偏差计算里加迟滞回环也就是压线阈值和释放阈值不设成同一个值压线阈值设得低一点释放阈值设得高一点。这样读数在边界处来回抖动时输出状态不会跟着乱跳。4.3 积分项失控与起步问题小车静止放在赛道上刚上电时偏差可能很大如果积分项累积得很快起步瞬间会猛打方向甚至原地转圈。这个问题在位置式PID里尤其明显增量式稍微好一些但也不能完全免疫。我的做法有两个一是在程序里加积分限幅这在前面的代码里已经写了二是加一个“起步平滑”逻辑上电后前50个控制周期强制Ki为0让P和D先把偏差带回去等偏差收敛到一定范围后再启用积分。这个平滑启动逻辑虽然只多几行代码但对比赛场景的稳定性提升很明显特别是小车需要在起跑线静止等待发车指令的时候。4.4 电池电压下降导致的性能漂移问题锂电池电压从充满的8.4V一路降到6.6V左右电机转速会明显下降同样一组PID参数满电时稳定低电时可能变得迟钝甚至无法完成转向。这个问题的本质是PID输出的PWM百分比对应的实际转速变了但控制算法不知道。排查方法是在程序中监控电池电压做电压前馈补偿。具体做法是设定一个基准电压比如7.4V实时测量当前电压然后对baseSpeed做一个修正电压越低baseSpeed的百分比越高保证实际转速恒定。这个功能在Arduino上实现也不难用两个电阻做个分压器接到模拟引脚用analogRead读电压在程序里做一个映射就行。实测下来加了这个补偿之后小车的速度稳定性在整个电池放电周期内保持得很好比赛最后一圈和第一圈的表现差距小很多。我自己做这些PID小车项目最大的体会是PID本身不是难点难的是搞清楚偏差是怎么算出来的、输出是怎么作用到执行器上的。很多初学者一上来就抄PID代码跑不直就开始怀疑算法其实大部分问题出在传感器数据不干净或电机响应不一致上。如果你打算做自己的循迹小车我建议先把传感器的偏差计算调试到平滑连续再用PID去放大这个偏差做控制顺序别反否则会把你绕晕在调参的死循环里。后面我还打算把手上的摄像头版本加个自适应速度调节弯道减速直道加速那个又是另一套优化思路了等调通之后再开一篇详细聊聊。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询