四元数基础:定义、运算与旋转表示
说到四元数,我得先坦白一件事。刚入行那会儿,我觉得这东西就是个数学玩具。直到有一次做无人机姿态解算,欧拉角把我坑惨了——万向锁一出现,整个控制系统直接炸了。从那以后,我再也不敢小看四元数。
说白了,四元数就是用来优雅地表示三维旋转的。它没有欧拉角的奇异性,也比旋转矩阵更紧凑。你想想看,一个旋转矩阵要9个参数,四元数只要4个。在嵌入式系统里,这省下来的可是真金白银的算力。
4.1 四元数的定义
四元数可以看作复数的推广。一个复数有实部和虚部,四元数则有一个实部和三个虚部。我习惯这样写:
q = w + xi + yj + zk
其中 w 是实部,x、y、z 是虚部。i、j、k 是三个虚数单位,它们满足一个很关键的关系:
i² = j² = k² = ijk = -1
嗯,这里要注意。ijk 的顺序不能乱。我在项目中遇到过有人把 ijk 写成 ikj,结果旋转方向全反了。这个错误特别隐蔽,调试了两天才发现。
四元数也可以写成向量形式:
q = [w, (x, y, z)]
或者更简洁地:
q = [w, v]
其中 v = (x, y, z) 是虚部向量。我个人更喜欢这种写法,因为它把标量部分和向量部分分开了,运算时思路更清晰。
4.2 四元数的基本运算
四元数的运算其实不复杂。我总结一下最常用的几个:
4.2.1 加法
两个四元数相加,对应分量相加就行:
q1 + q2 = [w1+w2, v1+v2]
4.2.2 乘法
四元数乘法就没那么简单了。它不满足交换律,也就是说 q1 * q2 不等于 q2 * q1。这个特性在旋转合成时特别重要。
乘法公式是这样的:
q1 * q2 = [w1*w2 - v1·v2, w1*v2 + w2*v1 + v1×v2]
你看,这里出现了点积和叉积。我建议你把这个公式记牢,因为后面所有的旋转操作都离不开它。
4.2.3 共轭与模长
四元数的共轭很简单,就是把虚部取反:
q* = [w, -v]
模长计算和向量一样:
|q| = sqrt(w² + x² + y² + z²)
单位四元数的模长为1。在旋转表示中,我们只用单位四元数。我曾经犯过一个错,忘了归一化,结果旋转后的物体越转越变形。嗯,这个坑你们别踩。
4.3 四元数与旋转
好了,终于到重点了。四元数怎么表示旋转?
假设我们要绕单位向量 n = (nx, ny, nz) 旋转角度 θ。对应的四元数是:
q = [cos(θ/2), sin(θ/2) * n]
注意,这里用的是半角。为什么是半角?我刚开始也困惑过。后来想明白了——因为四元数旋转是通过两次乘法实现的,一次左乘一次右乘,半角刚好凑成完整角度。
旋转一个向量 p 的步骤:
- 把 p 写成纯四元数:p_q = [0, p]
- 计算:p'_q = q * p_q * q*
- p' 就是旋转后的向量
代码实现大概是这样的:
def rotate_vector(q, v):
# q 是单位四元数,v 是三维向量
# 构造纯四元数
p = [0, v[0], v[1], v[2]]
# 计算 q * p * q*
q_conj = [q[0], -q[1], -q[2], -q[3]]
result = quaternion_multiply(
quaternion_multiply(q, p),
q_conj
)
# 提取向量部分
return [result[1], result[2], result[3]]
核心要点:四元数旋转的本质是「两次乘法」。左乘 q 再右乘 q*,相当于在四维空间里做了一次「双面旋转」,投影到三维空间就是我们要的旋转效果。
4.4 四元数 vs 其他旋转表示
我做个对比表格,这样更直观:
| 表示方法 | 参数数量 | 优点 | 缺点 |
|---|---|---|---|
| 旋转矩阵 | 9 | 直观,容易理解 | 冗余大,有累积误差 |
| 欧拉角 | 3 | 参数少,物理意义明确 | 有万向锁问题 |
| 四元数 | 4 | 无奇异性,插值平滑 | 不够直观 |
| 轴角 | 4 | 直观 | 插值困难 |
在实际项目中,我通常这样选:
- 做姿态解算用四元数,避免万向锁
- 做可视化调试用欧拉角,方便人看
- 做坐标变换用旋转矩阵,运算效率高
我的经验:在控制系统中,建议内部全部用四元数运算,只在输入输出时做转换。这样可以避免频繁转换带来的精度损失和bug。
4.5 四元数插值
这个在动画和轨迹规划里特别常用。两个姿态之间怎么平滑过渡?用四元数的球面线性插值(SLERP)。
公式是:
slerp(q1, q2, t) = (sin((1-t)Ω) / sinΩ) * q1 + (sin(tΩ) / sinΩ) * q2
其中 Ω 是 q1 和 q2 之间的夹角。t 从0到1变化。
我曾经在机械臂的轨迹规划里用过这个。如果不做SLERP,直接用线性插值,机械臂末端会走出奇怪的弧线。用了SLERP之后,运动平滑多了。
注意:做SLERP之前,一定要检查 q1 和 q2 的点积。如果点积为负,说明它们在四维空间里方向相反,需要先取反一个。否则插值会绕远路。
4.6 知识体系总览
下面这张图是我自己梳理的四元数知识结构,你看看:
这张图把四元数的知识体系串起来了。从定义出发,到运算,再到旋转表示,最后落到实际应用。你顺着这个脉络学,应该不会迷路。
好了,四元数的基础就讲到这里。记住,这东西刚开始可能觉得抽象,但用多了就会发现它真的很优雅。我在做控制算法时,几乎天天和它打交道。你多练练,也会习惯的。