3、刚体变换与位姿表示:旋转矩阵、平移向量、齐次变换矩阵、欧拉角、四元数

各位,欢迎来到第三章。这一章是手眼标定的数学基础,说白了就是解决一个核心问题:如何精确描述一个物体在三维空间里的位置和朝向。

我刚开始做机器人项目时,总觉得这些数学工具是纸上谈兵。直到有一次,我亲手搭建的视觉抓取系统,明明坐标算对了,机械臂就是抓不到工件。折腾了两天,最后发现是旋转矩阵用错了顺序。嗯,从那以后,我再也不敢小看这些“基础概念”了。

今天我们就把这几个概念彻底讲透。你想想看,搞懂了它们,手眼标定的底层逻辑你就掌握了八成。

3.1 刚体变换:空间中的“搬家”与“转身”

先说说什么是刚体变换。一个刚体,比如你手里的一个零件,它在空间里移动时,形状和大小不会变。这种变换只包含两件事:平移(搬家)和旋转(转身)。

为什么手眼标定离不开它?因为相机看到的物体坐标,和机器人抓取时用的坐标,是两个不同的“家”。我们需要一个数学工具,把物体从一个坐标系“搬”到另一个坐标系。这就是刚体变换的使命。

核心公式:

空间中一点 P,在坐标系 A 中的坐标是 PA,在坐标系 B 中的坐标是 PB。它们的关系是:

PA = RAB · PB + tAB

其中 RAB 是旋转矩阵,tAB 是平移向量。

3.2 旋转矩阵:最可靠的“方向描述器”

旋转矩阵,我个人的理解是——它是描述旋转最“老实”的方式。为什么这么说?因为它有严格的数学约束。

一个 3×3 的旋转矩阵 R,必须满足两个条件:

  • 正交性:RTR = I,即矩阵的逆等于它的转置。
  • 行列式为 +1:保证是纯旋转,没有镜像。

我在项目中遇到过一个问题:用欧拉角算出来的旋转,在连续运动时出现了“万向锁”,导致机械臂突然卡死。后来我改用旋转矩阵做插值,问题就解决了。旋转矩阵虽然参数多(9个元素),但它没有奇异性,非常稳定。

实用技巧:

旋转矩阵的每一列,其实就是旋转后新坐标系的基向量在原坐标系中的投影。记住这一点,很多推导就变得直观了。

3.3 平移向量:简单但容易出错

平移向量 t 就是一个 3×1 的向量 [x, y, z]T,表示两个坐标系原点之间的位移。听起来很简单,对吧?

但这里有个坑:平移向量是在哪个坐标系下表示的?

我曾经犯过一个错误:把相机坐标系下的平移向量,直接当成机器人坐标系下的平移来用。结果标定出来的结果完全不对。记住,公式 PA = R·PB + t 中的 t,是从 B 系原点到 A 系原点的向量,在 A 系下的表示。

避坑指南:

我曾经在标定脚本里把平移向量的符号搞反了,导致标定结果收敛到一个错误值。检查了两天才发现。建议你在代码里显式注释:# t_AB: 从B系到A系的平移,在A系下表示。

3.4 齐次变换矩阵:把旋转和平移“打包”

每次都要写 PA = R·PB + t,有点麻烦。而且旋转和平移是分开的,不利于连续变换。于是就有了齐次变换矩阵。

它是一个 4×4 的矩阵:

| R   t |
| 0   1 |

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

用齐次坐标表示点 P = [x, y, z, 1]T,变换就变成了简单的矩阵乘法:

PA = TAB · PB

连续变换也只需要矩阵相乘:TAC = TAB · TBC。这就是手眼标定中“链式法则”的数学基础。

为什么叫“齐次”?

因为引入了第4维,把非线性变换(旋转+平移)变成了线性变换(矩阵乘法)。这在计算机视觉和机器人学里是标准做法。

3.5 欧拉角:直观但“有脾气”

欧拉角用三个角度(比如绕 X、Y、Z 轴的旋转角)来描述朝向。非常直观,一看就懂。

但它有三个问题:

  • 万向锁:当中间轴旋转到 ±90° 时,会丢失一个自由度。
  • 顺序依赖:XYZ、ZYX、ZYZ……不同顺序结果完全不同。
  • 插值困难:在两个欧拉角之间做线性插值,得到的路径往往不是最短路径。

我记得有一次做视觉引导,用欧拉角给机械臂发姿态指令。结果在某个特定角度,机械臂突然做了一个大回环动作,差点撞到旁边的设备。后来查出来是万向锁导致的。从那以后,我在内部计算中只用旋转矩阵或四元数,只在人机交互界面才显示欧拉角。

表示方式 优点 缺点
旋转矩阵 无奇异性,适合计算 参数多,不直观
欧拉角 直观,参数少 万向锁,顺序依赖
四元数 无奇异性,适合插值 不够直观

3.6 四元数:工程师的“瑞士军刀”

四元数,听起来很玄乎,其实就是一个四维向量 [w, x, y, z],满足 w² + x² + y² + z² = 1。它用“轴角”的方式描述旋转:绕某个单位向量 (x, y, z) 旋转 θ 角度,对应的四元数是 [cos(θ/2), sin(θ/2)·x, sin(θ/2)·y, sin(θ/2)·z]。

为什么我推荐在代码里用四元数?

  • 无万向锁:这是最大的优势。
  • 插值平滑:球面线性插值(SLERP)可以生成最短路径的旋转动画。
  • 组合高效:两个四元数相乘就可以合成旋转,比矩阵乘法快。

你想想看,在手眼标定中,我们经常需要优化旋转参数。如果用旋转矩阵,9个参数还要满足正交约束,优化起来很麻烦。用四元数,只需要4个参数加一个归一化约束,简单多了。

代码示例:四元数与旋转矩阵互转(Python)

import numpy as np
from scipy.spatial.transform import Rotation as R

# 四元数 -> 旋转矩阵
quat = [0.707, 0.0, 0.707, 0.0]  # [w, x, y, z]
rot_mat = R.from_quat(quat).as_matrix()

# 旋转矩阵 -> 四元数
rot_mat = np.eye(3)
quat = R.from_matrix(rot_mat).as_quat()  # [x, y, z, w]

3.7 知识体系总览

下面这张图,是我自己总结的位姿表示知识体系。你可以把它当作一个快速索引。

刚体变换与位姿表示知识体系 刚体变换 旋转 平移 旋转矩阵 欧拉角 四元数 平移向量 [x, y, z] 齐次变换矩阵 核心:旋转 + 平移 → 齐次变换矩阵 → 手眼标定中的坐标系转换 推荐:内部计算用四元数或旋转矩阵,人机交互用欧拉角

3.8 实战建议:怎么选?

说了这么多,到底该用哪种?我个人的经验是:

  • 做标定计算:用齐次变换矩阵,方便链式相乘。
  • 做优化求解:用四元数,参数少,无约束。
  • 做数据存储:用旋转矩阵或四元数,避免欧拉角的歧义。
  • 做调试显示:转成欧拉角,方便人眼理解。

重要提醒:

不同库对四元数元素顺序的定义可能不同。比如 SciPy 用的是 [x, y, z, w],而 Eigen 用的是 [w, x, y, z]。我在项目里吃过这个亏,两个库互转时忘了调整顺序,结果标定结果全乱套了。建议你在代码开头就统一约定,并写清楚注释。

好了,这一章的内容就到这里。这些数学工具是手眼标定的“语言”,熟练使用它们,后面的章节你会走得很顺畅。