4. 四元数基础:定义、运算与旋转矩阵转换

聊到机器人连杆坐标系的姿态描述,绕不开四元数。说实话,我刚入行时也觉得这东西有点玄乎——四个数就能表示三维旋转?后来在写一个机械臂的实时插补程序时,遇到了万向锁问题,才真正体会到四元数的好。今天咱们就把这个工具彻底搞明白。

4.1 四元数的定义

四元数可以看作复数的推广。一个复数有实部和虚部,四元数则有一个实部和三个虚部。我习惯把它写成这样:

q = w + xi + yj + zk

其中 w 是实部,x、y、z 是虚部系数。i、j、k 是三个虚数单位,它们满足一个很关键的关系:

i² = j² = k² = ijk = -1

这个关系决定了四元数的所有运算规则。你也可以把四元数看作一个标量和一个三维向量的组合:

q = [w, v]  其中 v = (x, y, z)

在机器人学里,我们最常用的是单位四元数——它的模长为1。为什么?因为单位四元数正好对应三维空间中的旋转,没有缩放。

关键点:单位四元数满足 w² + x² + y² + z² = 1。任何旋转都可以用单位四元数唯一表示(除了正负号等价)。

4.2 四元数的基本运算

四元数的运算其实不复杂,记住几条规则就行。我在做运动学解算时天天跟它们打交道。

4.2.1 加法与数乘

加法和数乘都是逐分量进行的,跟向量一样:

q₁ + q₂ = (w₁+w₂) + (x₁+x₂)i + (y₁+y₂)j + (z₁+z₂)k
λq = (λw) + (λx)i + (λy)j + (λz)k

4.2.2 乘法

四元数乘法就不一样了。它不满足交换律,也就是说 q₁q₂ ≠ q₂q₁。这一点跟矩阵乘法很像。乘法规则来自 i、j、k 的运算关系:

q₁q₂ = (w₁w₂ - v₁·v₂) + (w₁v₂ + w₂v₁ + v₁×v₂)

展开成标量形式:

q₁q₂ = (w₁w₂ - x₁x₂ - y₁y₂ - z₁z₂)
     + (w₁x₂ + x₁w₂ + y₁z₂ - z₁y₂)i
     + (w₁y₂ - x₁z₂ + y₁w₂ + z₁x₂)j
     + (w₁z₂ + x₁y₂ - y₁x₂ + z₁w₂)k

我的经验:写代码时别手算这个展开式,直接用向量形式实现更不容易出错。我曾经在嵌入式平台上手写四元数乘法,结果把符号搞反了,机械臂转了个奇怪的角度……后来老老实实对照公式逐行检查。

4.2.3 共轭与模长

四元数的共轭就是把虚部取反:

q* = w - xi - yj - zk

模长就是各分量平方和开根号:

|q| = √(w² + x² + y² + z²)

单位四元数的模长就是1,它的逆等于共轭:q⁻¹ = q*。这个性质在旋转插值中特别有用。

4.3 四元数与旋转

好了,现在说重点——四元数怎么表示旋转?

假设我们要绕单位向量 n = (nx, ny, nz) 旋转 θ 角度,对应的四元数是:

q = cos(θ/2) + sin(θ/2)(nx i + ny j + nz k)

注意角度是半角。为什么是半角?嗯,这跟四元数旋转的运算方式有关。对一个三维向量 p 进行旋转,得到 p':

p' = q p q*

这里 p 被看作实部为0的四元数 (0, p)。这个公式就是四元数旋转的核心。你想想看,一次乘法不够,要前后各乘一次,所以角度要折半。

容易踩的坑:我曾经在写姿态控制代码时,直接用 θ 而不是 θ/2 构造四元数,结果旋转角度翻了一倍。机械臂差点撞到旁边的设备。从那以后,我每次写四元数构造都会加一行注释提醒自己。

4.4 四元数与旋转矩阵的转换

在实际工程中,我们经常需要在四元数和旋转矩阵之间来回切换。比如从 IMU 读出来的是四元数,但运动学计算用的是旋转矩阵。

4.4.1 四元数 → 旋转矩阵

给定单位四元数 q = (w, x, y, z),对应的旋转矩阵 R 为:

R = | 1-2y²-2z²   2xy-2zw     2xz+2yw   |
    | 2xy+2zw      1-2x²-2z²   2yz-2xw   |
    | 2xz-2yw      2yz+2xw     1-2x²-2y² |

这个公式看起来有点长,但写代码时直接套用就行。我建议把它封装成一个函数,别每次都重新推导。

4.4.2 旋转矩阵 → 四元数

反过来转换稍微麻烦一点。从旋转矩阵 R 的元素提取四元数:

w = √(1 + R₁₁ + R₂₂ + R₃₃) / 2
x = (R₃₂ - R₂₃) / (4w)
y = (R₁₃ - R₃₁) / (4w)
z = (R₂₁ - R₁₂) / (4w)

这里有个问题——如果 w 接近0,除法会放大数值误差。我一般会先判断哪个分量最大,用那个分量做分母:

if w 最大:
    用 w 公式
elif x 最大:
    x = √(1 + R₁₁ - R₂₂ - R₃₃) / 2
    w = (R₃₂ - R₂₃) / (4x)
    y = (R₁₂ + R₂₁) / (4x)
    z = (R₁₃ + R₃₁) / (4x)
elif y 最大:
    ...类似处理
elif z 最大:
    ...类似处理

避坑指南:我曾经在嵌入式平台上用单精度浮点做这个转换,结果在 w 接近0时出现了明显的数值抖动。后来改用双精度,并加了分支判断,问题就解决了。如果你的平台性能允许,尽量用 double。

4.5 四元数的优势与局限

我整理了一个表格,方便你对比四元数和旋转矩阵:

特性 四元数 旋转矩阵
存储空间 4个浮点数 9个浮点数
插值平滑性 球面线性插值(SLERP),平滑 线性插值会导致非正交
万向锁 无 欧拉角有
组合旋转 乘法,计算量小 矩阵乘法,计算量大
直观性 不直观 较直观
数值稳定性 容易归一化保持单位 累积误差需正交化

说白了,四元数在计算效率和数值稳定性上占优,但不够直观。我个人的习惯是:内部计算全用四元数,只在调试和显示时转成旋转矩阵或欧拉角。

4.6 知识结构总览

下面这张图把四元数的核心脉络梳理了一遍。你可以把它当作快速参考:

四元数知识结构 四元数 q = w + xi + yj + zk 定义与基本性质 单位四元数:w²+x²+y²+z²=1 表示三维旋转,无万向锁 四元数运算 乘法不满足交换律 共轭 q* = w - xi - yj - zk 逆 q⁻¹ = q* / |q|² 旋转表示 绕轴 n 转 θ:q = cos(θ/2) + sin(θ/2)n 旋转公式:p' = q p q* 与旋转矩阵的相互转换 四元数 → 旋转矩阵:直接代入公式 旋转矩阵 → 四元数:注意数值稳定性 应用:姿态插值、IMU数据融合、运动学解算

四元数这东西,刚开始用会觉得别扭,但用顺手了就离不开。尤其是在做姿态插值和传感器融合时,它的优势太明显了。记住:内部计算用四元数,对外接口用旋转矩阵,这样既高效又方便调试。