☰
两轮差速移动机器人运动建模与控制:从运动学到PID闭环实践
2026/10/6 3:34:12 网站建设 项目流程

做两轮差速移动机器人的朋友,多半都经历过这样的时刻:结构拼好了,代码烧进去了,轮子也转了,可小车就是不走直线,原地转圈转不准,画一个矩形回来能偏出几十厘米。我也一样,前后做过几台差速底盘,从 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 ω_R,v_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/s,v_L = 0.2 - 0.3 * 0.3 / 2 = 0.155 m/s。这个计算看起来简单,但很多控制代码里的“跑偏”问题,恰恰是律量纲没统一、正负号搞反、轮距量错导致的。

1.3 逆运动学:把期望的线速度和角速度拆成两个轮速

控制时我们往往先确定车体期望运动量 (v_ref, ω_ref),再计算左右轮速度:

v_R_ref = v_ref + (ω_ref * d) / 2

v_L_ref = v_ref - (ω_ref * d) / 2

这个式子就是逆运动学方程,是控制器到执行器之间的桥梁。注意单位:v 是 m/s,ω 是 rad/s,d 是 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_{k+1} = x_k + v_k Δt cosθ_k

y_{k+1} = y_k + v_k Δt sinθ_k

θ_{k+1} = θ_k + ω_k Δt

这是航迹推算的基础,也是轮式里程计模型的来源。只要左轮和右轮转速能通过编码器测到,就能不断累积得到小车在世界坐标系下的估计位置。缺点也很明显:它只依赖编码器积分,一旦轮子打滑,误差无法被自身纠正。所以工程上必须引入闭环反馈,比如陀螺仪、视觉、激光雷达,把里程计和外部传感器做融合。

2. 模型理想化假设与真实底盘之间的鸿沟:打滑、轮距误差和标定

2.1 纯滚动假设什么时候会失效

纯滚动假设最怕打滑。起步瞬间,电机快速输出大扭矩,如果轮子与地面之间的摩擦系数不足以提供所需力矩,轮子就会空转。急转弯时,左右轮速度差很大,同样容易打滑。重载、地面有灰、有水、有油时,打滑概率大幅增加。打滑的后果是:编码器以为轮子转了多少圈,但车体实际位移没有那么多,于是里程计虚读,位置估计越来越不准。

还有一个容易被忽略的点:万向轮的摩擦力也会影响底盘真实运动。万向轮在转向过程中存在滚动阻力,相当于给底盘施加一个附加偏航力矩。尤其在小车上,如果你发现直线高速行驶时小车缓慢偏向一侧,不一定是左右轮速差或轮胎磨损,有可能是万向轮装配不对称,弹簧压力不一致,导致一侧阻力大。这些机械层面的问题,模型里体现不出来,但最终都会反映到控制误差里。

2.2 轮距误差带来的系统性轨迹偏转

轮距 d 看起来是个几何参数,用游标卡尺量一下就行,但实际“有效轮距”不一定等于测量值。原因包括轮胎接地印痕中心偏离轮毂中心、轮子外倾角、左右轮不完全平行、橡胶轮受压变形等等。如果模型中的 d 比实际有效轮距大,那么控制器计算出的左右轮速差会比真正需要的小,导致实际转向角速度比期望值大。结果是,小车在走圆弧时轨迹会往里卷,在走直线后转固定角度时,终到角度总是偏大。这种误差是系统性的,不会因为你把 PID 调好就消失,只能靠标定去修正。

2.3 用方形标定实验反推有效轮距

我在实际项目里用的标定方法很简单,也就是很多论文里提到的方形标定实验。步骤是:

  1. 控制小车按程序走一个 1m × 1m 的正方形,每次转角 90 度;
  2. 完成后记录终点位置相对起点的横向误差和航向误差;
  3. 重复多次,取平均;
  4. 根据误差方向反推有效轮距。

如果轨迹整体比预期的“多转了一个角度”,比如正方形回到起点时车头朝向不是原来的方向,而是偏了好几度,说明实际转向角大于期望,需要增大模型里的轮距 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_measured

output_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.0,Ki = 0.2;
  • 航向环:Kp = 2.0,Kd = 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。机械和模型优先,控制只是放大器。这句话是我踩过很多次坑之后,最想留给你的经验。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询