3. 捷联惯导解算

好,咱们进入正题。捷联惯导解算,说白了就是回答三个问题:我现在朝哪看?我现在在哪?我现在跑多快? 这三个问题分别对应姿态、位置和速度的更新。

我个人习惯把姿态更新放在第一位。为什么?因为位置和速度的推算,全都依赖姿态。方向错了,后面全白搭。我在项目里见过太多这样的案例——陀螺数据漂移一点点,几分钟后位置误差就飞了。

3.1 姿态更新:四元数 vs 欧拉角

姿态更新的核心,就是利用陀螺仪测到的角速度,不断推算载体当前的朝向。常用的数学工具有两种:四元数和欧拉角。

3.1.1 欧拉角法

欧拉角最直观。俯仰、横滚、航向,三个角度一摆,谁都看得懂。但有个致命问题——万向锁。当俯仰角接近 ±90° 时,横滚和航向会耦合在一起,没法区分。我刚开始做飞控时就踩过这个坑,飞机垂直爬升时姿态直接炸了。

所以,欧拉角只适合用在俯仰角变化不大的场景,比如地面车辆或平飞状态。对于无人机、机器人这种全姿态运动的,千万别用。

3.1.2 四元数法

四元数没有奇异性,计算也高效。它用四个参数表示旋转,避免了欧拉角的三角函数运算。更新公式也很简洁:

// 四元数更新(一阶龙格-库塔法)
// q = [q0, q1, q2, q3]^T
// ω = [ωx, ωy, ωz]^T 为陀螺仪角速度

double dq[4];
double norm = 0.5 * dt;

dq[0] = -q[1]*ωx - q[2]*ωy - q[3]*ωz;
dq[1] =  q[0]*ωx - q[3]*ωy + q[2]*ωz;
dq[2] =  q[3]*ωx + q[0]*ωy - q[1]*ωz;
dq[3] = -q[2]*ωx + q[1]*ωy + q[0]*ωz;

q[0] += norm * dq[0];
q[1] += norm * dq[1];
q[2] += norm * dq[2];
q[3] += norm * dq[3];

// 归一化,防止累积误差
double inv_norm = 1.0 / sqrt(q[0]*q[0] + q[1]*q[1] + q[2]*q[2] + q[3]*q[3]);
q[0] *= inv_norm;
q[1] *= inv_norm;
q[2] *= inv_norm;
q[3] *= inv_norm;
我的经验:四元数更新后一定要归一化。别偷懒。我见过有人为了省那几次开方运算,结果姿态越跑越偏,最后整个系统都崩了。

3.2 速度与位置更新

姿态算准了,接下来就是速度和位置。说白了就是加速度计测到的比力,去掉重力,再积分。

公式其实不复杂:

// 速度更新(导航坐标系下)
// v_n 为导航系速度,f_b 为加速度计比力,g_n 为重力矢量
// C_b^n 为姿态矩阵(由四元数转换而来)

// 1. 将比力从载体系转换到导航系
double f_n[3];
f_n[0] = C_b_n[0][0]*f_b[0] + C_b_n[0][1]*f_b[1] + C_b_n[0][2]*f_b[2];
f_n[1] = C_b_n[1][0]*f_b[0] + C_b_n[1][1]*f_b[1] + C_b_n[1][2]*f_b[2];
f_n[2] = C_b_n[2][0]*f_b[0] + C_b_n[2][1]*f_b[1] + C_b_n[2][2]*f_b[2];

// 2. 去除重力,更新速度
v_n[0] += (f_n[0] + g_n[0]) * dt;
v_n[1] += (f_n[1] + g_n[1]) * dt;
v_n[2] += (f_n[2] + g_n[2]) * dt;

// 3. 位置更新(简单梯形积分)
p_n[0] += (v_n[0] + v_n_prev[0]) * 0.5 * dt;
p_n[1] += (v_n[1] + v_n_prev[1]) * 0.5 * dt;
p_n[2] += (v_n[2] + v_n_prev[2]) * 0.5 * dt;
注意:重力补偿一定要做对。我见过有人把重力方向搞反了,结果位置越算越往天上飞。另外,地球自转和哥里奥利力在低速短距场景下可以忽略,但高精度长航时千万别省。

