2. 运动学基础(上):刚体运动描述、旋转矩阵、欧拉角、齐次变换矩阵

各位,咱们开始聊运动学。说实话,刚接触机器人控制的时候,我总觉得运动学就是一堆矩阵乘法,枯燥得很。直到第一次调试机械臂,发现末端位置死活对不上——嗯,那时候我才意识到,坐标系没搞明白,后面全是白搭。

这一章,咱们把刚体运动描述这件事彻底讲透。你想想看,人形机器人的每个关节、每根连杆,本质上都是一个刚体。你要控制它,首先得知道它“在哪儿”、“朝哪儿”。这就是运动学要解决的核心问题。

核心认知: 运动学不关心力,只关心位置、姿态、速度、加速度之间的关系。它是控制算法的基础,也是你调试机器人时最先遇到坑的地方。

2.1 刚体运动:位置 + 姿态

一个刚体在三维空间里的状态,说白了就是两件事:位置和姿态。位置好办,三个坐标 (x, y, z) 就搞定了。但姿态呢?你得告诉机器人,它的“脑袋”是朝前还是朝左,是仰头还是低头。

我习惯把姿态理解成:刚体固连坐标系相对于世界坐标系的指向关系。比如,人形机器人的脚掌,它有一个自己的坐标系——脚尖方向是 X 轴,脚掌平面法线是 Z 轴。你要描述这个脚掌的姿态,就是描述这个坐标系相对于世界坐标系的旋转关系。

描述要素 数学表示 自由度 典型应用
位置 三维向量 p = [x, y, z]^T 3 关节位置、末端位置
姿态 旋转矩阵 R ∈ SO(3) 3 关节朝向、末端姿态
完整位姿 齐次变换矩阵 T ∈ SE(3) 6 连杆坐标系、运动链
我的习惯: 在代码里,我从来不会把位置和姿态分开存储为两个变量。我会直接用齐次变换矩阵 T,因为后续的坐标变换、链式乘法,用矩阵最顺手。分开存反而容易搞混。

2.2 旋转矩阵:姿态的“身份证”

旋转矩阵 R 是一个 3×3 的正交矩阵,行列式为 +1。它属于特殊正交群 SO(3)。

说白了,旋转矩阵的每一列,就是刚体固连坐标系的基向量在世界坐标系下的投影。比如,R 的第一列是固连坐标系 X 轴在世界坐标系下的方向余弦。

我记得刚学的时候,总觉得旋转矩阵有 9 个元素,太冗余了。但后来发现,它的好处是没有奇异性,而且组合旋转就是矩阵乘法,非常直观。

// 绕 Z 轴旋转 θ 角的基本旋转矩阵
R_z(θ) = [
  [cosθ, -sinθ, 0],
  [sinθ,  cosθ, 0],
  [0,      0,    1]
]

// 绕 X 轴旋转 α 角
R_x(α) = [
  [1,    0,      0   ],
  [0,  cosα,  -sinα ],
  [0,  sinα,   cosα ]
]

// 绕 Y 轴旋转 β 角
R_y(β) = [
  [ cosβ,  0,  sinβ ],
  [ 0,     1,   0   ],
  [-sinβ,  0,  cosβ ]
]
我曾经踩过的坑: 旋转矩阵的乘法顺序!R = R_z * R_y * R_x 和 R = R_x * R_y * R_z 结果完全不同。在代码里,一定要明确你的旋转顺序是“绕固定轴”还是“绕动轴”。我习惯用“绕动轴右乘,绕定轴左乘”这个口诀来记。

2.3 欧拉角:更直观,但有代价

旋转矩阵虽然好用,但 9 个参数太冗余了。实际调试的时候,你更想直接说“绕 X 轴转 30 度,再绕 Y 轴转 45 度”。这就是欧拉角。

欧拉角用三个角度来描述姿态。最常见的两种是:

  • ZYX 欧拉角(绕动轴): 先绕 Z 轴转 yaw(偏航),再绕新的 Y 轴转 pitch(俯仰),最后绕新的 X 轴转 roll(滚转)。这是人形机器人中最常用的,因为 yaw/pitch/roll 物理意义明确。
  • ZYZ 欧拉角: 常用于机械臂运动学分析,数学上更简洁。

