两轮差速机器人运动学模型:原理、推导与工程实践
1. 两轮差速机器人的底盘结构与转向原理1.1 底盘布局为什么绝大多数入门机器人都长这样两轮差速机器人在“底盘”这件事上几乎统治了从玩具车到仓储AGV的绝大多数场景。它的结构非常简单左右两个独立的驱动轮加上一个或多个起支撑作用的从动轮常见的有一前一后的万向轮也有在中心位置用球轮方案的。两个驱动轮分别由独立的电机驱动通过控制左右轮的速度差来实现转向甚至原地旋转。这种布局之所以能成为主流核心原因有三结构足够简单。左右轮、两个电机、一个驱动板、一个支撑轮硬件上的门槛低到中学生动手能力强的都能搭出来。控制不复杂。不需要像阿克曼转向那样去计算前轮转角与前轮转角的几何关系也不需要像全向轮那样做轮子速度的矢量分解只要管好左右轮转速这“两个数”就够了。灵活性反而高。阿克曼底盘转弯需要一定的转弯半径而差速底盘可以实现零半径原地旋转这在狭小空间作业场景里几乎是杀手级优势。很多朋友一上来直接研究代码、调PID、跑SLAM我反而建议先把底盘的运动学模型吃透。因为后面所有的高层功能——里程计、路径规划、轨迹跟踪、避障——全部建立在“机器人的轮速与机器人运动状态之间是什么关系”这个地基上。地基不稳后面再好的算法也会被底层的转向误差拖垮。1.2 差速转向的物理本质先说一个容易误解的地方差速转向靠的不是“单纯打死方向盘”而是左右轮子的线速度不一致导致机器人绕某个瞬时旋转中心ICR, Instantaneous Center of Rotation做圆弧运动。来想象一个生活场景你推一辆购物车两只手分别按在推杆的左右两端如果左手往前推的力大、右手推的力小车子就会往右偏。轮式机器人的差速转向就是这个原理的机器版本——只不过它控制的不是力而是轮子的线速度。我把机器人的运动状态拆开看通常可以分解成两个分量线速度v机器人整体沿正前方方向移动的快慢。角速度ω机器人绕自身中心轴转动的快慢。左右轮的速度分别为v_L和v_R轮距两个驱动轮中心线之间的距离为L。当两个轮子速度相同时机器人走直线当一个轮子快一个轮子慢时机器人转弯当两个轮子速度大小相等、方向相反时机器人在原地画圈。这些情况本质上都逃不开下面这条最核心的规律机器人的运动状态v, ω和左右轮的速度v_L, v_R之间是一对一的映射关系。一个v, ω组合对应唯一一对v_L, v_R反过来也一样。这就是运动学模型的全部意义把脑子里的运动意图翻译成电机能执行的轮速指令同时把轮子反馈回来的实际转速翻译成机器人真实的运动状态。2. 正向运动学与逆向运动学推导2.1 正运动学从轮速到机器人运动状态正向运动学解决的是“已知左右轮转速机器人实际怎么动”的问题。这个方向主要用于里程计计算、状态估计也就是我知道轮子转了多少圈推算机器人走了多远、转了多少角度。推导过程其实是初中几何。假设在极短的时间Δt内左轮走过的弧长为Δs_L右轮走过的弧长为Δs_R轮距为L。机器人中心点的位移Δs就是左右轮位移的平均值Δs (Δs_L Δs_R) / 2两边同时除以Δt就得到线速度v (v_L v_R) / 2这个很容易理解整体前行的速度就是两个轮子速度的平均。再看角速度。左右轮的位移差Δs_R - Δs_L除以轮距L就是机器人转过的角度Δθ。所以ω (v_R - v_L) / L这里有一个符号约定问题我习惯定义逆时针为正也就是当v_R v_L时机器人绕中心逆时针旋转。于是正向运动学模型可以写成矩阵形式[ v ] [ 1/2 1/2 ] [ v_L ] [ ω ] [ 1/L -1/L ] [ v_R ]光记住这个公式没有意义你得能感知到量纲是否正确。线速度单位是m/s角速度单位是rad/sL单位是m所以(v_R - v_L)除以L之后得出来的单位确实是rad/s量纲对得上公式才是自洽的。2.2 逆运动学从运动状态到轮速指令逆向运动学正好反过来我期望机器人以线速度v、角速度ω运动该给左右轮分别发多少速度指令这在轨迹跟踪、速度规划中每天都用。把前面正向的两个等式反解出来v_L v - (ω × L) / 2 v_R v (ω × L) / 2怎么记忆呢我习惯这样理解基准速度是v为了让机器人产生角速度ω需要在其中一个轮子上加一个修正量(ω·L)/2另一个轮子减去同样的量。转弯越急ω大轮距越宽L大这个修正量就越大。设计底盘控制器时的直接指导意义如果电机最大线速度是v_max那么在最大角速度ω_max和最大线速度之间有个约束关系指令不能超出电机的物理能力范围。根据底盘参数做速度指令限幅的时候要同时考虑这两个维度不能只看单个轮子的速度限制。2.3 全局坐标系下的位姿更新在实际导航中机器人关心的是自己在全局坐标系里到底在哪、朝向哪。这就需要在正向运动学算出的(v, ω)基础上再叠加一次坐标变换。设机器人当前位姿为(x, y, θ)在一个极短的时间间隔Δt内机器人的位置增量是Δx v × cos(θ) × Δt Δy v × sin(θ) × Δt Δθ ω × Δt把上一时刻的位姿加上增量就能做连续推演x_new x v × cos(θ) × Δt y_new y v × sin(θ) × Δt θ_new θ ω × Δt这种算法叫航迹推算。严格来说这是一种近似——它假设在一个Δt内机器人的朝向保持不变然后直接沿初始方向走直线。Δt越小近似越准确。实际工程中惯性测量单元IMU的航迹推算原理与此完全一致只是在更高频率上对三轴角速度和加速度做积分。需要注意这里的θ是相对于全局坐标系的绝对朝向角初始值通常由启动时的姿态决定后续完全靠积分累计。所以误差也会随运行时间累积这是航迹推算与生俱来的毛病跑得越久越不准这就是为什么后面必须引入激光雷达、视觉或者IMU做修正。3. 运动学模型的工程落地与代码实现3.1 里程计解算的完整流程理论说完了落地才是关键。里程计解算是运动学模型最典型的工程应用也就是把编码器测到的轮子脉冲数换算成机器人的位姿变化。完整的里程计解算流程分成四步从编码器读取脉冲增量计算左右轮的位移增量。用左右轮位移增量计算机器人中心的位移增量和航向角增量。把增量叠加到全局位姿上。周期性地重复这个过程并发布当前位姿。先解决第一步。假设编码器每转一圈产生N个脉冲如果是减速电机要考虑减速比、轮子的周长在Δt时间内测到脉冲增量Δpulse那么轮子位移增量Δs为Δs (Δpulse / N) × 减速比 × π × 轮径这里有几个单位容易踩坑轮径用的是直径还是半径必须心里有数公式里别混。减速比是电机轴转多少圈输出轴才转一圈是大于1的数换算到轮子位移时必须除以减速比。编码器如果是编码器轮和驱动轮同轴安装就不需要再乘轮子的减速比如果是电机轴上测的必须乘。第二步的换算直接用前面的公式Δs_center (Δs_L Δs_R) / 2 Δθ (Δs_R - Δs_L) / L第三、四步就是位姿更新与循环。3.2 一份可以直接用的C里程计算代码我按工程习惯写了一份简化但结构完整的C里程计解算代码核心逻辑已经写好直接改成你的底盘参数就能跑。这里我用的是常见的死循环固定周期计算方式和大多数嵌入式主控的写法一致#include cmath #include cstdint struct Pose2D { double x 0.0; // 全局坐标 x单位 m double y 0.0; // 全局坐标 y单位 m double theta 0.0; // 全局朝向角单位 rad }; struct WheelState { int64_t left_ticks 0; // 左轮编码器累计脉冲 int64_t right_ticks 0; // 右轮编码器累计脉冲 double timestamp 0.0; // 采样时间单位 s }; class DifferentialOdometry { public: DifferentialOdometry(double wheel_diameter, int ticks_per_rev, double reduction_ratio, double wheel_track) : wheel_radius_(wheel_diameter / 2.0), ticks_per_rev_(ticks_per_rev), reduction_ratio_(reduction_ratio), wheel_track_(wheel_track) {} void Update(const WheelState current, double current_time) { double dt current_time - last_state_.timestamp; if (dt 0.0) return; // 1. 计算左右轮在 dt 时间内的位移增量 double d_left ComputeDisp(current.left_ticks - last_state_.left_ticks); double d_right ComputeDisp(current.right_ticks - last_state_.right_ticks); // 2. 计算中心位移增量和航向角增量 double d_center (d_left d_right) / 2.0; double d_theta (d_right - d_left) / wheel_track_; // 3. 更新全局位姿算过渡角度用中点法提高精度 double mid_theta pose_.theta d_theta / 2.0; pose_.x d_center * std::cos(mid_theta); pose_.y d_center * std::sin(mid_theta); pose_.theta d_theta; // 4. 保存当前状态 last_state_ current; } Pose2D pose() const { return pose_; } private: double ComputeDisp(int64_t delta_ticks) const { double wheel_rotation_rev static_castdouble(delta_ticks) / static_castdouble(ticks_per_rev_); double output_shaft_rev wheel_rotation_rev / reduction_ratio_; return 2.0 * M_PI * wheel_radius_ * output_shaft_rev; } double wheel_radius_; int ticks_per_rev_; double reduction_ratio_; double wheel_track_; WheelState last_state_; Pose2D pose_; };几点说明位姿更新的公式里我用了一个叫“中点法”的小技巧先用d_theta/2作为过渡角度更新时用过渡角度计算Δx和Δy。这比直接用当前θ算出来的结果精度更高因为它在同一时间间隔内考虑了角度的连续变化。实测下来在角速度变化明显的场景里中点法能显著减小里程计误差。ComputeDisp里先算轮轴转了多少圈再换算成弧长位移逻辑很直白也方便替换单位。底层注意控制器的dt就是当前时间与上一次时间戳的差。如果dt为0或者负值说明数据异常直接跳过本次更新。工程上宁可丢一帧数据也不能用一个明显错误的dt去算位移。3.3 关键参数的实测标定方法运动学模型里的三个参数——轮距L、轮径D、编码器分辨率——任何一个不准模型输出都会与现实脱节。最经典的场景就是你给机器人发指令让它走2米直线结果它走了1.85米或者说好旋转90度实际只转了85度。轮径的标定方法很简单但极其有效让机器人以低速比如0.1m/s走一段很长的直线用皮尺量实际里程和理论里程的比值然后把轮径乘以这个比值。注意要走足够远——至少10米才能让毫米级的误差积累到肉眼可见的程度单纯走1米去标定误差会被测量噪声淹没。标定完轮径后再测转弯误差。轮距的标定比轮径麻烦一些。找一个平整场地让机器人原地旋转N圈实测旋转角度和理论角度的偏差。如果每次都转少了说明实际轮距比理论值大转多了说明实际轮距比理论值小。我见过很多新手以为轮距就是“两个轮子中心之间的距离”但这是理想值实际轮胎受载后的变形、轮子安装的微小倾斜都会改变有效轮距。所以我在工程上一律用“等效轮距”也就是用实测转角反推出来的轮距值。实际操作里轮径标定和轮距标定的顺序不能乱。必须先标定轮径再标定轮距。因为走直线实验不受轮距影响但原地旋转实验同时受到轮径和轮距的影响。如果轮径本身都不准你没法区分转角误差是哪个参数造成的。4. 运动学误差分析为什么轮子转了车却没走对4.1 系统误差与随机误差的阵营划分运动学模型听起来不难但实际跑起来各种“玄学”问题就冒出来了。要排查先得把误差分清楚。系统误差是确定性、可建模、可以通过标定消除的轮径不一致。左右轮工厂加工公差不一样或者两边轮胎气压不同导致同样的脉冲数对应不同的实际位移。表现为走直线跑偏。轮距测量不准。前面提到过轮胎受载变形的原因会让实际有效轮距和卡尺量出来的数据有偏差。编码器安装不同心。编码器和轮轴不同轴导致脉冲输出不均匀但总脉冲数是对的。随机误差是偶然性、突发性、无法通过标定彻底消除的轮子打滑。地面有油、瓷砖有水、急加速急减速都会让轮子空转。地面凹凸不平。轮子颠簸离地丢了位移增量。编码器受电磁干扰丢脉冲。判断误差属于哪一类有个很实用的方法同一条件下重复跑同一段路三次如果每次误差的方向和大小都差不多那是系统误差值得花时间去标定如果三次结果都乱飘那就是随机误差这时候再纠结标定参数意义不大要考虑传感器的融合作修正。4.2 一个典型的直线跑偏排查实录我碰到过一个典型案例一台差速底盘速度指令给左右轮完全一样PID反馈也显示左右轮实际转速几乎一致但跑10米下来总向右偏20厘米左右。排查过程是这样的第一步怀疑轮径标定问题。左右轮互换位置再做一次同样的测试发现依然向右偏。如果轮径不一致导致的跑偏反向安装后跑偏方向通常会改变所以基本排除了轮径的因素。第二步重新测量轮距。这台机器人的轮胎比较软尝试在正常负载下重新量了轮距发现装上负载后实际有效轮距比空载测量值大了约5毫米。虽然有影响但5毫米只导致约0.4度的转角偏差跑10米最多偏7厘米不足以解释20厘米的偏差。第三步检查编码器安装。拆开轮子后发现右轮编码器码盘没有完全压入轴肩码盘和光电探头之间有轻微摩擦导致右轮偶尔丢脉冲。右轮的实际转速被低估运动学计算认为右轮和左轮一样快但实际右轮比左轮快于是机器人向右偏。这个案例给我最大的教训是当参数标定已经排除得差不多时一定要回头看机械装配。编码器码盘松了、轮胎气不够、轴承卡滞这些机械层面的小毛病在运动学模型面前放大成几十厘米级的误差是非常正常的。4.3 累积误差的量化分析我们做一个量化估算让你对航迹推算的误差量级有直观的认识。假设一个机器人轮径为0.15m半径0.075m编码器每转输出1024个脉冲减速比30:1轮距0.5m。编码器反馈到轮子末端的分辨率是多少轮子每转一圈对应编码器脉冲数 1024 × 30 30720轮子周长 π × 0.15 ≈ 0.4712m每个脉冲对应的轮子位移 0.4712 / 30720 ≈ 15.34微米有意思的是这个分辨率其实非常高。15微米级别的位移精度在传感器层面已经相当理想了。但是再看转角分辨率θ分辨率 位移分辨率 × 2 / 轮距 15.34e-6 × 2 / 0.5 ≈ 61.4微弧度 ≈ 0.0035度单脉冲的转角分辨率也很优秀。理论上跑10米的距离误差大约也就几毫米、角度误差零点几度水平。但现实中的误差是这些底层误差被PID控制噪声、地面摩擦变化、机械间隙等因素放大后的结果。我实测过不少商业机器人和自研底盘纯靠编码器航迹推算跑20米之后位置误差通常在0.2到0.5米之间这个误差量级足以说明单纯靠编码器做长距离定位是不现实的。毫米级的理论精度和数十厘米的工程精度之间的鸿沟就是各种误差源放大效应积累出来的。5. 运动学模型的常见扩展与进阶方向5.1 从两轮差速到四轮差速、履带底盘两轮差速模型的思路可以平移到四轮差速和履带底盘上。四轮差速底盘四个轮子都由电机驱动左右侧各一组。它的运动学本质上和两轮差速一致——左侧轮子转速一致、右侧轮子转速一致把左右两组轮子分别视为一个整体即可。唯一需要注意的额外问题转弯时四个轮子无法都保持纯滚动一定会产生滑动摩擦这在低速场景问题不大但在高速转弯时功率损耗和轮胎磨损会比较明显。履带底盘更直接一些左右两条履带的转速差驱动转向建模方法与两轮差速完全一样。但履带与地面的接触面更大滑动转向现象更加严重尤其在松软地面或急转弯时实际等效轮距显著大于几何轮距。做这类底盘时轮距标定必须做“带载转弯标定”不能直接用工规几何数据。5.2 运动学约束下的速度指令平滑逆运动学公式本身很简单但直接发阶跃的线速度和角速度指令电机会受不了机器人也会出现猛加速打滑。我常用的平滑策略是斜坡限幅即线速度和角速度的变化率分别限制在最大加速度和最大角加速度以内。比如设定最大线加速度为0.5m/s²最大角加速度为1.0rad/s²。那么当目标线速度是0.5m/s、当前是0.1m/s时速度不能一步跳上去而是以0.5m/s²的斜率爬升经过0.8秒才能到达目标。具体实现就是在每个控制周期内计算v_cmd clamp(v_target, v_current - a_max×dt, v_current a_max×dt) ω_cmd clamp(ω_target, ω_current - α_max×dt, ω_current α_max×dt)然后在每个控制周期把(v_cmd, ω_cmd)通过逆运动学公式转换为左右轮速度指令送给电机驱动。这个环节看起来微不足道却是运动控制的“基本盘”。很多新手调不好底盘的直接原因不是PID参数不对——而是给PID喂的速度指令本身就是阶跃跳变驱动一直在追赶一个根本不连续的物理信号自然怎么调都振。5.3 模型与传感器融合的应用运动学模型的局限性前面已经提过很多次了开环推算误差必累积。工程上常见做法是用扩展卡尔曼滤波EKF把运动学模型的预测输出和其他传感器观测融合起来。一个典型的融合方案是轮式里程计运动学模型作为预测源频率高50-100Hz短期表现好。IMU的航向角作为观测量对航向误差进行修正。激光雷达或视觉SLAM的绝对位姿作为观测量抑制长时间累积漂移。三者组合后系统既有短期精度又有长期稳定性。实际做产品时还可以用运动学模型生成机器人的“速度先验”用它来辅助激光雷达点云的畸变校正——因为雷达扫描一圈需要时间如果机器人在移动点云会变扭必须先补偿掉这一段时间内的位移量这一切的前提就是运动学模型足够准确。6. 调参与标定的实操心得6.1 一张检查清单解决80%的底盘精度问题我整理过一份底盘定位质量的排查清单分享出来直接照着排查就能少走很多弯路排查项检查方法常见结果左右轮轮径一致性左右轮互换测试直线跑偏方向跑偏方向改变则轮径不一致轮距准确性原地旋转多圈对比实测转角每次固定偏差需反推修正编码器码盘同心度低速下匀速旋转看脉冲曲线等间距锯齿异常则装配有问题轮胎气压/胎面磨损肉眼加手按对比两侧气压差异影响等效轮径电机转速一致性空载下PID速度闭环对比两轮转速差影响走直线精度地面摩擦系数同一地面多次往返对比误差打滑导致随机误差严重加速度设置观察指令突变时是否打滑加速度过大引起系统性打滑这份清单不是死的。你如果压铸底盘、轮径一致性很好那可以跳过第一项如果地面环境可控、特别干净平滑打滑可能性低随机误差部分压力就小。关键是养成“先标定、后融合、再调上层”的顺序习惯。6.2 底盘标定优先级排序如果时间有限只能做三件事按重要性排序第一优先级轮径标定。轮径直接决定位移和速度的换算比例也是所有后续计算的基准量。 第二优先级轮距标定。轮距决定转角误差影响整个航向角的漂移速度。 第三优先级加速度限制。保证运动不突破摩擦极限避免频繁引发行人来不及处理的随机打滑。把这三样调好底盘基本就能跑正了。之后才是IMU的姿态初始化、EKF参数调节这类进阶活。我踩过的最深的坑就是上来直接调融合参数结果发现轮距差了2厘米融合出什么结果都救不回来。先回源头把运动学模型本身这个“地基”打牢上层建筑才能稳定。每次把知识反向应用回运动学模型时我都会想起一句话机械结构加基础数学往往才是机器人稳定运行的最后一道保险丝。愿你的差速小车既能画好圆环也能走对直线。