ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

循迹小车设计实战:从PID算法到红外传感器调参

循迹小车设计实战:从PID算法到红外传感器调参 1. 循迹小车到底该怎么设计先想清楚这四件事很多人一上来就搜“PID算法”“循迹小车代码”然后复制一片代码下载到板子里结果车要么不走直线要么过弯直接冲出去。我刚开始做那会儿也是这样后来才明白循迹小车最难的不是代码而是把机械、电路、控制逻辑和赛道环境凑到一起后系统能不能稳定工作。这里说的“稳定”不是让车走一两圈而是连续跑几十圈基本不失误。先聊方案选型。循迹传感器主流有三类红外对管阵列、摄像头、电磁传感器。它们的核心区别在于“你用什么信息来代表赛道偏差”。传感器类型采集信息计算方式适合赛道难度红外对管阵列黑白线反射差异根据管的亮灭计算位置偏差浅色底深色线或反线较低摄像头图像灰度/颜色提取赛道中线计算偏差多种环境高电磁传感器导线中交变电流产生的磁场根据感应电压差计算偏差电磁赛道中高红外对管最经典、最便宜也最适合入门。用一排红外对管比如8路、14路贴在车头下方黑色线吸收红外光白色底面反射红外光每个管子输出一个数字电平组合起来就是一个“赛道在哪儿”的位置值。把这个位置值做成偏差量喂给PID就是最常见的方案。摄像头方案更像人眼能看到赛道全貌但图像处理需要算力入门选手容易被光照干扰而且分帧率、透视畸变、白平衡这些细节搞起来很烦。电磁传感器是电磁组竞赛用的靠电感检测赛道导线里的20kHz交变电流信号计算左右电感电压差得到偏差这个方案不依赖光线但需要自己铺导线、准备信号源。我的建议是第一次做就用红外阵列 PID。这不是说其他方案不好而是红外阵列能让你把注意力集中在PID参数和整体逻辑上而不是被传感器硬件干扰。等把PID吃透了再去玩摄像头和电磁你会发现它们只是在“偏差信息来源”上不同控制逻辑是相通的。还有一个容易被忽略的点循迹小车是一个闭环系统不是简单的“检测到线就转弯”。所谓闭环就是不断地测量当前输出比如当前位置偏差与期望值比如偏差为0作比较计算出误差然后通过控制算法调整执行器电机让误差不断减小。PID就是最常用的闭环控制算法。没有闭环的车走直线都会歪更谈不上过弯。那么整个设计思路就很清晰了底盘和电机负责执行传感器负责“看”赛道单片机负责“想”该怎么转PID算法负责把“怎么转”这个模糊概念变成具体的PWM占空比输出。接下来我从算法讲起因为这部分是灵魂然后讲硬件和代码最后聊调试和排坑。2. PID算法是循迹的核心先把公式和参数吃透2.1 PID三个字母到底在干什么PID是比例Proportional、积分Integral、微分Derivative三个词的缩写。用一个生活化的例子来解释你开车想保持在车道正中间如果车偏左了你会怎么办比例P你看到偏了多少就往反方向打多少方向盘。偏差越大修正力度越大。积分I如果车一直有一点偏左但是偏差很小光靠P可能修正不到位时间久了就会累积一个“持续偏移量”。积分项就是把过去一段时间的偏差累加起来消除这个稳态误差。微分D你不仅看现在偏了多少还看它正在以什么速度偏向一边。如果车正在快速往左偏即使偏差还不大你也得提前往右发力抑制这种趋势。微分项就是阻尼防止过度修正和震荡。在连续系统里PID控制器的输出u(t)是u(t) Kp * e(t) Ki * ∫e(t)dt Kd * de(t)/dt其中e(t)是设定值与当前值的差也就是偏差。在循迹小车里设定值通常是0表示赛道中线当前值是传感器计算出的位置偏差。位置偏差可能不是一个角度而是一个归一化的数值比如从-100到100或者根据传感器索引计算出的连续值。2.2 位置式和增量式新手怎么选在单片机里实现PID必须离散化。针对离散系统有两种常见写法位置式PID和增量式PID。位置式PIDu(k) Kp * e(k) Ki * sum(e(i)) Kd * (e(k) - e(k-1))输出u(k)是绝对的PWM值比如0到7200。它的问题是积分项要一直累加如果长时间没消除误差累加值会很大可能导致输出饱和而且调试时一旦把积分系数调大车就可能剧烈震荡。增量式PIDdu(k) Kp * (e(k) - e(k-1)) Ki * e(k) Kd * (e(k) - 2*e(k-1) e(k-2))它计算的是本次输出相对上次输出的增量真正用来控制的是u(k) u(k-1) du(k)。增量式的优势在于不需要累加历史误差积分效果隐含在增量中输出限幅容易做执行器带记忆比如舵机或者电机控制中的PWM寄存器即使计算突然出错也不会产生很大的突变。我在自己的车上用的就是增量式PID原因很简单调试时更安全车不会因为积分饱和而发疯。你可能会问那位置式是不是就不好不是位置式适合那些执行器本身没有“保持”能力的场合比如某些阀门需要连续输入信号。对于小车电机PWM控制增量式实现简单且稳定。2.3 误差怎么计算从传感器到数字偏差这一步很多人忽略但直接决定PID能不能用。以8路红外阵列为例检测到黑线时输出0白线时输出1或反过来我们取中间两个管子作为“0号位”左边为负右边为正。比如用一个数组sensor[8]存储每路的状态。理想情况下当线在正中间时中间两路都是黑其他都是白。一种常见计算方法是加权平均position 0; sum 0; for (i 0; i 8; i) { position sensor[i] * (i - 3.5); // 根据物理位置做加权中心偏移为0 sum sensor[i]; } if (sum 0) { position position / sum; // 归一化得到-3.5到3.5之间的偏差 }比如只有最左侧那个管子检测到黑线sensor[0]1那么position (0-3.5)/1 -3.5。如果左边第2路也检测到sensor[1]1则position (-3.5 (-2.5))/2 -3.0。这样得到的position相当于赛道线相对于车体中心的位置偏差。将这个position作为PID的输入e(k)缩放比例可以根据你的转向灵敏度决定。对于摄像头方案通常先二值化图像提取黑线中心然后计算目标中线图像宽度/2与实际中心线的像素偏差映射到一个合适范围。电磁方案则是用左电感电压 - 右电感电压/左右之类的方式得到类似值。最终都要归一化到一个连续范围内让PID知道“偏了多少”。2.4 核心代码实现增量式PID的通用函数下面用C语言写一个随时可以移植的增量式PID函数。假设PWM输出范围是0到7200目标速度由外部给出转向修正通过PID输出叠加到一个基础占空比上。为简化这里只写偏差计算和PID更新typedef struct { float Kp; float Ki; float Kd; float output; // 控制量输出 float prev_error; // e(k-1) float prev_prev_error; // e(k-2) } IncrementalPID; float PID_Calc(IncrementalPID *pid, float error) { // 增量式PID核心 float increment pid-Kp * (error - pid-prev_error) pid-Ki * error pid-Kd * (error - 2.0f * pid-prev_error pid-prev_prev_error); // 更新历史状态 pid-prev_prev_error pid-prev_error; pid-prev_error error; // 累加输出 pid-output increment; // 输出限幅避免PWM超出合理范围 if (pid-output MAX_OUTPUT) pid-output MAX_OUTPUT; if (pid-output -MAX_OUTPUT) pid-output -MAX_OUTPUT; return pid-output; }使用的时候循环执行以下步骤读取传感器计算position误差。调用PID_Calc(pid, position - 0)其中目标值是0所以error position。将PID输出加到左右两个电机的PWM上左轮 基础速度 转向量右轮 基础速度 - 转向量。控制左右电机实现差速转向。注意这个转向量可能是负数代表向左或向右修正。还需要根据电机安装方向、PWM极性做正负号的调整这个只能在硬件上试出来。3. 硬件选型和搭建过程这些坑新手最容易踩3.1 底盘与电机差速转向是基础循迹小车常用两种驱动形式前轮转向后轮驱动像汽车或者两侧独立驱动差速驱动。绝大多数竞赛和入门方案都用差速驱动因为转向灵活可以在原地转控制简单。底盘可以选择亚克力板自己打孔也可以买成品底盘。动力上有两个选择带减速箱的直流电机如TT马达和编码器电机。TT马达便宜但转速一致性差如果没有编码器PID调节速度时无法做到精确基本靠电压占空比来调编码器电机带AB相编码器可以实现速度闭环跑直线更稳但成本高、代码复杂。我做第一辆车时用的TT马达后来发现左右电机死区电压不同——一边电机到20%占空比才转另一边要到25%才转。如果不加处理车会一直偏向一边。解决办法是在程序里做“最小死区补偿”或者做一个速度闭环。如果你预算允许强烈建议直接选带编码器的电机以后做速度闭环也用得上比折腾死区补偿省心得多。3.2 电机驱动模块L298N还是TB6612电机驱动是控制电机转动方向和速度的桥梁。常见的L298N模块便宜但内部压降大发热严重TB6612FNG体积小、效率高内置过流保护和Arduino或STM32配合得很好。我踩过的坑是L298N的5V输出不能给单片机供电它内部是线性稳压电流不够可能导致单片机反复重启。如果是TB6612电源逻辑也比较清爽VM接电池VCC接5V逻辑电源GND共地PWMA/PWMB接PWMAIN1/AIN2等控制方向。电池方面7.4V锂电池组是最常见的选择。TT马达驱动电压范围宽7.4V能获得不错的速度。注意电池电压会随着放电下降如果不加稳压PWM占空比对应的实际电压会变会影响控制稳定性。我的做法是用DC-DC模块给单片机和传感器稳压到5V电机直接用电池供电这样控制板不会因为电机大电流启动导致电压跌落而复位。3.3 红外传感器阵列安装高度和间距是关键红外对管阵列的离地高度建议控制在1~2厘米太近会因为底面不平而刮蹭太远则反射信号弱。阵列宽度应该略大于赛道线宽一般赛道线宽2.5厘米左右8路阵列覆盖宽度5~6厘米即可。此外环境光对红外管干扰很大。我的解决办法是给传感器外面加一个遮光罩或者用调制解调型红外传感器比如反射式光电开关它们内置了频率调制能有效抑制干扰。如果你用的是简单数字量输出的对管最好不要在阳光直射的环境下使用。3.4 主控选型Arduino还是STM32从入门角度我推荐Arduino Uno或Nano理由就三个资料多、上手快、不容易把IO弄坏。它的时钟是16MHz处理红外阵列和PID完全足够跑摄像头方案就比较吃力。如果做摄像头循迹建议改用STM32F103C8T6或更强的芯片因为图像处理和灰度运算需要更快速度。用Arduino时要注意它的PWM频率默认是490Hz对电机控制来说声音可能明显但能用。如果希望更安静、更稳定可以在初始化时修改定时器的分频系数把PWM频率提高到20kHz左右但要注意电机驱动器是否支持TB6612支持高频率L298N不行因为内部电路响应慢。3.5 整车供电逻辑示例一个典型的供电方案是这样的模块供电电压说明锂电池7.4V主电源电机驱动器VM7.4V直接给电机供电DC-DC稳压模块输入7.4V输出5V供传感器、单片机、蜂鸣器等Arduino VIN5V或者直接接5V电源口注意不要接USB这里最容易烧板子的操作是电机驱动器电源和单片机共用一个电池但不共地。电机启动瞬间电流很大会在导线上产生压降和噪声如果不共地单片机的逻辑电平和驱动器的逻辑电平参考点不同驱动信号会混乱。所以一定要把电池负极、驱动器GND、单片机GND、传感器GND全部连在一起。4. 软件架构与PID参数整定车能不能跑稳全靠这一步4.1 主循环的逻辑和状态机不要把所有逻辑都堆在loop里。建议把循迹分解成几个步骤初始化配置IO、ADC、PWM、PID参数。循环读取传感器如果是数字量直接读GPIO如果是模拟量用ADC转换。计算偏差调用传感器数组的加权平均函数。状态判断是否有交叉线是否丢线是否需要转向优先PID计算输出转向修正量。电机输出根据偏差和PID输出设置左右轮PWM。延时等待下一次采样一般用5~10ms间隔保证控制和传感器响应速度。如果采用带传感器的寻线通常还会加入“丢线处理”逻辑。比如连续几次检测不到任何传感器说明车冲出赛道或者到了终点这时候可以停车报警或按记忆前进一小段。我的做法是设定一个计数器丢线时计数加1连续丢线超过20次就停车。4.2 基础速度与转向量的混合策略PID输出是“转向量”如何叠加到基础速度上常见有两种模式差速模式左轮 基础速度 PID输出右轮 基础速度 - PID输出。这种模式下车在赛道正中时PID输出为0两轮同速直线稳定偏差大时一侧轮速增加另一侧轮速减小实现快速转弯。速度调整模式当偏差为正时以一定比例降低当前转向侧的速度另一侧保持基础速度实现“偏扣”式转向。这种模式在小偏差时更平滑但大弯道反应慢。对于入门差速模式最简单有效。需要注意PID输出限幅不要超过基础速度否则电机反转可能造成车打转。上代码示意void loop() { float error readSensorError(); // 返回归一化偏差 float turn PID_Calc(pid, error); // turn为转向量 int leftSpeed BASE_SPEED turn; int rightSpeed BASE_SPEED - turn; // 限幅 leftSpeed constrain(leftSpeed, -MAX_SPEED, MAX_SPEED); rightSpeed constrain(rightSpeed, -MAX_SPEED, MAX_SPEED); setMotorLeft(leftSpeed); setMotorRight(rightSpeed); delay(5); }BASE_SPEED取值取决于你的赛道和电机。如果你调PID时车总是在直线上来回晃那可能是P太大或者采样周期太长。4.3 参数整定先P再D最后给一点I整定PID是循迹车最耗时的一步。我的建议是“自下而上”先把积分和微分设为0Kp设一个很小的值比如从0.1开始观察车是否能在直线上保持。如果偏差出现明显回摆说明P偏大如果车依旧偏向一边回不来说明P偏小。逐渐增大Kp直到车在直线段能快速回正但仍然有一点抖动。这个KP可以记作初值。加入微分项。Kd从0.05开始逐渐增大目的是减小抖动。注意Kd过大的话车在弯道入口会出现“提前反打”的冲感甚至反向抖动。最后加积分项。对于循迹小车积分项通常可以很小甚至为零因为传感器偏差本身不存在恒定的稳态误差除非左右电机死区不同。如果你发现车总是往右偏一点点可以加一点Ki来补偿。整定过程中把PID参数写成一个全局变量在串口监视器里实时发送偏差和输出值可以极大提高效率。你可以观察输出history曲线看看有没有振荡、超调。这里我给一个经验范围对于偏差归一化在-3.5到3.5之间的8路红外PWM范围0~7200Kp大约在50~200Kd在30~150Ki在0~5。这只是一个起点最终以实测为准。不要照抄参数因为你车的重心、电机响应、传感器位置都不同。4.4 赛道元素处理直线、直角弯、发卡弯与十字线PID参数调好后还需要处理赛道上的特殊元素直角弯车在进入直角弯前会有很短的直线如果速度太快PID来不及修正就会冲出去。常见做法是增加速度前瞻根据当前偏差变化率自动减速。简单实现如果abs(error)阈值就把BASE_SPEED降低。十字交叉线红外阵列会同时识别到横向线和纵向线加权平均后可能会出现偏差突变。一个简单策略是当所有传感器都检测到黑线或大部分检测到黑线时当作十字线处理保持当前方向继续前进不做大转向。发卡弯/回头弯180度光靠PID可能不够需要根据传感器的丢线情况做一个“强制转向”状态。比如连续2个采样周期检测到最左侧或最右侧传感器一直为黑且其他一片白那么将PID输出强制设为最大转向限幅直到回到赛道上。这些逻辑不复杂但能大幅提升稳定性。我见过很多车PID调得很好但一到十字就乱甩就是因为没有做特殊赛道元素识别。5. 调试过程实录与常见问题排查5.1 用串口和上位机提升调试效率不要靠眼睛盯着车跑来调参。强烈建议在代码里添加串口输出把以下信息实时发出来传感器原始状态、偏差值、PID输出、左右轮PWM速度。调试时固定住车用手慢慢移动车头模拟赛道线位置观察串口数据是否线性变化。这样可以快速发现传感器接线错误、偏差方向反了、输出限幅没生效等问题。如果是摄像头循迹还可以在屏幕上叠加显示二值化结果或中线位置看起来更直观。这部分属于调试工具虽然前期花一点时间但后期能省几小时。5.2 常见问题速查表现象可能原因排查方法车一开机就往一边冲传感器偏差方向反了PID输出符号和电机方向不匹配电机接线极性接反用手模拟偏差观察串口数值确认偏差正负与电机转向逻辑对应直线走不直左右偏摆Kp过大传感器高度不稳定PWM频率太低两侧电机死区不同减小Kp检查传感器离地间隙加最小占空比补偿直角弯冲出去速度太快Kp不够D太大抑制了早期转向传感器检测范围不够减速入弯提高激光传感器灵敏度或增加传感器路数临时提高Kp看反应十字线乱转没有处理十字状态误判为急弯增加十字检测逻辑当大部分传感器为黑时保持方向过弯后回不到直线Kd过大导致过冲PID输出限幅太低转向太猛减小Kd适当增大限幅或提高基础速度电池电压下降后车变得无力没有稳压电机实际电压不足使用稳压模块给逻辑稳压或改用带升压的电源系统监控电压并限制最大占空比PID积分项导致输出无限叠加积分饱和禁用积分项或者对增量式PID做输出限幅即可避免位置式PID需做积分限幅5.3 一个典型调试案例我调过一台车直线段走得好好的到直角弯就冲出去。传感器是8路红外偏差范围±3.5基础速度设为600PWM满量程7200。先怀疑是Kp太小把Kp从80调到150结果直线开始抖动过弯还是冲。后来发现是因为车速太快车在进入弯道前已经把线丢了而逻辑上检测不到线时偏差变成0PID输出为0车就按当前方向直行冲出去。解决办法是在代码里加“丢线记忆”和“速度前瞻”如果偏差超过一定阈值把基础速度降低30%如果完全丢线则根据丢失前最后有效的偏差方向继续输出一个较大的转向量而不是置为0。改完以后直角弯通过率明显提升。所以调试时不仅要调PID参数还要考虑控制逻辑的整体性。5.4 参数整定的独家小技巧最后分享几个我实际用下来的办法先慢后快调PID时把基础速度调到以极慢速度通过直线确认传感器和PID能把偏差归零再逐步提高速度观察哪些地方开始出错。分段测试不要一股脑跑完整个赛道。先在直线段调Kp/Kd再单独放一个直角弯测过弯最后拼完整赛道。记录参数变化每调整一组PID参数用便签记录下赛道表现。不要只凭感觉调否则调了十几次忘掉好参数会崩溃。给转向输出加死区当偏差值很小比如小于0.2直接将PID输出设为0避免车在直线段因为轻微抖动而左右晃。使用软限幅输出限幅时不要直接硬截断而应做一个渐变的限幅避免转向量突变造成电机冲击。6. 扩展方向从普通循迹到进阶智能车做完基础PID循迹你还可以往这几个方向升级难度是阶梯式增加速度闭环加入编码器和测速模块用PID控制电机转速让左右轮速度完全一致直线更稳。加减速策略根据前方赛道特征调整目标速度比如进入弯道前减速、出弯后加速。这需要更多传感器信息或预置赛道地图。摄像头视觉循迹用摄像头代替红外对管通过图像处理提取赛道中线再用同样的PID算法控制转向。图像处理就变成了新重点。四轮转向或麦克纳姆轮如果底盘是麦克纳姆轮控制逻辑从差速变成全向移动但PID的思想不变只是输出不再是左右轮速而是多个电机的力分配。多传感器融合把红外、摄像头、编码器数据融合起来用卡尔曼滤波或其他方法估计车的“真实姿态”控制更精准。这些升级路线都有共同点控制层始终跑PID或者更高级的串级/模糊PID感知层决定用什么数据喂给PID。所以老老实实把PID调明白是所有进阶玩法的基础。我个人做了几辆车后最深的感觉是循迹小车看着简单但每一个环节都能让你的车表现天差地别。PID参数只是最后一步前面传感器、机械、供电、逻辑任何一处有短板车都跑不快。不用急着追求高速先把车稳稳地沿赛道跑完一整圈再慢慢提高基础速度。等你把KP、KD调出感觉来那时候你才真正理解了什么叫“闭环控制”。最后再分享一个小技巧调参的时候养成把所有关键变量通过串口打印到电脑上的习惯然后用串口绘图工具比如Arduino IDE自带的串口绘图器实时看波形。你会亲眼看到偏差从振荡变收敛的过程那种感觉比看车在地上跑几十圈直观多了。这个习惯我到现在做其他控制项目也在用。
返回列表