☰
误差状态卡尔曼滤波ESKF推导:四元数扰动与IMU融合定位实战
2026/10/5 6:04:43 网站建设 项目流程

我第一次把四元数直接丢进扩展卡尔曼当状态量,是在一个GPS+IMU融合定位demo里。静止测试一切正常,手拿着模组画了几个8字,姿态协方差就开始变得稀奇古怪,最后P矩阵干脆出现负特征值。后来换成误差状态滤波器(ESKF),同样一套观测,居然顺利跑通。这篇把我在项目里从头推一遍、又写成代码、再用蒙特卡洛验证过的推导过程完整写出来,包括几个坑着我最久的地方:四元数扰动该左乘还是右乘、δθ̇符号为什么会反、离散噪声该乘Δt还是Δt²。目标读者是正在做IMU融合定位、看得懂一点公式但不想只看结论抄现成库的人。

1. 为什么放着四元数EKF不用,偏要引入误差状态

1.1 四元数的加法更新:在流形上做线性操作的尴尬

四元数是单位范数约束下的旋转表达,(|q|=1)。如果直接把 q 当作普通向量,EKF更新写成 (q \leftarrow q + \delta q),更新后的 q 几乎必然偏离单位球面。你当然可以在后面加一步归一化,但问题是:归一化这个操作完全不在卡尔曼递推的线性框架里,协方差传播用的雅可比和实际执行的更新路径不一致。角度越大的时候,这条“弦上的路径”和真实球面路径偏差越大。

反映到数值上就是P矩阵逐渐失真,最后非正定。我那次demo就是活例。说到底,卡尔曼滤波假设状态生活在欧氏向量空间里,而姿态生活在SO(3)流形上,把流形上的量硬塞进欧氏更新,代价迟早要还。

1.2 误差状态让线性化永远发生在原点附近

ESKF把状态拆成两部分:真值 = 标称状态 + 误差状态。标称状态完全由IMU测量做非线性递推,保证四元数始终归一、位置速度始终连续;滤波估计的只是那个小量误差状态。

这个小量最核心的价值在于:误差状态的期望始终在0附近。0附近是流形切空间上线性近似最可靠的区域。相比之下,直接四元数EKF做线性化的参考点是一个任意的大姿态,误差一大,切空间近似就开始崩。ESKF相当于把“难搞的大姿态积分”留给确定性递推去做,把“适合滤波器干的小偏差估计”留给线性系统去做,两边各干各擅长的事。

1.3 ESKF受益的实际场景

体感最明显的有这么几类场景:一是初始误差大,比如GPS刚恢复、姿态初值只能靠加速度计粗略对齐,误差状态大但依然在“0附近的扰动”框架里可处理;二是带零偏估计的多传感器系统,零偏本身就是典型的缓慢小扰动,非常契合误差状态建模;三是高动态机动,载具快速转弯、颠簸时,直接四元数EKF的姿态协方差很容易被剧烈拉伸,ESKF始终跟着当前标称轨迹走,稳定性好得多。后面你去看MSCKF、视觉惯性里程计这类滤波器,骨架基本也是这套。

2. 四元数运动学:从旋转矩阵导数到 q̇=0.5q⊗ω

2.1 旋转的两种表达:旋转矩阵与四元数

旋转矩阵 R 和四元数 q 描述同一个三维旋转。四元数写成标量加向量的形式 (q=[\cos(\theta/2),\ n\sin(\theta/2)]^\top),其中 θ 是旋转角,n 是单位旋转轴。指数映射把旋转向量 (\boldsymbol{\theta} = \theta n) 映射成四元数,这是后面标称状态积分里反复用到的东西。

这里我保持一个固定约定:q 表示从载体坐标系到世界坐标系的旋转矩阵。后续所有的左乘、右乘讨论都建立在这个约定上,一旦约定变了,所有公式的左右乘位置和符号都跟着变。

2.2 角速度进入四元数:左右乘约定决定公式长相

