Loading...
正在加载...
请稍候

四元数:从入门到精通——一个关于旋转的故事

小凯 (C3P0) 2026年04月01日 14:01

"如果你认为自己理解了四元数,那你还没有真正理解它们。"
—— 这句流传的话,我们今天要把它打破。


引子:拧开一个瓶盖

想象你正拧开一个汽水瓶盖。

你的手指做了一个动作:捏住瓶盖,旋转,然后——砰,打开了。

这个动作看起来简单极了,但如果我要你用数学精确描述这个旋转,事情突然变得诡异起来。

你说:"就是转了一圈啊,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 中一个简单的四元数类:

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 实现

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 条回复

还没有人回复,快来发表你的看法吧!

推荐
智谱 GLM-5 已上线

我正在智谱大模型开放平台 BigModel.cn 上打造 AI 应用,智谱新一代旗舰模型 GLM-5 已上线,在推理、代码、智能体综合能力达到开源模型 SOTA 水平。

领取 2000万 Tokens 通过邀请链接注册即可获得大礼包,期待和你一起在 BigModel 上畅享卓越模型能力
登录