3. 姿态解算:欧拉角与四元数、旋转矩阵、互补滤波与Mahony滤波算法详解

姿态解算是飞控的“眼睛”。没有它,你连飞机是正着飞还是倒着飞都不知道。

我刚开始做飞控那会儿,踩过最大的坑就是姿态解算。传感器数据明明看着没问题,一上天飞机就开始“抽风”。后来才发现,不是传感器坏了,是算法没选对。

这一章,咱们就把姿态解算的底裤扒干净。从数学基础到工程实现,一条龙讲透。

3.1 姿态的数学表示:三种语言,一个意思

描述一个物体的朝向,说白了就是描述“它绕三个轴转了多少”。但同一个旋转,可以用三种不同的数学工具来表达。我习惯把它们叫做“三种语言”。

3.1.1 欧拉角:最直观,但最危险

欧拉角就是俯仰角(Pitch)、横滚角(Roll)、偏航角(Yaw)。你拿手机转一下,就能直观感受到。

但这里有个大坑——万向锁。当俯仰角接近±90°时,横滚和偏航会“耦合”在一起,丢失一个自由度。

⚠️ 我曾经踩过的坑: 有一次做固定翼飞控,在垂直爬升时姿态突然炸了。查了两天,发现就是欧拉角在俯仰90°附近出现了万向锁。从那以后,我所有飞控的核心解算只用四元数,欧拉角只用来做显示和日志。
特性 欧拉角 四元数 旋转矩阵
直观性 ★★★★★ ★★☆☆☆ ★★☆☆☆
无奇点 ✗(万向锁) ✓ ✓
计算量 小 中 大
插值平滑 差 好 一般
工程常用场景 显示、遥控 核心解算 坐标变换

3.1.2 旋转矩阵:数学上最完美

旋转矩阵是一个3×3的正交矩阵。它能把一个向量从一个坐标系变换到另一个坐标系。

举个例子,你要把机体坐标系下的加速度,转换到地理坐标系下,旋转矩阵就是那个“翻译官”。

// 绕Z轴旋转ψ角的旋转矩阵
R_z(ψ) = [cosψ  -sinψ  0]
         [sinψ   cosψ  0]
         [0      0     1]

// 绕Y轴旋转θ角
R_y(θ) = [cosθ   0  sinθ]
         [0      1  0   ]
         [-sinθ  0  cosθ]

// 绕X轴旋转φ角
R_x(φ) = [1   0      0   ]
         [0   cosφ  -sinφ]
         [0   sinφ   cosφ]

// 完整旋转矩阵(Z-Y-X顺序)
R = R_z(ψ) * R_y(θ) * R_x(φ)

但旋转矩阵有9个元素,而且必须保持正交约束。每次更新后还得重新正交化,计算量不小。

3.1.3 四元数:飞控工程师的“真香”选择

四元数只有4个参数,没有奇点,插值平滑。说白了,它就是为姿态解算而生的。

// 四元数定义
q = w + xi + yj + zk
// 或者写成向量形式
q = [w, x, y, z]

// 用四元数旋转向量 v
v' = q * v * q_conjugate

// 四元数乘法(核心运算)
q1 * q2 = [w1*w2 - x1*x2 - y1*y2 - z1*z2,
           w1*x2 + x1*w2 + y1*z2 - z1*y2,
           w1*y2 - x1*z2 + y1*w2 + z1*x2,
           w1*z2 + x1*y2 - y1*x2 + z1*w2]
💡 我个人习惯: 在代码里,四元数永远保持单位化。每次更新后,哪怕只偏离了一点点,也要做归一化。这个小习惯,帮我避免过无数次“灵异”炸机。

3.2 传感器融合:为什么需要互补滤波?

你想想看,陀螺仪测角速度,积分得到角度。但陀螺仪有零偏,积分会漂移。加速度计和磁力计没有漂移,但噪声大,动态响应慢。

一个高频不准但短期可靠,一个低频准确但长期稳定。互补滤波就是把两者“互补”起来。

3.2.1 互补滤波原理

核心思想很简单:

  • 陀螺仪积分角度 → 通过高通滤波器(信任短期变化)
  • 加速度计/磁力计解算角度 → 通过低通滤波器(信任长期稳定)
  • 两者相加 → 得到最终姿态
// 一阶互补滤波伪代码
angle = 0.98 * (angle + gyro * dt) + 0.02 * acc_angle;