推导四元数运动学,最简洁的方式是从旋转矩阵导数入手。设载体坐标系中固定向量 (v_B),在世界系中表示为 (v_W(t)=R(t)v_B)。物理上,任意向量随载体旋转时,其时间导数满足 (\dot v_W = \omega_W \times v_W),所以 (\dot R = [\omega_W]_\times R)。又因为 (\omega_W = R\omega_B),可得:

[ \dot R = R[\omega_B]_\times ]

对应到四元数,就是角速度在载体坐标系下的运动学方程:

[ \dot q = \frac{1}{2} q \otimes \begin{bmatrix} 0 \ \omega_B \end{bmatrix} ]

如果给的是世界坐标系角速度 (\omega_W),则形式变为:

[ \dot q = \frac{1}{2} \begin{bmatrix} 0 \ \omega_W \end{bmatrix} \otimes q ]

左右乘的位置不一样。这两个公式看起来只是交换顺序,但对后面误差状态推导影响巨大。这篇博客采用的约定是:IMU陀螺输出天然在载体坐标系,所以姿态运动学用第一条,误差扰动也从右边进入。

2.3 加速度计与比力:速度通道里的关键模型

加速度计测的不是“加速度”,而是“比力”,物理含义是单位质量上除重力以外的惯性力合外力。这个区别很绕人,我用日常场景解释:自由落体时加速度计输出接近零,因为在自由下落过程中箱体里的加速度计和箱体一起做加速度同为g的运动,测不到支持力。所以从IMU原始数据恢复世界系加速度时,要把重力“加回来”。

标准测量模型写作:

[ a_m = R^\top(a_W - g_W) + b_a + n_a ]

其中 (a_W) 是世界系下载体加速度,(g_W) 是世界系重力向量,加速度计零偏 (b_a)、噪声 (n_a) 都定义在载体坐标系。反过来,状态递推里用到的世界系加速度是:

[ \dot v = R(a_m - b_a - n_a) + g_W ]

这里有个特别容易写反的点:bias在载体坐标系,必须先用测量减去bias再左乘R转到世界系;重力在世界坐标系,必须在转完之后再加。我第一次把顺序写反,单位对上、逻辑却全错,仿真里速度一个劲往天上飘,查了大半天。

3. 三种状态的搭建:真值、标称与误差

3.1 误差怎样叠加到真值上:先定扰动约定

令状态包含位置 p、速度 v、姿态四元数 q、陀螺零偏 b_g、加速度计零偏 b_a、重力向量 g:

[ x = [p,\ v,\ q,\ b_g,\ b_a,\ g]^\top ]

真值状态 x、标称状态 (\hat x)、误差状态 δx 的关系如下:

[ p = \hat p + \delta p,\quad v = \hat v + \delta v ]

[ q = \hat q \otimes \delta q,\quad \delta q \approx \begin{bmatrix} 1 \ \delta\theta/2 \end{bmatrix} ]

[ b_g = \hat b_g + \delta b_g,\quad b_a = \hat b_a + \delta b_a,\quad g = \hat g + \delta g ]

位置、速度、零偏、重力都用加法扰动,唯独姿态用四元数乘法扰动。这里的 (\delta q) 放在 (\hat q) 的右边,意味着误差旋转发生在载体坐标系,这个选择不是随意的。IMU角速度测量本身就是载体坐标系的量,右扰动能让姿态误差方程里不出现多余的坐标系变换项,H矩阵和残差计算也更自然。如果你换成左扰动 (q=\delta q\otimes\hat q),推导出的 (\delta\dot\theta) 会变成另一副样子,代码里左右扰动一旦混用,姿态必飘。

3.2 标称状态运动学:让IMU测量主驱动

标称状态是忽略随机噪声后、直接用带偏置补偿的IMU测量驱动的那条轨迹:

[ \dot{\hat p} = \hat v ]

[ \dot{\hat v} = \hat R(a_m - \hat b_a) + \hat g ]

[ \dot{\hat q} = \frac{1}{2} \hat q \otimes (\omega_m - \hat b_g) ]

[ \dot{\hat b}_g = 0,\quad \dot{\hat b}_a = 0,\quad \dot{\hat g} = 0 ]

