姿态表示方法:三种工具,一个核心

做飞控这么多年,我越来越觉得姿态表示是EKF的基石。选错了表示方法,后面全是坑。今天咱们就把欧拉角、旋转矩阵、四元数这三兄弟聊透。

一、欧拉角:直观但危险

欧拉角大家最熟悉。俯仰、横滚、偏航,三个角度搞定。我刚开始做飞控时,第一版代码用的就是欧拉角。为什么?直观啊!调试时直接看角度变化,多舒服。

但问题很快就来了。

万向锁(Gimbal Lock)

当俯仰角接近±90°时,横滚和偏航会耦合。说白了,你失去了一个自由度。我记得第一次遇到这个问题是在一次高机动测试中,飞机突然翻滚失控。查了半天,发现是万向锁导致姿态解算炸了。

为什么会这样?因为欧拉角的旋转顺序是固定的。比如按Z-Y-X顺序旋转,当Y轴旋转90°时,Z轴和X轴就重合了。你想想看,两个轴重合,你还能独立控制吗?

另外,欧拉角还有个大问题——三角函数计算。每次更新都要算sin、cos,计算量不小。而且角度变化不连续,比如从359°到0°,数值跳变,EKF处理起来很麻烦。

我的建议:欧拉角只适合做显示和简单控制。千万别在EKF核心里用。我曾经在早期项目里吃过这个亏,后来全改成四元数了。

二、旋转矩阵:稳定但笨重

旋转矩阵用3×3的矩阵表示姿态。它没有万向锁问题,这是最大的优势。

但旋转矩阵有个硬约束——正交性。说白了,矩阵的列向量必须互相垂直,且模长为1。这个约束在数值计算中很难保持。

我遇到过这种情况:EKF跑着跑着,旋转矩阵慢慢变形了。矩阵不再正交,姿态就歪了。怎么办?需要定期做正交化修正。这个过程叫Gram-Schmidt正交化,代码写起来不复杂,但增加了计算量。

// 旋转矩阵正交化示例
void normalizeRotationMatrix(float R[3][3]) {
    // 对列向量进行Gram-Schmidt正交化
    // 第一列归一化
    float norm = sqrt(R[0][0]*R[0][0] + R[1][0]*R[1][0] + R[2][0]*R[2][0]);
    R[0][0] /= norm; R[1][0] /= norm; R[2][0] /= norm;
    
    // 第二列减去在第一列上的投影
    float dot = R[0][0]*R[0][1] + R[1][0]*R[1][1] + R[2][0]*R[2][1];
    R[0][1] -= dot * R[0][0];
    R[1][1] -= dot * R[1][0];
    R[2][1] -= dot * R[2][0];
    // 再归一化...
}

旋转矩阵还有一个问题:9个参数。你想想看,EKF的状态向量里塞9个参数,协方差矩阵就是9×9,计算量直接爆炸。对于嵌入式MCU来说,这太奢侈了。

核心结论:旋转矩阵理论完美,但实际用起来太笨重。除非你资源特别充裕,否则不推荐在EKF里直接用。

三、四元数:飞控界的宠儿

四元数,说白了就是四个数表示旋转。一个标量加三个矢量,没有奇异点,计算高效。我个人习惯在EKF里用四元数,原因有三:

  • 无奇异:不存在万向锁问题,任意姿态都能表示
  • 计算高效:只有4个参数,乘法比矩阵快
  • 易于插值:姿态平滑过渡很方便

四元数的形式是这样的:

q = [w, x, y, z]^T
其中 w^2 + x^2 + y^2 + z^2 = 1

注意这个约束——单位四元数。模长必须为1。这和旋转矩阵的正交性约束类似,但处理起来简单多了。只需要归一化就行:

void normalizeQuaternion(float q[4]) {
    float norm = sqrt(q[0]*q[0] + q[1]*q[1] + q[2]*q[2] + q[3]*q[3]);
    q[0] /= norm; q[1] /= norm; q[2] /= norm; q[3] /= norm;
}

就这么简单。每次EKF更新后,做一次归一化,约束就保持了。

避坑指南:我曾经在四元数归一化上栽过跟头。有一次忘记归一化,结果EKF跑了半小时后姿态完全漂移。查了两天才发现是归一化代码被注释掉了。嗯,从那以后我把归一化写进了EKF的核心循环里,每次更新后必做。

四、三种方法对比与选择

咱们直接上表格,一目了然:

特性 欧拉角 旋转矩阵 四元数
参数数量 3 9 4
奇异问题 有(万向锁)
计算效率 中等(三角函数) 低(9参数运算) 高(4参数运算)
约束条件 正交性(难保持) 单位模长(易保持)
直观性
EKF适用性 差(奇异+不连续) 一般(计算量大) 最佳

从表格能看出来,四元数在EKF里是综合最优的选择。我做了这么多年飞控,几乎所有项目都用四元数做核心姿态表示。欧拉角只用来做显示和日志记录,旋转矩阵偶尔用在一些特殊计算中。

五、我的选择策略

如果你问我具体怎么选,我的建议是这样的:

  1. EKF核心状态:用四元数。4个参数,无奇异,计算快,约束好保持。
  2. 用户交互层:用欧拉角。显示给用户看,或者做简单的角度控制。
  3. 特殊计算:用旋转矩阵。比如需要多次旋转叠加时,矩阵乘法更直观。

你想想看,EKF里状态向量用四元数,观测模型里把四元数转成欧拉角,这样既保证了核心的稳定性,又方便了用户理解。两全其美。

核心总结:姿态表示没有绝对的好坏,关键看场景。但在多旋翼飞控的EKF里,四元数是当之无愧的主角。记住:核心用四元数,界面用欧拉角,特殊场景用旋转矩阵。

好了,这一章就聊到这里。下一章咱们深入四元数的数学细节,看看它到底怎么旋转的。


专注资料整理