// 0.98 和 0.02 是权重系数
// 权重取决于你对陀螺仪和加速度计的信任程度

这个0.98和0.02怎么来的?说白了就是截止频率。我一般这样算:

// 截止频率 fc = 1 / (2 * π * τ)
// 其中 τ = (1 - α) / (α * dt)
// α 是陀螺仪权重

// 示例:dt=0.004s(250Hz), 希望截止频率2Hz
α = 1 / (1 + 2 * π * 2 * 0.004) ≈ 0.95
// 所以代码里写 0.95 * gyro + 0.05 * acc

3.2.2 Mahony滤波:工程界的“标准答案”

Mahony滤波是互补滤波的进阶版。它不直接融合角度,而是融合“误差”。

我当年第一次看到Mahony论文时,觉得这算法真巧妙——它用一个PI控制器来修正陀螺仪的偏差。

// Mahony滤波核心步骤(简化版)
// 1. 用当前四元数计算理论重力方向
g_estimated = q * [0, 0, 0, 1] * q_conj;

// 2. 与加速度计实测方向做叉积,得到误差
error = acc_measured × g_estimated;

// 3. PI控制器修正陀螺仪
gyro_corrected = gyro_raw + Kp * error + Ki * ∫error dt;

// 4. 用修正后的角速度更新四元数
q_dot = 0.5 * q * gyro_corrected;
q += q_dot * dt;

// 5. 归一化
q = q / ||q||;
🔑 关键参数调优:
  • Kp(比例系数):决定对加速度计的信任速度。Kp越大,收敛越快,但噪声也越大。我一般从0.5开始调。
  • Ki(积分系数):消除陀螺仪零偏。Ki太小,零偏消不掉;Ki太大,动态响应会变慢。建议从0.01开始。
  • 经验值:对于常见的MPU6050,Kp=0.5~2.0,Ki=0.01~0.1。

3.3 完整姿态解算流程

说了这么多,咱们把整个流程串起来。这是我个人在STM32上跑过无数次的框架。

姿态解算完整流程图 陀螺仪 (gyro) 加速度计 (acc) 磁力计 (mag) 数据预处理:去噪、校准、坐标系对齐、单位转换 Mahony 互补滤波核心 误差计算 → PI修正 → 四元数更新 → 归一化 输出:四元数 → 欧拉角(用于控制/显示) 💡 核心循环频率通常为 250Hz~1kHz,取决于MCU性能和传感器输出速率

3.4 工程实现中的避坑指南

理论讲完了,咱们聊聊实战。这些坑,我当年一个一个踩过来的。

3.4.1 传感器数据同步

陀螺仪和加速度计的数据不是同一时刻采的,怎么办?

  • 用硬件FIFO:MPU6050等传感器自带FIFO,可以保证数据时间对齐。
  • 软件插值:如果实在没办法,用线性插值把时间戳对齐。

3.4.2 磁力计干扰

电机一转动,磁力计数据就乱飞。我遇到过最夸张的一次,偏航角直接漂了30°。

  • 做硬铁/软铁校准
  • 在代码里加磁力计信任度判断——如果磁场强度超出正常范围,直接降低磁力计权重

3.4.3 初始化姿态

飞机上电时,四元数初始值怎么设?

// 从欧拉角初始化四元数
// 假设初始俯仰=0, 横滚=0, 偏航=0
q = [1, 0, 0, 0];

// 如果飞机不是水平的,用加速度计算初始俯仰和横滚
roll  = atan2(acc_y, acc_z);
pitch = atan2(-acc_x, sqrt(acc_y*acc_y + acc_z*acc_z));
// 然后用欧拉角转四元数公式初始化
💡 我的小技巧: 上电后先让飞机静止2秒,用这段时间的加速度计均值来初始化姿态。这样能避免上电瞬间的噪声干扰。

3.5 总结

姿态解算没有银弹。欧拉角直观但怕万向锁,旋转矩阵完美但计算量大,四元数才是飞控的“真命天子”。

互补滤波简单可靠,Mahony滤波是工程界的标准方案。你如果刚开始做飞控,我建议直接从Mahony入手,把Kp和Ki调明白,比什么都强。

嗯,这一章的内容就到这儿。代码写再多,不如自己跑一遍试试。找个开发板,把MPU6050焊上,跑一跑Mahony,你会有更深的体会。