ARTICLE DETAIL

资讯详情

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

轮式编码器里程计精度调优:差速机器人定位与标定实战解析

轮式编码器里程计精度调优:差速机器人定位与标定实战解析 先说我自己的结论轮式编码器里程计这东西看着简单真正把它调明白里面全是细节。我最早做差速机器人底盘的里程估计时以为就是数脉冲、乘个系数、累加坐标结果一跑起来画出来的圆弧歪得没法看。后来硬着头皮把误差一项项拆开重新标定轮径、轮距甚至重新设计了一下编码器的安装方式才算是把这个“看似人人都能做但人人做出来精度都不一样”的活儿给摸透了。这篇东西我就按自己做项目时踩坑的顺序来聊。从硬件选型、底层原理、算法实现到最烦人的标定和误差排查全部串一遍。无论你是刚入门准备给自己小车补一个定位模块还是已经在用编码器但觉得精度始终差那么点意思这篇文章应该都能给你提供一些可以直接落地的参考。1. 项目概述与整体思路1.1 轮式编码器里程计到底在解决什么问题先说清楚它是什么。轮式里程计英文叫Wheel Odometry本质上是利用安装在不同驱动轮上的编码器记录每个轮子转了多少圈、以什么速度转然后通过一个运动学模型在二维平面上推算出机器人相对于起始位置的位姿变化也就是X坐标、Y坐标和朝向角。机器人的定位方案很多激光雷达、视觉SLAM、惯性导航哪一个听起来都比编码器高级。但编码器里程计有一个其他传感器都替代不了的价值它输出的频率可以很高而且短期内非常平滑稳定。激光雷达一帧数据可能是10赫兹IMU高频但会漂视觉可能直接丢帧。编码器则是每毫秒就能给出一条轮速推算值不会突然跳到离谱的位置局部短时间内的位移测算非常可信。这就决定了它的核心应用场景第一作为扩展卡尔曼滤波或粒子滤波的预测输入在主传感器刷新间隙里补充位姿第二在弱纹理、无GPS、无特征的室内通道或仓库环境下做短时间的自主定位兜底第三给低成本的差速底盘提供闭环速度反馈。我自己的项目里它主要就是负责“守底”让机器人即使暂时失联环境传感器也能靠自己的腿知道走到了哪里。1.2 为什么轮式编码器依然是性价比很高的选择很多人问我既然有那么多更“聪明”的传感器为什么还要做轮式编码器的里程估计。我一般会回答三个词便宜、可靠、够用。先说便宜。一个带AB相输出的增量式霍尔编码器加在普通直流减速电机尾端整套下来可能就几十块钱。一套精度还不错的激光雷达可能要大几千一个能满足SLAM需求的深度相机也要一两千而编码器的成本和它们相比几乎可以忽略。对于项目预算有限、但又需要稳定里程来源的自主移动机器人来说这个优势非常明显。再说可靠。编码器测量的是轮子的机械转动不依赖外部环境。在光线变化剧烈的走廊里视觉里程计可能就挂了在光滑反光的地面上激光匹配也可能出问题。但编码器只要轮子在转它就有输出几乎不受光照、粉尘、电磁干扰影响。我做过一次在傍晚夕阳直射的楼道里跑直线视觉方案差点飘掉但编码器里程计依然稳稳当当地数着脉冲。最后说够用。如果你不是要做高精度建图而只是让机器人在房间里大致知道自己走了多远、转了多少度编码器里程计完全够。在短距离、平整地面的场景下它的误差累积速度其实没那么夸张。定期检修、正确标定之后做几十米来回往返回到原点的精度做到厘米级是完全可以实现的。1.3 适合什么场景又不适合什么场景任何技术都有边界编码器里程计也一样提前搞清楚它适合干什么能避免很多后期返工。适合的场景有三个共同点轮子与地面接触良好、地面相对平整、行驶距离相对短。比如室内办公区巡检机器人、仓库地板上的AGV、餐厅的送餐底盘、教学用的差速小车这些都是编码器里程计的舒适区。在这些场景下配合一个合适的标定流程里程计的短期精度非常可靠。不适合的场景包括长距离越野、地面湿滑或者有大量微小碎石的环境还有机器人经常原地疯狂打转的场景。一旦轮子出现打滑编码器依然会计数但实际车体没有动误差瞬间就累积出来。有人踩过坑在光滑的瓷砖地面让机器人高速急转弯位置估计直接“漂移”了半米。此外长时间行驶时由于它的误差是积分性质的会不断累积所以不能单独依赖它做长时间的绝对定位需要配合其他传感器做定期校正。这是我做一个扫地机器人底盘项目时最深刻的体会跑一圈下来回环闭合时发现起点位置已经偏了十几厘米。2. 硬件选型与关键概念详解2.1 编码器类型到底怎么选增量式还是绝对式做轮式里程计第一步是选编码器这一步直接决定了后面所有软件能达到的上限。编码器按输出类型分有增量式和绝对式两种。增量式编码器输出的是脉冲信号只能告诉控制器“轮子转了多少步”但断电后无法获知当前绝对角度。绝对式编码器则能在上电瞬间输出当前轴的绝对位置。从价格和常见的移动机器人底盘设计来看增量式编码器是绝对的主流。原因很简单里程计需要的是“相对变化量”本来就不关心上电时的绝对角度所以增量式完全够用。增量式编码器内部又分为光电式和磁电式。光电式编码器精度高、响应快但怕尘、怕油污磁电式编码器结构简单、抗污性强价格低廉是现在很多直流减速电机尾置编码器的标配。我的经验是用在教育机器人、室内AGV上的话磁电式增量编码器完全够用没必要迷信光电式的更高分辨率。反而是编码器与轮轴的装配同心度更值得关注装偏了再高的分辨率都会被引入的周期误差毁掉。2.2 几个必须搞清楚的参数PPR、减速比、轮径很多新手一上来直接看编码器“是否带AB相”然后就下单了这是不行的。决定里程精度的参数至少有四个编码器每转脉冲数PPR、电机减速比、轮子的有效半径、驱动器是否支持AB相四倍频解码。PPR全称Pulses Per Revolution含义是电机输出轴转一圈所能产生的脉冲数。不过要注意很多编码器是安装在电机尾端的测量的是电机转子端的转速不是轮子端。如果电机前端有减速箱轮子转一圈对应的编码器脉冲数就需要乘上减速比。举个例子一个常见的小车电机参数标注为减速比1:30尾部霍尔编码器PPR是20。那么轮子转一圈编码器会产生 20×30 600 个脉冲。如果用AB相四倍频模式控制器实际可以数出 600×4 2400 个计数步。轮子的有效半径是0.0325米周长就是大约0.204米也就是说每一个编码器计数步对应的实际位移约为0.085毫米这个精度对小车的短距离自定位来说已经非常可观。从这些参数关系里可以得出一个结论不要只盯着PPR看增量减速比和四倍频带来的“等效分辨率提升”往往比单纯提高编码器线数更明显、更划算。我见过很多项目硬件上买了一堆高分辨率编码器结果驱动板不支持四倍频解码白花了不少钱。2.3 安装与接线这部分比你想的更影响精度编码器装得好不好对里程计精度的影响甚至大过于编码器本身的分辨率。这里面最需要注意的是安装同心度。如果编码器的码盘或磁铁安装得和轮轴不同心轮子每转一圈输出脉冲的间隔就会忽大忽小呈周期性波动。这种波动很难通过标定完全消除因为它不是恒定误差而是一种随转角变化的正弦误差。在低分辨率下可能感知不明显但在高分辨率下会发现车明明匀速直线走轮速曲线却有规律地抖动。所以我在做底盘的时候会特别检查三样东西编码器码盘/磁铁的安装是否居中可以使用分中测量工具辅助编码器的读头与码盘之间的间隙是否均匀不能有倾斜电机轴和轮子的连接是否紧固不能出现轴向窜动或径向松动。接线方面同样不能草率。AB两相脉冲信号是高频率方波线束太细、距离太长、没有屏蔽都可能导致脉冲丢失或产生额外计数。控制器端要接上拉电阻保证空闲状态下信号线为确定电平。曾经遇到过一次很诡异的现象机器人静止时里程计的数值仍然在变化。排查了半天发现是差分驱动板出来的编码器线与电机大电流线绑在同一束线卡里瞬间尖峰干扰直接灌进编码器信号里把脉冲整出了毛刺。后来把编码器线和动力线彻底分开走问题就消失了。3. 核心原理解读与数学模型推导3.1 增量式编码器计数背后的机制咱们用一个简单例子来理解编码器计数的基本原理。增量式编码器内部通常有一圈均布的栅格随着电机轴旋转传感器会输出两路互相错开90度相位的方法信号也就是A相和B相。A、B两相之间的相位关系非常关键。正转时A相上升沿相对于B相提前90度反转时则反过来。控制器通过检测这两路信号的电平变化状态既可以判断旋转方向又可以把每个脉冲分为4个边沿事件从而实现“四倍频”计数。这就是为什么光学或磁编码器只有几百线最终却可以得到几千个计数步的分辨率。如果只用单相计数不仅无法区分正反转分辨率还低一旦在零速附近有抖动计数还会乱跳。所以我强烈建议做差速底盘的里程计至少要用带AB相输出的编码器并且软件里要正确实现正交解码逻辑而不是简单地对单个边沿计数。3.2 差驱底盘的直线速度与角速度换算双轮差速底盘是目前小型移动机器人上最常见的驱动结构左右轮各自独立驱动。里程计推算的第一步是把左右轮各自的转速转换成机器人的整体运动状态。假设左轮的线速度为v_left右轮的线速度为v_right机器人的轮距左右轮与地面接触点之间的有效距离为d。那么机器人底盘中心的线速度v和角速度w分别由下面两式给出v (v_left v_right) / 2w (v_right - v_left) / d这个公式非常基础但它揭示了两个重要信息。第一底盘中心线速度是两轮速度的平均值想要准确地计算位移就必须让左右轮转速测量值准确如果左右轮轮径不一致直接套公式就会导致机器人走弧线。第二角速度与两轮速度差成正比与轮距成反比。轮距越小同样的差速产生的转向角越快轮距误差对角速度的影响就越明显。从工程角度看逐步推导之后你会发现轮距d并不是电机轴距而是轮子接地点的有效横向距离。对于宽轮胎左右轮接地点可能在轮胎中心也可能因为轻微的侧倾发生变化。这就要求我们对“标定用轮距”和“结构设计轮距”保持区分实际中的轮距往往需要通过实验修正而不是直接拿尺子量。3.3 位姿更新把轮速积分成二维坐标有了线速度和角速度下一步就是将其转换成机器人在地图坐标系中的位姿。设机器人在t时刻的位姿为(x, y, θ)其中θ是朝向角。在Δt足够小的时间片内我们可以认为机器人做匀速直线运动那么位姿更新公式为θ_new θ w × Δtx_new x v × Δt × cos(θ_new)y_new y v × Δt × sin(θ_new)在工程实现上有两种做法。一种是把Δt取为固定节拍比如每10毫秒在定时器中断里做一次位姿更新另一种是每当编码器读数变化时立即处理。固定节拍更稳定也方便和IMU做融合缺点是如果主循环偶尔卡顿可能丢失部分区间边沿触发式的优点是响应即时但要小心多个轮子的编码器中断相互竞争导致数据不同步。我的建议是固定节拍为主编码器数值在每个节拍统一采集一次左右各自计数同一时刻的快照保证两轮数据一致性。这里还有一个容易被忽略的细节θ应该采用更新前的角度还是更新后的角度。严格来说在Δt内角度是连续变化的使用积分公式中点法会更精确但对于较小Δt直接用θ_new的误差几乎可以忽略。如果机器人转速很快比如高速原地转向建议把Δt缩小或者在角度上做一个简单迭代补偿。3.4 轮距、轮径和速度分辨率对估算精度的数学影响想深入理解标定为什么重要需要先看懂误差传播关系。假设左右轮的实际有效半径存在偏差δr机器人在Δt内计算的位移为Δs (v_left v_right) × Δt / 2如果轮径实际偏大而参数里没有修正结果是测量位移会比实际位移偏小——因为真实线速度更大但里程计只按偏小的半径换算。同理如果左右轮径不一致即使两个轮子的电机转速完全一致机器人在物理上也是走弧线的但里程计按“直线”更新坐标累计出来的误差会朝一个方向弯曲。这也是“跑直线不直”最经典的原因之一。轮距误差对角速度的影响是线性放大的。假如真实轮距是200毫米标定时用成了195毫米那么同样的差速计算出的转动角速度会比实际大2.5%。如果机器人要转一整圈360度角度偏差就可能达到9度左右这将显著影响后续的坐标计算。从这点来看标定是数学上绕不开的一环。编码器只是提供原始数字真正决定定位精度的是模型参数是否贴近机械真实情况。所以下一章我重点讲怎么样一步步地把这些参数给标定出来。4. 软件实现与关键代码流程4.1 单片机端的脉冲计数策略底层脉冲计数是里程计软件的第一关。常见方案有两种外部中断计数和编码器接口定时器模式。外部中断方案可以让任意IO口支持编码器输入缺点是当转速高、中断频繁时主程序会被频繁打断影响整体实时性。STM32等芯片的定时器编码器接口模式则更推荐它能直接在硬件层面完成AB相解码、四倍频和计数方向判断完全不需要软件干预。我自己的项目用的就是定时器编码器模式读取的时候只需要从定时器计数寄存器里取出当前值即可。在纯单片机实现中计数值需要周期性地被上层读取并清零比如10毫秒读一次。为了防止累计溢出处理时需要按照环形计数的思想先减去上次读到的值得到本周期内的脉冲增量再把这个增量叠加到总里程表中。4.2 里程解算的Python示例在调试或仿真阶段我一般会在PC上先用Python把核心算法跑通。下面这段代码基本就是车载端定时器中断里会执行的内容拿出来供大家做参考。import math # 物理与编码器参数 PPR 20 REDUCTION_RATIO 30 PULSES_PER_REVOLUTION PPR * REDUCTION_RATIO * 4 # 四倍频后 WHEEL_RADIUS 0.0325 # 单位m WHEEL_BASE 0.200 # 轮距单位m TICK_TO_METER 2 * math.pi * WHEEL_RADIUS / PULSES_PER_REVOLUTION class Odometry: def __init__(self): self.x 0.0 self.y 0.0 self.theta 0.0 self.last_left_ticks 0 self.last_right_ticks 0 def update(self, left_ticks, right_ticks, delta_t): delta_left left_ticks - self.last_left_ticks delta_right right_ticks - self.last_right_ticks self.last_left_ticks left_ticks self.last_right_ticks right_ticks # 单位换算脉冲数 - 轮子前进距离 dist_left delta_left * TICK_TO_METER dist_right delta_right * TICK_TO_METER # 底盘中心位移与角度变化 delta_theta (dist_right - dist_left) / WHEEL_BASE delta_s (dist_left dist_right) / 2.0 # 位姿更新 self.theta delta_theta avg_theta self.theta - delta_theta / 2.0 self.x delta_s * math.cos(avg_theta) self.y delta_s * math.sin(avg_theta) return self.x, self.y, self.theta各位注意这段代码里我用了一个小小的技巧角度用的是更新前的角度加上半个角度增量也就是求中值。这是一种简化后的中点积分法在Δt比较大或不均匀的时候比直接使用θ_new更不容易引入偏差。如果你的控制节拍足够稳也可以用常规方式问题不大。4.3 滤波处理与抖动抑制电机低速时编码器输出脉冲间隔会很不均匀如果直接把瞬时速度作为变量去算里程会导致位姿抖动。一个相对简单又有效的办法是对脉冲增量做滑动平均滤波。比如维护一个长度为N的窗口每次把当前周期脉冲增量丢进去同时移除最旧的一个然后取平均。N一般取510。这个窗口不会对稳态速度带来明显的滞后但对抑制低速抖动很有帮助。注意一点滤波后的增量用于计算位移但原始脉冲增量仍然要累加到一个“总里程”变量中用于和滤波结果做交叉校验。这样可以及时发现滤波是否过度平滑了。另外转向过程中如果两轮中有一个轮子的速度接近零编码器方向判定的死区可能会导致脉冲计数异常波动。我在做原地旋转测试时发现零速附近的微小振动会让编码器输出一正一反的脉冲单相计数很容易把它看成若干正向脉冲导致角度虚增。解决方法是给速度设置一个很小的死区区间低于某个阈值就直接按零处理宁可牺牲极小部分灵敏度也要避免误差累积。5. 标定流程与参数校准实战5.1 有效轮径标定直线往返法轮径是影响里程计最直接的参数但真实有效轮径和设计值往往有不小差异。原因包括轮胎充气程度充气轮、橡胶受压形变、长期磨损等。我的标定方法是直线往返法。选择一个足够长的平直走廊长度尽量大于5米。让机器人以较低速度、恒定的PWM或速度指令从A点沿直线走到B点用卷尺或者激光测距仪量出实际距离L_real。同时里程计读取并记录“名义行驶距离”L_odo。那么实际有效轮径R_true可以修正为R_true R_nominal × (L_real / L_odo)注意这里的L_odo不是靠硬算出来的而是用当时参数算出来的累计位移。一次直线标定并不能覆盖左右轮径向不一致的问题所以需要再做一次反向行驶或者将左右轮单独标定。实际操作时还可以让机器人只用左轮驱动、右轮自由标定左轮再对称标定右轮。这样出来的左右轮径比值才是最准的。5.2 轮距标定原地旋转法轮距标定比轮径标定麻烦一点因为很难直接用尺子量出真正的有效轮距。我常用的是原地旋转法。具体做法是在机器人上固定一个指向明确方向的激光笔或者直接用量角器观测底盘朝向。让机器人执行“原地旋转”动作即左右轮速度相反、大小相等使底盘绕中心旋转整圈。里程计根据当前轮距参数计算出旋转角度实际角度则通过陀螺仪或外部量角工具得到。假设名义轮距为d_nominal里程计测得的旋转角度为θ_odo实际旋转角度为θ_real那么修正后的轮距为d_true d_nominal × (θ_real / θ_odo)为什么是这个方向从角速度公式w (v_right - v_left) / d来看如果里程计角度偏大说明分母d偏小需要把d调大。这个公式反过来即可。标定时多转几圈取平均效果更好。我一般会连续转三圈记录累计角度和实际圈数最终算出一个平均值。5.3 标定中的常见误区标定的时候有几个坑需要特别提示。标定地面必须和实际使用地面尽量一致。在地毯上标定的轮径拿到瓷砖直接跑误差立刻变大。标定时速度不宜过快。高速起步和刹车会有明显的滑动容易污染标定数据。最好在机器人已经达到稳定速度后再进入测量段。如果轮胎是软质橡胶负载变化会造成明显形变。标定时要模拟实际负载满载和空载的轮径可能差到1%以上这部分差距在几十米距离上已经可观了。不要只做一次标定就算完事。设备使用一段时间后轮子磨损、编码器安装松动都可能导致参数漂移。定期做一次快速校验是维护里程精度成本最低的手段。5.4 参数修正后的验证方案标定做完并不是终点必须用闭环验证来确认。我的做法是设计一个矩形路径让机器人沿线走一个3米×4米的矩形走完整整一圈回到起点。用最终位置与起点的偏差作为评价里程精度的综合指标。如果标订准确软硬件没有异常一圈下来误差应该控制在总路程的1%以内状态好的系统甚至可以做到0.3%左右。如果误差远大于这个范围别急着调参数先检查轮子是否打滑、编码器是否有失步、底座结构是否有松动。很多时候数据“不正确”并不是模型参数的问题而是物理条件根本没满足。6. 常见问题与排查技巧实录6.1 典型问题速查表我把实际项目中遇到过的问题和解决思路整理成表格按出现频率排序方便各位对照排查。现象可能原因排查与解决跑直线越来越偏左右轮有效半径不一致分别标定左右轮轮径检查两侧轮胎胎压原地旋转角度明显偏多或偏少轮距参数不准用真正原地旋转法标定轮距静止时里程还在变化编码器信号受干扰或读数为旧值分线走线束启用硬件滤波检查IO悬空电平速度较高时数据丢失中断响应不及时改用定时器编码器模式或者提高中断优先级起步刹车阶段位移异常车轮空转或滑动摩擦不够降低加速度使用更软胎面或增大对地压力左右轮行驶距离明明相等但角度漂移大轮距误差或编码器安装轴线倾斜检查安装同心度重新标定轮距长时间运行后位置越来越离谱误差不断累积缺乏外部校准部署里程计环境传感器的融合定位6.2 软硬件联合排查的步骤遇到问题时我习惯按“后处理数据分离”的顺序来排查不靠猜。第一步先把轮子架空用手转动轮子观察左右轮速度是否和转动完全对应。这一步能排查机械装配问题和编码器接线问题。第二步再用一个已知速度的传送带或手推匀速跑一段短距离重点观察左右轮各自计算的行驶距离是否接近实测。这一步能定位是轮径标定问题还是差速转弯问题。第三步再做原地旋转测试把角度误差单独分离出来。这套步骤的好处是每一步的结论都只受当前环节因素影响。举个例子如果手推直跑时距离准确但角度漂移大那基本可以肯定是轮距或者差速不对称问题而不是轮径标定问题。从一次“跑歪了”的表面现象反推哪里出问题时往往会绕弯子不如直接把误差源分开测。6.3 我做项目时踩过的几个坑最后分享几个我个人的实际经历都是文档里查不到的那种。第一坑编码器电源不稳导致计数异常。当时为了省空间把编码器电源直接从电机电源上取电结果电机一加速编码器电压就跌落瞬间丢失一堆脉冲。后面改成了独立的稳压源供电再也没出现过类似的丢步。第二坑过分追求高分辨率。有一次换了个高PPR编码器理论上精度应该更高但实际效果反而变差了因为机械抖动被放大导致计数抖动明显。后来发现在低速运动场景下高分辨率并不总是优势必须搭配合理的滤波方案才好用。第三坑把轮距直接按电机轴间距填了。后来做旋转标定时才发现由于轮胎接地宽度的影响真实有效轮距其实比轴间距大了几毫米。就这么几毫米也会在长时间转向时积累出可感知的角度误差。6.4 升级路径从纯里程计到多传感器融合如果你做完以上步骤发现单靠编码器里程计的精度还是满足不了项目需求那就该考虑多传感器融合了。在大多数实际项目里我不会让里程计单独扛大梁而是把它和IMU、磁力计甚至视觉里程计一起做滤波融合。最常见的融合方案是扩展卡尔曼滤波。编码器提供高频的位移预测IMU提供角速度辅助偶尔的环境观测如激光匹配到的位姿作为更新量来修正累积漂移。这样既能利用编码器平滑、高频的优点又能通过绝对观测打断误差累积是工业上成本最低的可靠方案之一。从单纯做“轮式编码器的里程估计”到“多传感器融合定位”是一个很自然的演进路径。如果你在这个方向上准备长期做下去建议早点在软件架构里预留好通用接口——比如把编码器里程计算的结果定义成一个主题Topic或一个类和后面的IMU、视觉一起挂到融合框架里会省掉很多重构的麻烦。说到底编码器里程计是一个看着赢不了、做起来很多讲究的基础模块。把它调准了之后做任何上层定位和导航都会轻松很多。这也是我建议每个做移动机器人的朋友无论最后用到什么高级传感器都先把这一课扎实补上的原因。
返回列表