同一个朝向的三种写法:四元数、轴角与 6D

读 SuperMimic 一个公开复现(dhmbb2/supermimic_vibe)的世界模型代码时,我发现机器人骨盆的朝向在一次前向里换了三种写法:缓冲区里存四元数,网络输出”这一步转了多少”用轴角,喂给网络时又变成 6D。它们描述的是同一件事,为什么要来回换?

先说背景:为什么没有完美的写法

朝向有 3 个自由度,最直接的写法是 3×3 旋转矩阵 :三列分别是机身的 x、y、z 轴在世界坐标系里的方向。9 个数只有 3 个自由度,太冗余。

很自然会想用 3 个数,比如欧拉角(横滚、俯仰、偏航)。但数学上有个结论:只用 3 个数,一定会在某处出现奇异或跳变。欧拉角在俯仰 ±90° 时会丢一个自由度(万向锁),偏航在 ±180° 处会从 179° 跳到 −179°。

所以每种写法都是一种取舍:用几个数、有没有约束、毛病出在哪里。下面三种,各自把毛病放在了”用的地方碰不到”的位置。

四元数:用来存储和插值

是什么。绕单位轴 转角度 ,写成 4 个数:

模长恒为 1。多用 1 个数、加 1 个约束,换来的是处处没有奇异点。

好在哪。

  • 两个旋转的组合就是四元数相乘,比矩阵乘法便宜。
  • 两个朝向之间可以做球面插值(slerp),沿最短路径匀速转过去。
  • 数值误差让模长偏离 1 时,除以模长就修好了。

毛病。 和 是同一个朝向(角度加 360°,所有分量变号,物理上没变)。插值前如果两帧符号相反,slerp 会绕远路。dhmbb2 的数据准备脚本专门处理了这点:相邻帧点积为负就翻号,然后用 slerp 把动作数据从 30 帧插到 50 帧。

用在哪。MuJoCo 自由关节的状态本来就是”位置 3 + 四元数 4”,dhmbb2 的 71 维状态和回放缓冲区都直接这样存。真正计算时,再转成旋转矩阵。

轴角:用来表示”转了多少”

是什么。任何旋转都等价于绕某根轴 转 ,把它们合成一个三维向量 。

毛病。只有 3 个数,所以逃不掉奇异点:转角接近 180° 时, 和 是同一个旋转,从矩阵反求轴角也不稳定。

好在哪。转动很小时, 接近 0,离奇异点最远:没有约束,网络随便输出 3 个数都是合法的旋转。它和角速度的关系也很直接:很短时间 内转过的轴角约等于角速度乘 。

用在哪。SuperMimic 的世界模型不预测下一步状态,而是预测 20 毫秒内的变化量。20 毫秒里转动很小,正好落在轴角最舒服的区域,所以朝向变化用 3 维轴角,再乘回当前朝向。这也是为什么状态是 71 维(四元数 4 个数)、而网络输出是 70 维(轴角 3 个数)。比较预测朝向和真实朝向时,误差同样用两者相对旋转的轴角。

6D:用来喂给神经网络

问题出在网络身上。网络是连续函数,最怕输入或输出有跳变。前两种写法都有跳变:四元数有 的歧义,规定 后,在转角接近 180° 时又会突然翻号;轴角在 180° 处也跳。朝向只变一点点,网络看到的数字却跳得很远。

Zhou 等人(CVPR 2019)证明了:用 4 个或更少的数表示三维旋转,必然不连续。最简单的连续写法,是取旋转矩阵的前两列,共 6 个数。第三列可由前两列叉乘得到,所以没丢信息。它和矩阵一样连续、唯一,又比 9 个数少了 3 个冗余。

用在哪。dhmbb2 把 6D 用作网络输入:世界模型特征里的骨盆倾斜,和策略目标里”参考朝向相对机器人的朝向”,都是 6D。如果要让网络输出 6D,还需要把两列正交化,才能变回合法的旋转矩阵。这里网络输出的是小变化量,所以用了轴角。

小结

写法 几个数 毛病在哪 最适合
四元数 4 是同一朝向 存储、组合、插值
轴角 3 转角接近 180° 时奇异 小转动、变化量、误差
6D 6 输出时要正交化 神经网络的输入或输出

一句话:存储用四元数,增量用轴角,喂网络用 6D,计算时统一换成矩阵。哪种写法在哪一步最不容易出问题,就在哪一步用它。