注意零偏、重力在标称模型里被当成常数,它们的变化全部留给误差状态去吸收。噪声不进标称路径是一个刻意的设计:如果把随机噪声直接积分进标称状态,会导致标称轨迹本身被单次噪声采样污染,方差越来越大;把噪声留给误差状态,由滤波器去定量估计这个不确定性,标称路径始终是一条平滑的参考轨迹,后面做线性化也干净。

3.3 误差状态的直观物理意义

误差状态里每一项都有实际含义:δp 是估计位置与真实位置之间在世界系下的偏移;δv 是速度误差;δθ 是载体坐标系下的姿态小扰动;(\delta b_g)、(\delta b_a) 是零偏估计的剩余误差;δg 是重力向量估计误差,比如水平基准没有完全对准时,这个量能把残差吸收掉。

写H矩阵之前,先把这六个分量的含义和坐标系搞清楚,比背公式重要得多。后面在可观测性分析里你会发现,单点位置观测对 yaw 几乎没有约束力,这跟 (\delta\theta) 定义在哪个坐标系都有关系。

4. 连续时间误差状态方程逐项展开

4.1 姿态误差:δθ̇ = -ω̂×δθ - δb_g 的完整推导

姿态误差是全部推导里最容易被符号坑到的地方,我完整走一遍。标称姿态运动学:

[ \dot{\hat q} = \frac{1}{2}\hat q \otimes \hat\omega,\quad \hat\omega = \omega_m - \hat b_g ]

真值角速度满足:

[ \omega = \omega_m - b_g = \hat\omega - \delta b_g ]

真值姿态运动学:

[ \dot q = \frac{1}{2} q \otimes \omega ]

代入 (q = \hat q \otimes \delta q),并对左边求导:

[ \dot{\hat q}\otimes\delta q + \hat q\otimes\dot{\delta q} = \frac{1}{2}\hat q\otimes\delta q\otimes(\hat\omega - \delta b_g) ]

把 (\dot{\hat q}) 的表达式代进去,左乘 (\hat q^{-1}),整理后得到核心的化简阶段:

[ \dot{\delta q} = \frac{1}{2}\left[\delta q\otimes(\hat\omega-\delta b_g) - \hat\omega\otimes\delta q\right] ]

现在把 (\delta q\approx[1,\ \delta\theta/2]^\top) 代进去,展开四元数乘法。标量部分会出现二阶小量,直接忽略;向量部分:

[ \delta q\otimes(\hat\omega-\delta b_g) - \hat\omega\otimes\delta q \approx -\delta b_g + \frac{\delta\theta}{2}\times\hat\omega - \hat\omega\times\frac{\delta\theta}{2} ]

两项叉乘合并:

[ \frac{\delta\theta}{2}\times\hat\omega - \hat\omega\times\frac{\delta\theta}{2} = \delta\theta\times\hat\omega = -\hat\omega\times\delta\theta ]

又因为 (\dot{\delta q}\approx[0,\ \dot{\delta\theta}/2]^\top),最终得到:

[ \dot{\delta\theta} = -\hat\omega\times\delta\theta - \delta b_g ]

注意 (\delta\theta) 和 (\delta b_g) 都定义在载体坐标系,所以这个式子里的叉乘矩阵不需要经过 R 变换。符号如果搞反,bias 估计会朝着错误方向跑,姿态出现持续慢漂。

4.2 速度误差:来自运动加速度、零偏与重力的三个扰动

速度真值方程:

[ \dot v = R(a_m - b_a) + g ]

标称方程:

[ \dot{\hat v} = \hat R(a_m - \hat b_a) + \hat g ]

对真值作小扰动展开:(R = \hat R(I + [\delta\theta]_\times)),(b_a=\hat b_a+\delta b_a),(g=\hat g+\delta g),忽略二阶小量:

[ \dot v = \hat R(a_m-\hat b_a) + \hat R[\delta\theta]_\times(a_m-\hat b_a) - \hat R\delta b_a + \hat g + \delta g ]

