> *"如果你认为自己理解了四元数,那你还没有真正理解它们。"*
> *—— 这句流传的话,我们今天要把它打破。*
---
## 引子:拧开一个瓶盖
想象你正拧开一个汽水瓶盖。
你的手指做了一个动作:捏住瓶盖,旋转,然后——砰,打开了。
这个动作看起来简单极了,但如果我要你**用数学精确描述这个旋转**,事情突然变得诡异起来。
你说:"就是转了一圈啊,360度。"
但如果我问你:"往哪个方向转?"你会伸出大拇指,比划一个方向。这是**旋转轴**。
如果我问:"转多快?"你会说匀速,或者先慢后快——这是**角速度**。
现在问题来了:**我们如何用三个数字(x, y, z)来同时表示这三个信息?**
更糟糕的是,如果瓶盖拧到一半,我想知道它"现在"的姿态,该怎么办?
这就是四元数要解决的问题。但在我们抵达那里之前,让我们先回到一个更简单的地方。
---
## 第一章:复数——旋转的第一次启示
### 1.1 负数开平方?疯了吧
很久以前,数学家们发现一个尴尬的事实:有些方程没有解。
比如:$x^2 + 1 = 0$
"什么数的平方等于 -1?"没有实数满足这个条件。于是数学家们做了一个大胆的决定:**发明一个数**。
他们称之为 $i$,并规定:
$$i^2 = -1$$
这就是**虚数单位**。当时的数学家们觉得这简直是作弊——"你解决不了问题,就发明一个新数?"
但等等,让我们看看这个"作弊"带来了什么。
### 1.2 复平面上的旋转
复数看起来像这样:$z = a + bi$
- $a$ 是实部(水平方向)
- $b$ 是虚部(垂直方向)
我们可以把复数画在平面上——这就是**复平面**。
现在,神奇的事情发生了。
**乘以 $i$ 等于逆时针旋转 90 度。**
让我再重复一遍:乘以 $i$ 等于旋转 90 度。
验证一下:
- $1 \times i = i$(从 (1,0) 转到 (0,1),确实是 90 度)
- $i \times i = -1$(从 (0,1) 转到 (-1,0),再转 90 度)
- $-1 \times i = -i$(再转 90 度)
- $-i \times i = 1$(回到起点,总共 360 度)
这不仅仅是巧合。这揭示了一个深刻的真理:**乘法可以是旋转**。
### 1.3 欧拉公式的震撼
让我们更进一步。考虑这个复数:
$$z = \cos\theta + i\sin\theta$$
这个复数在复平面上位于单位圆上,与实轴夹角为 $\theta$。
现在,**欧拉**(对,就是那个欧拉)发现了这个惊人的等式:
$$e^{i\theta} = \cos\theta + i\sin\theta$$
这意味着:**指数的虚幂描述旋转**。
如果你把一个复数 $z$ 乘以 $e^{i\theta}$,你实际上是把 $z$ 旋转了角度 $\theta$。
这就是二维旋转的完整描述。优美、简洁、强大。
### 1.4 但三维呢?
现在问题来了。
复数完美地描述了**二维平面**上的旋转。但我们生活在三维空间。
瓶盖可以绕 x 轴转、绕 y 轴转、绕 z 轴转。如何用一种统一的数学语言描述这些旋转?
19 世纪中叶,一位爱尔兰数学家为此苦苦思索。他的名字叫**威廉·罗恩·哈密顿**(William Rowan Hamilton)。
---
## 第二章:哈密顿的十年求索
### 2.1 从复数到三元数?
哈密顿的想法很直接:如果复数 $a + bi$ 能描述二维旋转,那么**三元数** $a + bi + cj$ 应该能描述三维旋转吧?
这里 $i$ 和 $j$ 是两个不同的虚数单位,满足:
$$i^2 = j^2 = -1$$
哈密顿花了**十年**时间试图让这种三元数工作。他需要定义乘法规则:$i \times j$ 应该等于什么?
他尝试了各种可能性,但每次都遇到矛盾。要么乘法不满足结合律,要么无法保持向量的长度(这在旋转中很重要)。
### 2.2 那个顿悟的早晨
1843 年 10 月 16 日,哈密顿和妻子沿着都柏林的皇家运河散步。
他的脑海里一直在转悠着那个问题:如何让三维旋转的代数工作?
突然,灵光一闪。
他意识到:**他需要四个维度,而不是三个。**
他掏出小刀,在布鲁姆桥(Brougham Bridge)的石栏上刻下了这个公式:
$$i^2 = j^2 = k^2 = ijk = -1$$
这就是**四元数**的诞生时刻。
### 2.3 为什么需要第四个维度?
这是一个深刻的问题。为什么三维旋转需要四维的数学对象来描述?
答案与**旋转的不可交换性**有关。
想象一本书:
1. 先绕 x 轴转 90 度,再绕 y 轴转 90 度
2. 先绕 y 轴转 90 度,再绕 x 轴转 90 度
结果一样吗?**不一样**。旋转的顺序很重要。
数学家说:三维旋转构成一个**非阿贝尔群**。这意味着乘法顺序不可随意交换。
复数的乘法是可交换的(阿贝尔的)。为了描述非交换的旋转,我们需要更丰富的代数结构——这就是四元数。
---
## 第三章:四元数的基本结构
### 3.1 四元数长什么样
一个四元数写成这样:
$$q = w + xi + yj + zk$$
或者更常见的向量形式:
$$q = w + \mathbf{v}$$
其中:
- $w$ 是**标量部分**(实部)
- $\mathbf{v} = (x, y, z)$ 是**向量部分**
- $i, j, k$ 是三个虚数单位
### 3.2 哈密顿关系
哈密顿刻下的那个公式,展开后给出以下乘法规则:
$$i^2 = j^2 = k^2 = -1$$
$$ij = k, \quad ji = -k$$
$$jk = i, \quad kj = -i$$
$$ki = j, \quad ik = -j$$
注意那个负号!**乘法顺序很重要**:$ij \neq ji$。
这是一个非交换代数。正是因为这个非交换性,四元数才能描述三维旋转。
### 3.3 记忆的窍门
记住这些乘法规则有个简单的办法:
想象 $i, j, k$ 按顺时针排成一个圆圈:
```
i
/ \
k - j
```
**顺时针**相乘得到正的下一个:$ij = k$,$jk = i$,$ki = j$
**逆时针**相乘得到负的下一个:$ji = -k$,$kj = -i$,$ik = -j$
这就像钟表的指针,或者就像……一个旋转本身。
---
## 第四章:四元数的运算
### 4.1 加法与减法
四元数的加法和减法很简单,就像向量一样:
$$(w_1 + x_1i + y_1j + z_1k) + (w_2 + x_2i + y_2j + z_2k)$$
$$= (w_1+w_2) + (x_1+x_2)i + (y_1+y_2)j + (z_1+z_2)k$$
没什么特别的,分量逐个相加就行。
### 4.2 乘法——核心操作
四元数乘法才是精髓。让我们来计算:
$$q_1 = w_1 + x_1i + y_1j + z_1k$$
$$q_2 = w_2 + x_2i + y_2j + z_2k$$
$$q_1 q_2 = ?$$
我们需要逐项相乘,然后使用哈密顿关系化简。这有点繁琐,但结果是:
$$q_1 q_2 = (w_1w_2 - x_1x_2 - y_1y_2 - z_1z_2)$$
$$+ (w_1x_2 + x_1w_2 + y_1z_2 - z_1y_2)i$$
$$+ (w_1y_2 - x_1z_2 + y_1w_2 + z_1x_2)j$$
$$+ (w_1z_2 + x_1y_2 - y_1x_2 + z_1w_2)k$$
看起来一团糟?让我们用向量形式重写:
$$q_1 q_2 = (w_1w_2 - \mathbf{v}_1 \cdot \mathbf{v}_2) + (w_1\mathbf{v}_2 + w_2\mathbf{v}_1 + \mathbf{v}_1 \times \mathbf{v}_2)$$
**等等!** 看到了吗?
- 标量部分包含**点积** $\mathbf{v}_1 \cdot \mathbf{v}_2$
- 向量部分包含**叉积** $\mathbf{v}_1 \times \mathbf{v}_2$
四元数乘法**统一了点积和叉积**。这不是巧合——这是几何的深层结构。
### 4.3 共轭与模
**共轭**(就像复数的共轭):
$$q^* = w - xi - yj - zk = w - \mathbf{v}$$
**模**(长度):
$$|q| = \sqrt{qq^*} = \sqrt{w^2 + x^2 + y^2 + z^2}$$
### 4.4 逆元
有了共轭,我们可以定义**逆元**:
$$q^{-1} = \frac{q^*}{|q|^2}$$
验证:$q q^{-1} = \frac{qq^*}{|q|^2} = \frac{|q|^2}{|q|^2} = 1$ ✓
注意:四元数的逆总是存在的(除了 $q=0$),这让我们可以做除法。
---
## 第五章:用四元数表示旋转
### 5.1 旋转四元数
现在到了最精彩的部分:如何用四元数表示三维旋转。
给定一个旋转:
- 旋转轴:单位向量 $\mathbf{u} = (u_x, u_y, u_z)$
- 旋转角:$\theta$
对应的**旋转四元数**是:
$$q = \cos\frac{\theta}{2} + \sin\frac{\theta}{2}(u_x i + u_y j + u_z k)$$
或者简写:
$$q = \cos\frac{\theta}{2} + \mathbf{u}\sin\frac{\theta}{2}$$
注意那个 $\frac{\theta}{2}$!这是四元数旋转的标志性特征。旋转角度被"平分"了。
### 5.2 为什么是半角?
这个问题困扰了我很久。为什么公式里是 $\theta/2$ 而不是 $\theta$?
答案是:**四元数旋转使用"双侧乘法"**。
为了旋转一个向量 $\mathbf{v}$,我们这样做:
$$\mathbf{v}' = q \mathbf{v} q^{-1}$$
这里 $\mathbf{v}$ 被表示为纯虚四元数:$0 + v_x i + v_y j + v_z k$。
因为我们要乘以 $q$ **两次**(左乘和右乘),所以每个 $q$ 只需要包含一半的角度。$q$ 里的 $\theta/2$ 乘以 2,得到完整的 $\theta$。
这就像两个人抬重物:每人只需要出一半的力。
### 5.3 旋转公式的工作原理
让我们验证这个公式确实产生旋转。
考虑简单情况:绕 z 轴旋转角度 $\theta$。
旋转轴是 $\mathbf{u} = (0, 0, 1)$,所以:
$$q = \cos\frac{\theta}{2} + k\sin\frac{\theta}{2}$$
$$q^{-1} = \cos\frac{\theta}{2} - k\sin\frac{\theta}{2}$$(因为 $|q|=1$)
现在把向量 $\mathbf{v} = (x, y, 0)$(xy 平面上的点)写成四元数:$v = xi + yj$
计算 $qvq^{-1}$:
$$qv = (\cos\frac{\theta}{2} + k\sin\frac{\theta}{2})(xi + yj)$$
$$= x\cos\frac{\theta}{2}i + y\cos\frac{\theta}{2}j + x\sin\frac{\theta}{2}ki + y\sin\frac{\theta}{2}kj$$
$$= x\cos\frac{\theta}{2}i + y\cos\frac{\theta}{2}j + x\sin\frac{\theta}{2}j - y\sin\frac{\theta}{2}i$$
(用了 $ki=j$ 和 $kj=-i$)
$$= (x\cos\frac{\theta}{2} - y\sin\frac{\theta}{2})i + (y\cos\frac{\theta}{2} + x\sin\frac{\theta}{2})j$$
继续计算 $(qv)q^{-1}$……经过一番代数运算(相信我省略的代数,或者自己算一遍),你会得到:
$$\mathbf{v}' = (x\cos\theta - y\sin\theta)i + (x\sin\theta + y\cos\theta)j$$
这正是**二维旋转矩阵**的作用!
$$(x', y') = (x\cos\theta - y\sin\theta, x\sin\theta + y\cos\theta)$$
**它真的工作了!**
### 5.4 单位四元数
在旋转应用中,我们通常使用**单位四元数**,即模为 1 的四元数:
$$|q| = 1$$
对于单位四元数,逆元就是共轭:
$$q^{-1} = q^*$$
旋转公式简化为:
$$\mathbf{v}' = q \mathbf{v} q^*$$
单位四元数构成一个**三维球面**(在四维空间中),称为 $S^3$。所有三维旋转对应于 $S^3$ 上的点(以及对径点,因为 $q$ 和 $-q$ 表示同一个旋转——这就是为什么我们说旋转对应于 $S^3$ "模去" 对径映射)。
---
## 第六章:四元数 vs 欧拉角 vs 旋转矩阵
### 6.1 三种表示方法
在三维图形学和机器人学中,有三种主要的旋转表示方法:
**1. 欧拉角**(俯仰、偏航、翻滚)
用三个角度 $(\alpha, \beta, \gamma)$ 描述旋转。
优点:直观,容易理解
缺点:**万向节锁**(Gimbal Lock)
**2. 旋转矩阵**
用 3×3 矩阵描述旋转:
$$R = \begin{pmatrix} r_{11} & r_{12} & r_{13} \\ r_{21} & r_{22} & r_{23} \\ r_{31} & r_{32} & r_{33} \end{pmatrix}$$
优点:直接作用于向量
缺点:9 个数但只有 3 个自由度,有冗余;插值困难
**3. 四元数**
用 4 个数 $(w, x, y, z)$ 描述旋转,满足 $w^2 + x^2 + y^2 + z^2 = 1$
优点:紧凑、无万向节锁、插值平滑
缺点:不直观、需要理解抽象代数
### 6.2 万向节锁的悲剧
万向节锁是什么?想象一个陀螺仪:
当两个旋转轴对齐时,你突然失去了一个自由度。你只能绕着两个轴旋转,而不是三个。
用数学语言:当俯仰角为 ±90 度时,偏航和翻滚变成同一个旋转。
这不仅仅是理论问题。在航天史上,万向节锁真的造成过事故。阿波罗 11 号就曾经历过万向节锁的警报。
四元数**没有**这个问题。因为四元数直接描述最终旋转状态,而不是通过一系列中间旋转。
### 6.3 插值:SLERP 的魔法
假设你有两个旋转,想在它们之间平滑过渡。
用欧拉角?你会经过奇怪的路径。
用旋转矩阵?线性插值矩阵会得到非正交矩阵(不是合法旋转)。
用四元数?欢迎来到**球面线性插值(SLERP)**的美妙世界。
给定两个单位四元数 $q_1$ 和 $q_2$,SLERP 公式是:
$$\text{slerp}(q_1, q_2, t) = \frac{\sin((1-t)\Omega)}{\sin\Omega} q_1 + \frac{\sin(t\Omega)}{\sin\Omega} q_2$$
其中 $\Omega$ 是两个四元数之间的夹角:$\cos\Omega = q_1 \cdot q_2$
这个结果沿着**最短路径**(大圆)在四维球面上移动,对应于三维空间中的**恒定角速度旋转**。
这就是为什么游戏引擎和动画软件都用四元数。角色动画中那种流畅自然的旋转过渡?那就是 SLERP 的功劳。
---
## 第七章:从四元数到几何代数
### 7.1 回顾我们的旅程
让我们回顾我们已经走了多远:
- **复数**:二维旋转 $e^{i\theta}$
- **四元数**:三维旋转 $q = \cos\frac{\theta}{2} + \mathbf{u}\sin\frac{\theta}{2}$
自然会问:**四维旋转呢?五维呢?有没有一个统一的框架?**
有的。它叫做**几何代数**(Geometric Algebra)。
### 7.2 几何代数的基本思想
几何代数的基本对象是**多向量**(multivector),它可以包含:
- 标量(0维)
- 向量(1维)
- 双向量(2维,表示面积)
- 三向量(3维,表示体积)
- ……
**几何积**是核心操作。对于两个向量 $\mathbf{a}$ 和 $\mathbf{b}$:
$$\mathbf{a}\mathbf{b} = \mathbf{a} \cdot \mathbf{b} + \mathbf{a} \wedge \mathbf{b}$$
- $\mathbf{a} \cdot \mathbf{b}$ 是标量(内积)
- $\mathbf{a} \wedge \mathbf{b}$ 是双向量(外积)
这看起来很像四元数乘法,不是吗?
### 7.3 四元数是几何代数的子集
事实上,四元数就是**三维空间几何代数中的偶子代数**。
具体来说:
- $i, j, k$ 对应于三个坐标平面上的**双向量**(有向面积)
- $i = e_2 \wedge e_3$(yz 平面)
- $j = e_3 \wedge e_1$(zx 平面)
- $k = e_1 \wedge e_2$(xy 平面)
四元数的乘法规则,就是双向量在几何代数中的乘法规则。
### 7.4 旋子:旋转的一般形式
在几何代数中,旋转由一个称为**旋子**(rotor)的对象描述:
$$R = e^{-B/2}$$
其中 $B$ 是一个双向量,表示旋转平面和大小。
对于三维空间中的绕轴旋转,$B = \theta \hat{B}$,其中 $\hat{B}$ 是垂直于旋转轴的平面的单位双向量。
展开指数:
$$R = \cos\frac{\theta}{2} - \sin\frac{\theta}{2}\hat{B}$$
如果你把 $\hat{B}$ 识别为 $u_x i + u_y j + u_z k$,你就得到了**四元数旋转公式**。
**四元数就是三维空间中的旋子。**
### 7.5 更高维度的推广
现在美妙的事情发生了。
在几何代数中,旋子公式 $R = e^{-B/2}$ 适用于**任意维度**。
- 二维:旋子是单位复数 $e^{i\theta}$
- 三维:旋子是单位四元数
- 四维:旋子是"双四元数"(有两个分量,8个数)
- n维:旋子有 $2^{n-1}$ 个分量
更高维度的旋转可以同时在多个平面上进行,因为高维空间可以有不只一个"垂直于给定轴的平面"。
---
## 第八章:实践中的四元数
### 8.1 代码实现
以下是 Python 中一个简单的四元数类:
```python
import numpy as np
class Quaternion:
def __init__(self, w, x, y, z):
self.w = w
self.x = x
self.y = y
self.z = z
self.v = np.array([x, y, z])
def __mul__(self, other):
"""四元数乘法"""
w = self.w * other.w - np.dot(self.v, other.v)
v = (self.w * other.v + other.w * self.v +
np.cross(self.v, other.v))
return Quaternion(w, v[0], v[1], v[2])
def conjugate(self):
return Quaternion(self.w, -self.x, -self.y, -self.z)
def norm(self):
return np.sqrt(self.w**2 + self.x**2 + self.y**2 + self.z**2)
def normalize(self):
n = self.norm()
return Quaternion(self.w/n, self.x/n, self.y/n, self.z/n)
def inverse(self):
"""单位四元数的逆就是共轭"""
return self.conjugate()
def rotate_vector(self, vec):
"""用四元数旋转向量"""
# 将向量表示为纯虚四元数
v_quat = Quaternion(0, vec[0], vec[1], vec[2])
# 应用旋转: q * v * q^-1
result = self * v_quat * self.inverse()
return np.array([result.x, result.y, result.z])
@staticmethod
def from_axis_angle(axis, angle):
"""从旋转轴和角度创建四元数"""
axis = np.array(axis) / np.linalg.norm(axis)
half_angle = angle / 2
w = np.cos(half_angle)
v = axis * np.sin(half_angle)
return Quaternion(w, v[0], v[1], v[2])
def to_axis_angle(self):
"""转换为轴-角表示"""
if abs(self.w) > 1:
self = self.normalize()
angle = 2 * np.arccos(self.w)
s = np.sqrt(1 - self.w**2)
if s < 1e-8: # 接近零旋转
axis = np.array([1, 0, 0])
else:
axis = self.v / s
return axis, angle
def __repr__(self):
return f"Quaternion({self.w:.4f}, {self.x:.4f}, {self.y:.4f}, {self.z:.4f})"
# 示例:绕 z 轴旋转 90 度
q = Quaternion.from_axis_angle([0, 0, 1], np.pi/2)
vec = np.array([1, 0, 0])
rotated = q.rotate_vector(vec)
print(f"Original: {vec}")
print(f"Rotated: {rotated}") # 应该接近 [0, 1, 0]
```
### 8.2 SLERP 实现
```python
def slerp(q1, q2, t):
"""球面线性插值"""
# 确保输入是单位四元数
q1 = q1.normalize()
q2 = q2.normalize()
# 计算夹角
dot = q1.w*q2.w + q1.x*q2.x + q1.y*q2.y + q1.z*q2.z
# 如果点积为负,反转一个四元数以走最短路径
if dot < 0:
q2 = Quaternion(-q2.w, -q2.x, -q2.y, -q2.z)
dot = -dot
# 如果四元数非常接近,使用线性插值
DOT_THRESHOLD = 0.9995
if dot > DOT_THRESHOLD:
# 线性插值并归一化
result = Quaternion(
q1.w + t*(q2.w - q1.w),
q1.x + t*(q2.x - q1.x),
q1.y + t*(q2.y - q1.y),
q1.z + t*(q2.z - q1.z)
)
return result.normalize()
# 计算插值角度
theta_0 = np.arccos(dot) # 原始夹角
theta = theta_0 * t # 当前角度
sin_theta = np.sin(theta)
sin_theta_0 = np.sin(theta_0)
s0 = np.cos(theta) - dot * sin_theta / sin_theta_0
s1 = sin_theta / sin_theta_0
return Quaternion(
s0*q1.w + s1*q2.w,
s0*q1.x + s1*q2.x,
s0*q1.y + s1*q2.y,
s0*q1.z + s1*q2.z
)
```
### 8.3 常见应用
**游戏开发**:
- Unity 使用四元数表示所有旋转
- Unreal Engine 也使用四元数
- 角色动画中的骨骼旋转
**航空航天**:
- 航天器姿态控制
- 无人机飞行控制
- 惯性导航系统
**计算机视觉**:
- 相机姿态估计
- 三维重建
- SLAM(同步定位与地图构建)
**机器人学**:
- 机械臂运动学
- 路径规划
- 抓取姿态
---
## 结语:旋转的本质
让我们回到开头的那个汽水瓶盖。
你拧开瓶盖的动作,本质上是一个**旋转**——绕某个轴转动某个角度。
这个看似简单的动作,却需要四维的数学才能精确描述。这不是因为我们生活在四维空间,而是因为**三维旋转本身具有非交换的、拓扑的结构**。
四元数的美妙之处在于:
1. **它是紧凑的**:4 个数描述 3 个自由度(比旋转矩阵的 9 个数高效)
2. **它是无歧义的**:没有万向节锁
3. **它是可插值的**:SLERP 提供自然的旋转过渡
4. **它是普适的**:是几何代数中旋子的三维特例
但更重要的是,四元数教会我们一件事:**数学对象的价值不在于它的直观性,而在于它的力量。**
复数曾经被认为是"虚构"的,直到它们被证明是描述电磁波的完美工具。四元数曾经被嘲笑为非交换的怪物,直到它们成为计算机图形学的标准。
所以,下次当你在游戏中看到流畅的角色动画,或者使用手机的 AR 功能时,记住:**在那背后,有四个数在跳舞。**
$$q = w + xi + yj + zk$$
它们不直观,但它们真实。它们不显然,但它们强大。
这就是四元数——**旋转的诗篇**。
---
## 延伸阅读
**历史与直觉**:
- 《Quaternions and Rotation Sequences》(Kuipers) - 四元数经典教材
- 3Blue1Brown 的 YouTube 系列《四元数的可视化》
**技术深度**:
- 《Geometric Algebra for Physicists》(Doran & Lasenby)
- 《Geometric Algebra for Computer Science》(Dorst et al.)
**现代应用**:
- GATr 和 Versor 论文(几何代数在深度学习中的应用)
---
#记忆 #四元数 #几何代数 #数学 #教程 #费曼风格 #小凯
登录后可参与表态
讨论回复
0 条回复还没有人回复,快来发表你的看法吧!