机器人 3D 位姿与 5 种旋转表示法详解
介绍机器人位姿概念,包含位置与姿态。详细解析五种姿态表示法:欧拉角(直观但有万向锁)、旋转矩阵(无歧义但冗余)、轴 - 角(物理意义清晰)、四元数(高效无锁但不直观)及 6D 旋转(ML 友好)。重点阐述为何神经网络偏好 6D 旋转以避免约束问题,并给出从 6D 向量到旋转矩阵的 Python 实现代码。

介绍机器人位姿概念,包含位置与姿态。详细解析五种姿态表示法:欧拉角(直观但有万向锁)、旋转矩阵(无歧义但冗余)、轴 - 角(物理意义清晰)、四元数(高效无锁但不直观)及 6D 旋转(ML 友好)。重点阐述为何神经网络偏好 6D 旋转以避免约束问题,并给出从 6D 向量到旋转矩阵的 Python 实现代码。

当我们教一个机器人执行任务时,比如'拿起桌上的杯子',我们到底在教它什么?我们不能只说'去拿杯子'。相反,我们必须给它一串精确的、机器可读的指令。
这个指令的核心,就是**'位姿 (Pose)'**。
在机器人学和 3D 视觉中,位姿是描述一个物体在空间中完整状态的术语。本文深入探讨这个概念,特别是描述'朝向'的五种主流方法。理解这些,你就能明白为什么现代机器人(尤其是那些由机器学习驱动的)会使用一些看起来非常'奇怪'的数学表示。
一个完整的'位姿'由两部分组成:
这部分很简单。我们通常用一个 3D 向量 (x, y, z) 来表示,这就是我们都熟悉的笛卡尔 (Cartesian) 坐标。它回答了'物体在哪里?'
这部分复杂得多。它回答了'物体朝向何方?'
想象一个杯子在桌上的 (x, y, z) 位置是固定的,但它可以'正着放'、'倒着放'或'躺着放'——这就是姿态。
描述 3D 旋转有非常多种方式,每种都有其独特的优缺点。下面,我们将详细介绍五种最主要的'姿态'表示法,从最直观的到最适合机器学习的。
这是最直观、最'人类友好'的一种方式。
Python 代码示例 (使用 scipy)
import numpy as np
from scipy.spatial.transform import Rotation as R
# 创建一个欧拉角:绕 Z 轴 30 度,Y 轴 45 度,X 轴 60 度
# 'zyx' 是旋转顺序,degrees=True 表示单位是度
r_euler = R.from_euler('zyx', [30, 45, 60], degrees=True)
# 转换为旋转矩阵
print("--- 欧拉角 ---")
print(r_euler.as_matrix())
这是最基础、最没有歧义的表示方式。
Python 代码示例
# 绕 Z 轴旋转 90 度
theta = np.pi / 2 # 90 degrees in radians
c, s = np.cos(theta), np.sin(theta)
# Z 轴旋转矩阵
mat_z = np.array([[c, -s, 0],
[s, c, 0],
[0, 0, 1]])
r_matrix = R.from_matrix(mat_z)
print("\n--- 旋转矩阵 ---")
print(r_matrix.as_matrix())
# 从矩阵转换回欧拉角
print(f"对应的欧拉角 (zyx): {r_matrix.as_euler('zyx', degrees=True)}")
这是一种在物理上很直观的表示。
Python 代码示例
注意:
scipy中使用'旋转向量 (Rotation Vector)',它是'轴 - 角'的一种紧凑表示,即vector = axis * angle。
# 绕 Z 轴 (0, 0, 1) 旋转 90 度 (pi/2)
# 旋转向量 = [0, 0, 1] * (np.pi / 2)
rot_vec = np.array([0, 0, np.pi / 2])
r_rotvec = R.from_rotvec(rot_vec)
print("\n--- 轴 - 角 (旋转向量) ---")
print(f"对应的旋转矩阵:\n{r_rotvec.as_matrix()}")
这是在图形学、机器人和航空中最常用的一种。
Python 代码示例
# Scipy 的 from_quat 期望 (x, y, z, w) 顺序
# 绕 Z 轴 90 度 (theta=pi/2)
# w = cos(pi/4) = 0.707
# z = sin(pi/4) = 0.707
quat_xyzw = np.array([0, 0, np.sin((np.pi/2)/2), np.cos((np.pi/2)/2)])
r_quat = R.from_quat(quat_xyzw)
print("\n--- 四元数 ---")
print(f"对应的旋转矩阵:\n{r_quat.as_matrix()}")
这是专门为 机器学习 设计的表示法,也是 VLA(视觉 - 语言 - 动作)模型中的新宠。
这才是问题的核心。为什么不让神经网络直接输出欧拉角(3 个数)或四元数(4 个数)呢?
6D 旋转是完美的'甜点':
神经网络可以直接、无约束地输出 6 个数字。我们只需要一个明确的、可微分的数学步骤,就能将这 6 个数'修复'回一个标准、有效的 3x3 旋转矩阵。
这个修复过程,就是 格拉姆 - 施密特正交化 (Gram-Schmidt process)。
下面,我们用 Python 和 NumPy 来实现这个转换。这就是在许多 VLA 模型脚本中能找到的核心函数。
import numpy as np
def six_d_to_rotation_matrix(d6: np.ndarray) -> np.ndarray:
"""
将 6D 旋转表示 (旋转矩阵的前两列) 转换回一个 3x3 旋转矩阵。
使用格拉姆 - 施密特正交化。
参数:
d6: shape (..., 6) 的 6D 旋转向量
返回:
R: shape (..., 3, 3) 的旋转矩阵
"""
# 1. 将 6D 向量重塑为两个 3D 列向量 a1 和 a2
# a1 是旋转矩阵的第一列,a2 是第二列
a1 = d6[..., 0:3]
a2 = d6[..., 3:6]
# 2. 正交化:计算 b1 (第一列)
# b1 = a1 / ||a1||
b1 = a1 / np.linalg.norm(a1, axis=-1, keepdims=True)
# 3. 正交化:计算 b2 (第二列)
# b2_un = a2 - (a2·b1) * b1
# (从 a2 中减去它在 b1 上的投影,使其与 b1 正交)
proj_a2_on_b1 = np.sum(b1 * a2, axis=-1, keepdims=True) * b1
b2_un = a2 - proj_a2_on_b1
# b2 = b2_un / ||b2_un||
b2 = b2_un / np.linalg.norm(b2_un, axis=-1, keepdims=True)
# 4. 计算 b3 (第三列)
# b3 = b1 x b2 (叉乘)
b3 = np.cross(b1, b2, axis=-1)
# 5. 将 b1, b2, b3 堆叠成 3x3 矩阵
# (..., 3, 3)
rot_mats = np.stack((b1, b2, b3), axis=-1)
return rot_mats
我们来总结一下这五种表示法:
| 表示法 | 维度 | 优点 | 缺点 |
|---|---|---|---|
| 欧拉角 (RPY) | 3 | 直观,紧凑 | 万向锁, 顺序依赖 |
| 旋转矩阵 | 9 (3x3) | 无歧义,易于组合 | 冗余 (9 个数), 强约束 |
| 轴 - 角 | 4 (轴 3+ 角 1) | 物理意义清晰 | 不易于计算组合 |
| 四元数 | 4 | 无万向锁, 计算高效 | 极不直观,双重覆盖 |
| 6D 旋转 | 6 | ML 友好, 连续,无约束 | 冗余 (6 个数), 不直观 |
正如你所见,没有'一个'完美的公式,而是一个转换网络。你总是在:
A -> 旋转矩阵 -> B 或者 A -> 四元数 -> B
之间切换。
下次当你看到一个机器人模型输出一个 6 维或 7 维(3D 位置 + 4D 四元数)或 9 维(3D 位置 + 6D 旋转)的动作向量时,你就彻底明白它到底在做什么了。

微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online
生成新的随机RSA私钥和公钥pem证书。 在线工具,RSA密钥对生成器在线工具,online
基于 Mermaid.js 实时预览流程图、时序图等图表,支持源码编辑与即时渲染。 在线工具,Mermaid 预览与可视化编辑在线工具,online
解析常见 curl 参数并生成 fetch、axios、PHP curl 或 Python requests 示例代码。 在线工具,curl 转代码在线工具,online
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online