ARTICLE DETAIL

资讯详情

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

轮式编码器里程估计实战:从脉冲换算到差速模型与标定排坑

轮式编码器里程估计实战:从脉冲换算到差速模型与标定排坑 轮式编码器这东西看着不起眼但在移动机器人项目里里程估计十有八九都要靠它起步。我做过几个AGV和巡检机器人项目从最初拿编码器数脉冲做速度环到后来用它做定位先验踩过的坑比想象中多得多。这篇就围绕“基于轮式编码器的里程估计”展开聊聊它到底怎么算、参数怎么定、左右轮速度怎么合成航向和位移以及我实际调试中遇到的那些容易让人抓狂的问题。如果你刚接触里程估计或者正被“机器人走着走着就偏了”困扰这篇应该能帮你把思路理顺。1. 里程估计到底解决什么问题从速度闭环到定位先验1.1 轮式编码器是怎么数出“转了几圈”的编码器本质上是一个转动传感器装在电机输出轴、减速器输出端或轮毂上把旋转角度变成脉冲信号。常见的有光电编码器和霍尔编码器两种。光电编码器内部有个码盘码盘上均匀分布透光孔LED照过去接收端在码盘转动时输出高低电平一个透光孔对应一个脉冲。霍尔编码器则是靠磁铁和霍尔元件感应磁场变化输出脉冲。两者原理不同但到了代码层面我们关心的只有一个东西单位时间内收到多少个脉冲。编码器又分增量式和绝对值式。做里程估计常用增量式因为它便宜、安装简单、输出是正交信号AB相既能数脉冲还能通过相位差判断方向。绝对值式自带零点断电后位置不丢多用于关节模组和高精度设备但在轮式里程计里相对少见因为轮子转多少圈本来就是相对量增量式完全够用。把这个原理理解透了你就明白里程估计的起点是什么编码器告诉我们轮子转过的角度结合轮径就能估算出车身走过的路程。这也是为什么轮式编码器被称作“穷人的定位传感器”它不需要外部环境信息只要轮子转它就有输出。1.2 里程估计能干什么不能干什么在很多项目里编码器里程计不是用来做“终局定位”的而是承担三类任务。第一类任务是速度闭环。控制层需要实时知道轮子转多快编码器脉冲换算成轮速喂给PID做反馈。这个场景下里程估计关注的是短周期内的速度准确性误差大一点问题不大因为反馈控制会把误差收敛掉。第二类任务是短时定位。在激光雷达、GPS这些外部参考暂时失效的时候用里程计递推车身当前在哪儿撑住几秒到几十秒。比如AGV在货架区穿行激光被遮挡的瞬间靠里程计提供连续轨迹不让导航彻底中断。第三类任务是给SLAM提供先验。激光匹配、视觉特征匹配都需要一个初始猜测值。里程计给出的“我大概在这个位置附近”能大大缩小匹配搜索范围减少误匹配概率。很多SLAM框架里里程计不是用来跟激光抢定位精度的而是给优化提供一个可靠的局部约束。但里程计也有硬伤它是开环递推没有绝对校正误差只增不减。走得越远位置方差越大。这就好比你闭着眼睛走路脚每迈一步都有一点偏差几百米下来你觉得往东实际上已经偏到东南方向了。所以我们后面会反复强调标定、局部可靠、融合校正。这三个动作缺一个里程计都会飘。2. 从脉冲到米核心换算公式与参数选型2.1 三个必须搞清楚的参数分辨率、减速比、轮径做里程估计第一个要面对的问题是收到N个脉冲车子到底走了多远公式不复杂总行程 (脉冲数 / 每圈脉冲数 / 减速比) × π × 轮径但这里的三个参数每个都有坑。第一个是编码器分辨率常见标注有PPR、CPR、线数。PPR是每转输出的脉冲数光电编码器经常写多少线比如500线。要区分清楚有的编码器在AB相上每个周期输出1个脉冲有的会输出4倍频如果频率测量用1倍频还是4倍频算出来的结果差4倍。我建议在代码里统一按“实际计到的脉冲数”来标定别拿理论PPR直接计算因为倍频方式和计数方式一变数值就变。第二个是减速比。编码器如果装在电机轴上电机转30圈输出轴才转1圈那减速比就是30:1。这时轮轴每转一圈编码器实际输出PPR×30个脉冲。不少项目直接在代码里写“减速比30”但没注意到编码器输出轴和轮轴的关系导致里程整体放大或缩小。第三个是轮径这里有个关键概念不能用几何直径要用有效滚动半径。轮胎压在地上会变形实际上等于用一个比自由直径略小的“有效直径”滚动。橡胶轮、充气胎尤其明显。所以理论算出来的轮径只能当初始值后续必须用实测标定。举个例子。假设编码器PPR500减速比30:1轮子标称直径120mm。轮轴转一圈编码器输出15000个脉冲。车轮周长是π×0.12≈0.377米。那么每个脉冲对应的位移是0.377/15000≈2.5×10⁻⁵米大约0.025毫米。这个数值很小说明编码器分辨率足够细腻。但如果你把减速比漏掉了每个脉冲对应位移就变成0.754毫米里程会直接放大30倍这种错误在实际项目里真的有人犯过。2.2 用固定采样周期转成速度注意低速量化误差里程估计通常按固定周期执行比如10ms、20ms、50ms。每次读一次编码器累计值跟上次做差得到这个周期内的脉冲增量。然后换算成位移增量最后累加到总里程里。速度就是位移增量除以采样周期。这里有个很常见的坑低速时的量化误差。假设采样周期20ms机器人以0.1m/s缓慢行进20ms内实际位移只有2mm。如果编码器每个脉冲对应0.025mm那理论上应该有80个脉冲。这个量看起来不少但如果机器人更慢比如0.01m/s20ms内位移0.2mm只有8个脉冲慢速运动时每次可能只跳到个位数甚至0个脉冲。于是速度信号呈现“一步有一步没有”的阶梯状PID看到这种反馈轻则颤动重则系统振荡。解决思路有三种。一是提高编码器分辨率或者采用时间戳法记录相邻脉冲的时间间隔用间隔的倒数算瞬时速度低速下更平滑。二是适当增大采样窗口比如把20ms的测速窗口改成50ms减少单周期的量化跳跃。三是加入滤波但注意滤波会引入相位滞后不能过度滤波。我个人的经验是低速场景优先采用时间戳法或M法/T法结合的方式。M法适合中高速单位时间数脉冲T法适合低速数两个脉冲之间的时间。两者切换逻辑不复杂但效果改善非常明显。2.3 速度信号要不要滤波怎么滤最划算滤波这块很容易走向两个极端要么完全不滤速度曲线毛刺多要么滤得太狠系统响应变肉。做速度环时我推荐先用滑动平均或一阶低通把高频毛刺拉平。滑动平均的窗口别开太大5到10个样本就够。一阶低通可以写成filtered_v alpha × raw_v (1 - alpha) × filtered_valpha一般取0.2到0.5具体调。调alpha的原则是观察机器人低速启动一瞬间速度是从零缓慢爬升还是出现反向抖动。如果抖动说明alpha偏大毛刺偏高。滤波会带来延迟。在里程计做定位更新时我不建议对位移增量做重滤波因为位移是积分量本身已经平滑了。需要滤波的是速度信号因为控制环对噪声敏感。弄明白“哪里该滤、哪里别多此一举”往往比学会滤波器本身更重要。3. 左右轮合成差速模型与航向推算3.1 线速度与角速度的计算公式两轮差速机器人是最常见的底盘形式左右轮各自独立驱动。给定左右轮速度vl和vr车身中心的线速度和角速度用两个公式算v (vl vr) / 2 ω (vr - vl) / b其中b是左右轮之间的轮距也叫基线宽度。理解这个公式可以想象你走路两脚速度一样时走直线左脚快右脚慢时向右转速度差越大转弯半径越小。角速度公式里的轮距b出现在分母上意味着b估算不准航向角误差会被直接放大。实际代码里更常用的是先算左右轮各自的位移增量Δsr和Δsl再合成Δs (Δsr Δsl) / 2 Δθ (Δsr - Δsl) / b这里的Δθ单位是弧度。一旦算错单位把角度当弧度用航向会偏差得非常离谱。我见过有人把弧度结果直接拿来当度数累加转一圈下来航向变成360度乘以57.3倍直接爆表。3.2 弧长法更新坐标比直线近似更稳拿到Δs和Δθ怎么更新x、y坐标最直观的是直线近似x Δs × cos(θ) y Δs × sin(θ) θ Δθ这在采样周期很短、转向很慢时是够用的。但采样周期稍长或者转向较猛直线近似会低估转弯路径的弧线长度。更稳妥的办法是用弧长法θ_mid θ Δθ / 2 x Δs × cos(θ_mid) y Δs × sin(θ_mid) θ Δθ也就是说位移增量按这一小段弧线上的中间航向角来投影。这个做法的物理含义是在Δt内机器人不是走一条直线而是走一条微弧用弧中点方向计算投影更贴近真实轨迹。下面给一段简单的Python示例方便直接理解整个更新流程import math class Odometry: def __init__(self, baseline): self.x 0.0 self.y 0.0 self.theta 0.0 self.baseline baseline # 轮距单位米 def update(self, d_sr, d_sl): d_s (d_sr d_sl) / 2.0 # 位移增量 d_theta (d_sr - d_sl) / self.baseline # 航向角增量 theta_mid self.theta d_theta / 2.0 self.x d_s * math.cos(theta_mid) self.y d_s * math.sin(theta_mid) self.theta d_theta每周期调用一次update传入左右轮各自的位移增量。这种写法有两个好处一是没有多余的变量累积二是弧中点投影已经在公式里不会因为采样周期拉长而出现明显的轨迹畸变。3.3 轮距不准时航向误差会被悄悄放大轮距b在公式里是分母。假设实际轮距是500mm你代码里写成480mm那么所有角速度计算都会放大4%左右。这个误差最明显的后果是原地旋转时机器人实际转了100度里程计认为转到96度左右看起来差距不大但累积一圈后方向误差会明显反映在终点位置上。更麻烦的是轮距误差和轮径误差产生的现象很像都表现为“转完之后位置偏了”。区别在于轮距误差主要影响航向轮径误差除了影响航向还会影响直行距离。排查时要区分跑直线不直可能是左右轮径不一致原地旋转后航向偏移优先检查轮距跑正方形最后无法回原点就要结合起来看。所以我强烈建议在新底盘接入里程计之前先把轮径和轮距标定做掉。这一步偷懒后面所有调试都会被里程误差干扰你会分不清是控制器问题、SLAM问题还是里程计问题。4. 标定轮径和基线宽度入手里程估计的第一道关4.1 直线往返标定法校准轮子的有效直径标定轮径的土办法也很有效找一块平整地面用卷尺量出10米的直线距离让机器人以较低速度、开环方式沿直线跑过去。注意一定要开环不要用里程反馈闭环因为闭环会修正误差导致测不出真实的轮径偏差。记录机器人从起点到终点累计的里程计位移用实际距离除以里程计读数得到一个修正系数。然后把理论轮径乘上这个系数得到新的有效轮径。举个例子理论轮径120mm在10米直线行驶后里程计显示9.35米修正系数就是10/9.35≈1.0695实际有效轮径约为128.3mm。这个值比卡尺量的几何直径更接近真实滚动半径。往返各跑几次取平均能排除地面坡度、起步打滑等随机误差。标定速度不要高建议0.2到0.5m/s因为低速下轮子和地面的滑移最小测出来的滚动半径更接近真实。4.2 原地旋转标定法反推真实基线宽度轮距标定可以用原地旋转法。让机器人原地旋转N圈比如5圈同时用高精度陀螺仪或外部量角器测出实际旋转角度。里程计内部会按照当前代码里的轮距b计算累计航向角。如果实际旋转角度和里程计计算角度不一致就能反推出真实轮距真实b 理论b × 里程计计算角度 / 实际旋转角度比如代码设b500mm原地旋转5圈后里程计显示1800度但陀螺仪实测为1820度里程计比实际转少了1.1%说明真实轮距比500mm大修正为500×1820/1800≈505.6mm。这里有个细节旋转速度不能太快否则打滑会影响旋转圈数旋转时地面要平整不能有坡度和斜纹。如果没有陀螺仪也可以让机器人贴着一个圆柱或墙角旋转用外部标记物做角度参考但精度会差一些适合没传感器的情况。4.3 跑一圈正方形校验把误差现象和原因对上标定完之后强烈建议做一个综合校验控制机器人沿正方形跑一圈边长3到5米记录回到起点后的位置偏差。这个实验能一次性暴露多个误差源。如果终点横向偏移明显比如向右偏了20cm大概率是左右轮径不一致导致直线不直如果终点朝向偏了但位置偏移不明显比如回到起点附近但车头角度不对说明轮距标定还有误差如果整个正方形轨迹呈顺时针旋转扩散状说明左右轮径误差和轮距误差叠加了。根据现象再稍微微调一两次参数里程计就能达到“短距离内很好用”的状态。这个校验过程我建议保留数据。把每次标定的参数、跑正方形后的偏差都记录下来后面换轮胎、换底盘后直接对比基线数据比重新从头摸索快很多。我自己就有一个专门的本子记录这些参数每次改完都会注释在代码文件头部方便回滚对比。5. 常见问题与排坑实录5.1 编码器丢脉冲接线、中断与软件滤波丢脉冲是里程估计里最隐蔽的问题不会直接报错只是里程悄悄变短。比如轮子转了一圈按分辨率应该输出500个脉冲但实际只计到480个长期累积下来里程明显偏小且无规律。这类问题通常有三个来源。一是接线接触不良特别是电机运行振动后编码器线束插头松动产生瞬间丢脉冲。二是电气干扰电机PWM驱动线上的大电流变化会干扰编码器信号线脉冲毛刺可能导致计数器多计或者少计。三是中断处理太慢如果单片机主循环里有耗时操作外部中断的脉冲计数服务被抢占就会漏计。排查建议先把编码器线换成屏蔽双绞线屏蔽层单端接地并且让编码器线和电机动力线分开走线槽不要平行捆在一起。然后在电机驱动输出端加续流吸收或者给编码器供电加一级LC滤波。代码层面优先用硬件定时器捕获外部中断中断服务函数里只做计数和清零标志不要在中断里做浮点运算或里程更新。5.2 打滑和空转里程虚增的几个典型现场打滑是轮式里程计最无解的问题因为它相当于给编码器一个虚假的转角输入轮子转了但车身没动里程计却以为走了很远。打滑常发生在急加速、急刹车、粗糙度差异大的地板、上坡启动等场景。针对打滑软件层面能做的事有限。一种思路是检测“轮速异常”比如一个轮子瞬间速度远大于另一个轮子或者当前电流很大但轮速很小就认为打滑暂时冻结里程更新等运动恢复后再继续。另一种思路是限制加速度让底盘柔和启停从物理上减少打滑机会。悬空空转也是类似问题。有些机器人会被人工抬起来转运此时轮子还在转里程计已经在“虚拟跑图”。如果传感器里有IMU可以通过加速度计检测到机身姿态变化或振动特征识别“被抬起”的状态主动把里程计清零或者标记为无效。这个功能在AGV项目里很实用不然一台被搬走的Robot定位会瞬间漂到十万八千里外。5.3 轮径随负载变化不要指望一次标定吃一辈子橡胶轮和充气轮其实是被压扁的负载越大变形越厉害有效滚动半径越小。同一个人之后空载和满载状态下跑同样的路程里程计读数能差1%到3%。如果你在AGV上装了较重的料架里程计的定位误差会在几个小时后变成明显的累积误差。在实际工程中很难对每种负载都重新标定。比较可行的做法是把里程计当作短时相对约束定期用激光雷达、二维码或地面磁条等绝对参考来校正位置。也就是说里程计的定位输出只用于前后几秒内的推算一旦绝对参考更新就重置里程计的累积误差。这种做法既能发挥里程计平滑连续的优势又不会让它把系统误差带飞。另外定期检查轮胎胎压。市面上很多机器人用低配橡胶轮胎压变化对有效半径的影响肉眼可见。气胎胎压不足时轮径变小里程计系统性偏大体现在直行距离“虚胖”上。如果你发现同一路线一段时间后里程偏差明显变大先去看看胎压再考虑别的。5.4 与IMU融合时的协方差设置思路里程计和IMU是经典互补组合里程计短时位移稳定但航向角受轮距误差影响IMU的加速度计噪声大但陀螺仪短时航向变化很准。融合时要注意里程计的航向在长时间后会漂移陀螺仪也漂移两者不能互相彻底修正只能做短期内的互相约束。一个我踩过的坑是把里程计的协方差设得太小导致EKF过度信任里程计一旦轮子打滑融合结果直接被带偏。后来我把里程计的噪声方差设置为随着运行距离增长而增大也就是信任度随时间降低IMU的陀螺仪航向在短时间内的权重调高这样才能在打滑瞬间让融合输出更加平滑。里程估计在工程里的定位应该是“好用的局部约束”而不是“全局万能定位”。想明白这一点融合的参数调节才不会走弯路。最后分享一点个人体会里程估计的实现并不难难的是理解它的边界。编码器给出的永远是“轮子转了多少圈”而不是“车身实际走了多远”。如果能接受这个前提老老实实做标定、做校验、合理设置融合权重轮式编码器绝对是最可靠又低调的定位基石。如果非要拿它当万能定位源再好的公式和代码也救不回来。
返回列表