
1. 这组仿真到底在对比什么先把这个项目的目标说清楚我们有一台GPS接收机的伪距和伪距率测量要估计接收机的位置、速度以及接收机内部时钟的偏移和漂移。传统做法是把每个历元的观测单独拎出来用非线性最小二乘直接解现代做法是把位置、速度、时钟状态放进一个状态方程里用扩展卡尔曼滤波做序贯估计。这篇内容就是围绕这两种方案在仿真数据下比较它们的估计误差和置信区间覆盖度。为什么值得做这个对比因为我见过太多人一上来就在工程里套EKF觉得滤波一定比最小二乘高级结果不会调过程噪声Q滤波发散也有人死守LS遇到遮挡、断续观测就不知道怎么办。其实两种算法各有适用边界。用一个可控的GPS仿真环境把两种方法“关在同一考场里”你才能真正理解它们的差异而不是停留在“EKF是‘更先进的最小二乘’”这种模糊感觉上。这次项目比较适合三类人看一是刚开始接触GNSS定位和卡尔曼滤波的学生想做课程设计但不知道从哪入手二是做组合导航或者传感器融合的工程师想理清EKF的协方差和真实误差之间的关系三是想在MATLAB里快速搭一套可以改参数、出图、支撑论文结论的仿真框架。项目中采用的对比口径也很关键两者处理的是同样的伪距和伪距率观测估计的是同一组8维状态即三维位置、三维速度、接收机钟差用米表示、接收机钟漂用米/秒表示。这样比较才公平下面所有算法细节都会先围绕这个状态定义展开。2. 先别急着跑代码把GPS观测模型写对2.1 状态向量为什么要定义成8维很多入门教程里的GPS定位只解4个未知数x、y、z和钟差b。那是默认接收机静止或者只做单点定位。一旦接收机在动并且你想一起估计速度就必须把三维速度vx、vy、vz纳入状态如果再想估计时钟漂移就必须再加一个钟漂状态项。综合下来就是一个8维状态向量x [x, y, z, vx, vy, vz, b, bd]T这里有几个单位上的约定必须提前钉死不然后面代码全是坑。位置用米速度用米/秒时钟偏移b是用光速乘接收机钟差得到的单位是米时钟漂移bd是接收机钟差变化率乘以光速单位是米/秒。之所以把钟差折成米是因为GPS伪距观测方程里时钟误差直接在距离域和几何距离相加。观测方程变成ρ ||r_sat - r_user|| b ερ其中||r_sat - r_user||是卫星到接收机的几何距离ερ是伪距测量噪声。伪距率观测对应写成ρdot u · (v_sat - v_user) bd ερd这里的u是接收机指向卫星的单位向量v_sat是卫星速度v_user是接收机速度。用这种单位约定整个系统的位置、速度、时钟状态全部统一到“米”和“米/秒”体系里状态转移矩阵也会非常整齐。2.2 接收机时钟动态模型接收机时钟不是一个理想时钟它会有频率误差频率误差还会随时间缓慢变化。在仿真里我通常把真实时钟写成确定性多项式模型加随机扰动b(t) b0 bd0·t 0.5·rate·t²bd(t) bd0 rate·t其中b0是初始钟差bd0是初始钟漂rate是频漂速率。做滤波器设计时钟差和钟漂的状态转移可以近似写成b_{k1} b_k Δt·bd_kbd_{k1} bd_k这种写法本质上是把时钟漂移当成一个随机游走过程。真实的原子钟、温补晶振会有更复杂的噪声功率谱但在这个仿真项目里用一阶积分模型加上过程噪声驱动已经足够说明问题。有一点我要特别提醒如果你真的在测试EKF对钟漂的估计并且观测里只有伪距没有伪距率那么单历元下钟漂基本不可观。伪距只跟钟差直接相关跟钟漂是通过时间差间接相关的。也就是说伪距观测要积累好几个历元才能让钟漂信息慢慢“浮现”出来。这也是为什么我说项目里一定要同时用伪距率或者做多历元批处理否则EKF和LS在钟漂估计上都没有公平的题目可做。2.3 伪距方程非线性在哪里为什么要线性化伪距方程里有位置和卫星位置之间的欧几里得范数这是一个典型的非线性函数。如果你只用线性卡尔曼滤波状态转移是线性的没问题但观测方程不是Hx那种矩阵形式而是h(x)的非线性映射。EKF的做法是在当前状态估计值附近做一阶泰勒展开把观测方程近似成线性形式非线性LS则是在每个迭代点附近做同样的线性化通过高斯牛顿法逐步逼近最优解。很多教材会把这两件事分开讲好像一个是滤波、一个是批处理但它们内部的核心技术其实是同一个对非线性观测方程求雅可比矩阵。只要这个雅可比矩阵写对了整个项目就成功了一大半。以我生成的仿真实例来说明雅可比结构。对第i颗卫星的伪距∂ρ_i / ∂r_user -u_i∂ρ_i / ∂b 1这里的u_i就是接收机指向卫星的单位向量。对第i颗卫星的伪距率忽略卫星距离变化率随接收机位置变化的高阶小项后有∂ρdot_i / ∂v_user -u_i∂ρdot_i / ∂bd 1写程序时千万不要把符号搞反。我的习惯是先写成可读的行向量再填进去而不是手工算半天然后一把梭。雅可比矩阵行数是观测数量列数是状态维度。假设一历元看到6颗卫星伪距加伪距率共有12个观测雅可比矩阵就是12×8冗余度足够。2.4 卫星位置和可见性怎么生成仿真里最容易被忽略但又最影响结果的是卫星几何。很多人喜欢随便生成几颗卫星的位置但那样卫星分布可能长期集中在一个方向定位精度会很差LS和EKF的对比结论也就没有说服力。我在项目中使用了一套简化但合理的卫星星座生成方案按照GPS星座的基本构型用几个轨道面近似分布24颗卫星每颗卫星运行在中圆轨道上。仿真时间轴上让部分卫星从地平线上升起或下落模拟进出可见范围的过程。每历元选取仰角高于10度的卫星参与定位。如果你不追求轨道力学的精密推演也可以直接给定几组卫星的ECEF坐标随时间正弦变化的轨迹关键是要让几何精度因子保持在合理范围。否则得到的误差曲线中很大一部分不是算法差异而是卫星几何变化造成的。实验设计时我会固定同一组卫星轨迹让LS和EKF跑同一批数据这样最后对比的就是纯算法差异。3. 非线性LS和EKF差在一个“时间记忆”上3.1 非线性LS单历元加权高斯牛顿迭代非线性LS实现起来最简单但它没有时间记忆。每个新历元到达后算法只用当前时刻的所有伪距和伪距率观测构造最小二乘问题完全不使用上一历元的估计结果。迭代公式为Δx (JT·W·J)^(-1) · JT·W·Δz其中Δz是观测残差J是雅可比矩阵W是观测权重矩阵。每次迭代更新x。一般3到5次迭代就能收敛因为GPS伪距观测模型的非线性程度并不算剧烈。如果初始位置偏差在百公里量级就需要多做几次迭代。在8维状态下一历元至少需要8个独立观测。如果看到6颗卫星12个观测端到端解算实际上就是在做超定非线性最小二乘。得到的协方差为P (JT·W·J)^(-1)这个协方差只反映测量噪声的传播不包含任何运动模型信息。它反映的是“如果没有时间相关性、只看这一秒数据”状态估计的不确定度有多大。3.2 EKF时间预测加量测更新EKF在LS的基础上多了一个东西状态方程。每个历元先做一个预测x_pred F·x_estP_pred F·P_est·F^T Q然后再用当前历元观测做更新本质上就是在上一步的预测状态附近重新做一次高斯牛顿式更新区别是这个更新有相当强的先验信息约束。这里的F是状态转移矩阵如果我采用匀速度模型把位置和速度状态串起来状态转移矩阵是这样的分块结构F_pv [1 dt; 0 1]时钟子块也用同样的积分结构因为钟漂对钟差的影响本质上就是一个加速度式积分F_clk [1 dt; 0 1]三个位置子块加一个时钟子块合并成8×8的块对角矩阵。整个EKF的“时间记忆”就来自这个预测步骤。上一历元经过观测修正得到的位置、速度、钟差、钟漂会被状态方程作为先验带到当前历元。正因为有了这一项EKF在某些条件下的误差往往比单历元LS更平滑尤其是在测量噪声比较大或者出现短暂缺星时。3.3 为什么不能用线性卡尔曼滤波代替EKF有些刚接触滤波的同学会问既然伪距方程看着也不是特别非线性能不能直接把观测矩阵设成单位阵或者固定一个系数矩阵这是不行的。当接收机运动范围很大或者卫星相对接收机的方向变化明显伪距关于位置的雅可比每历元都会变。设计一个固定的H矩阵相当于假设所有卫星都在同一个位置、几何关系恒定这在GPS定位里完全不成立。EKF做到的就是“每个历元重新线性化”基于当前的预测位置重新计算卫星到接收机的单位向量再实时组装新的雅可比。这种在线性化思想和LS迭代中的做法是一模一样的。理解了这一点你就会明白为什么网上很多EKF的代码看起来和加权迭代最小二乘那么像因为它们骨子里共用同一个观测模型模块。3.4 两者在统计意义下的边界从估计理论角度看如果系统是高斯的并且初始协方差和过程噪声Q设置正确EKF本质上是在做递归最大后验估计。而LS如果只跑一个历元只用了当前历元观测做最大似然估计如果LS做多历元、批处理它也类似做一段窗口内的最大似然估计。因此差别主要体现在三方面。第一EKF的估计误差会因为运动模型而受到约束轨迹更连续第二EKF的协方差P不仅包括测量噪声传播还包括过程噪声对状态的扩散所以它能预报未观测时刻的不确定度而LS不能第三如果过程噪声Q设置太小或者运动模型与真实轨迹偏差过大EKF会产生偏差甚至发散LS反而因为不依赖模型而显得更鲁棒。这个项目后面评估置信区间覆盖度本质上就是在检验EKF的P矩阵到底有没有和真实误差保持一致。4. 位置、钟差、钟漂误差和置信区间要这么量化4.1 三个指标别用错项目里提到的“估计误差”和“置信区间覆盖度”在描述时要拆开。位置误差通常看三维位置误差的均方根即对每一历元计算pos_err_k ||r_true_k - r_est_k||然后取均方根。时钟偏移误差和时钟漂移误差则是直接比较b、bd这两个状态分量。位置误差的单位是米钟差误差单位也是米钟漂误差单位是米/秒。评价维度不一样不要混在一起画一条曲线。置信区间覆盖度是另一个维度它不直接衡量误差大小而是看滤波器给出的协方差是否可信。假设某一历元EKF输出的状态协方差是P_k真实误差是e_k那么归一化误差平方为maha_k e_k^T · P_k^(-1) · e_k在滤波模型正确、噪声分布高斯的前提下这个量近似服从自由度为状态维数的卡方分布。对8维状态95%置信对应的卡方分位数是15.507。统计整个仿真过程中有多少比例的历元满足maha_k低于这个阈值得到的百分比就叫覆盖度。覆盖度接近95%说明滤波器对不确定度的描述基本可信远低于95%说明协方差太小、过于乐观远高于95%说明协方差太大、过于保守。4.2 位置误差的典型对比结果在一次代表性仿真中我设置了接收机以约12米/秒的速度沿x方向运动仿真时长120秒采样周期1秒可见卫星数约8颗伪距噪声标准差3米伪距率噪声标准差0.1米/秒。位置误差的结果大致如下。我观察到的位置误差RMSEEKF约是3.0米左右单历元LS约是4.5米左右。单历元LS之所以差一些是因为它只是对每一时刻的观测独立做曲线拟合误差会跟随每颗星的测量噪声上下跳动缺少时间的平滑约束。EKF因为状态方程把相邻历元的位置变化约束住了在噪声相对大的时候整体曲线更贴近真实轨迹。但这里有一个非常容易误读的地方位置误差更小并不意味着在任意场景下EKF都更优。如果接收机做高机动转弯而你给EKF的过程噪声Q设置得很小那么滤波器会跟不上真实运动此时EKF的位置误差可能远大于LS。实践里一定要让Q反映真实的动态不确定性。4.3 钟差和钟漂估计误差的差异更明显时钟相关状态的估计对比比位置误差更有意思。因为钟差、钟漂和位置在观测方程中互相耦合单历元LS把每个历元的钟差当成独立量求解无法利用“钟差不会突变”这一物理约束。因此LS解出的钟差序列往往表现出高频抖动钟漂的跳动幅度也很大。EKF则不同。状态方程里写入了钟差与钟漂的积分关系上一历元的钟漂会平滑地影响下一历元钟差的变化。这样即使在某一历元某颗星的伪距噪声异常大EKF给出的钟差也不会突然跳得很远钟漂估计更是被随机游走模型约束得相对平滑。在我仿真中钟差估计误差的RMSEEKF大概是单历元LS的30%到50%左右钟漂估计误差的RMSE差距更大因为LS如果不加伪距率观测几乎很难单独分辨钟漂。这个结果非常直观地说明了“为什么接收机芯片内部几乎不会用独立单历元LS来解钟漂而是会保存历史状态做连续性约束”。4.4 置信区间覆盖度怎么算才不骗自己置信区间覆盖度在作业里最容易“被做出很漂亮的结果”因为如果你只跑一次仿真而且只算位置分量的椭圆覆盖偶尔碰巧覆盖率很高意义不大。正确的做法是固定场景跑蒙特卡洛多次通常至少50到100次每次重新生成测量噪声然后统计所有历元、所有次数中的覆盖率。计算时也可以按状态子块分别统计位置子块用三维卡方阈值7.815钟差钟漂子块用二维卡方阈值5.991全状态用八维阈值15.507。对于LS依然可以计算类似覆盖率但要注意其协方差只包含测量噪声信息。真实轨迹如果存在过程噪声或者动力学模型偏差LS的覆盖率就会偏低因为它的椭圆没有把模型不确定性囊括进去。很多同学发现“LS的置信区间覆盖率只有70%”就以为代码错了其实这是正常的这说明LS根本没有给运动模型不确定性留余量。EKF的Q矩阵如果设置有偏差覆盖率也会明显偏离95%。这个指标其实是用来检验你的滤波器“自认知”是否诚实的而不是单纯说明谁定位更准。5. MATLAB程序结构与关键模块拆解5.1 整个仿真主流程怎么组织这个项目我强烈建议不要写成一个巨大的main脚本而是拆成几个函数模块每一块都可以单独替换。我常用的组织方式是这样的% main_gps_ls_ekf.m clear; close all; rng(42); % 1. 加载或生成卫星轨迹、真实用户轨迹 [sv_pos, sv_vel, t, user_true] genTestScenario(); % 2. 预先分配存储 N length(t); x_ekf zeros(8,N); P_ekf zeros(8,8,N); x_ls zeros(8,N); P_ls zeros(8,8,N); % 3. 初始化 x_ekf(:,1) initState; P_ekf(:,:,1) diag([10,1,10,1,10,1,100,1].^2); for k 1:N % 当前历元观测 [z, ~] simulateObs(user_true(:,k), sv_pos{k}, sv_vel{k}); % 非线性LS求解 [x_ls(:,k), P_ls(:,:,k)] solveNonlinearLS(z, sv_pos{k}, sv_vel{k}, x_ls_prev); % EKF求解 if k 1 [x_ekf(:,k), P_ekf(:,:,k)] ekfUpdate(x_ekf(:,k-1), P_ekf(:,:,k-1), z, sv_pos{k}, sv_vel{k}, dt, Q, R); end end这个流程里最关键的是把“真实状态生成”“观测生成”和“算法估计”分离开。这样你要测试不同的接收机动态模型时只需改genTestScenario和Q矩阵不必动LS和EKF的观测模型代码。5.2 观测模型和雅可比矩阵统一封装观测模型是整个项目复用率最高的模块LS迭代和EKF更新都要调用它。我一般这么写function [z_model, J] computeModel(x, sv_pos, sv_vel) % x: 8x1 [pos; vel; bias; drift] % sv_pos: Nx3, sv_vel: Nx3 N size(sv_pos, 1); r sv_pos - repmat(x(1:3), N, 1); % 从接收机指向卫星 dist sqrt(sum(r.^2, 2)); u r ./ dist; % 单位向量接收机指向卫星 z_model zeros(2*N, 1); z_model(1:N) dist x(7); z_model(N1:2*N) sum(u .* (sv_vel - repmat(x(4:6), N, 1)), 2) x(8); J zeros(2*N, 8); % 伪距对状态偏导 for i 1:N J(i, 1:3) -u(i, :); % d rho_i / d r_u J(i, 7) 1; end % 伪距率对状态偏导忽略v_sat-v_u对位置的导数高阶项 for i 1:N J(Ni, 4:6) -u(i, :); % d rho_dot_i / d v_u J(Ni, 8) 1; end end伪距率对位置求导那部分严格来说还有一个高阶耦合项但工程仿真中通常可以忽略。如果你在做论文或者要检查线性化精度可以用数值差分做对照。5.3 非线性LS的实现核心非线性LS用高斯牛顿迭代代码量非常小。关键点是迭代初值不能太离谱否则会收敛到局部极小点。实际中如果有上一历元的解可以用上一历元结果作为当前初值如果没有历史信息就先用一个粗略的先验位置。function [x_est, P] solveNonlinearLS(z, sv_pos, sv_vel, x0) W blkdiag((1/sigma_pr^2)*eye(nSat), (1/sigma_prr^2)*eye(nSat)); x_est x0; for iter 1:10 [z_model, J] computeModel(x_est, sv_pos, sv_vel); r z - z_model; dx (J*W*J) \ (J*W*r); x_est x_est dx; if norm(dx) 1e-6 break; end end P inv(J*W*J); end这段代码第4章提到的LS协方差就是从这里得到的。注意P用的是最后一次迭代雅可比矩阵数学上这叫CRB近似。如果观测噪声是高斯的这个近似在收敛点附近是比较准确的。5.4 EKF状态转移和更新实现EKF的时间更新部分要用分块矩阵构造F和Q。我在代码中写成Fpv [1 dt; 0 1]; Fclk [1 dt; 0 1]; F blkdiag(Fpv, Fpv, Fpv, Fclk); % 连续白噪声加速度模型离散化 q 0.05; % 位置过程噪声强度 Qpv q * [dt^3/3, dt^2/2; dt^2/2, dt]; % 时钟过程噪声 qclk_phase 1e-2; qclk_freq 1e-3; Qclk [qclk_phase*dt qclk_freq*dt^3/3, 0.5*qclk_freq*dt^2; 0.5*qclk_freq*dt^2, qclk_freq*dt]; Q blkdiag(Qpv, Qpv, Qpv, Qclk);更新阶段和LS代码很相似但多了一步卡尔曼增益融合先验与量测[z_pred, H] computeModel(x_pred, sv_pos, sv_vel); S H * P_pred * H R; K P_pred * H / S; innovation z - z_pred; x_est x_pred K * innovation; P (eye(8) - K*H) * P_pred * (eye(8) - K*H) K * R * K;这里用Joseph形式更新协方差虽然比教科书上最简形式多算一点但数值稳定性更好尤其在仿真时间长了以后能避免P矩阵失去对称正定性。5.5 蒙特卡洛评估和画图评估模块我会单独写一个runAnalysis.m脚本负责存储多轮仿真的统计量。伪代码如下for mc 1:60 for k 1:N % 记录真实状态、估计状态、协方差 e_k x_true(:,k) - x_est(:,k); maha_ekf(mc,k) e_k * inv(P_ekf(:,:,k)) * e_k; err_pos_ekf(mc,k) norm(e_k(1:3)); err_b_ekf(mc,k) abs(e_k(7)); err_bd_ekf(mc,k) abs(e_k(8)); end end % 覆盖率 cov_ekf mean(maha_ekf chi2inv(0.95, 8), all); % RMSE rmse_pos_ekf sqrt(mean(err_pos_ekf(:).^2));画图时可以画三组图三维位置误差随时间曲线、钟差和钟漂误差随时间曲线、归一化误差平方与卡方阈值的对比曲线。置信区间覆盖度最好用直方图或累计曲线呈现而不是只给一个最终数字这样可以看出覆盖率是否随时间稳定。6. 最容易翻车的几个细节和调参赛路6.1 初值选择LS和EKF都怕初值飞了LS在单历元独立求解时如果某一历元可见卫星少于4颗或者几何退化严重直接解算可能得到离谱的位置。初值误差超过几百公里以后高斯牛顿迭代的雅可比矩阵依然能算但伪距残差函数存在多个局部极小点容易收敛到错误解。工程上我推荐第一次迭代先做一次粗略的线性化定位或使用上一历元位置做载体跟踪。EKF初值对结果影响更大。状态初值差得太远前几个历元的线性化点就会很差滤波在收敛前可能已经积累了一堆错误。常见做法是先跑若干个历元的LS用LS的稳定解初始化EKF的位置和时钟状态速度和钟漂置为0或给一个合理猜测。这个“LS预热EKF”的手段在实践里非常常见。6.2 Q矩阵怎么调为什么覆盖率会偏高或偏低Q矩阵是EKF里最玄学但其实也最有迹可循的参数。位置过程噪声的q含义是“你不知道的真实加速度有多大”。如果接收机是低速运动q取0.01到0.1就够如果做高机动q要放大到10甚至更高。钟差部分的Q取决于你采用的晶振质量和建模置信度。覆盖率是调Q的很好的裁判。如果你发现EKF的覆盖度远低于95%比如只有60%说明P矩阵整体偏小滤波器太自信了。这时候要把Q调大因为模型不确定性没有被充分吸收。如果覆盖度到了99%以上说明P矩阵偏大误差虽然被包住但估计结果被过度“谨慎”的协方差包裹住了实际精度被低估。比较理想的Q应该让覆盖率在名义水平附近波动同时误差RMSE不显著增大。这里要特别说一句覆盖率是一个长期统计量单次仿真跑个30历元根本看不出门道。至少要跑上百个历元并叠加蒙特卡洛覆盖率才有统计意义。6.3 观测噪声协方差R不能拍脑袋伪距和伪距率噪声的标准差决定了W矩阵和R矩阵直接影响LS的权重和EKF的滤波增益。如果你的仿真数据是用3米标准差生成的但在算法里把R设成0.3米滤波器就会过度相信观测位置误差会被噪声牵着走覆盖率也会崩掉。反过来R设太大滤波器会过度依赖运动模型动态响应变慢。我在仿真中会先用一批观测数据的残差来估计实际噪声标准差再把它填入R。如果在真实的接收机数据上不同仰角的卫星噪声差别明显可以考虑按卫星仰角加权低仰角卫星给更大的噪声方差。6.4 LS和EKF用同一套观测模型但千万别忘了状态转移这个坑我踩过很多次。写好一个非线性LS模块后复制粘贴成EKF模块结果忘了构造F矩阵直接把上一历元状态当作当前预测状态。相当于没有时间更新EKF变成了一个带遗忘因子很弱的迭代滤波器效果会比LS还差。检查方法很简单把EKF的时间更新去掉直接跑一遍结果应该和LS差不太远加上时间更新后如果精度没有改善或者协方差矩阵没有先增后减的趋势说明状态方程部分有问题。另外状态向量的排序必须保持一致。我习惯固定为[x, y, z, vx, vy, vz, b, bd]雅可比矩阵列顺序和F矩阵对角子块的顺序必须与此对应。如果中途把时钟状态插到前面很多维度索引都要跟着改非常容易出错。6.5 想让仿真更有说服力建议做两组额外实验第一组是“有缺星历元”的实验。在某几个历元人为把可见卫星数从8颗降到4颗甚至降到3颗观察LS和EKF的反应。LS在3颗星时如果没有伪距率辅助完全无法解出8维状态即使有伪距率4颗星的几何也很差解算结果会剧烈跳动。而EKF依靠状态预测依然能维持一个相对合理的估计只是协方差会明显变大。这才是EKF的核心价值不是每一历元都能解算而是用时间累积弥补单历元信息的缺失。第二组是“模型失配”的实验。把真实轨迹设计成包含转弯机动而EKF的状态方程仍然假设匀速。这种情况下LS反而可能表现得更好因为它不受错误模型的拖累EKF如果Q太小就会跟不上如果Q足够大就会慢慢把机动“吸收”进过程噪声里。做这种对比能让你真正理解EKF的适用范围和代价。7. 最后给你一套可直接演进的调试思路这套程序最让我觉得顺手的地方就是LS和EKF可以共用同一个computeModel函数。你只要把观测模型、雅可比、噪声设置写对剩下的工作就是比较不同策略下的估计误差和协方差一致性。即使换到UWB定位、声呐定位或者惯导辅助定位这个框架依然成立因为核心思想是一样的有一个非线性观测方程有一个状态演化模型用最小二乘或滤波去求解。实际调试时我建议按这个顺序排查先跑单历元LS如果LS的定位结果都乱七八糟不要碰EKF赶紧检查卫星坐标生成、伪距生成和雅可比符号然后跑EKF关闭过程噪声Q设成0观察滤波是否退化成类似“顺序LS”的形式最后再把Q调起来看覆盖率是否落在合理区间。这个由简到繁的过程能省下不少查错时间。如果你按这套方法调出了一组相对理想的EKF覆盖率再把LS的误差曲线和EKF叠在一张图上你会看到很有趣的现象EKF的误差领先往往不是因为单历元观测信息用得更充分而是因为它把时间上的物理约束用上了。建模准确的时候时间约束就是信息建模不准确的时候时间约束就是误导。这个边界只有亲手改过Q、跑过蒙特卡洛之后才能真正体会。