第1章:四元数基本运算

各位同学,今天咱们来聊聊四元数的基本运算。说实话,我刚接触四元数那会儿,也觉得这东西挺抽象的。但后来在项目里用多了,发现它其实就是一套很优雅的数学工具。

1.1 四元数的定义与表示

四元数,说白了就是一个四维的超复数。它由一个实部和三个虚部组成:

q = w + xi + yj + zk

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

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

嗯,这里要注意,i、j、k 之间是相互正交的,而且乘法不满足交换律。也就是说,i×j ≠ j×i。我在做惯性导航时,就因为这个特性吃过亏,后面会详细说。

在实际编程中,我们通常用四维向量来表示四元数:

q = [w, x, y, z]

或者用结构体:

typedef struct {
    float w;
    float x;
    float y;
    float z;
} quaternion_t;

1.2 四元数乘法

四元数乘法是姿态更新的核心运算。它不像普通乘法那么简单,因为涉及到虚数单位之间的交互。

假设有两个四元数:

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

它们的乘积 q = q₁ × q₂ 的计算公式如下:

w = w₁w₂ - x₁x₂ - y₁y₂ - z₁z₂
x = w₁x₂ + x₁w₂ + y₁z₂ - z₁y₂
y = w₁y₂ - x₁z₂ + y₁w₂ + z₁x₂
z = w₁z₂ + x₁y₂ - y₁x₂ + z₁w₂

你想想看,这个公式其实是有规律的。实部是点积的负值,虚部是交叉项的组合。我个人习惯把这个公式记成矩阵形式,写代码时不容易出错:

// 四元数乘法 C 代码实现
void quaternion_multiply(quaternion_t *q1, quaternion_t *q2, quaternion_t *result) {
    result->w = q1->w * q2->w - q1->x * q2->x - q1->y * q2->y - q1->z * q2->z;
    result->x = q1->w * q2->x + q1->x * q2->w + q1->y * q2->z - q1->z * q2->y;
    result->y = q1->w * q2->y - q1->x * q2->z + q1->y * q2->w + q1->z * q2->x;
    result->z = q1->w * q2->z + q1->x * q2->y - q1->y * q2->x + q1->z * q2->w;
}

注意:四元数乘法不满足交换律!q₁ × q₂ ≠ q₂ × q₁。我在项目中遇到过,有人把乘法顺序搞反了,结果姿态解算出来的角度完全不对,查了两天才找到问题。

1.3 四元数的共轭

四元数的共轭,其实就是把虚部取反。这个操作在求逆四元数时非常有用。

q = w + xi + yj + zk
q* = w - xi - yj - zk

代码实现很简单:

void quaternion_conjugate(quaternion_t *q, quaternion_t *result) {
    result->w =  q->w;
    result->x = -q->x;
    result->y = -q->y;
    result->z = -q->z;
}

共轭有一个重要性质:用四元数旋转向量时,旋转后的向量可以通过 q × v × q* 得到。这个公式在姿态更新中会反复用到。

1.4 四元数的范数

范数,说白了就是四元数的"长度"。它的定义和向量范数一样:

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

代码实现:

float quaternion_norm(quaternion_t *q) {
    return sqrtf(q->w * q->w + q->x * q->x + q->y * q->y + q->z * q->z);
}

为什么要关心范数?因为单位四元数的范数等于1,而只有单位四元数才能表示旋转。如果范数偏离1,姿态就会产生畸变。

小技巧:我建议每次做完四元数乘法后,都检查一下范数。如果偏离1超过某个阈值(比如0.001),就做一次归一化。这样可以有效防止数值误差累积。

1.5 四元数的归一化

归一化就是把四元数变成单位四元数。操作很简单,每个分量除以范数:

void quaternion_normalize(quaternion_t *q) {
    float norm = quaternion_norm(q);
    if (norm < 1e-6f) {
        // 防止除零
        q->w = 1.0f;
        q->x = 0.0f;
        q->y = 0.0f;
        q->z = 0.0f;
        return;
    }
    q->w /= norm;
    q->x /= norm;
    q->y /= norm;
    q->z /= norm;
}

我曾经在无人机项目里,因为忘记做归一化,导致姿态漂移越来越严重。后来在代码里加了个定时器,每10ms强制归一化一次,问题就解决了。

1.6 知识体系总览

下面这张图,是我自己总结的四元数基本运算之间的关系:

四元数 q 乘法 共轭 范数 归一化 姿态更新核心 求逆的基础 检查数值误差 保证单位性

从图中可以看出,四个基本运算环环相扣。乘法用于组合旋转,共轭用于求逆,范数用于检查精度,归一化用于保证单位性。我在实际项目中,通常把这四个函数封装成一个工具库,每次调用都很方便。

1.7 运算性质总结

为了方便查阅,我把四元数基本运算的性质整理成了表格:

运算 公式 关键性质 工程用途
乘法 q = q₁ × q₂ 不满足交换律 组合旋转、姿态更新
共轭 q* = w - xi - yj - zk (q₁ × q₂)* = q₂* × q₁* 求逆、向量旋转
范数 ||q|| = √(w²+x²+y²+z²) ||q₁ × q₂|| = ||q₁|| × ||q₂|| 数值误差检测
归一化 q' = q / ||q|| ||q'|| = 1 保证旋转精度

核心要点:四元数的四个基本运算,是姿态解算的基石。乘法负责"动",共轭负责"反",范数负责"查",归一化负责"稳"。把这四个运算吃透了,后面的互补滤波和姿态更新就水到渠成了。

好了,这一章的内容就到这里。四元数虽然看起来有点绕,但只要你动手写几遍代码,就会发现它其实很直观。下一章我们聊聊四元数如何表示旋转,那才是真正好玩的地方。


公众号:蓝海资料掘金营,微信deep3321