1. 机器人运动学基础:刚体运动描述、齐次变换矩阵、旋转矩阵的几何意义

各位同学,欢迎来到《机器人运动学与力控算法联调》的第一章。

说实话,我见过不少刚入行的工程师,一上来就急着调PID、写轨迹规划代码,结果机器人动起来完全不是那么回事——要么末端位置算错了,要么姿态飞了。为什么?因为运动学基础没打牢。你想想看,连机器人“现在在哪儿、朝哪个方向”都描述不清楚,后面的控制全是空中楼阁。

这一章,我们就来啃下运动学最核心的三块基石:刚体运动描述、旋转矩阵的几何意义、齐次变换矩阵。这些东西,我几乎每天都会在代码里跟它们打交道。

本章核心脉络: 刚体在空间中的位置 + 姿态 → 旋转矩阵描述姿态 → 齐次变换矩阵统一描述位置与姿态 → 为后续正/逆运动学求解打下数学基础。

第1章 知识体系结构 刚体运动描述 位置描述 姿态描述 齐次变换矩阵 坐标向量 参考坐标系 旋转矩阵 R ∈ SO(3) 4×4 矩阵 复合变换 核心目标:用数学语言唯一描述刚体在三维空间中的位姿

1.1 刚体运动描述——先搞清楚“在哪儿”和“朝哪儿”

什么叫刚体?说白了,就是受力不变形的物体。机器人每个连杆都可以近似看作刚体。描述一个刚体在空间中的状态,需要两样东西:位置和姿态。合起来叫位姿。

  • 位置:刚体上某一点(通常取质心或坐标系原点)在参考坐标系下的坐标。用三维向量 p = [x, y, z]^T 表示。
  • 姿态:刚体相对于参考坐标系的朝向。用旋转矩阵 R 表示。

我记得刚入行时,带我的老工程师跟我说过一句话,我一直记到现在:“没有参考系的位置,就是耍流氓。” 你报一个坐标 (1, 2, 3),不说是相对于哪个坐标系,别人根本不知道你在说哪儿。所以,描述位姿的第一步——明确参考坐标系。

个人习惯: 我在代码里给每个坐标系都取一个明确的名字,比如 world_frame、base_frame、tool0_frame。变量名里带上坐标系后缀,比如 pos_in_world。这习惯帮我省了无数调试时间。

1.2 旋转矩阵的几何意义——不只是9个数字

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

但别被这些数学名词吓到。你只需要记住旋转矩阵的几何意义:

  1. 列向量是旋转后的基向量:R 的三列,分别是被旋转坐标系 {B} 的三个单位主矢量在参考坐标系 {A} 下的投影。
  2. 行向量是参考系的基向量在旋转系下的投影:反过来看也行。
  3. 它描述了一个刚体从参考系到当前姿态的旋转操作。

举个例子,绕 Z 轴旋转 θ 角的旋转矩阵长这样:

R_z(θ) = | cosθ  -sinθ  0 |
         | sinθ   cosθ  0 |
         | 0      0     1 |

你看,第一列 [cosθ, sinθ, 0]^T 就是旋转后的 X 轴在原始坐标系下的方向。是不是很直观?

避坑指南: 我曾经在项目里直接用欧拉角做插值,结果机器人末端走了一条奇怪的弧线,差点撞到工件。后来才意识到——欧拉角有万向锁问题。从那以后,我内部计算一律用旋转矩阵或四元数,只在人机界面显示时才转成欧拉角。

1.3 齐次变换矩阵——把旋转和平移装进一个“盒子”

有了旋转矩阵描述姿态,加上位置向量描述平移,我们能不能把两者统一起来?

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

T = | R   p |
    | 0   1 |

其中 R 是 3×3 旋转矩阵,p 是 3×1 平移向量,最后一行是 [0 0 0 1]。

为什么要加这一行?为了能用矩阵乘法把多个变换串联起来。你想想看,如果只有旋转矩阵和平移向量,你要先旋转再平移,得写两步。用齐次变换矩阵,一步到位:

p' = T * p

这里的 p 和 p' 都是齐次坐标(4维,最后一维是1)。

我的经验: 在写运动学代码时,我习惯把所有连杆变换都写成齐次变换矩阵。这样从基座到末端执行器,只需要把一串 T 矩阵乘起来:T_0_n = T_0_1 * T_1_2 * ... * T_{n-1}_n。代码结构清晰,调试也方便。

1.4 三种常见旋转表示法对比

