移动机器人底盘运动学模型全解析
目录
前言
在上一篇文章 《一文搞懂移动机器人底盘结构模型》 中,我们已经对常见移动机器人底盘进行了整体梳理,包括两轮差速、四轮差速、履带式、阿克曼、麦克纳姆轮、全向轮以及舵轮 AGV 等典型结构。链接如下:
一文搞懂移动机器人底盘结构模型-CSDN博客
https://blog.csdn.net/m0_58954356/article/details/161477629通过上一章的学习,我们主要解决的是一个问题:
不同移动机器人底盘在结构上有什么区别?
但是,仅仅知道底盘结构还不够。真正进行机器人控制时,我们还需要进一步回答:
机器人想要以某个速度运动时,每个轮子到底应该怎么转?
也就是说,移动机器人底盘控制的核心并不是直接控制机器人“往前走”或“向左转”,而是要通过运动学模型,将机器人整体速度指令转换为每个轮子的速度、角速度、转向角或电机 RPM。
本文将围绕常见底盘的运动学建模方法展开,重点介绍差速底盘、四轮差速、履带底盘、阿克曼底盘、麦克纳姆轮底盘等模型的速度分解关系、公式推导思路、C++ 代码实现以及工程调试注意事项。
通过本章内容,可以帮助读者从“认识底盘结构”进一步过渡到“理解底盘如何运动、如何控制、如何把速度指令转换成电机指令”,为后续学习机器人里程计、路径跟踪、导航控制和 ROS 底盘驱动开发打下基础。
阿克曼底盘基本模型简化为自行车模型相关推导见之前博客链接:

一、移动机器人底盘控制的核心逻辑
1.1 上位机给底盘发送什么?
在移动机器人系统中,上位机一般不会直接控制每个电机,而是发送机器人整体速度指令。
常见速度指令为:
vx // x 方向线速度
vy // y 方向线速度
wz // 绕 z 轴角速度
不同底盘使用的速度量不同。
两轮差速底盘通常只需要:
vx
wz
麦克纳姆轮底盘通常需要:
vx
vy
wz
阿克曼底盘通常需要:
v
delta
其中:
v表示车辆前进速度;delta表示前轮转角。
1.2 底盘控制器做什么?
底盘控制器通常负责以下工作:
(1)接收上位机速度指令;
(2)解析通信协议;
(3)根据底盘模型进行运动学逆解;
(4)计算每个轮子的目标速度;
(5)将轮速转换为电机转速;
(6)发送控制指令给电机驱动器;
(7)读取编码器反馈;
(8)计算里程计;
(9)上传电池、电机、故障和传感器状态。
二、两轮差速底盘运动学模型
2.1 两轮差速基本原理
两轮差速底盘由左右两个主动轮控制运动。
当左右轮速度相同,机器人直行;
当左右轮速度不同,机器人转弯;
当左右轮速度大小相等、方向相反,机器人原地旋转。
设:
V // 机器人中心线速度
W // 机器人角速度
VL // 左轮线速度
VR // 右轮线速度
L // 左右轮间距
则:
V = (VR + VL) / 2
W = (VR - VL) / L
反过来:
VR = V + W * L / 2
VL = V - W * L / 2
2.2 差速底盘速度分解代码
#include <iostream>
using namespace std;
struct DiffWheelSpeed
{
double left;
double right;
};
DiffWheelSpeed diff_inverse_kinematics(double v, double w, double wheel_base)
{
DiffWheelSpeed speed;
speed.right = v + w * wheel_base / 2.0;
speed.left = v - w * wheel_base / 2.0;
return speed;
}
int main()
{
double v = 0.5; // 机器人线速度 m/s
double w = 0.3; // 机器人角速度 rad/s
double wheel_base = 0.6; // 左右轮间距 m
DiffWheelSpeed speed = diff_inverse_kinematics(v, w, wheel_base);
cout << "left wheel speed = " << speed.left << " m/s" << endl;
cout << "right wheel speed = " << speed.right << " m/s" << endl;
return 0;
}
2.3 轮子线速度转电机 RPM
在移动机器人底盘控制中,我们通常会先根据底盘运动学模型计算出左右轮的线速度,例如:

但是在实际电机控制时,电机驱动器往往不能直接接收“线速度”,而是需要接收电机的转速指令,例如:

也就是每分钟转多少圈。
因此,需要将轮子的线速度换算成电机转速。
2.3.1 轮子线速度与轮子转速的关系
设:
v_wheel // 轮子线速度,单位 m/s
D // 轮子直径,单位 m
gear_ratio // 减速比
rpm // 电机转速,单位 r/min
轮子转动一圈,机器人前进的距离等于轮子的周长。

2.3.2 每秒转速转换为每分钟转速

这个结果表示的是轮子本身的转速,还不是电机转速。
2.3.3 考虑减速比后的电机 RPM
实际机器人底盘中,电机通常不会直接驱动车轮,而是通过减速器带动车轮运动。

也可以写成代码形式:
rpm = v_wheel * 60 * gear_ratio / (PI * D);
2.3.4 公式含义总结

