
搞机器人底盘绕不开两轮差速这个经典构型。我之前折腾过几台不同尺寸的差速小车从玩具级的Arduino小板子到带编码器和EtherCAT总线的大负载AGV踩过的坑可以写一沓。这篇东西我打算把——两轮差速移动机器人从运动学建模、动力学分析到控制落地的完整链路掰开揉碎了讲清楚。不是那种教科书式地念公式而是告诉你每个公式背后的物理直觉以及到了控制代码和板子上它到底怎么用。无论你是刚接触数学建模的学生还是在做产品级底盘开发的工程师这篇都值得你花20分钟认真看完。1. 运动学建模先从“为什么车能转弯”说起1.1 差速驱动的本质——不靠转向轮靠速度差普通汽车转弯靠前轮偏转但差速底盘没有转向机构两个驱动轮是固定的平行轮它的转向完全依靠左右车轮的转速差。想象一下你推一个没有转向的手推车左边轮子转得快一点右边轮子转得慢一点小车自然而然就向左拐弯。这就是差速驱动的全部秘密。从数学建模的角度两轮差速底盘可以抽象成一个刚体在二维平面内运动。它在任意时刻的状态可以用三个量来表示位姿(x, y, θ)其中x和y是位置坐标θ是朝向角。而系统给轮子的输入是两个转速左轮ω_L右轮ω_R。我们最关心的运动学问题就是知道了ω_L和ω_R小车怎么走反过来想让小车沿某条轨迹走两个轮子各应该转多快1.2 正运动学公式——从两个轮速推出底盘速度和角速度正运动学相对简单。假设轮子半径为r两轮间距为d指左右轮轮心之间的距离。不考虑打滑时两个轮子在地面的线速度分别为v_L r × ω_Lv_R r × ω_R那么底盘中心的线速度v和角速度ω直接是这两个速度的平均和差值v (v_L v_R) / 2ω (v_R - v_L) / d就这么两个公式几乎所有的两轮差速运动学都从这里出发。v决定小车走多快ω决定小车转多快。注意这个ω就是小车绕瞬时旋转中心ICRInstantaneous Center of Rotation的角速度。一个直观的感觉是当v_R v_Lω 0这时车向左转逆着看是逆时针v_R v_L时ω 0车直线跑v_R -v_L时v 0ω最大车原地旋转。在很多实验和项目里我们就是靠“直行—原地转—再直行”这种简单的控制模式把车开到目标点的。1.3 摆好坐标系再算位姿——数学建模里最容易翻车的地方上面只是速度层面的关系如果要拿它做路径规划或者数据融合还得算出具体的位置和姿态也就是把v和ω在每一瞬间积分起来。这也是数学建模国赛、研究生数模里面常考的知识点——建立一个差分模型把机器人每一步的位移和角度的变化量推出来。套用最朴素的做法假设控制周期是Δt第k个周期的航向角是θ_k那么在一个周期内Δx_k v_k × cos(θ_k) × ΔtΔy_k v_k × sin(θ_k) × ΔtΔθ_k ω_k × Δt然后更新x_{k1} x_k Δx_ky_{k1} y_k Δy_kθ_{k1} θ_k Δθ_k这个模型叫航迹推演Dead Reckoning是所有轮式机器人定位的基础。但这里有个隐藏陷阱θ_k应该取周期开始时的角度还是周期中间的角度如果Δt足够小比如10ms以下直接用θ_k问题不大如果控制周期较大最好用θ_k加上Δθ_k/2即梯形积分精度会有明显改善。提示在写数学模型论文时这个离散化的细节很能体现你建模的能力。评委或者审稿人对“连续模型→离散算法→代码实现”这条链路的完整性非常看重。1.4 最小转弯半径和转向几何约束因为底盘靠轮速差转向自然存在一个最小转弯半径。两轮到瞬时旋转中心的距离R和两轮轮距、轮速之间有这样的几何关系R (d × (v_R v_L)) / (2 × (v_R - v_L))当v_R和v_L非常接近时R会变得很大车近乎直线行驶当两者符号相反且绝对值相同时R趋近于0车绕着底盘中心原地转。这个约束看起来简单在实际路径规划时却很要命。如果你直接用纯跟踪算法让车跟踪一条曲率半径小于R_min的路径车是绝对跟踪不上的表现出来就是一直在路径附近振荡或者直接飞线。所以做控制之前一定要先检查规划的参考路径是否满足底盘的曲率约束。这是我在做AGV产线对接时吃过亏后学到的教训。2. 动力学建模光有几何还不够得搞清楚力矩和加速度2.1 为什么要从运动学升级到动力学运动学模型只描述“速度之间的几何关系”完全没提质量、惯量、力、力矩这些东西。如果是仿真端做纯路径规划运动学基本够用但落到实物上哪怕是一台几十千克的小型AGV启动、刹车、爬坡时都会暴露出一个关键问题——你给轮子一个目标转速它不是瞬间就到达的加速度受限于电机能输出的最大扭矩。转矩不够车就跑不动甚至还会堵转。动力学模型的作用就是把这个过程描述出来给定两侧车轮的驱动力矩底盘会产生多大的线加速度和角加速度。工程上常见的做法是拉格朗日方程或牛顿-欧拉法建模但实际项目里很少有人为了差速小车去推一整套复杂的拉格朗日方程。我看过不少数学建模优秀论文对两轮差速机器人做动力学推导一般也是先写个简化版把两个驱动轮加上车身视为整体然后给出纵向力平衡和绕质心的力矩平衡。2.2 一个简化却足够用的动力学模型假设机器人质量是m绕质心的转动惯量是I左右轮驱动力分别为F_L和F_R地面阻力忽略不计那么m × a F_L F_RI × α (F_R - F_L) × (d / 2)其中a是质心线加速度α是绕质心的角加速度。而每个轮子的驱动力和电机扭矩τ之间的关系是F τ / rr为轮半径。如果再考虑电机到车轮的减速比n和传动效率η那么作用在车轮上的有效扭矩就是n × η × τ_motor。这样整理一下控制量是电机的期望扭矩然后经过动力学方程得到加速度再积分得到速度再到前面的运动学部分积分得到位置。整个“电机扭矩→车轮力→车身加速度→速度→位姿”的链路就打通了。在写控制代码的时候这条链路至关重要如果你用的是带FOC矢量控制的驱动器那你就有能力直接控制电机扭矩而不是仅仅发一个转速指令这时动力学模型就能显著提升控制精度。2.3 重心位置与底盘特性的影响两轮差速机器人如果加上沉重的电池、顶部的机械臂质心位置会偏离旋转中心。质心偏移后在加速和刹车时会出现偏航力矩具体表现为——直行指令下发后车却往一边跑偏两轮看起来速度一致但车身就是转弯。在这种实际工况下模型里m和I并不是固定常数。最简单的做法是把它当作不确定性靠后面讲的PID控制器自身去补偿更讲究一点可以加一个干扰观测器DOB去实时估算这个偏置力矩然后在前馈通道里抵消它。我自己做负载变化大的巡检机器人时就采用了“动力学前馈误差反馈”的结构效果比单独靠PID好不少。3. 控制架构从底层电机环到上层运动控制3.1 三层控制结构各管各的事控制系统设计是整个项目的核心。大多数两轮差速底盘的软件架构从上到下可以划分为三层顶层运动规划与路径跟踪输出期望的线速度v_ref和角速度ω_ref。中层运动解算与控制把v_ref、ω_ref通过逆运动学换算成左右轮的期望转速然后做速度环控制。底层电机驱动速度环再往下拆成电流环让电机快速、准确地达成目标转速。从代码角度看这就是一个典型的级联PID控制结构。底层的电流环周期一般在100 μs~1 kHz速度环周期在1 kHz~100 Hz左右最上层的路径跟踪控制通常只有10 Hz~50 Hz。每一层的响应速度差一个数量级这样控制带宽才不会打架。3.2 经典级联PID在差速底盘上的内部实现级联PID是能实现精确控制的一个关键。以每个轮子为例外环是速度环输入是目标轮速由运动学逆解算出输出是目标电流值内环是电流环输入是目标电流输出是PWM占空比或转矩指令。在单片机开发里如果用STM32做主控速度环中断一般是1 ms进入一次电流环则往往在驱动器的FOC库里完成比如常见的逐飞、磁场定向控制库。如果你只是用普通带AB相的直流减速电机没有真正意义上的电流环也可以只保留速度环用PWM输出电压来控制。这时的控制模型就退化为“电压→转速”的直流电机单环系统把电机的机电时间常数考虑进去就够用了。速度环PID的离散实现建议写成位置式PID并加入积分限幅和微分滤波typedef struct { float Kp, Ki, Kd; float integral, max_integral; float prev_error; float derivative; /* 经过低通滤波后的微分值 */ }PID_t; float PID_Update(PID_t *pid, float target, float current) { float error target - current; pid-integral error; pid-integral CLAMP(pid-integral, -pid-max_integral, pid-max_integral); pid-derivative (1.0f - alpha) * pid-derivative alpha * (error - pid-prev_error); pid-prev_error error; float output pid-Kp * error pid-Ki * pid-integral pid-Kd * pid-derivative; return output; }这个结构看起来不复杂但它隐藏了三个关键痛点积分限幅是必须的否则起步前误差一直积累启动的时候会瞬间“窜”出去微分项一定要滤波因为编码器在低速时分辨率不够直接微分噪声大得离谱另一个常见问题是控制周期不恒定时PID参数失效所以中断里务必保证调度优先级不能干别的耗时操作。3.3 中层的逆运动学解算——左右轮速怎么分控制器输出的v_ref和ω_ref不能直接发给电机必须经过逆运动学换算成左右轮速ω_L_ref (v_ref - ω_ref × d / 2) / rω_R_ref (v_ref ω_ref × d / 2) / r很多人在这一步翻车v_ref和ω_ref单位不统一。v_ref的单位是m/sω_ref的单位是rad/sd的单位是mr的单位是m算出来的ω单位是rad/s。这套换算看起来是人都会但一旦团队里有人把ω_ref用rpm表示翻车就是必然的。我的习惯是在整个代码链路里统一使用国际单位制只在最后给电机的接口层才做单位转换。还有一个细节是左右轮的速度解算到轮子目标转速后速度环的输入是目标转速rpm或rad/s而反馈则来自编码器测速。编码器测速有两种方式M法和T法。M法是在固定时间窗内数脉冲数适合高速T法是测量两个相邻脉冲的时间间隔适合低速。差速底盘经常要在低转速下精细控制比如毫米级移动这时用M法完全不行脉冲太少分辨率太低。我一般用互补式测速高速用M法低速用T法在中速段做加权切换。这个细节对低速微调、姿态对齐特别关键。3.4 最上层的跟踪控制——PID之外的选择搞定底盘的速度控制后剩下来就是“怎么走”的问题即路线跟踪。这里有三档选择最简单点到点控制go to goal。先原地转向对准目标点再直线开过去到了再转向。代码好写但效率低姿态连续性差。进阶纯跟踪Pure Pursuit。在每个控制周期从当前位置出发找到参考路径上前方一个固定距离的前瞻点然后算出一条圆弧期望曲率能用简单的几何关系解出来。剩下的就是控制底盘以某个速度沿该圆弧行驶。再进阶控制器设计如MPC模型预测控制或者线性二次控制器加前馈。这个方法门槛高一点要能写出线性化的运动学误差方程然后设计一个虚线和角速度的控制律。数学上非常漂亮很多数学建模竞赛的优秀论文里都会给出这个推导。对多数小型项目纯跟踪配上合适前瞻距离就非常好用。前瞻距离的选法要诀是速度越高前瞻距离越长。如果前瞻点取得太近车会在路径附近画龙太远则会直接切弯在转角处抄近道。可以按速度做比例映射比如L_f clamp(k × v, L_min, L_max)然后根据实际效果去调整k。4. 从仿真到实车参数整定与调参技巧4.1 先在仿真里跑通别急着上硬件我强烈建议在写实际控制代码之前先在仿真环境里把整个模型跑通。对做数学建模项目的人来说用Python里的numpy和matplotlib就能编一个差速底盘运动学模拟器想做实时控制验证的可以上ROS Gazebo或者MATLAB/Simulink。Simulink里自带车辆运动学模块但自己搭一遍运动学积分模型再用手写的S函数或者MATLAB Function Block实现控制器能让你对每个计算环节的理解深得多。仿真模型有几个主要优点可以无代价地做极限工况测试可以精确记录每一个状态量可以反向验证控制器参数。我记得有一次在做竞赛方案时我是先把控制器的PID参数在仿真里调得比较稳然后烧到小车上实车再微调一遍前后不到半小时就完成了。如果直接上实车光是处理线接反、电机异响、通讯失败这些问题就能耗掉一整天。4.2 参数整定从内环到外环顺序不能乱级联PID参数整定和单个PID不一样顺序必须是从内到外先整定电流环再整定速度环最后整定位置环或路径跟踪控制器的参数。因为外环的控制效果是建立在所有内环都稳定的基础之上的。如果速度环还没调稳你去调路径跟踪很难判断跟踪偏差到底是路径控制律的问题还是底层速度没跟上。速度环的整定方法我推荐先用Ziegler-Nichols开环法做一个初值然后手动微调。具体做法是把Ki和Kd设成0从较低的Kp开始加大逐步增加直到轮速出现小幅持续振荡记录此时Kp为Ku然后根据临界比例度法给出一个参考值再在实际响应曲线上微调。我的经验是差速底盘用增量式或位置式PID都可以关键看你要不要抗饱和。位置式必须做积分限幅增量式天然不带老误差但输出限幅保护一定得有。在调速度环时我经常会看两个指标阶跃响应的上升时间和超调量。上升时间一般在50~200 ms是合理的如果太慢说明Kp太小如果出现剧烈超调或者“嗡嗡”声说明Kp偏大或者积分项过强。电机低速时的“吱吱”尖啸听感大部分都是PID振荡引起的电流变化叠加齿槽转矩这时候优先检查是不是微分噪声放大或者速度反馈周期太长。打开一个真实案例我给一台小车配的是250线的霍尔编码器电机4倍频后是1000个脉冲每转轮子直径125 mm减速比1:30。我的测速周期是5 ms那么轮子一转大概会发出1000个脉冲折算下来每个脉冲对应0.36 mm车轮平移。如果在5 ms周期内只抓到几个脉冲速度反馈的分辨率就很差反馈值像台阶一样跳动。为了让低速反馈稳定我不得不把测速周期适当拉长同时给速度反馈做低通滤波。这是个典型的“反馈分辨率不足→控制器性能下降”问题。4.3 硬件选型里的几个数学关键点硬件层面的几个关键参数轮径r、轮距d、减速比n、编码器每转脉冲数P直接决定了控制系统的精度上限。轮径r的误差对速度闭环的影响是线性的但要注意——轮子是有弹性的实际负载后直径会略微变小充气轮更明显。所以做精确标定时不应该用出厂标称直径来计算而应该用实际走一段已知距离反推出来的有效轮径。两轮距离d的误差主要影响转向精度d偏大时原地旋转的实际角度会比指令角偏大因为它分母偏大了。一个简单的标定方法让车原地旋转360度看实际转了多少角度反过来校正d。编码器分辨率P×4决定了最小可感知位移。假设轮子周长0.4 m减速比30电机输出轴编码器1000线那么车轮转一圈需要电机转30圈编码器脉冲是30×100030,000个每个脉冲对应的车轮位移约0.013 mm精度非常充裕。但如果是直驱电机、编码器在车轮轴上那么每圈只有1000个脉冲每个脉冲对应0.4 mm在需要毫米级停靠的场景下就比较吃力了。5. 常见问题与排查技巧实录5.1 疑难杂症速查表现象可能原因排查方向直行指令下发后车辆跑偏左右轮PID参数不一致 / 有效轮径不一致 / 重心偏移先检查闭环速度响应曲线再做直线标定原地转向时车身抖动转向半径接近零导致轮子受力突变 / 速度环超调把旋转速度限制在底盘的稳定速度区间 / 延长升降速时间低速行走出现一卡一卡编码器反馈分辨率不足 / 测速周期太短切换T法测速 / 加大测速周期 / 加入低通滤波高速突然失速或堵转电机电流受限 / 电源压降过大 / 驱动器过流保护抓取母线电压波形检查电池内阻和电源线径负载增大后明显吃不上速动力学前馈没有 / PID积分饱和增加积分限幅 / 加入电流前馈补偿跟踪路径时画龙前瞻距离过短 / 控制周期太大增大前瞻距离 / 提高控制频率 / 减小速度5.2 从现象到根因的排查思路我排查问题时严格按照“从底层往上层”的顺序来绝不跳层。第一件事是抓底层的轮速响应曲线确认两轮的速度跟踪误差都在允许范围里。如果底层速度误差大其他层面全都白搭。第二件事是检查坐标变换和单位设置一个固定速度指令让车开1米看实际位移差多少再做一个90度原地转向看实测角度差多少。这两项标定都通过之后才去调上层的跟踪控制。特别想说一个非常隐蔽的坑两轮控制周期不同步。如果你在代码里用了两个定时器分别控制左右轮的速度环或者用的是同一个定时器但进入了两个不同的if分支左轮和右轮的控制更新时刻相差几毫秒甚至几十毫秒这在低速时看不出来在中高速转弯时会造成很明显的车头摆动。解决办法是把左右轮速度环放到同一个中断服务程序里保证采样和控制输出同步。另一个办法是轮速反馈加上时间戳在控制时用插值同步对齐这对于分布式的多机系统尤其重要。5.3 抖振与噪声PID参数和机械共振有时候车体在特定速度区间出现明显抖动但PID响应曲线看着又没什么问题这时候往往不是控制参数的问题而是机械共振。轮子、联轴器、电机装配的模态频率如果和控制器的带宽比较接近就会在某个速度段激起振荡。缓解手段包括降低该速度段的加减速斜率、给速度指令加S型曲线滤波、改变电机安装的刚度。我曾经遇到过一台底盘在全速的40%左右总是出现周期性异响排查了很久最后发现是轮毂上的螺丝松动导致不平衡拧紧后立即消失。所以排查时也不能只盯着软件硬件装配状态往往占了大头。6. 写在后面一点个人体会做差速机器人控制最难的不是某一个单独的数学模型而是把运动学、动力学、控制算法、硬件特性这几块内容缝在一起的过程。单看运动学公式十几分钟就能看懂但要让小车稳稳地沿着一条圆弧走需要处理的是反馈噪声、量化误差、机械公差这些“脏活累活”。我个人的经验是每当控制表现不佳先别急着改PID参数先问自己三个问题模型算对了吗单位统一了吗数据可信吗很多时候答案都出在这三件事上。两轮差速看似简单真正深入下去涉及的知识面横跨刚体力学、信号处理、自动控制和嵌入式实时系统是一个性价比极高的练手选题。希望这篇分享能帮你少踩几个坑把更多时间花在真正有意思的算法和场景拓展上。如果你正在做数学建模竞赛或者智能车项目也欢迎把实际调试中的反馈拿来一起交流。