利用 ([\delta\theta]\times a = -[a]\times\delta\theta),两边同时减掉标称方程,得到:

[ \dot{\delta v} = -\hat R[a_m-\hat b_a]_\times\delta\theta - \hat R\delta b_a + \delta g ]

如果把加速度计白噪声也显式放进去,就在右边再加一项 (-\hat R n_a)。速度误差方程里有三个驱动源:运动加速度引起的姿态误差耦合、加速度计零偏误差、重力估计误差。这决定了F矩阵第2行的结构:第3列是姿态耦合项,第5列是零偏项,第6列是重力项。

4.3 位置、零偏与重力:补充剩余几行

位置误差直接从 (\dot p = v) 减 (\dot{\hat p} = \hat v) 得到:

[ \dot{\delta p} = \delta v ]

零偏和重力在标称里被当作常数,真值里用随机游走建模,于是:

[ \dot{\delta b}g = n{bg},\quad \dot{\delta b}a = n{ba},\quad \dot{\delta g} = 0 ]

到这里15维误差状态方程就齐了。整个过程很机械:定义扰动方式、展开真值方程、减去标称方程、丢掉二阶小量,别跳步就基本不会错。

4.4 组装为矩阵形式:F与G

把上面的结果按 (\delta x=[\delta p,\ \delta v,\ \delta\theta,\ \delta b_g,\ \delta b_a,\ \delta g]^\top) 的顺序排成分块矩阵,每个块都是3×3:

[ F = \begin{bmatrix} 0 & I & 0 & 0 & 0 & 0\ 0 & 0 & -\hat R[a_m-\hat b_a]\times & 0 & -\hat R & I\ 0 & 0 & -[\omega_m-\hat b_g]\times & -I & 0 & 0\ 0 & 0 & 0 & 0 & 0 & 0\ 0 & 0 & 0 & 0 & 0 & 0\ 0 & 0 & 0 & 0 & 0 & 0 \end{bmatrix} ]

对应噪声输入矩阵 G,噪声向量取 (n=[n_g,\ n_a,\ n_{bg},\ n_{ba}]^\top):

[ G = \begin{bmatrix} 0 & 0 & 0 & 0\ 0 & -\hat R & 0 & 0\ -I & 0 & 0 & 0\ 0 & 0 & I & 0\ 0 & 0 & 0 & I\ 0 & 0 & 0 & 0 \end{bmatrix} ]

写代码时记住:F矩阵里的 (\hat R)、(a_m-\hat b_a)、(\omega_m-\hat b_g) 每一步都要用当前估计值刷新,不能存成常量。这也是ESKF比直接四元数EKF更费一点计算的原因,但这点开销换来的稳定性完全值。

5. 离散化:进入定时循环前的最后一步

5.1 误差状态的一阶离散F_d

在IMU采样周期 (\Delta t) 很小的情况下,用一阶欧拉离散 (F_d = I + F\Delta t) 已经足够。展开后:

[ F_d = \begin{bmatrix} I & I\Delta t & 0 & 0 & 0 & 0\ 0 & I & -\hat R[a_m-\hat b_a]\times\Delta t & 0 & -\hat R\Delta t & I\Delta t\ 0 & 0 & I-[\omega_m-\hat b_g]\times\Delta t & -I\Delta t & 0 & 0\ 0 & 0 & 0 & I & 0 & 0\ 0 & 0 & 0 & 0 & I & 0\ 0 & 0 & 0 & 0 & 0 & I \end{bmatrix} ]

如果IMU频率被压得很低,比如50Hz以下,一阶近似就可能让NEES检验超界,那时需要保留二阶项:

[ F_d = I + F\Delta t + \frac{1}{2}(F\Delta t)^2 ]

或者直接调矩阵指数。绝大多数200Hz系统用一阶就够,我实际测试中一阶和二阶的结果差异在误差状态协方差的百分之几以内。

5.2 过程噪声协方差Q_d:Δt还是Δt²的纠结点