因此,这个公式的本质就是:
轮子线速度 -> 轮子转速 -> 电机转速
2.3.5 C++ 代码实现
下面给出一个简单的 C++ 示例,用于将轮子线速度转换为电机 RPM。
#include <iostream>
#include <cmath>
using namespace std;
double wheel_speed_to_rpm(double v_wheel, double wheel_diameter, double gear_ratio)
{
const double PI = 3.1415926;
double rpm = v_wheel * 60.0 * gear_ratio / (PI * wheel_diameter);
return rpm;
}
int main()
{
// 轮子线速度,单位 m/s
double v_wheel = 0.5;
// 轮子直径,单位 m
double wheel_diameter = 0.15;
// 减速比,例如 30:1
double gear_ratio = 30.0;
double rpm = wheel_speed_to_rpm(v_wheel, wheel_diameter, gear_ratio);
cout << "motor rpm = " << rpm << endl;
return 0;
}
程序运行后,输出结果大约为:
motor rpm = 1909.86
这表示:
当轮子线速度为 0.5m/s、轮子直径为 0.15m、减速比为 30:1 时,电机大约需要输出:
1909.86rpm
2.3.6 注意事项

三、四轮差速与履带底盘模型
3.1 四轮差速底盘简化模型
四轮差速可以简化成左右两侧差速模型。
设:
V_left // 左侧轮组平均速度
V_right // 右侧轮组平均速度
L // 左右轮距
则:
V = (V_right + V_left) / 2
W = (V_right - V_left) / L
反解为:
V_right = V + W * L / 2
V_left = V - W * L / 2
如果四个轮子独立驱动,则可以简单分配为:
front_left = V_left
rear_left = V_left
front_right = V_right
rear_right = V_right
3.2 四轮差速控制注意点
四轮差速和两轮差速公式看起来很像,但工程效果不完全一样。
原因是四轮差速转弯时,四个轮子的理论运动轨迹并不完全一致。特别是原地旋转时,轮胎必须发生一定滑移。
所以四轮差速调试时要注意:
(1)电机功率要足够;
(2)轮胎不能太软;
(3)地面摩擦不能过大;
(4)速度不要突然变化;
(5)原地旋转不要频繁长时间执行;
(6)编码器里程计需要结合 IMU 修正。
3.3 履带底盘控制模型
履带底盘本质上也是左右差速控制。
设:
V_left = 左履带速度
V_right = 右履带速度
则:
V = (V_right + V_left) / 2
W = (V_right - V_left) / L
履带底盘的问题是滑移更严重,所以编码器里程计误差通常更大。
四、阿克曼底盘运动学模型
4.1 阿克曼底盘基本模型
阿克曼底盘类似汽车结构,一般由前轮转向,后轮驱动。
设:
x // 车辆 x 坐标
y // 车辆 y 坐标
theta // 车辆航向角
v // 车辆速度
delta // 前轮转角
L // 轴距
简化自行车模型为:
x_dot = v * cos(theta)
y_dot = v * sin(theta)
theta_dot = v / L * tan(delta)
转弯半径为:
R = L / tan(delta)
角速度为:
w = v / L * tan(delta)
如果已知期望角速度 w 和速度 v,可以反求前轮转角:
delta = atan(w * L / v)
4.2 阿克曼底盘控制代码
#include <iostream>
#include <cmath>
using namespace std;
double calculate_steering_angle(double v, double w, double wheel_base)
{
if (fabs(v) < 1e-6)
{
return 0.0;
}
return atan(w * wheel_base / v);
}
int main()
{
double v = 1.0; // 车辆速度 m/s
double w = 0.3; // 期望角速度 rad/s
double wheel_base = 1.2; // 轴距 m
double delta = calculate_steering_angle(v, w, wheel_base);
cout << "steering angle = " << delta << " rad" << endl;
cout << "steering angle = " << delta * 180.0 / 3.1415926 << " deg" << endl;
return 0;
}
4.3 阿克曼底盘工程注意点
阿克曼底盘不能像差速底盘一样直接原地旋转。
当速度 v 很小时,不能简单用 delta = atan(wL/v),否则会出现除零或者转角过大的问题。
所以工程中一般要限制:
- 最大前轮转角;
- 最大角速度;
- 最大横向加速度;
- 最小转弯半径;
- 最大转向角速度。
五、麦克纳姆轮底盘运动学模型
5.1 麦克纳姆轮速度分解
麦克纳姆轮底盘可以实现平面三自由度运动。
设:
vx // 车体 x 方向速度
vy // 车体 y 方向速度
wz // 车体绕 z 轴角速度
r // 轮子半径
lx // 左右半轮距
ly // 前后半轮距
设四个轮子编号为:
w1:左前轮
w2:右前轮
w3:左后轮
w4:右后轮
一种常见模型为:
w1 = (vx - vy - (lx + ly) * wz) / r
w2 = (vx + vy + (lx + ly) * wz) / r
w3 = (vx + vy - (lx + ly) * wz) / r
w4 = (vx - vy + (lx + ly) * wz) / r
注意:
不同资料中轮子编号、坐标系方向和滚子安装方向可能不同,所以公式正负号也可能不同。
5.2 麦克纳姆轮代码示例
#include <iostream>
using namespace std;
struct MecanumWheelSpeed
{
double w1;
double w2;
double w3;
double w4;
};
MecanumWheelSpeed mecanum_inverse_kinematics(
double vx, double vy, double wz,
double r, double lx, double ly)
{
double k = lx + ly;
MecanumWheelSpeed speed;
speed.w1 = (vx - vy - k * wz) / r;
speed.w2 = (vx + vy + k * wz) / r;
speed.w3 = (vx + vy - k * wz) / r;
speed.w4 = (vx - vy + k * wz) / r;
return speed;
}
int main()
{
double vx = 0.5;
double vy = 0.2;
double wz = 0.3;
double r = 0.075;
double lx = 0.25;
double ly = 0.20;
MecanumWheelSpeed speed = mecanum_inverse_kinematics(vx, vy, wz, r, lx, ly);
cout << "w1 = " << speed.w1 << endl;
cout << "w2 = " << speed.w2 << endl;
cout << "w3 = " << speed.w3 << endl;
cout << "w4 = " << speed.w4 << endl;
return 0;
}
5.3 麦克纳姆轮调试顺序
麦轮底盘调试时,建议不要一开始就测试复杂运动,而是按照以下顺序:
(1)单独测试每个电机方向;
(2)测试四轮同时前进;
(3)测试四轮同时后退;
(4)测试左右横移;
(5)测试原地旋转;
(6)测试斜向移动;
(7)测试复合运动。
最常见问题有:
- 轮子编号错;
- 电机方向反;
- 滚子安装方向错;
- 坐标系定义不一致;
- 左右轮距或前后轮距参数填错。
六、里程计计算与工程总结
6.1 差速底盘里程计
机器人位姿通常表示为:
pose = (x, y, theta)
其中:
x表示世界坐标系 x 方向位置;y表示世界坐标系 y 方向位置;theta表示航向角。
如果机器人线速度为 v,角速度为 w,控制周期为 dt,则:
delta_s = v * dt
delta_theta = w * dt
位姿更新:
x = x + delta_s * cos(theta)
y = y + delta_s * sin(theta)
theta = theta + delta_theta
代码示例:
#include <iostream>
#include <cmath>
using namespace std;
struct Pose2D
{
double x;
double y;
double theta;
};
void update_odometry(Pose2D& pose, double v, double w, double dt)
{
pose.x += v * dt * cos(pose.theta);
pose.y += v * dt * sin(pose.theta);
pose.theta += w * dt;
}
int main()
{
Pose2D pose{0.0, 0.0, 0.0};
double v = 0.5;
double w = 0.2;
double dt = 0.02;
for (int i = 0; i < 100; i++)
{
update_odometry(pose, v, w, dt);
}
cout << "x = " << pose.x << endl;
cout << "y = " << pose.y << endl;
cout << "theta = " << pose.theta << endl;
return 0;
}
6.2 编码器距离计算
如果使用编码器,需要先把编码器脉冲转换成轮子移动距离。
设:
N // 编码器每圈脉冲数
r // 轮子半径
delta_n // 当前周期编码器增量
则单个脉冲对应的距离为:
distance_per_pulse = 2πr / N
当前周期轮子运动距离为:
delta_s = delta_n * distance_per_pulse
如果考虑减速比:
distance_per_pulse = 2πr / (N * gear_ratio)
6.3 编码器 + IMU 融合思路
- 仅靠编码器会受到轮子打滑影响。
- 仅靠 IMU 会受到漂移影响。
所以工程中常用:
- 编码器计算位移;
- IMU 修正航向角;
- 激光 SLAM 或视觉定位修正全局位置。
常见组合方式:
轮式里程计 + IMU
轮式里程计 + 激光 SLAM
轮式里程计 + IMU + EKF
轮式里程计 + IMU + UWB
轮式里程计 + IMU + 视觉定位
6.4 不同底盘控制难点总结
| 底盘类型 | 控制难点 | 里程计难点 |
|---|---|---|
| 两轮差速 | 左右轮速度同步 | 打滑和轮距误差 |
| 四轮差速 | 转弯滑移控制 | 原地旋转误差大 |
| 履带底盘 | 滑移严重 | 编码器误差大 |
| 阿克曼底盘 | 转角限制和最小转弯半径 | 低速转向模型不稳定 |
| 麦克纳姆轮 | 四轮速度解算和安装方向 | 横移误差大 |
| 全向轮底盘 | 坐标变换和轮速分配 | 地面摩擦影响大 |
| 舵轮底盘 | 舵角和轮速同步 | 标定复杂 |
七、总结
移动机器人底盘控制的本质是:
把机器人整体速度指令,转换成每个轮子的速度、角度或电机转速。
- 对于差速底盘,核心是左右轮速度差;
- 对于四轮差速和履带底盘,核心是左右两侧速度差;
- 对于阿克曼底盘,核心是车辆速度和前轮转角;
- 对于麦克纳姆轮底盘,核心是将
vx、vy、wz分解到四个轮子;- 对于舵轮底盘,核心是同时控制每个轮子的转向角和驱动速度。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)