做机器人小车有些年头了,说实话“跑偏”这个毛病,是新手问得最多的问题,也是我自己早年调车时最头疼的问题。一辆小车放在地上,让它直线往前走,结果它总往一边画弧;你以为是电机坏了,换一个还是偏;你以为是程序bug,改了半天还是偏。最后折腾一晚上,才发现是轮子直径差了那么一点,或者左右两边的PWM占空比压根不一样。
这篇文章就围绕“机器人小车总跑偏”这个具体症状,把背后常见的几个环节挨个拆开讲清楚。核心围绕四层:机械结构、电机驱动、控制算法、传感器反馈,外加一套常用的调试工具链。无论你是刚入手STM32智能车、树莓派小车,还是在做麦克纳姆轮底盘、差速转向底盘,这套排查思路基本都通用。我会按排查顺序走一遍,每层都会说明现象特征、判断方法、以及实操中容易忽略的细节,帮你少走几趟弯路。
先给出一个判断框架:跑偏不等于某一个环节坏了,而是多个环节叠加的结果。下面这张表是这几年调试中积累的“现象→嫌疑环节”快速索引,后面每一条都会展开细讲。
| 跑偏表现 | 优先排查环节 | 备选排查点 |
|---|---|---|
| 起步瞬间猛拐一下,然后慢慢直 | 电机加速曲线、PWM死区 | 电源瞬时压降、驱动板电流限制 |
| 始终匀速画大弧,方向固定 | 轮径差异、重心偏移 | 左右电机转速-占空比特性不一致 |
| 直线走几秒后越来越偏 | 编码器反馈不一致、PID积分未整定 | IMU零漂、里程计误差累积 |
| 抖动+跑偏,声音“滋滋”响 | PID比例系数过大 | 控制周期不稳定、滤波延迟过大 |
| 转向后恢复直线出现回摆 | 积分饱和、目标速度阶跃过大 | 底盘运动学换算错误 |
1. 机械结构:天生的“长短腿”问题
很多人一上来就怀疑程序,但实际上机械层面的不对称是最容易被忽略、却最致命的跑偏源头。你可以把小车想象成人:两条腿不一样长,再怎么学走路也走不直。对小车来说,“两条腿”就是左右驱动轮、底盘重心、悬挂和轮轴阻力这几个机械因素。
1.1 轮子直径差异:最隐蔽的“画弧元凶”
同一个底盘,左边轮子直径 65mm,右边 66mm,你可能觉得差 1mm 无所谓。实际跑起来,转速相同的情况下,直径大的轮子线速度更大,单位时间走的距离更远,小车自然向直径小的一侧转弯。
我测试过一个两驱小车,左右轮直径差了 1mm,在 1 米长的直道上偏移了差不多 15 厘米。当时还以为是编码器问题,后来用游标卡尺量了轮子才找到原因。
怎么排查:把左右驱动轮拆下来,用游标卡尺测轮子外径,重点测轮子接触地面的那一圈(实际滚动直径)。如果误差超过 0.5mm,建议直接换同批次轮子。橡胶轮胎还有老化变形的问题,新的和旧的混用,跑偏更是必然。
另一个细节是轮轴和轴承的阻力。左右轮轴承一个顺滑、一个涩滞,即使电机转速指令相同,阻力大的那侧实际转速也会被拖慢。判断方法:把车悬空,用手拨动两个轮子,感受旋转阻力是否一致;再把车放地上不供电,沿直线推动,如果它自己会偏向某侧,说明机械阻力已经不对称了。
1.2 重心偏移:慢慢“压”出来的跑偏
重心问题往往不会让新车一上电就偏,但会在运行过程中逐渐显现。比如电池固定的位置偏左,或者车身上加了摄像头、超声波模块,却没有对称安装,重心就不在几何中心上。
重心偏移后,左右轮的压地力不同,摩擦力也产生差异,跑起来会像“拖着一条腿走路”,稳定跑偏。尤其底盘两侧有独立悬挂(四个轮子都有避震)时,重心偏了一侧,该侧弹簧被压缩更多,底盘高度变化又会改变轮子的有效直径和接触角,跑偏会更明显。
排查方法:把小车放在水平桌面上,用手指轻轻压在车体中心位置,试着推动车头,观察车身是否“歪着走”。更量化的方法是分别抬起左右侧,用电子秤称一下轮子对地的压力,如果差异超过 10%,就得重新布置电池和电路板的位置。
1.3 底盘安装与车架变形:一票否决项
我见过一个同学的亚克力底盘,装好电池和驱动板用了半年,底盘中间那块板已经明显下凹。车架变形直接改变轮距和悬挂几何,里外都调不好。
还有底盘螺丝松紧不一致的问题。固定电机的螺丝一边拧得特别紧、另一边偏松,电机的安装位置会出现微小位移,齿轮啮合阻力也不同,跑起来会有轻微的周期性跑偏。
所以机械层的排查顺序建议是:先目测车架是否水平、螺丝是否对称紧固,再量轮径和轴距,最后检查重心和轮轴阻力。这几步全都不涉及代码,五分钟做完,却可能解决一半以上的跑偏问题。
2. 电机驱动:左右手劲不一样大
机械结构查完没问题,下一个环节就是电机和驱动板。这层的问题核心是:左右电机在相同的控制指令下,实际输出转速却不一样。原因又可以细分为 PWM 占空比标定、死区差异、驱动板通道差异、电源压降几个方向。
2.1 左右电机转速-占空比特性:开环标定的必经之路
同一个型号的直流电机,参数也有离散性。它们在相同电压和 PWM 占空比下的空载转速,可能相差 5% 甚至更多。如果你不接编码器,直接给左右两个轮子相同的占空比(比如 50%),理论上期望它们转速一致,实际上常常一边快一边慢。
所以做底盘驱动时,第一步要做的是“开环标定测试”:接通电路后,用串口调试助手或者简单控制程序,分别给左轮和右轮施加一组 PWM 占空比(从 10% 到 90%,间隔 10%),同时用编码器测量(或者用测速仪测)每个占空比下的实际转速,记录成表格:
| PWM占空比 | 左轮转速(rpm) | 右轮转速(rpm) | 偏差 |
|---|---|---|---|
| 30% | 125 | 118 | 5.6% |
| 50% | 210 | 203 | 3.3% |
| 70% | 296 | 292 | 1.4% |
从这张表就能看出,低速段左右偏差明显偏大,高速段反而接近。这就是低速时跑偏严重、高速时反而好一点的原因之一。
解决方法有两种。一是软件补偿:在程序里建立一张“占空比→目标转速”的映射表,让两边各自按表输出不同的 PWM。二是上闭环:给左右轮分别装编码器,用 PID 速度闭环直接让左右轮转速等于目标值。第二种是工程上最靠谱的方案,后面控制算法部分会详细讲。
2.2 PWM 死区:低速时左右手劲不一致的放大器
直流电机存在启动死区——PWM 占空比太低时,电机力矩不足以克服静摩擦和转子惯性,根本不会转。这个死区阈值对于左右电机来说,很可能不一样。
比如左轮 15% 占空比就能转,右轮要 18% 才转。那么你在程序里给一个 15% 的极小速度指令,期望两边慢慢前进,结果左轮动、右轮不动,车直接原地画弯。这种现象在缓慢行走模式、原地转向微调时特别明显。
排查时,在开环标定测试里记录“最小启动占空比”,如果两边差异超过 3 个百分点,要么更换电机,要么在软件里做一个“死区补偿”:低于阈值的指令直接映射到阈值,保证两边动作一致。还要留意电机减速箱的齿轮润滑状态,润滑不良会让死区阈值变大。
2.3 驱动板与电源压降:看着是软件问题,实则是硬件问题
驱动板的两个通道输出能力也会有差异。同样用 TB6612、DRV8833 这类驱动芯片,两个 H 桥的 MOS 管导通电阻、散热条件不完全一样,大电流时产生的压降就不同,左右电机实际电压也就不同。
更常见的坑是电源线和接线端子。左右电机的电源线一根长一根短,或者端子接触不良,大电流时线路压降差异会非常明显。我试过一次跑偏,排查所有东西都没问题,最后发现是电池到驱动板的一根杜邦线内部折断了,连接着右电机供电,接触电阻忽大忽小,车一会儿直一会儿偏。
排查建议:用万用表分别量驱动板输出端给左右电机的实际电压,尤其是在电机堵转或大电流时对比。或者把左右电机驱动线对调,如果偏向反过来了,说明硬件通道问题坐实。
3. 控制算法:PID 不是“抄参数”就完事
机械和驱动都正常,左右轮也能精确输出相同转速了,这时候还跑偏,问题就转移到控制算法上。很多新手拿到 PID 直接网上抄一组参数,结果车不是振荡就是“画龙”,第三件事就是怀疑传感器坏了。其实玩转 PID,三句话就能归纳:Kp 管“则”不跑偏,Ki 管“残余误差”,Kd 管“别过头”。
3.1 PID 调试顺序:从比例开始,一步步来
我给小车调速度闭环时,习惯先全部清零,只设一个非常小的 Kp(比如 0.1),然后给一个固定目标速度(比如 0.2m/s),观察响应。如果转速达不到目标且有明显稳态误差——比如实测只有 0.15m/s——就加大 Kp。Kp 越大,转速越接近目标,但超过临界值后,转速就开始围绕目标值来回振荡,甚至发出尖锐的电机鸣叫声。
这时候再加 Ki。Ki 的作用是消除稳态误差:Kp 已把速度拉到接近目标但差一点,Ki 会一点点补足这个差值,让误差归零。Ki 在爬坡、负载变化时特别关键,但也最容易引起“超调+回摆”——速度越过目标后又冲回来,来回几次才稳定,车走起来像喝醉了。
Kd 是速度环里用得最少的,因为编码器反馈本身有噪声,Kd 一大会放大噪声,效果适得其反。如果你用的是陀螺仪/IMU 航向闭环,Kd 才有实际的阻尼价值。
调试时有个技巧:用串口调试助手,把目标转速、实测转速、PID 输出这三个变量以固定频率打印出来。观察曲线,比肉眼盯着车跑要准确得多。
3.2 积分饱和:为什么一松手就转圈
积分饱和是个极容易踩的坑。假设小车被卡住或者正以大力矩加速,目标速度 0.3m/s,实际却一直达不到,PID 的积分项会持续累计一个很大的值。等障碍解除或车动起来后,这个大积分输出一送,车会猛地冲出去,甚至原地打转。
解决积分饱和的标准做法是限幅:给积分项设一个最大最小值,比如最大 20,最小 -20;或者做“积分钳位”,只有输出在限幅范围内时积分才累加。对于跑偏问题,积分饱和体现为:直线走得好好的,突然收到一个转向指令再松开,车会朝一个方向额外多转一点——这就是积分项没有清零导致的“余尾”。
所以做小车转向控制时,转向执行完记得把该轴的积分项清零;做速度闭环时,给目标突变前,也应把积分项复位。
3.3 差速运动学换算:直线不是“左右 PWM 相同”那么简单
很多人在做差速小车时,以为“直行 = 左右轮 PWM 相同”。这个想法在低速均匀负载下勉强成立,实际中却不一定。因为转向时给定的角速度、线速度,需要换算到左右轮各自的目标转速,换算公式是:
左轮目标速度 = 线速度 - 角速度 * 轮距 / 2 右轮目标速度 = 线速度 + 角速度 * 轮距 / 2如果你的目标速度换算错了,或者轮距写错,直行时就等于给左右轮设了不同的目标速度,车自然跑偏。轮距要用“左右轮中心到中心”的距离,差一厘米,长时间运行累积的偏向会特别明显。
这里最容易踩的坑是:用麦克纳麦轮(全向轮)时也要做运动学分解,但公式和普通差速不一样。直行时四个轮子的转速”合成”后才能保证直线,任意一个轮子的映射关系写错,就会出现斜着走甚至原地旋转的诡异表现。
4. 传感器反馈:测不准才是原罪
如果你已经确认机械没问题、驱动一致、PID 也调好了,剩下的跑偏源头,十有八九是传感器“测不准”。对跑偏影响最大的两个感知设备:轮速编码器和航向传感器(陀螺仪/IMU)。
4.1 霍尔编码器:极对数配错导致测速差一倍
霍尔编码器常见的有 13 极对、20 极对等规格,代码里CPR参数(每圈脉冲数)写错的话,实际测出来的转速就会和真实值相差数倍。左右轮如果配置错误,会导致一边看起来转得快、一边转得慢,PID 就会拼命调整真实转速去迁就错误测量,车自然跑不直。
有一个更隐蔽的坑:编码器 A/B 相接线反了。虽然很多程序可以反向计数,但如果你没做方向适配,左右轮一个加数一个减数,PID 控制直接紊乱。排查时,先把车悬空,用手慢慢转动左轮和右轮,用串口助手打印编码器计数。两个轮子的数值应同方向增加,且大小和手转速度线性匹配。
4.2 陀螺仪/IMU 安装位置和数据滤波
如果小车有航向闭环(比如用陀螺仪维持直线),那 IMU 的安装位置就特别讲究。芯片必须尽量靠近车体几何中心,且 Z 轴垂直于底盘平面。装歪哪怕几度,航向角输出就会有常数偏差,小车会朝着一个固定方向慢慢转弯。
传感器数据如果不做滤波,波形会带毛刺。但滤波太激进又会导致相位延迟,控制上表现为“转过了才发现偏了”的滞后跑偏。折中方案是滑动窗口平均,窗口大小选 10~20 个数据点,或者用轻量级低通滤波,截止频率选 20~30Hz,兼顾平滑和实时性。
我踩过的一个坑是 IMU 供电电压不稳。陀螺仪对电源纹波很敏感,电压波动会在航向角上叠加周期性噪声,车走起来像“蛇形前进”。给 IMU 单独加 LDO 或滤波电容后,现象立刻消失。
4.3 里程计标定:跑得越远,偏差越大
没有航向传感器的小车,走直线靠的是左右轮编码器计数一致。但编码器本身也有误差,加上轮子打滑、轮径误差,里程计会随时间累积漂移。表现为:刚启动时走得很直,跑十几米后渐渐偏离。
解决方法是做“里程计标定”:让小车跑一段已知长度的直线(比如 10 米),用卷尺量实际位移与编码器累计位移,算出一个比例系数。把左右轮的比例系数分别标定到一致,能显著延长“走直线”的有效距离。更高级的做法是用 UWB、视觉标签或者磁条做全局修正,这个属于后续扩展方向,但初级阶段的里程计标定绝对值得做好。
5. 调试工具链:用对工具才能定位问题
写代码、调小车这些年,靠肉眼去“看”跑偏,效率极低。真正高效的做法是利用串口、波形、日志等调试工具,把看不见的数据“拉”出来看。这套工具链的熟练程度,决定了你排查跑偏问题的速度。
5.1 串口调试助手:最基础也最刚需
排查跑偏时,我用串口助手打印四个关键数据:左右轮目标转速、左右轮实测转速、PID 输出、航向角(如果有)。打印频率 50~100Hz 即可,别太高,否则串口会阻塞主循环。
打印格式建议带时间戳和标头,方便后期分析:
[100ms] L_target:20.0 L_real:19.6 R_target:20.0 R_real:20.4 L_PID:12.5 R_PID:-8.2 yaw:0.05串口助手里,SSCOM、正点原子串口助手这类软件都够用。注意波特率、换行符(\r\n还是\n)要跟单片机打印格式一致,否则显示乱码。数据量大时不要用“显示模式”肉眼看,打开“保存到文件”功能,事后用 Excel 或 Python 画曲线分析。有一段时间我用 win11 的 windbg 双机调试调试上位机程序,也踩过波特率配置不一致导致调试失效的坑。
5.2 上位机波形:从“看现象”到“看数据”的思维转变
串口助手适合打印数值,但看趋势和波动不够直观。这时候需要用上位机绘图工具。我常用的方案里,VOFA+ 是免费且好上手的:它的“JustFloat”协议可以把单片机发来的 float 数组实时画成波形,左右轮速度曲线、PID 输出曲线一目了然。
具体做法是:在单片机里按固定格式发送数据帧,比如帧头 + 左轮速度 float + 右轮速度 float + PID 输出 float。VOFA+ 收到后自动绘图。这样跑偏时,一眼就能看出是“左轮 PI D输出爆了”还是“右轮反馈一直跟不上”还是“两个轮子都在正常响应但车还是偏”。
我印象很深的一次:调一辆四驱麦克纳姆轮小车,出发时是直的,跑 3 秒后开始斜向漂移。用 VOFA+ 看四个轮子的转速,才发现其中一个轮子的反馈信号周期性跌落,原因是编码器焊点虚焊。这种问题,用肉眼观察车体几乎不可能定位,但波形极其明显。
5.3 仿真与在线调试工具:别直接照搬参数
现在网上有不少“PID 在线调试网站”和仿真工具,用虚拟模型演示 PID 参数变化对系统响应的影响。这类工具对理解 PID 的“脾气”很有帮助,建议第一次接触 PID 的人先去点一点,直观感受比例、积分、微分各自的效果。
但一定注意:仿真参数不能直接照搬到真车。真车有电机力矩饱和、底盘柔性、编码器噪声、通信时延,仿真里完美稳定的参数,上实车可能直接振荡。仿真的价值在于“理解规律”,实车调参还得靠串口和波形。
另一个工具方向是系统日志回放。比如在 Linux 小车(树莓派、Jetson 系列)上,可以把传感器数据和控制输出写入日志文件,跑一圈回来再离线分析。跑偏问题通常偶尔出现一次,抓不住现场的话,日志回放就是最好的“事后监控”手段。
最后再分享一个小经验
把所有改动记录成版本号,每次只改一个变量。这句话听起来像废话,但踩坑多了就明白它的分量。我曾经调一辆两驱小车,同时改了 PID 参数、换了新轮子、又调整了重心位置,结果车还是跑偏,我根本没法判断到底哪个改动起了反作用。后来老老实实回到“每次只改一项,改完立刻验证”的节奏,问题很快定位到姿态传感器安装角度上。
跑偏排查的本质,不是找到“一个原因”,而是把机械、驱动、控制、反馈这四个环节当成一根链条,逐个排除。机械上看轮径和重心,驱动上看 PWM 和电源,控制上看 PID 和运动学换算,反馈上看编码器和 IMU。配合串口助手观察数据、上位机绘制波形,跑偏这个问题,其实是机器人调试里最容易“系统化解决”的。
如果你也正好卡在某辆小车跑偏的问题上,照着这个顺序过一遍,大概率能省下好几个晚上。