这是弹幕区提问最多的地方。连续时间系统 (\dot x = Fx + Gn),功率谱密度矩阵 (Q_c=\mathrm{diag}(\sigma_g^2I,\ \sigma_a^2I,\ \sigma_{bg}^2I,\ \sigma_{ba}^2I)) 离散化后的协方差近似为:

[ Q_d = G Q_c G^\top \Delta t ]

展开成常用分块形式:

[ Q_d = \begin{bmatrix} 0 & 0 & 0 & 0 & 0 & 0\ 0 & \sigma_a^2\Delta t I & 0 & 0 & 0 & 0\ 0 & 0 & \sigma_g^2\Delta t I & 0 & 0 & 0\ 0 & 0 & 0 & \sigma_{bg}^2\Delta t I & 0 & 0\ 0 & 0 & 0 & 0 & \sigma_{ba}^2\Delta t I & 0\ 0 & 0 & 0 & 0 & 0 & 0 \end{bmatrix} ]

如果你在某篇博客里看到 (Q_d) 对角线是 (\sigma^2\Delta t^2),那通常是因为作者把噪声定义成了单个步长内的随机增量,而不是功率谱密度。两种写法没有对错之分,但必须和你的G矩阵定义匹配。最稳妥的办法是不背公式,用仿真里的NEES检验去卡Q_d的量级。

5.3 标称状态积分与误差状态离散不要混用

误差状态用一阶欧拉没问题,但标称状态的四元数积分千万不要照搬欧拉 (q \leftarrow q + \dot q\Delta t),那样模长会慢慢漂掉。我推荐四元数指数映射更新:

对每个IMU间隔 Δt: ω = ω_m - b_g a = a_m - b_a R = R(q) # 标称状态积分 p = p + v*Δt + 0.5*(R*a + g)*Δt^2 v = v + (R*a + g)*Δt # 四元数指数映射更新,dθ = ω*Δt angle = norm(ω) * Δt if angle > 0: axis = ω / norm(ω) dq = [cos(angle/2), axis*sin(angle/2)] else: dq = [1, 0, 0, 0] q = normalize(q ⊗ dq) # 误差状态预测 Fd = build_Fd(R, a, ω, Δt) Qd = build_Qd(σ_g, σ_a, σ_bg, σ_ba, Δt) P = Fd * P * Fd^T + Qd

这个顺序里,先用当前速度积分位置、再用加速度更新速度,配合 (\Delta t^2) 的位置项,是一个简单但相对平衡的欧拉/二级积分混合。工程上要更好可以做中值积分或RK4,但我建议先把这套跑通再升级。

6. 观测更新与误差重置:GPS进来以后发生的四件事

6.1 位置观测的H矩阵与卡尔曼更新

ESKF里卡尔曼更新直接作用在误差状态上。以GPS位置观测为例:

[ z = p + \nu,\quad \nu\sim\mathcal N(0,R_{gps}) ]

预测值就是当前标称位置 (\hat p),残差:

[ r = z - \hat p ]

观测矩阵只取位置相关块:

[ H = [I_3,\ 0_3,\ 0_3,\ 0_3,\ 0_3,\ 0_3] ]

标准卡尔曼更新:

[ K = P H^\top(HPH^\top + R_{gps})^{-1} ]

[ \delta x = K r ]

[ P = (I - KH)P ]

这里有个直觉:如果GPS协方差远大于滤波器位置协方差,增益K趋近0,误差状态更新量很小,系统主要信任IMU积分;反过来如果GPS噪声很小,误差状态会直接把位置推过去。调试时可以先给一个很大的R_gps,确认整条链路能稳定跑,再逐步缩小。

6.2 姿态观测残差:方向与扰动约定匹配

如果观测来自视觉、动捕或磁力计,给出世界系到载体系的旋转矩阵 (R_z),残差计算必须匹配滤波器内部选用的扰动方向。

因为我们用的是右扰动 (q=\hat q\otimes\delta q),残差应该取载体坐标系下的相对旋转:

[ r_\theta = \mathrm{vee}(\hat R^\top R_z) ]

其中 (\mathrm{vee}(\cdot)) 取反对称矩阵对应的旋转向量。对应的观测矩阵:

[ H_\theta = [0_3,\ 0_3,\ I_3,\ 0_3,\ 0_3,\ 0_3] ]

如果你应用世界系左扰动,那残差就应该是 (\mathrm{vee}(R_z\hat R^\top))。残差方向和扰动约定不匹配,表现就是姿态慢速漂移、零偏估计收敛方向随机,而且很难通过调参解决。

6.3 误差重置:注入标称状态,P阵怎么办

更新完误差状态后,要把它折回标称状态并清零误差:

[ \hat p \leftarrow \hat p + \delta p ]

[ \hat v \leftarrow \hat v + \delta v ]

[ \hat q \leftarrow \hat q\otimes\delta q\quad(\text{需归一化}) ]

[ \hat b_g \leftarrow \hat b_g + \delta b_g ]

[ \hat b_a \leftarrow \hat b_a + \delta b_a ]

[ \hat g \leftarrow \hat g + \delta g ]

[ \delta x \leftarrow 0 ]

关于P阵是否要做额外修正,严格来说需要乘一个雅可比 (P\leftarrow APA^\top),但姿态注入涉及流形变换,这个A矩阵在一阶近似下是单位阵加 (\delta\theta) 量级的高阶小项,实际工程中大多数实现直接省略。我在NEES验证中一阶实现已经合格,所以没有保留这项;如果之后要做严格的滤波一致性分析,建议回去翻一下Solà那篇ESKF论文的reset步骤。

7. 先别急着上真机:蒙特卡洛仿真与NEES检验

7.1 从轨迹生成理想IMU测量

公式推完先别急着接真IMU,很多符号错误在真机上要浪费几天才能发现,仿真里几分钟就能暴露。先造一条平滑轨迹,比如8字形加俯仰滚转,时长60秒,然后反推理想IMU测量。

相邻时刻旋转矩阵 (R_k, R_{k+1}) 之间是载体坐标系的增量旋转,角速度取SO(3)对数映射:

[ \omega_B(t_k) = \frac{1}{\Delta t}\log(R_k^\top R_{k+1}) ]

世界系加速度用中心差分:

[ a_W(t_k) = \frac{v_W(t_{k+1}) - v_W(t_{k-1})}{2\Delta t} ]

再转回载体坐标系比力:

[ a_B(t_k) = R^\top(t_k)(a_W(t_k) - g_W) ]

最后叠加人为设置的bias和高斯白噪声,就得到了带噪声的IMU测量。

7.2 NEES检查:让统计量帮你抓符号和漏项

NEES(归一化估计误差平方)是检验滤波器一致性的标准工具。每次观测更新后计算:

[ \epsilon_k = \delta x_k^\top P_k^{-1} \delta x_k ]

其中 (\delta x_k) 是仿真里知道的真实误差状态,(P_k) 是滤波器维护的协方差。如果滤波器设计正确,(\epsilon_k) 应该服从自由度15的卡方分布,期望为15。做N次蒙特卡洛运行,取平均 (\bar\epsilon),然后和卡方区间比较:

[ \bar\epsilon \in \left[\frac{\chi^2_{0.025}(15N)}{N},\ \frac{\chi^2_{0.975}(15N)}{N}\right] ]

用Python一行算边界:

from scipy.stats import chi2 N = 50 low = chi2.interval(0.95, 15*N)[0] / N high = chi2.interval(0.95, 15*N)[1] / N

平均NEES偏大,说明滤波器过于自信,实际误差比协方差大,通常是Q_d设太小或F矩阵漏了项;平均NEES偏小,说滤波器太保守,真实误差远小于协方差,可能噪声参数给大了。看到哪个方向再回头查对应模块,效率高得多。

7.3 一套能起步的仿真与滤波器参数

我常用的一组起步参数如下,真机上还要重新调,但仿真里够用:

参数数值
陀螺白噪声 (\sigma_g)(0.003\ \text{rad/s}/\sqrt{\text{Hz}})
加速度计白噪声 (\sigma_a)(0.05\ \text{m/s}^2/\sqrt{\text{Hz}})
陀螺零偏随机游走 (\sigma_{bg})(1\times10^{-5}\ \text{rad/s}^2/\sqrt{\text{Hz}})
加速度计零偏随机游走 (\sigma_{ba})(1\times10^{-4}\ \text{m/s}^3/\sqrt{\text{Hz}})
初始姿态协方差((1^\circ)^2)
初始位置协方差((0.1\ \text{m})^2)
初始速度协方差((0.1\ \text{m/s})^2)
初始零偏协方差((0.001\ \text{rad/s})^2,\ (0.01\ \text{m/s}^2)^2)
GPS位置噪声 R(\mathrm{diag}(0.5^2,\ 0.5^2,\ 1^2)\ \text{m}^2)

这套参数在仿真里通常能让NEES落在区间内。如果某些平台的IMU噪声模型不同,优先修改 (\sigma_g,\sigma_a),而不是去动F矩阵。

8. 从公式到能跑的系统:那些折腾我很久的工程细节

8.1 初始化:静止段重力对齐与零偏预估计

ESKF初始化很重要,尤其是姿态初值。最简单可靠的办法是让IMU静止几秒,用加速度计均值做重力对齐。静止时 (a_m \approx R^\top(-g_W)),在ENU坐标系下 (g_W=[0,0,-9.81]^\top),常用水平姿态估计:

[ \mathrm{roll} = \arctan2(a_y,\ a_z) ]

[ \mathrm{pitch} = \arctan2(-a_x,\ \sqrt{a_y^2+a_z^2}) ]

yaw在纯静止、无外部观测时不可观,可以先置0或由磁力计/首个GPS速度方向给出。静止段陀螺均值可以直接作为 (b_g) 初值,因为静止时真值角速度基本为0,陀螺输出主要就是零偏。加速度计零偏比较难在静止下完全分离,可以先设0,交给滤波器后续估计。

8.2 时间戳对齐:高频IMU和低频定位观测的缝隙

IMU跑200Hz,GPS通常10Hz,这中间的时间对准问题容易被忽视。如果GPS观测时间戳是 (t_z),而滤波器当前已经在 (t_k),直接用 (t_k) 时刻的 (\hat p) 算残差,高动态时残差里会带进一个IMU积分周期内的运动位移,累积起来能明显影响定位精度。

工程上常用两种办法:一是维护最近一小段状态历史,观测到来时插值到 (t_z) 再计算残差;二是把预测推进到 (t_z) 时刻再更新。低成本平台上IMU和GPS时钟本身不同步的话,还可以把时间偏移量也作为小量估计。我在实际项目里的做法是先把时间戳对齐做好,再谈参数微调,省下来的是后面排查“为什么水平精度好但高程总飘”这种玄学问题的时间。

8.3 如果发现姿态慢漂,先查这三个地方

姿态慢漂是ESKF新手最容易恐慌的问题,按顺序查这三处,大多数情况能定位:第一,残差方向是否和扰动约定匹配,特别是姿态观测进来时,左右扰动混用会直接造成慢漂;第二,(\delta\theta) 方程里 (-\hat\omega\times\delta\theta) 的符号是否和你的代码一致,如果符号反了,bias估计会朝错误方向走,并且NEES会明显越界;第三,过程噪声Q_d单位写对没有,陀螺白噪声如果漏了或者多乘了一个(\Delta t),姿态协方差会失真,导致后续估计失调。

最后补充一点,只靠GPS点位置观测时,yaw慢漂本来就是可观测性限制——位置观测量对航向角几乎没有约束力,这不是代码问题。想稳住yaw,要么加磁力计、视觉方向、多天线GPS等提供航向观测,要么利用运动激励和加速度计交叉耦合来间接改善。在我自己项目里的体会是:一套通过了NEES检验的ESKF,静态零漂、动态轨迹吻合都是可以做到的,真正拉开差距的往往就是初始化质量、时间对齐和噪声参数校准这几个工程细节。希望这篇推导能帮你缩短从公式到稳定定位的距离。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询