资讯动态

无迹卡尔曼滤波(UKF)原理与实战:从Sigma点到完整Python实现

发布时间:2026/10/17 1:29:09 来源:尧图企业网站定制
1. 先聊两句为什么有了卡尔曼滤波还不够做状态估计的人基本都绕不开卡尔曼滤波这套东西。但大家实际用下来会发现经典卡尔曼滤波能处理的范围非常有限它要求系统的状态方程和观测方程都是线性的噪声还得是高斯的。放到现实里这个条件太苛刻了——目标跟踪里有角度观测导航里有正余弦函数机器人定位里经常出现极坐标和直角坐标的来回转换但凡方程里有任何一个非线性项标准卡尔曼滤波就很难直接套上去。这时候大家首先想到的一般是扩展卡尔曼滤波EKF。EKF的思路听着也简单把非线性函数在某个点附近做一阶泰勒展开用完事后发现这玩意儿在一阶导数不稳定的场景下实在太脆。我见过不少同行调的EKF在系统非线性程度稍高、初始误差稍大的情况下误差直接崩掉协方差矩阵还经常非正定滤波器发散得莫名其妙。UKFUnscented Kalman Filter无迹卡尔曼滤波就是冲着这个问题来的。它不使用线性化转而用一组精心挑选的采样点去逼近非线性变换后的概率分布。这组采样点叫sigma点它们在状态空间中按照均值与协方差的约束被构造出来经过经过真实非线性函数传播后再由样本点的加权统计量还原出新的均值和协方差。整个过程没有雅可比矩阵没有求偏导没有线性化截断误差实现起来清爽得多精度却能达到二阶以上。这篇文章就是把我自己从“理解UKF公式”到“在项目里把它跑通并调稳”的全过程写下来。适合三类人看一是刚接触滤波算法、在KF和EKF之间徘徊的入门者二是已经把EKF调吐了、想换方案的实际项目开发者三是想在GPS/惯导组合、雷达跟踪、机器人定位这类场景里快速落地一个非线性状态估计器的人。看完你不但能理解UKF每一步在做什么还能直接拿着后面的代码改一改放到自己的项目里去。2. UT变换是UKF的灵魂先把它吃透2.1 sigma点到底是怎么选出来的UKF的全名里有个词叫“Unscented”直译过来就是“无味”早期的论文翻译也叫“不敏变换”或“无痕变换”。但别被名字唬住它的数学思想其实特别直白——用一个分布去近似另一个分布而不是用一个函数去近似另一个函数。假设当前状态的均值是 μ协方差是 P维度是 n。UT变换做的事就是在这 n 维空间中取 2n1 个点让这些点的均值和协方差恰好等于 μ 和 P。这个约束不是近似是精确成立的。Sigma点的构造方式如下第一个点就是均值本身χ₀ μ剩下 2n 个点沿每个主轴的 ± 方向偏移偏移量由协方差矩阵的平方根决定写成式子就是χᵢ μ (√((nλ)P))ᵢi 1, …, nχᵢ₊ₙ μ - (√((nλ)P))ᵢi 1, …, n这里面 λ α²(nκ) - nα 和 κ 是缩放参数。矩阵平方根的列向量就是协方差矩阵Cholesky分解的结果。我在实际写代码的时候一般用np.linalg.cholesky得到下三角矩阵 L那么第 i 列就是 L 的第 i 列乘以 √(nλ)。你可能会问为什么偏偏取 2n1 个点因为 n 维高斯分布的自由度就是 n取 n 个正交方向的两侧恰好能有一阶矩和二阶矩的完全表达能力。取少了协方差还原不出来取多了没有额外收益计算量反而白白增加。2.2 权重怎么分配有了点还要给每个点分配权重。权重的设计原则是加权还原的均值和协方差必须精确等于原始 μ 和 P。推导下来的结果长这样W₀⁽ᵐ⁾ λ / (nλ)W₀⁽ᶜ⁾ λ / (nλ) (1 - α² β)Wᵢ⁽ᵐ⁾ Wᵢ⁽ᶜ⁾ 1 / (2(nλ))i 1, …, 2n这里的上标 m 表示求均值用的权重c 表示求协方差用的权重。第一点的均值权重和协方差权重不同多出来的那一项 (1-α²β) 是专门用来补偿高阶信息的。β 在高斯分布下取 2 是最优的这个结论在文献里已经被证明了自己推导也能得出但先记住这个结论就行。权重加起来是不是等于 1是的这正是它能够精确还原均值的原因。在后续的预测和更新步骤中只要保持这套权重不变sigma点经过任意非线性函数传播后再加权就能给出变换后分布的一阶矩和二阶矩的近似。2.3 经过非线性函数之后怎么还原统计量现在让每个 sigma 点分别经过非线性函数 g(·)得到变换后的点集 yᵢ g(χᵢ)。问题来了yᵢ 的加权均值和加权协方差该怎么算直接用权重算ȳ Σ Wᵢ⁽ᵐ⁾ yᵢPᵧ Σ Wᵢ⁽ᶜ⁾ (yᵢ - ȳ)(yᵢ - ȳ)ᵀ这就是 UT 变换的全部核心。值得注意的是这个加权协方差的公式是纯样本统计量它没有对 g(·) 做任何近似。也就是说不管 g 是正余弦、指数、还是分段函数只要 sigma 点覆盖了分布的主要区域这个估计就是可靠的。很多人第一次看完这个过程会嘀咕这不就是蒙特卡洛吗其实两者有本质区别。蒙特卡洛需要成百上千个随机采样点靠大数定律收敛UT 只需要 2n1 个确定性的点靠“矩匹配”直接精确匹配前两阶矩。所以 UT 的计算量远小于粒子滤波但精度又明显高于一阶线性化。2.4 为什么UT能优于线性化用一个具体例子解释我每年带新人做滤波入门的时候都习惯用一个标量例子解释这个问题。假设 x ~ N(0, 1)非线性函数 y x²。用一阶线性化的做法是在 x0 处展开导数 2x 在 0 处等于 0结果 y 的估计均值是 0方差也是 0。可真实情况呢y x² 的均值应该是 1方差是 2。这是极端场景但说明问题——低阶展开丢掉了非线性项携带的大量信息。UT 的做法是取 3 个 sigma 点x 0、x √3、x -√3权重分别为 2/3、1/6、1/6。通过 yx² 映射后y 的加权均值是 (2/3)·0 (1/6)·3 (1/6)·3 1协方差也能算出正确值。n1 的情形下UT 对二次函数是精确的因为高斯分布的三阶以上矩为零而二次函数刚好只产生到二阶矩的变化。这个例子的意义在于UKF 不是比 EKF 多一点两点精度而是从根本上换了一种近似对象。EKF 近似的是非线性函数本身UKF 近似的是概率分布的传播前者在函数曲率大的地方必然失真后者只要 sigma 点的覆盖范围跟真实的分布扩散范围接近就会非常稳。这也是为什么 UKF 在相同计算量下精度普遍比 EKF 高一个档次的原因。3. UKF完整算法的每一步手把手拆开看3.1 先交代一个前提状态模型和观测模型UKF 的标准问题形式跟卡尔曼滤波是一样的只是 f 和 h 可以是非线性函数xₖ f(xₖ₋₁) wₖzₖ h(xₖ) vₖx 是状态z 是观测w 是过程噪声v 是观测噪声。两者都假设为零均值高斯白噪声协方差分别是 Q 和 R。第一步初始化。需要给出初始状态估计 x̂₀ 和初始协方差 P₀。P₀ 的取法很有讲究后面我会专门讲。现在先假设已经有了。3.2 预测步预测步做的事就是把上一时刻的后验分布通过状态方程 f 推到当前时刻。先基于上一时刻的 x̂ₖ₋₁ 和 Pₖ₋₁ 生成 sigma 点χₖ₋₁⁽⁰⁾ x̂ₖ₋₁χₖ₋₁⁽ⁱ⁾ x̂ₖ₋₁ (√((nλ)Pₖ₋₁))ᵢχₖ₋₁⁽ⁱ⁺ⁿ⁾ x̂ₖ₋₁ - (√((nλ)Pₖ₋₁))ᵢ然后每个点都通过状态方程传播χₖ⁽ⁱ⁾ f(χₖ₋₁⁽ⁱ⁾)加权合并得到预测均值x̂ₖ⁻ Σ Wᵢ⁽ᵐ⁾ χₖ⁽ⁱ⁾预测协方差则是 sigma 点离散程度加上过程噪声Pₖ⁻ Σ Wᵢ⁽ᶜ⁾ (χₖ⁽ⁱ⁾ - x̂ₖ⁻)(χₖ⁽ⁱ⁾ - x̂ₖ⁻)ᵀ Q注意如果你用的是加性噪声模型Q 是最后直接加上去的如果噪声是乘性的、或者跟状态耦合在一起就需要把噪声扩维到状态里用增广状态法。我个人的建议是能加性处理的场合就不要用增广法计算量差 2~3 倍精度提升有限代码复杂度却上去了。3.3 更新步进入更新步先把预测步得到的 sigma 点或者重新基于 x̂ₖ⁻ 和 Pₖ⁻ 生成一组新的 sigma 点通过观测函数映射到观测空间Zₖ⁽ⁱ⁾ h(χₖ⁽ⁱ⁾)然后计算观测预测均值ẑₖ⁻ Σ Wᵢ⁽ᵐ⁾ Zₖ⁽ⁱ⁾观测预测协方差也叫新息协方差Sₖ Σ Wᵢ⁽ᶜ⁾ (Zₖ⁽ⁱ⁾ - ẑₖ⁻)(Zₖ⁽ⁱ⁾ - ẑₖ⁻)ᵀ R状态与观测的互协方差Tₖ Σ Wᵢ⁽ᶜ⁾ (χₖ⁽ⁱ⁾ - x̂ₖ⁻)(Zₖ⁽ⁱ⁾ - ẑₖ⁻)ᵀ然后就能算卡尔曼增益了Kₖ Tₖ Sₖ⁻¹最后是标准的卡尔曼滤波更新公式x̂ₖ x̂ₖ⁻ Kₖ(zₖ - ẑₖ⁻)Pₖ Pₖ⁻ - Kₖ Sₖ Kₖᵀ整套流程走完一个滤波周期就结束了。对比标准卡尔曼滤波你会发现框架几乎一模一样都是预测、算增益、更新三步走。区别仅仅在于均值和协方差的传播不再通过矩阵乘法而是通过 sigma 点的非线性映射和加权统计来完成。这正是 UKF 工程上容易上手的原因——框架不变只是换了个“搬运”统计量的工具。3.4 参数选择α、β、κ到底该怎么调这三个参数是UKF里最让新手头疼的地方因为公式里它们直接决定了 sigma 点的散布程度和权重分配。我在项目里踩过坑这里统一说一下经验。α 控制 sigma 点在均值周围的散布范围一般取 1e-3 到 1 之间的值。α 越小sigma 点离均值越近对局部非线性的刻画越精细但同时数值稳定性越差——协方差矩阵分解后如果出现很小的特征值很容易产生数值误差。α 越接近 1sigma 点散布越宽对大范围分布捕获得更好但高阶误差也会变大。我通常先用 α1e-2 起步如果系统模型温和、非线性不强就调到 1e-1 到 1 之间如果状态维度高或者初值不确定谨慎一点用 1e-2。κ 是次级缩放参数早期文献里推荐取 3-nn为状态维度目的是让四阶矩匹配得更准。但在实际工程里取 κ0 就行了我试过 3-n 和 0 的差别绝大多数场景看不出差异。只有在高维状态比如 n 大于 10时3-n 可能变成负数注意别让 λ 为负到导致权重为负就行。β 前面提过高斯分布取 2。这个参数只影响协方差权重中第一点的修正项对均值的估计没有影响你要是拿不准直接写 2 就好。常用的一组合法参数配置是α1e-3κ0β2。这组配置在开源项目里出现频率最高我理解是因为它在平衡“局部精度”和“数值稳定”之间做得比较中庸。真想细调的话最好的办法是对着实际数据和定位误差做扫描没有一劳永逸的全局最优值。3.5 一个总被忽略的细节协方差分解的数值技巧每个滤波周期至少要做一次 P 矩阵的分解。最常见的做法是 Cholesky 分解它要求 P 必须是正定矩阵。但实际运行中由于数值累加误差、状态不可观等原因P 矩阵可能失去正定性Cholesky 直接崩溃报错“矩阵不是正定的”。我的处理经验有三层第一分解失败时不要慌先用np.linalg.eigh做一次特征值分解看最小特征值是否只是轻微的负值比如 -1e-15 这种量级如果是那问题不大把负特征值裁掉重构成半正定矩阵再分解第二在 P 的对角线上加一个很小的正数 εI这个操作叫 jittering可以让分解稳定通过代价是协方差稍微被“膨胀”了一点一般取 1e-9 到 1e-6 之间就够了第三如果长期出现分解失败就要回到模型层面检查了——大概率是哪个状态根本不可观或者 Q 给得太小让协方差压缩成了奇异的形状。这里多说一句用scipy.linalg.cholesky时可以传入lowerTrue明确返回下三角很多人在 MATLAB 转 Python 的时候在上下三角问题上栽过跟头。记住公式里用的是列向量也就是 L 的第 i 列别取成行。4. 一个能直接跑的Python实现与仿真验证4.1 场景设计雷达测距测角跟踪匀速运动目标光讲公式没用我们来做一个具体场景。假设有一个匀速直线运动的物体状态向量为 x [px, py, vx, vy]。传感器每秒测量若干次每次给出目标的距离 r 和方位角 θ。距离和方位角与状态的关系是典型的非线性观测r √(px² py²)θ atan2(py, px)这就是一个教科书级的UKF适用场景——状态方程是线性的观测方程是非线性的。选择这个场景的原因是它能让你把注意力完全集中在“观测非线性”的处理上不至于被复杂的运动模型干扰。现实项目里如果想移植到匀转弯模型或者带加速度模型只需要改 f 函数和状态增广部分就行。4.2 完整的UKF类代码下面是这个场景的完整实现我尽量写成通用形式你只需要替换 f 和 h 就能复用到别的项目里。import numpy as np from scipy.linalg import cholesky class UKF: def __init__(self, dim_x, dim_z, alpha1e-2, beta2.0, kappa0.0): self.dim_x dim_x self.dim_z dim_z self.alpha alpha self.beta beta self.kappa kappa self.lamb alpha**2 * (dim_x kappa) - dim_x # 计算权重 self.Wm np.full(2 * dim_x 1, 1.0 / (2 * (dim_x self.lamb))) self.Wc self.Wm.copy() self.Wm[0] self.lamb / (dim_x self.lamb) self.Wc[0] self.lamb / (dim_x self.lamb) (1 - alpha**2 beta) self.x np.zeros(dim_x) self.P np.eye(dim_x) self.Q np.eye(dim_x) * 0.01 self.R np.eye(dim_z) * 0.1 self._sigmas None # 预留缓存 def compute_sigma_points(self, x, P): n self.dim_x sigma np.zeros((2 * n 1, n)) sigma[0] x U cholesky((n self.lamb) * P, lowerTrue) for i in range(n): sigma[i 1] x U[:, i] sigma[i n 1] x - U[:, i] return sigma def predict(self, f): n self.dim_x sigmas self.compute_sigma_points(self.x, self.P) # 状态方程传播 sigmas_f np.array([f(s) for s in sigmas]) self.x np.sum(self.Wm[:, None] * sigmas_f, axis0) d sigmas_f - self.x self.P np.sum(self.Wc[:, None, None] * (d[:, :, None] d[:, None, :]), axis0) self.Q self._sigmas sigmas_f # 缓存给更新步用这里是线性预测也可直接传 def update(self, h, z): n self.dim_x # 这里用预测步的 sigma 点也可以重新生成结果差别很小 sigmas self._sigmas if sigmas is None: sigmas self.compute_sigma_points(self.x, self.P) # 映射到观测空间 Z np.array([h(s) for s in sigmas]) z_pred np.sum(self.Wm[:, None] * Z, axis0) # 新息协方差 dz Z - z_pred S np.sum(self.Wc[:, None, None] * (dz[:, :, None] dz[:, None, :]), axis0) self.R # 互协方差 dx sigmas - self.x T np.sum(self.Wc[:, None, None] * (dx[:, :, None] dz[:, None, :]), axis0) K T np.linalg.inv(S) innovation z - z_pred self.x self.x K innovation self.P self.P - K S K.T这个类在每次 predict 之后缓存了传播后的 sigma 点update 直接用。需要说明的是严格的标准 UKF 流程里更新步应该重新生成 sigma 点但在工程实现中很多开源库都直接复用预测步的 sigma 点差别很小。如果你想严格遵守理论把 update 里那段缓存逻辑去掉改为基于 self.x 和 self.P 重新计算 sigma 点即可。4.3 主循环与仿真结果下面给一个主循环的示例目标从原点出发以 [5, 3] 的速度运动传感器每 0.1 秒测一次距离和方位角距离噪声方差 4标准差 2 米角度噪声方差 0.0025标准差约 2.9 度。import matplotlib.pyplot as plt # 构造滤波器 ukf UKF(dim_x4, dim_z2, alpha1e-2, beta2.0, kappa0.0) ukf.x np.array([0.0, 0.0, 5.0, 3.0]) ukf.P np.diag([100.0, 100.0, 10.0, 10.0]) ukf.Q np.diag([0.1, 0.1, 0.05, 0.05]) * 0.5 ukf.R np.diag([4.0, 0.0025]) def f(state): px, py, vx, vy state dt 0.1 return np.array([px vx*dt, py vy*dt, vx, vy]) def h(state): px, py, _, _ state r np.sqrt(px**2 py**2) theta np.arctan2(py, px) return np.array([r, theta]) # 生成真实轨迹与带噪观测 dt 0.1 steps 200 truth [] measurements [] for k in range(steps): if k 0: state np.array([0.0, 0.0, 5.0, 3.0]) else: state f(state) truth.append(state[:2]) r np.sqrt(state[0]**2 state[1]**2) np.random.randn() * 2.0 theta np.arctan2(state[1], state[0]) np.random.randn() * 0.05 measurements.append([r, theta]) # 滤波主循环 estimates [] for z in measurements: ukf.predict(f) ukf.update(h, np.array(z)) estimates.append(ukf.x[:2].copy()) # 计算均方根误差这里只做示意实际调试请打印详细数据 estimates np.array(estimates) truth np.array(truth) rmse np.sqrt(np.mean((estimates - truth)**2, axis0)) print(RMSE x:, rmse[0], RMSE y:, rmse[1])我在实际运行这段代码时RMSE 的径向误差能控制在预估的 CRLB 附近位置误差不超过 2~3 米。你可以用 matplotlib 把真实轨迹和估计轨迹画在同一张图上直观看到滤后曲线比原始量测平滑得多且不存在 EKF 常见的初始阶段大幅度震荡问题即使我把初值故意设偏到 [20, -20, 1, 1]UKF 也能在十几步内拉回真实轨迹附近。这里要提醒你注意一点滤波器的初始协方差 P₀ 设多大直接决定了初期的收敛速度。P₀ 太小滤波器会“过度自信”对观测不敏感初期误差收敛慢P₀ 太大滤波器会把过大的不确定性带到非线性变换里sigma 点散布太广对强非线性观测可能产生较大偏差。一般的经验是P₀ 对角线按“最大可能初始误差的平方”来设置比如位置初始误差可能 50 米就设 2500速度误差可能 5 m/s就设 25。上面代码里的 P₀ 只是示意自己用的时候一定要按实际场景改。5. 实战中那些躲不过的坑与排查技巧5.1 滤波器直接发散误差爆炸怎么定位问题我见过很多朋友第一次跑 UKF 时前几步效果还不错从某一步开始误差骤然变大后面再也不回来了。遇到这种情况我建议你按以下顺序排查。先看新息序列。新息就是 zₖ - ẑₖ⁻也就是“实际观测减去预测观测”。正常运行的时候新息应该是一个零均值、方差等于 S 的噪声序列。我在项目里习惯把 (z - z_pred)ᵀ S⁻¹ (z - z_pred) 除以观测维度得到一个归一化的新息平方NIS。理论上NIS 应该在一个固定值附近波动大致服从卡方分布。如果 NIS 突然飙到几十甚至上百说明滤波器已经对观测不再信任K 阵估计出了问题。常见原因有三个。一是 Q 给得太小模型预测过于“自信”导致 sigma 点散布不够S 矩阵虚小增益 K 失真。二是 R 给得太小滤波器过度信任观测一旦出现一个野值就把状态拉飞。三是状态模型的 f 写错了比如漏了 dt 或者方向余弦符号弄反这种错误会让预测均值系统性偏移新息里会出现明显的非零均值模式。新的零均值假设被破坏滤波器的“正确但无法解释”的观测会被当成噪声拒绝。5.2 协方差矩阵非正定Cholesky 崩溃这是 UKF 实现里最常遇到的数值问题。解决办法我前面提了一些这里再展开。最经典的原因有三个第一浮点误差累积。滤波跑了几千步以后P 矩阵不再是严格对称的即使初始对称重复的矩阵乘法也会引入不对称误差。解决办法是每步强制对称化P (P P.T) * 0.5。这个操作看着笨却非常有效。第二观测不可观导致某方向方差被压到极小。比如目标做匀速直线运动如果你只有距离观测而没有方位观测那么垂直于视线方向的速度分量不可观P 矩阵在某个方向上的方差会趋近于数值下限最终变成负值。解决方法不是修数值而是反思观测模型是否足以支撑你估计的状态向量。第三权重出现负值。当状态维度很高或 λ 取值让它变成负数时Wc[0] 可能为负这在数学上没问题但在数值上会放大舍入误差。缓解方式就是前面说的用 α 和 κ 保证 λ 处于合适区间。5.3 调参到底有没有顺序很多人拿到 UKF 第一件事就是调 Q 和 R其实顺序反了。我建议的顺序是先固定 R它一般可以直接从传感器手册或者量测数据方差估出来然后调 P₀ 让它在合理范围内尽量大一点确认滤波器能收敛最后才去调 Q。Q 是最难调的因为它不是直接可测量的量它代表你对“模型有多不信任”的先验。Q 调太小的后果是状态估计飘逸、响应迟缓Q 调太大的后果是滤波结果毛糙、噪声大。一个非常实用的经验是Q 的对角线量级取“每步状态增量不确定性的平方”。比如位置每步可能因为目标机动偏差 0.1 米那么位置项的 Q 取 (0.1)² 0.01速度项相应取加速度噪声造成的速度变化量的平方。这个东西可以通过一段真实数据反复试验找到误差和光滑度的平衡点。5.4 量纲差异大的时候要小心求逆距离的量级可能是几十上百米角度的量级是零点几弧度两者在数值上差了 2~3 个数量级。虽然理论上 R 矩阵已经做了归一化但 S 矩阵的条件数仍然可能很糟糕np.linalg.inv在极端情况下给出的逆矩阵精度不足。我的处理方式是尽量不对矩阵做直接求逆而是用np.linalg.solve(S, T.T).T解线性方程组或者用 Cholesky 分解 S LLᵀ 后两次回代。不仅数值稳定速度还更快。如果你发现 S 的条件数依然奇大那就要考虑是不是观测单位不统一先把距离换算成千米或者把角度换算成度再进滤波器但换算后别忘了同步把 R 的值改掉。5.5 关于“增广状态”的取舍有些资料推荐在 UKF 里把噪声扩维到状态向量中也就是所谓的增广UKF。在加性高斯噪声的假设下这其实是没必要的。增广后的 sigma 点数量从 2n1 变成 2(nq)1计算量大幅上升精度却没有本质提升。我在实际项目中只在以下两种情况启用增广一是噪声与状态存在乘性耦合二是部分噪声项会周期性变化需要在线估计。其他情况下加性模型就够了。这也是很多现代开源库的默认选择。6. 一些经验之谈与进一步扩展方向UKF 这套方法我在不同的项目里用过很多次从目标跟踪到组合导航从位姿估计到在线参数辨识。跟 EKF 比起来我最深的体会是UKF 的“手感”好很多——不用求导、不用在代码里维护一堆雅可比矩阵模型一旦变动改 f 和 h 两个函数就行其余框架代码完全不动。这对项目迭代特别友好。调试方面也有一个感受UKF 的协方差传播相对 EKF 更“诚实”。EKF 因为线性化误差协方差的估计经常偏小导致滤波器对观测的信任长期偏低UKF 的 sigma 点精确捕捉非线性传播协方差估计更接近真实滤波输出的置信区间因此更可用。这在做决策层融合的时候非常重要一个不诚实的协方差可能让下游逻辑误判系统状态。如果接下来想继续深挖有几个方向我认为非常值得。一是平方根UKF它对 P 矩阵直接做 Cholesky 因子的递推更新数值稳定性更强适合高维状态和嵌入式平台二是交互式多模型与 UKF 的结合在机动目标跟踪里很有用三是信息形式的 UKF它在多传感器分布式融合时能避免反复求逆的代价。另外如果你用的是 Python不妨试试滤波库 FilterPy它封装了 UKF 的完整实现但是我还是建议你至少亲手写一遍把每一步对应到公式上之后再换库心里才有底。最后分享一个小技巧在你写完 UKF 以后先用一个“非线性程度很夸张”的场景去测试它——比如把测量方程改成 y sin(x) 或者 y exp(x)再看看滤波输出是不是仍然合理。这种压力测试能最快暴露你实现里的 bug。我当年第一次写完 UKF就是用 y x³ 这个函数直接试出来的问题——当初把更新步里的 Pzz 和 Pxz 算反了滤波结果完全对不上顺手一测就发现了。现在每写一个新版本我都会保留这组测试场景。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