
做两轮差速移动机器人的朋友多半都经历过这样的时刻结构拼好了代码烧进去了轮子也转了可小车就是不走直线原地转圈转不准画一个矩形回来能偏出几十厘米。我也一样前后做过几台差速底盘从 Arduino 换到 STM32从开环调到闭环最后回头发现所有问题几乎都能追溯到同一件事运动分析没做透模型没建准控制自然无从谈起。这篇文章我就用实际做项目的话把两轮差速移动机器人的运动分析、建模和控制串起来讲一遍适合刚接触轮式机器人、准备参加数学建模或竞赛、做毕设小车、以及在实验室里被“小车跑偏”折磨过的同学参考。1. 差速底盘的运动学建模轮速到机器人速度的核心方程组1.1 先把模型假设说清楚两轮差速底盘通常由两个独立驱动的驱动轮和一个或两个万向支撑轮构成。我们建模的时候不需要一上来就考虑轮胎橡胶形变、电机内部反电动势、地面摩擦系数这些东西而是先把底盘抽象成最简形式。我拿到一辆差速小车时第一件事永远是画一张俯视图标出左右驱动轮的中心、轮距、轮子半径。所谓轮距指的是左右两个驱动轮与地面接触点之间的横向距离工程上一般近似取左右轮中心的间距后面所有控制计算都要用到这个变量。基础假设通常有三条轮子与地面之间是纯滚动没有侧向滑动左右驱动轮尺寸完全一致不考虑气压、磨损差异车体质心位于左右轮中心连线的中点附近至少在低速下可以这样近似。其实真实的底盘不一定满足这些条件但运动学建模就是要先抓住主矛盾先假定理想条件得到一套干净的公式再回头处理偏差。这个思路贯穿整个移动机器人建模也是工程上从简单到复杂、从理想到现实的标准路径。1.2 正运动学由左右轮速推出车体速度 v 和角速度 ω定义车体速度v 是车体沿车身纵轴方向的线速度ω 是车体绕垂直轴的角速度。设左轮线速度为 v_L右轮线速度为 v_R。当车体在做圆弧运动时左右轮绕同一个瞬时旋转中心转动两者的角速度相同只是转动半径不同。假设瞬时转弯半径为 R则有v (v_R v_L) / 2ω (v_R - v_L) / d推导过程不复杂v 是左右轮速的平均值因为车体中心处于左右轮的正中间而ω由左右轮速差产生速差除以轮距就是单位距离上的旋转量。如果轮子半径为 r驱动轮角速度为 ω_R、ω_L那么 v_R r ω_Rv_L r ω_L代进去就能从电机角速度换算到车体速度。这套公式是后面所有控制器的输出接口。比如想让小车以 0.2 m/s 的速度前进、以 0.3 rad/s 向左转向轮距 d 为 0.3 m那么左右轮速就是 v_R 0.2 0.3 * 0.3 / 2 0.245 m/sv_L 0.2 - 0.3 * 0.3 / 2 0.155 m/s。这个计算看起来简单但很多控制代码里的“跑偏”问题恰恰是律量纲没统一、正负号搞反、轮距量错导致的。1.3 逆运动学把期望的线速度和角速度拆成两个轮速控制时我们往往先确定车体期望运动量 (v_ref, ω_ref)再计算左右轮速度v_R_ref v_ref (ω_ref * d) / 2v_L_ref v_ref - (ω_ref * d) / 2这个式子就是逆运动学方程是控制器到执行器之间的桥梁。注意单位v 是 m/sω 是 rad/sd 是 m。从表达式里能看到差速底盘的核心语义ω_ref 项对左右轮速度符号相反当 v_ref 0 时左右轮速度方向相反小车原地绕左右轮中心连线的中点旋转当 ω_ref 0 时左右轮速度相同小车直线行驶当 v_ref 和 ω_ref 都不为 0 时小车走圆弧。这种解耦关系让差速底盘非常直观也是很多移动机器人教材喜欢拿它做例子的原因。1.4 航迹推算从轮速到全局路径有了车体速度 v 和 ω就可以积分得到全局位姿。设全局坐标系中的位姿为 (x, y, θ)θ 是航向角运动学微分方程为dx/dt v cosθdy/dt v sinθdθ/dt ω实际控制器是离散的假设控制周期为 Δt那么欧拉递推式是x_{k1} x_k v_k Δt cosθ_ky_{k1} y_k v_k Δt sinθ_kθ_{k1} θ_k ω_k Δt这是航迹推算的基础也是轮式里程计模型的来源。只要左轮和右轮转速能通过编码器测到就能不断累积得到小车在世界坐标系下的估计位置。缺点也很明显它只依赖编码器积分一旦轮子打滑误差无法被自身纠正。所以工程上必须引入闭环反馈比如陀螺仪、视觉、激光雷达把里程计和外部传感器做融合。2. 模型理想化假设与真实底盘之间的鸿沟打滑、轮距误差和标定2.1 纯滚动假设什么时候会失效纯滚动假设最怕打滑。起步瞬间电机快速输出大扭矩如果轮子与地面之间的摩擦系数不足以提供所需力矩轮子就会空转。急转弯时左右轮速度差很大同样容易打滑。重载、地面有灰、有水、有油时打滑概率大幅增加。打滑的后果是编码器以为轮子转了多少圈但车体实际位移没有那么多于是里程计虚读位置估计越来越不准。还有一个容易被忽略的点万向轮的摩擦力也会影响底盘真实运动。万向轮在转向过程中存在滚动阻力相当于给底盘施加一个附加偏航力矩。尤其在小车上如果你发现直线高速行驶时小车缓慢偏向一侧不一定是左右轮速差或轮胎磨损有可能是万向轮装配不对称弹簧压力不一致导致一侧阻力大。这些机械层面的问题模型里体现不出来但最终都会反映到控制误差里。2.2 轮距误差带来的系统性轨迹偏转轮距 d 看起来是个几何参数用游标卡尺量一下就行但实际“有效轮距”不一定等于测量值。原因包括轮胎接地印痕中心偏离轮毂中心、轮子外倾角、左右轮不完全平行、橡胶轮受压变形等等。如果模型中的 d 比实际有效轮距大那么控制器计算出的左右轮速差会比真正需要的小导致实际转向角速度比期望值大。结果是小车在走圆弧时轨迹会往里卷在走直线后转固定角度时终到角度总是偏大。这种误差是系统性的不会因为你把 PID 调好就消失只能靠标定去修正。2.3 用方形标定实验反推有效轮距我在实际项目里用的标定方法很简单也就是很多论文里提到的方形标定实验。步骤是控制小车按程序走一个 1m × 1m 的正方形每次转角 90 度完成后记录终点位置相对起点的横向误差和航向误差重复多次取平均根据误差方向反推有效轮距。如果轨迹整体比预期的“多转了一个角度”比如正方形回到起点时车头朝向不是原来的方向而是偏了好几度说明实际转向角大于期望需要增大模型里的轮距 d。反之如果转不够就减小 d。我通常调整一次后重跑两遍两三轮就能收敛。这个实验同时也能标定轮径方法是让小车走一段固定直线距离比较编码器积分的行程和实际尺子量的行程然后修正轮半径 r。原理上就是用多组输入输出数据反解几何参数本质上是最小二乘参数辨识很值得一做。2.4 运动学模型无法覆盖动力学问题运动学模型只描述了“轮速 → 车体速度”的几何关系没有考虑“电机电压/PWM → 轮速”的过渡过程。实际中电机的电枢时间常数、转子惯量、减速器齿轮间隙、地面摩擦阻尼都会让轮速建立存在延迟甚至出现超调。因此当我们说做“两轮差速移动机器人建模”时至少要分两层运动学层负责几何关系动力学层负责扭矩与速度变化的因果关系。低速小车的控制器设计用运动学模型就够但高速、重载或地面变化明显的场景就必须考虑执行器带宽和动力学约束否则外环位姿控制器会一直显得“反应迟钝”或者“过冲”。3. 控制策略设计从双轮速度环到位姿跟踪的外环结构3.1 为什么必须闭环开环控制小车给左右轮一个固定的 PWM效果通常不会理想。即使两个电机型号完全相同实际转速也可能存在差异因为电机内部摩擦、绕组电阻、减速器阻尼都不可能完全一致更不用说电池电压下降、地面摩擦力变化、负载增加这些外部因素。如果只靠开环小车跑直线跑不出直线转固定角度转不准。所以工程上至少做轮速闭环通过编码器测出实际轮速与期望轮速做差用 PID 调整 PWM 输出让实际轮速收敛到期望值。这是差速底盘一切控制的基础也是从“能转”到“能精确转”的第一步。3.2 速度环左右轮独立 PI 控制速度环是执行层控制的核心一般用 PI不一定需要 D。因为轮速反馈本身来自编码器信号相对干净微分项容易放大噪声。P 负责比例响应I 负责消除稳态误差。左右轮各自独立闭环目标是让左右轮实际线速度分别跟踪 v_L_ref 和 v_R_ref。速度环的表达式很简单error_L v_L_ref - v_L_measuredoutput_L Kp * error_L Ki * integral_L将 output_L 限幅后映射到 PWM 占空比。要注意的是左右轮控制器的参数最好保持一致否则两边动态响应不同低速阶段会出现意外的速度差导致车体转向。调参时先给一个阶跃轮速指令观察上升时间、超调量和稳态误差。超调控制在 10% 以内比较合适响应时间取决于车体重量和电机功率通常在 50ms 到 200ms 之间。3.3 位姿环航向角纠偏与轨迹跟踪只有速度环小车可以保证左右轮速按照指令输出但无法保证全局位置和航向准确。原因在于速度环只能保证“轮子转得准”并不能保证“车体在地图上走得准”。轮子打滑、轮距误差、初始位置偏差都会导致实际轨迹偏离期望。这时候需要外环即位姿环。最简单的位姿环是航向角保持固定 v_ref按下式计算角速度ω_ref k_θ * (θ_ref - θ)θ 可以由陀螺仪测得也可以由编码器航迹推算得到。这个比例控制能让车头朝向逐渐收敛到目标角度。更高的要求是跟踪直线或任意路径。跟踪直线时可以同时利用横向偏差 e_y 和航向偏差 e_θω_ref k_x * e_y k_θ * e_θ这本质上是一个 PD 型控制横向偏差相当于“位置误差”航向偏差相当于“阻尼”能让小车平稳地回归直线。跟踪任意曲线则常用纯追踪算法在期望轨迹上找前瞻点根据车辆当前位置到前瞻点的圆弧曲率反推需要的角速度。纯追踪参数只有一个前瞻距离工程上非常好调也是很多实际系统中的默认选择。3.4 级联 PID 和参数整定顺序热词里反复出现的“级联 PID 控制”在差速小车上的典型形式就是外环位姿环 内环速度环。为什么不用一个高增益 PID 直接根据位置误差输出 PWM因为电机、轮子、地面组成的执行系统动态响应不够快单环控制器为了消除位置误差会把增益抬得很高结果激励出高频震荡而降低增益后响应又太慢位置误差迟迟收不回来。级联结构把不同时间尺度的反馈分开内环负责电机轮速的快速响应外环负责车体位姿的缓慢收敛这样内外环各司其职稳定性更容易保证。参数整定顺序一定是先内后外。先把速度环中断开外环给左右轮阶跃指令整定 Kp 和 Ki直到轮速跟踪又快又稳然后固定内环参数再加上外环位姿反馈从小到大增加外环增益观察小车直线跟踪和转角收敛情况。如果外环增益太大小车会出现明显的“蛇形”折返甚至发散太小则收敛慢轨迹看起来总是慢慢悠悠地贴不上去。4. 仿真先于实车用运动学模型把控制器跑通4.1 搭建离散运动学仿真环境我平时调试差速小车一定先在仿真里跑一遍因为很多“跑偏”问题不等到实车就能在模型里暴露。我用 Python 写的仿真骨架非常简单核心就十几行。把运动学递推方程直接搬过来import numpy as np dt 0.01 x, y, theta 0.0, 0.0, 0.0 v_ref 0.2 omega_ref 0.0 for k in range(total_steps): x v_ref * dt * np.cos(theta) y v_ref * dt * np.sin(theta) theta omega_ref * dt如果要把控制器加进去需要建立左右轮速到 v、ω 的反馈再对比参考值。这样在仿真里可以快速验证运动学公式有没有写反、控制器的正负号有没有接对而不是到实车现场才去看小车原地打转。4.2 加入噪声、延迟和电机饱和的仿真模型理想模型太“干净”跑出来的轨迹自然漂亮但实车绝不是那个样子。为了让仿真更可信我会在仿真里主动加三类“脏”因素编码器噪声在轮速测量值上叠加高斯白噪声模拟量化误差执行器延迟控制器计算出的指令要延迟一到两个控制周期才生效模拟中断任务或其他系统延迟电机惯性用一阶惯性环节模拟电机不能瞬间达到目标转速。一阶惯性的 Python 表达很简洁tau 0.05 motor_state (cmd_state - motor_state) * dt / tau上面这个式子表示电机转速变化存在约 50ms 的时间常数。把这些因素加进去后仿真里出现的蛇形轨迹、稳态误差、转向偏差和真车现象会非常相似。这样在调试 PID 时你可以先在电脑前观察响应曲线再去实车验证效率会高很多。4.3 基于仿真的 PID 参数整定与鲁棒性测试仿真环境里做参数扫描很安全可以放心地试增益。我这里给一组经过量纲归一化的初始参考值具体要根据轮距、控制周期、电机响应调整不能直接照抄速度环Kp 1.0Ki 0.2航向环Kp 2.0Kd 0.1。调参时重点观察三个指标阶跃响应超调量是否小于 10%直线跟踪时的横向误差能否收敛到 ±1cm 以内原地转 90 度后的航向误差是否小于 1 度。仿真还有一个独特优势就是可以故意触发打滑事件。比如在仿真跑到一半时人为让一个轮子的编码器读数与实际位移脱钩观察控制器能否把车体拉回原轨迹。如果仿真里都纠不回来那实车大概率更不行。这能帮你提前判断控制律的鲁棒性边界。5. STM32 实车落地编码器测速、中断控制和现场调参5.1 整车硬件架构与电机驱动选型低成本 DIY 最常见的组合是 STM32F103C8T6 核心板 带霍尔编码器的直流减速电机 TB6612FNG 电机驱动。选 STM32 的理由很简单片上定时器自带编码器接口模式可以直接读取 AB 相增量编码器计数既能测速又不额外占用 CPU 资源。电机驱动选型要注意额定电流TB6612 单路峰值大约 1.2A适合小型底盘L298N 压降大、发热明显电池供电的小车不如用 TB6612 或 DRV8833。接线的逻辑不复杂。左电机编码器 AB 相接 TIM2 的 CH1/CH2右电机接 TIM3两路 PWM 接到 TB6612 的 AIN1/AIN2 和 BIN1/BIN2 控制方向与占空比。编码器供电用 5V电机用独立电源注意共地否则编码器信号参考电位不一致测速会经常跳变。5.2 编码器测速原理与轮速换算增量式编码器输出 A、B 两路方波相位差 90 度。STM32 定时器配置成编码器模式后硬件根据 A、B 相位的相对关系自动加减计数这就相当于四倍频每个码盘周期会数出 4 个脉冲。假设电机减速比为 30:1编码器码盘每圈 PPR 13那么输出轴每转一圈定时器计数变化量为counts_per_rev PPR × 4 × gear_ratio 13 × 4 × 30 1560如果轮子直径 65mm周长就是 0.204m控制周期 dt 10ms在 dt 内测得编码器增量为 N轮速就是v N × 0.204 / (1560 × 0.01)算下来每计数 N对应速度约为 N × 0.01308 m/s。这个换算系数建议在代码里单独定义换轮径、换减速比时只要改一个参数就行。5.3 控制循环的代码骨架在实际控制任务里我会用一个固定 10ms 的定时器中断执行控制循环。代码骨架大致是这样void controlLoop(void) { int32_t encL __HAL_TIM_GET_COUNTER(htim2); int32_t encR __HAL_TIM_GET_COUNTER(htim3); __HAL_TIM_SET_COUNTER(htim2, 0); __HAL_TIM_SET_COUNTER(htim3, 0); speedL encL * LEFT_WHEEL_SCALE; // 编码器增量 - m/s speedR encR * RIGHT_WHEEL_SCALE; float v (speedL speedR) * 0.5f; float omega (speedR - speedL) / WHEEL_BASE; // 外环根据位姿误差计算 v_ref, omega_ref // 内环左右轮 PI 控制 pidUpdate(...); }这里有几个容易踩的坑。第一个是编码器计数清零必须在读取之后立刻做否则漏掉脉冲测速就偏低。第二个是方向和正负号一定要验证。如果编码器的正负方向和电机的 PWM 驱动方向不一致反馈就变成正反馈小车会猛冲或抖动。最简单的检查方法是手动让电机转一小段观察编码器计数的符号是否和预期一致。5.4 实测踩坑死区、积分饱和、供电电压波动实车调试时最常遇到三个问题电机死区。PWM 占空比低于某个阈值比如 15% 时电机不转动超过阈值后又突然加速。这种非线性会让低速控制变得非常困难。解决方法是加死区补偿输出绝对值小于阈值时直接置 0大于阈值时加上一个与方向相同的偏置让 PWM 进入可控制区间。我一般会把占空比 0-100% 和实测转速的关系测成一张表再在控制里做静态校正。积分饱和。启动或堵转时速度误差很大PI 里的积分项会快速累积到限幅值之后输出长期饱和导致停车后还会猛冲一段。解决办法是积分钳位判断如果输出已经到限幅值就不再累加积分。供电电压波动。电池满电和亏电时同样的 PWM 对应的实际电压差很多。如果做了速度闭环这个差异会被 PI 自动补偿但开环调试时特别明显不要用开环结果来判断电机好坏。调参时我习惯先把串口的数据导出来画图看左右轮速跟踪曲线确认内环无误后再调外环。很多“小车跑不直”的问题其实是左右轮一个通畅一个卡顿速度闭环都补偿不过来这时要从机械结构找原因而不是继续加增益。6. 差速模型与其它轮式平台的关系从经典到扩展6.1 非完整约束下的运动规划差速底盘有一个特点不能横向平移只能沿着当前车头方向前进或后退。这个约束在控制理论里被称为非完整约束。反映到运动规划上差速小车不能像全向机器人那样横着平移到目标点需要规划出圆弧或样条路径保证任意时刻曲率半径不小于某个最小值。很多机器人教材把差速模型作为非完整系统的基础案例因为它的约束简单、物理意义明确又足以引出庞特里亚金最小值原理、Dubins 路径等规划概念。在数学建模题目里设计一条让差速小车从 A 到 B 同时避障的路径本质上就是在这种非完整约束下做最优化。6.2 差速、麦克纳姆轮、阿克曼模型对比不同轮式底盘的运动能力差异很大选型时要结合场景。可以用一张表快速对比底盘类型运动自由度原地转向典型应用建模难度两轮差速2前进 旋转可以室内巡检、竞赛小车简单麦克纳姆轮3前进 平移 旋转可以全向搬运、仓储 AGV中等阿克曼转向2前进 转向不可以遥控车、自动驾驶平台中等偏简单麦克纳姆轮的运动学解算是把 4 个轮子的转速合成 3 个自由度 vx、vy、ω每个轮子上的小辊子与地面成 45 度角控制逻辑比差速复杂一些但能实现横移。阿克曼底盘则可以用自行车模型近似用前轮转向角 δ 和车速 v 表示曲率结构上更接近道路车辆高速稳定性好但机动性差不能原地转弯。差速底盘最大的优势是结构简单、成本低、模型直观。很多学术论文里的“轮式移动机器人”默认就是差速底盘所以把差速模型吃透等于拿到了进入轮式机器人控制领域的第一把钥匙。6.3 从运动模型到 SLAM 与后续扩展运动模型最终要和定位建图、路径规划、自主导航串起来。轮式里程计本身由运动学方程和编码器数据构成虽然会漂移但短时间相对可靠可以用于和 IMU、激光雷达、摄像头数据进行融合。卡尔曼滤波里的状态转移矩阵很多情况下就来自我们前面写的三个微分方程。所以这篇文章讲的内容不只是“让小车走直线”而是移动机器人定位导航的底层基础。后面你可以在这个模型基础上加入 ROS 控制框架、激光雷达做 SLAM、自瞄跟踪等甚至拿它做数学建模竞赛里的移动机器人路径规划与控制题。我一直觉得做差速小车最值得花的钱不是买更好的电机而是花时间做一套能复用的运动学模型和调试流程。模型清楚了硬件选型、代码结构、调参顺序都能顺着推导。最后再分享一个我的工程小习惯给差速小车换轮子或更换电机之后不要急着调 PID先花十分钟做一次 1m 的直线往返测试记录终点横向偏差。如果车往左偏多半是右轮轮径偏小、右轮安装松动或者左轮打滑。先解决机械问题再回到电流上的参数。很多时候你调了一晚上的 PID 没解决的跑偏其实只是新换的轮子比另一边小了 1mm。机械和模型优先控制只是放大器。这句话是我踩过很多次坑之后最想留给你的经验。