实际工程中,我们不止用旋转矩阵。这里我列个表,把几种常见表示法放在一起对比,你一看就明白各自的应用场景了。

表示法 维度 优点 缺点 常用场景
旋转矩阵 3×3 (9个参数) 无奇异性,组合方便 参数冗余,需满足正交约束 内部计算、运动学推导
欧拉角 (RPY) 3个角度 直观,易理解 万向锁,插值不平滑 人机交互、示教器显示
四元数 4个参数 无奇异性,插值平滑 不够直观 姿态插值、滤波、SLAM

我个人在写运动学算法时,内部统一用旋转矩阵和齐次变换矩阵。为什么?因为矩阵乘法可以直接用线性代数库优化,计算效率高。只有在需要跟用户交互或者做可视化时,我才转成欧拉角。

1.5 齐次变换矩阵的复合——串联机器人的“通关密码”

串联机器人,说白了就是一系列连杆通过关节串起来。每个关节都有一个变换矩阵描述相邻连杆之间的位姿关系。

假设机器人有 n 个关节,从基座到末端执行器的总变换就是:

T_0_n = T_0_1(q1) * T_1_2(q2) * ... * T_{n-1}_n(qn)

这里的 qi 是关节变量(旋转关节是角度,移动关节是位移)。每个 T_{i-1}_i 都是 qi 的函数。

这就是正运动学的核心——给定关节角度,求末端位姿。

注意: 矩阵乘法不满足交换律!T_0_1 * T_1_2 和 T_1_2 * T_0_1 的结果完全不同。我在刚学的时候犯过这个错,把变换顺序搞反了,结果机器人末端跑到天上去。记住:左乘表示相对于固定参考系变换,右乘表示相对于当前坐标系变换。

1.6 代码实战:用 Python 实现齐次变换矩阵

光说不练假把式。我们直接上代码。这里我用 NumPy 实现一个齐次变换矩阵的构建和复合操作。

import numpy as np

def rotation_x(theta):
    """绕X轴旋转theta弧度"""
    c, s = np.cos(theta), np.sin(theta)
    return np.array([
        [1, 0,  0],
        [0, c, -s],
        [0, s,  c]
    ])

def rotation_y(theta):
    """绕Y轴旋转theta弧度"""
    c, s = np.cos(theta), np.sin(theta)
    return np.array([
        [ c, 0, s],
        [ 0, 1, 0],
        [-s, 0, c]
    ])

def rotation_z(theta):
    """绕Z轴旋转theta弧度"""
    c, s = np.cos(theta), np.sin(theta)
    return np.array([
        [c, -s, 0],
        [s,  c, 0],
        [0,  0, 1]
    ])

def homogeneous_transform(R, p):
    """由旋转矩阵R和平移向量p构建齐次变换矩阵"""
    T = np.eye(4)
    T[:3, :3] = R
    T[:3, 3] = p
    return T

# 示例:绕Z轴旋转30度,再沿X轴平移1米
theta = np.deg2rad(30)
R = rotation_z(theta)
p = np.array([1.0, 0.0, 0.0])
T = homogeneous_transform(R, p)

print("齐次变换矩阵 T:")
print(T)

# 复合变换:连续两次变换
T1 = homogeneous_transform(rotation_z(np.deg2rad(45)), np.array([0.5, 0, 0]))
T2 = homogeneous_transform(rotation_x(np.deg2rad(30)), np.array([0, 0.3, 0]))
T_composite = T1 @ T2  # 先T2后T1(左乘)

print("\n复合变换 T1 * T2:")
print(T_composite)

小技巧: 我在调试时经常用 np.allclose(T @ T_inv, np.eye(4)) 来验证变换矩阵的逆是否正确。齐次变换矩阵的逆有解析形式,不需要真的去求4×4矩阵的逆——T_inv = [[R^T, -R^T * p], [0, 1]],这样算又快又准。

1.7 本章小结

好了,这一章的内容就到这里。我们走完了运动学的第一公里:

  • 刚体运动需要位置 + 姿态才能完整描述
  • 旋转矩阵的列向量就是旋转后的基向量,几何意义非常直观
  • 齐次变换矩阵把旋转和平移统一成4×4矩阵,方便串联复合
  • 三种旋转表示法各有优劣,我推荐内部计算用旋转矩阵
  • 正运动学就是齐次变换矩阵的连乘

这些内容看起来是纯数学,但相信我——后面每一章都会用到它们。你花时间把这一章吃透,后面的路会顺很多。