
我第一次把四元数直接丢进扩展卡尔曼当状态量是在一个GPSIMU融合定位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_{k1}) 之间是载体坐标系的增量旋转角速度取SO(3)对数映射[ \omega_B(t_k) \frac{1}{\Delta t}\log(R_k^\top R_{k1}) ]世界系加速度用中心差分[ a_W(t_k) \frac{v_W(t_{k1}) - 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^2a_z^2}) ]yaw在纯静止、无外部观测时不可观可以先置0或由磁力计/首个GPS速度方向给出。静止段陀螺均值可以直接作为 (b_g) 初值因为静止时真值角速度基本为0陀螺输出主要就是零偏。加速度计零偏比较难在静止下完全分离可以先设0交给滤波器后续估计。8.2 时间戳对齐高频IMU和低频定位观测的缝隙IMU跑200HzGPS通常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静态零漂、动态轨迹吻合都是可以做到的真正拉开差距的往往就是初始化质量、时间对齐和噪声参数校准这几个工程细节。希望这篇推导能帮你缩短从公式到稳定定位的距离。