3.3 圆锥误差与划船误差补偿

这两个误差,是捷联惯导里最容易被忽视的坑。说白了,它们都源于一个事实:陀螺和加速度计的采样是离散的,而真实运动是连续的。

3.3.1 圆锥误差

圆锥误差发生在姿态更新中。当载体同时存在两个正交方向的角振动时,如果只用简单的角速度积分,算出来的姿态会慢慢偏离真实值。形象点说,就像圆锥的母线绕轴旋转,你只采样了几个点,结果算出来的方向一直在漂。

补偿方法也很经典——多子样算法。我常用的三子样补偿:

// 三子样圆锥误差补偿
// Δθ1, Δθ2, Δθ3 为三个子样间隔的角增量

double Δθ_sum = Δθ1 + Δθ2 + Δθ3;
double Δθ_cross = (1.0/3.0) * (Δθ1×Δθ2 + Δθ2×Δθ3) 
                + (2.0/3.0) * (Δθ1×Δθ3);

// 补偿后的等效旋转矢量
double Φ = Δθ_sum + Δθ_cross;
核心思想:圆锥误差补偿的本质,是用多个子样的叉积来近似角速度的非线性变化。子样数越多,补偿精度越高,但计算量也越大。一般二子样或三子样就够用了。

3.3.2 划船误差

划船误差是速度更新里的对应问题。当载体同时存在线振动和角振动时,比力积分也会产生类似的漂移。名字很形象——就像划船时,船身的晃动和桨的摆动耦合在一起。

补偿公式和圆锥误差很像,只是把角增量换成了速度增量:

// 三子样划船误差补偿
// Δv1, Δv2, Δv3 为三个子样间隔的速度增量
// Δθ1, Δθ2, Δθ3 为对应的角增量

double Δv_sum = Δv1 + Δv2 + Δv3;
double Δv_cross = (1.0/3.0) * (Δθ1×Δv2 + Δθ2×Δv3) 
                + (2.0/3.0) * (Δθ1×Δv3);

// 补偿后的速度增量
double Δv_comp = Δv_sum + Δv_cross;
我曾经踩过的坑:一开始做车载惯导时,觉得车辆振动不大,就没做划船补偿。结果在颠簸路面上跑了一圈,位置误差比预期大了三倍。后来加上补偿,立马就收敛了。所以,别小看振动,哪怕是在看似平稳的环境里。

3.4 知识体系总览

下面这张图,把本章的核心逻辑串起来了。你可以把它当作一个速查地图:

捷联惯导解算核心流程 陀螺仪 (角速度 ω) 加速度计 (比力 f) 姿态更新 四元数 / 等效旋转矢量 圆锥误差补偿 (多子样) 速度更新 比力转换 + 重力补偿 划船误差补偿 (多子样) 位置更新 姿态 / 速度 / 位置 姿态矩阵 C_b^n

嗯,这张图把整个流程串起来了。你从左边陀螺仪和右边加速度计出发,分别走姿态和速度两条线,中间用姿态矩阵耦合,最后汇合到位置更新。圆锥和划船补偿就像两个「纠偏员」,在关键节点上把离散采样带来的误差拉回来。

我个人建议,在实际写代码时,先把不带补偿的版本跑通,再逐步加上圆锥和划船补偿。这样出了问题也好定位。我曾经一上来就全加上,结果某个子样符号写反了,查了三天才找到bug。

好了,这一章的内容就到这。记住:姿态是灵魂,补偿是细节,两者缺一不可。

本章要点速记:
  • 姿态更新用四元数,别用欧拉角(除非你确定不会遇到万向锁)
  • 速度更新别忘了重力补偿,方向别搞反
  • 圆锥误差补偿姿态,划船误差补偿速度——两者是「孪生兄弟」
  • 多子样算法是补偿的核心,二子样起步,三子样稳妥