欧拉角最大的问题是什么?万向锁(Gimbal Lock)。当 pitch = ±90° 时,yaw 和 roll 的旋转轴会重合,丢失一个自由度。我在做人形机器人腰部控制时就遇到过——当躯干前倾到 90 度时,偏航和滚转突然无法独立控制了。

表示方法 优点 缺点 适用场景
旋转矩阵 无奇异性,组合方便 9 参数冗余,不直观 内部计算、链式变换
欧拉角 直观,3 参数 有万向锁,插值不平滑 人机交互、姿态显示
轴角/四元数 无奇异性,插值平滑 不够直观 姿态插值、滤波
我的建议: 在控制循环内部,永远用旋转矩阵或四元数。只在用户界面显示、或者手动输入姿态时,才用欧拉角。这样可以避免万向锁带来的麻烦。

2.4 齐次变换矩阵:把旋转和平移统一起来

位置用向量,姿态用矩阵。但实际中,你经常需要同时变换位置和姿态。比如,从肩关节坐标系转换到肘关节坐标系,既要旋转,又要平移。

齐次变换矩阵 T 就是干这个的。它是一个 4×4 矩阵:

T = [ R   p ]
    [ 0   1 ]

其中:
R ∈ SO(3) —— 旋转部分
p ∈ ℝ³   —— 平移部分
最后一行是 [0, 0, 0, 1]

齐次变换矩阵属于特殊欧氏群 SE(3)。它的好处是:一次矩阵乘法,同时完成旋转和平移。而且多个变换可以连乘:T_13 = T_12 * T_23。

我记得第一次写正运动学代码时,就是用齐次变换矩阵把每个关节的变换乘起来,最后得到末端位姿。代码非常简洁,而且不容易出错。

// 示例:从基座到末端的正运动学
// 假设有 3 个关节,每个关节的齐次变换矩阵为 T1, T2, T3
// 末端位姿 T_end = T1 * T2 * T3

Eigen::Matrix4d T1 = get_joint_transform(joint1_angle);
Eigen::Matrix4d T2 = get_joint_transform(joint2_angle);
Eigen::Matrix4d T3 = get_joint_transform(joint3_angle);

Eigen::Matrix4d T_end = T1 * T2 * T3;

// 提取位置和姿态
Eigen::Vector3d position = T_end.block<3,1>(0,3);
Eigen::Matrix3d rotation = T_end.block<3,3>(0,0);
注意: 齐次变换矩阵的逆矩阵不是简单的转置。它的逆是: T^{-1} = [ R^T -R^T * p ] [ 0 1 ] 千万别搞错了。我曾经在逆运动学里用错了逆矩阵,结果末端轨迹完全乱套。

2.5 知识体系总览

下面这张图,把本章的核心概念串起来了。你可以把它当作一个思维导图,每次写运动学代码前扫一眼,思路会清晰很多。

运动学基础(上)知识体系 刚体运动描述 位置 (Position) 姿态 (Orientation) 完整位姿 (Pose) 旋转矩阵 (SO(3)) 欧拉角 (ZYX/ZYZ) 轴角 / 四元数 齐次变换矩阵 (SE(3)) 正运动学链式乘法 逆变换与求逆 核心要点 • 位置用向量,姿态用旋转矩阵,完整位姿用齐次变换矩阵 • 欧拉角直观但有万向锁,控制循环内部建议用旋转矩阵或四元数 • 齐次变换矩阵的乘法顺序和求逆公式是常见错误点

2.6 实战中的选择建议

说了这么多理论,最后给点实在的建议。你在写人形机器人控制代码时,到底该用哪种表示?

  • 运动学计算(正解、逆解): 用齐次变换矩阵。4×4 矩阵乘法,Eigen 库一行搞定,不容易出错。
  • 姿态插值(比如步态规划): 用四元数。SLERP 插值平滑,没有万向锁问题。
  • 用户界面显示、手动示教: 用欧拉角(ZYX)。操作员说“绕 Z 轴转 10 度”比说“乘以一个旋转矩阵”直观得多。
  • 状态估计(IMU 融合): 用四元数或旋转矩阵。欧拉角在滤波里容易出问题。
一个小技巧: 我习惯在代码里定义一个 Pose 结构体,内部用齐次变换矩阵存储,但提供 toEuler()、toQuaternion() 等转换函数。这样既保证了内部计算的一致性,又方便外部接口使用。