ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

自适应卡尔曼滤波AKF:Q/R失配、新息诊断与工程调参

自适应卡尔曼滤波AKF:Q/R失配、新息诊断与工程调参

做过状态估计的同行大概都有过这种体验:仿真里滤波器跑得干干净净,参数调到残差曲线贴着零线,心里美滋滋,结果一上了真实设备,跑两三个小时就开始慢慢偏,最后干脆不跟了。第一次遇到这种情况,我以为是传感器坏了,换了一个还是偏;后来以为是模型写错了,把运动学方程从头推了一遍,没毛病。真正的问题藏在一个平时不太显眼的地方——滤波器对噪声统计特性的假设,和真实世界对不上。而自适应卡尔曼滤波(Adaptive Kalman Filter,AKF),本质上就是一套让滤波器自己去修正这份假设的机制。它适合已经能跑通标准KF、但被时变噪声、未知量测精度或者机动目标折腾过的朋友;也适合刚学完卡尔曼公式、想知道"这堆协方差矩阵到底该怎么给"的新手。下面这些内容,是我在几个不同的估计任务里反复踩坑之后攒下来的,尽量把公式背后的判断讲清楚,而不是把教科书再抄一遍。

1. 标准卡尔曼滤波在真实设备上最先失守的位置

1.1 Q 和 R 从来不是手感旋钮,而是两个统计承诺

很多人入门时形成的直觉是:Q 调大一点滤波器跟得快,R 调大一点输出平滑。这个直觉不算错,但它把 Q 和 R 降格成了"调参旋钮",丢掉了它们真正的含义。Q 是过程噪声协方差,它声明的是"我的状态转移方程有多不可信";R 是量测噪声协方差,它声明的是"我的传感器读数有多不靠谱"。这两个矩阵不是自由参数,它们对应真实物理世界里存在的扰动与噪声,只是我们往往拿不到准确值。

一旦把它们当旋钮拧,就会出现一个隐蔽的后果:滤波器在你的测试数据上表现很好,但那是因为你把 Q 和 R 调成了"适配这段数据"的形状,而不是"适配这个系统"的形状。换一段工况、换一个温度区间、换一批器件,性能立刻塌。我自己最早做的一个匀速运动目标跟踪,Q 是在一段几乎无扰动的数据上定下来的,小到接近奇异。后来目标开始做转弯机动,滤波器完全反应不过来,滞后了差不多三四帧才跟上,那几帧里误差直接翻倍。

从数学上讲,卡尔曼增益 K = P⁻Hᵀ(HP⁻Hᵀ + R)⁻¹ 完全由 P⁻ 和 R 的相对大小决定。Q 大则 P⁻ 大,K 大,滤波器更信量测;R 大则分母大,K 小,滤波器更信预测。Q 与 R 的比值才真正决定稳态增益,绝对数值只影响收敛速度。理解了这一点,后面所有自适应方法做的事情,其实都是在动态地维持这个比值。

1.2 协方差失配会以什么形态外显

失配不会直接告诉你"我错了",它只会通过一些可观测的现象露头。把这些现象归好类,排查时能省一大半时间。

外显现象最可能的失配方向典型场景
输出抖动明显、噪声压不住R 设得过小,滤波器过度信任量测换用了精度更差的传感器但没有同步改 R
输出过于平滑、机动时严重滞后R 设得过大,或 Q 设得过小参数在静止工况下整定,上线后目标会机动
误差缓慢单调增长,最终完全偏离Q 与 R 严重失配导致 P 失去正定性,或模型结构性错误长时间运行、采样周期漂移
偶发跳变后能自己拉回来量测野值,R 未做鲁棒处理有遮挡、有电磁干扰的环境
一开始正常,运行十几分钟后开始偏噪声特性随时间/温度漂移长时间连续工作、器件发热

第三种情况要单独拎出来说,因为它最容易被误判成"参数没调好",然后你去反复改 Q,改了也没用。P 矩阵在递推中如果因为浮点累积或者减法形式不当而失去正定性,增益的计算就会失真,这时候再怎么调 Q 和 R 都是徒劳。这是数值问题,不是统计问题,得用不同的手段治。

第五种情况则是 AKF 真正的主场。传感器出厂标称的噪声方差通常是在实验室恒温条件下测的,而实际设备在连续工作时温升可能几十度,某些器件的噪声水平会跟着变。固定 R 在这种情况下必然失配,只是失配的速度快慢不同。

1.3 用新息序列给滤波器做一次体检

判断滤波器健不健康,最直接的工具是新息(innovation),也叫残差:ν_k = z_k − H x̂_k⁻,就是"实际量测减去预测的量测"。理论上的关键性质只有一条:如果模型和噪声假设都是对的,新息序列应该是零均值白噪声,其协方差为 S_k = H P_k⁻ Hᵀ + R。这一条性质是整个自适应理论的地基,因为所有基于新息的估计方法,都是反过来用样本统计量去倒推 R 或 Q。

实际检验时我一般看三个指标。第一个是新息的均值,理想情况接近零;如果长期偏移,说明存在未建模的系统性偏差,比如传感器零偏或者坐标对齐错误,这时候自适应也救不了,得先修模型。第二个是新息的自相关,如果相邻几拍的新息明显正相关,说明滤波器"吃得不够快",模型跟不上真实动态,通常对应 Q 偏小。第三个就是归一化新息平方(NIS):d_k = ν_kᵀ S_k⁻¹ ν_k,在 m 维量测下,理论上服从自由度为 m 的卡方分布,长期均值应当接近 m。

提示:NIS 的均值和 m 做对比时,一定要用足够长的窗口。单拍的 NIS 波动极大,看几十拍的滑动平均才有意义,我一般用 50 到 100 拍的窗口,采样率低的时候还要更长。

我把这套体检写在了一个小脚本里,每次换传感器或者换工况,先跑一遍看三条曲线,再决定要不要上自适应。这个习惯帮我省掉了很多"明明调好了却跑不准"的困惑,也避免了一上来就堆复杂算法的情况。

2. 自适应机制拆解:究竟在估谁的协方差

2.1 协方差匹配法的基本等式与它绕不开的前提

最朴素的思路是这样的:既然新息的样本协方差可以算出来,理论协方差又有解析式,那让两者相等,就能解出未知的 R。取一个长度为 N 的窗口,样本协方差 Ĉ_ν = (1/N) Σ ν_i ν_iᵀ,令 Ĉ_ν ≈ H P⁻ Hᵀ + R,于是得到 R 的估计:

R̂ = Ĉ_ν − H P⁻ Hᵀ

这个式子形式极简,但用起来有两个必须处理的问题。第一个是减法可能让 R̂ 失去正定性或者出现负对角元。样本协方差本身是半正定的,但减去 H P⁻ Hᵀ 之后不一定还是。工程上的做法是逐元素处理:只保留对角元,把负值钳到一个下限 r_min,非对角元一般直接置零。这个近似会损失一些精度,但在大多数传感器噪声近似独立的情况下完全够用,而且换来的是数值上的稳定。

第二个问题是窗口长度的权衡,这一点在实际调试里比第一个更折磨人。窗口太短,样本协方差估计的方差大,R̂ 会剧烈跳动,滤波器输出跟着抖;窗口太长,估计滞后,遇到噪声特性快速变化时根本追不上,自适应就变成了摆设。我的经验是:如果噪声变化的时间尺度在秒级,取 0.5 到 2 秒对应的采样点数比较合适;如果只是缓慢漂移(分钟级),窗口可以放到几百拍,同时配合下一节要讲的平滑系数。这个参数没有通用最优值,得根据你的具体工况用实测数据试。

还有一个经常被忽略的前提:协方差匹配法假设状态估计误差与新息在统计上独立,且新息是各态历经的。窗口内的数据如果高度相关(比如低采样率下状态几乎不变),样本协方差就会低估真实值。这种情况下要么加长窗口,要么改用带遗忘因子的递推形式。

2.2 Sage-Husa 递推估计器:好用的一半和危险的一半

Sage-Husa 估计器把窗口平均改成了指数加权递推,形式更优雅,也省内存。引入遗忘因子 b(通常取 0.95 到 0.99),权重序列为 d_k = (1 − b)/(1 − b^(k+1)),随着 k 增大,d_k 逐渐趋近于 1 − b。R 的递推式为:

R̂_k = (1 − d_k) R̂_(k-1) + d_k (ν_k ν_kᵀ − H P_k⁻ Hᵀ)

这个结构等价于一个一阶低通滤波器作用在瞬时估计上,d_k 就是等效的平滑系数。b 越接近 1,平滑越重,响应越慢;b 越小,跟踪越快,但噪声越大。我一般先按 b = 0.98 起步,观察 R̂ 的曲线,如果波动太大就往上调,如果跟不上变化就往下调。

真正要小心的是 Q 的估计算法。Sage-Husa 也能给出 Q 的递推估计,形式里包含 K_k ν_k ν_kᵀ K_kᵀ 加上 P_k 再减去 F P_(k-1) Fᵀ 这类项。问题在于,这个式子里出现了 P 在两个时刻的差,而 P 本身又依赖于 Q̂,形成了一种耦合。实际跑起来,一旦某个时刻 Q̂ 出现了负特征值,下一拍的 P 就会跟着出问题,误差迅速累积,几拍之内滤波器就彻底废了。我在这上面栽过不止一次。

比较稳妥的工程策略是只自适应 R,Q 保持固定但设得偏保守。理由很实在:R 对应的是可以直接测量的量(传感器的输出噪声),估计结果的物理意义清晰,出问题也容易发现;Q 对应的是模型不确定性,本身就很难有客观的参照,估计出来对不对你也不知道。而且 Q 设得保守(略大)的代价只是跟踪稍快、噪声稍大,属于可接受的性能损失;Q 估计错误则是灾难性的。有些文献会给出 Q 估计的改进形式(比如加正定化的投影步骤),但除非你的场景确实需要,我不建议一上来就用。

2.3 渐消因子与强跟踪:不动 Q 也能拿到鲁棒性

如果不想估计 Q,又想在对机动目标时保持响应速度,另一条路是渐消记忆滤波,也叫强跟踪滤波。核心做法是在预测步给协方差乘上一个大于等于 1 的渐消因子 λ_k:

P_k⁻ = λ_k · F P_(k-1) Fᵀ + Q

λ_k = 1 时退化为标准 KF。当目标发生机动、模型跟不上时,新息会变大,通过正交性原理可以解析地求出所需的 λ_k,让新息序列近似保持正交。直观理解就是:检测到预测不准了,就人为放大预测协方差,逼着滤波器多信量测,等机动结束 λ 又回落到 1。

这个方法对我这种"不想碰 Q"的人特别友好,因为 λ 是一个标量,物理意义明确,调试时直接看它的时间曲线就能知道滤波器什么时候在"紧张"。它的局限也很明显:λ 是一个全局缩放,如果状态向量里有些分量在机动、有些分量很稳定,一刀切的放大就会让稳定的那部分也跟着抖。多分量场景下需要给每个分量配独立的渐消因子,复杂度就上去了。

还有一种更轻量的做法叫自适应衰减因子,直接对 P 做指数衰减,连正交性推导都省了,代价是 λ 的取值更依赖经验。我一般在快速原型阶段用这个,验证思路可行之后再换成解析求 λ 的形式。

2.4 多模型并行:把自适应提到结构层

前面几种方法都是在同一个滤波器内部调参数,还有一类思路是干脆跑多个滤波器,每个用不同的 Q/R 组合,最后按各模型的似然度加权融合,这就是交互多模型(IMM)。它可以说是最"彻底"的自适应:不再纠结参数怎么变,而是让一组参数同时存在,由数据自己投票。

IMM 的每一拍要做四件事:模型交互(按转移概率把各模型的状态和协方差混合)、各自预测、各自更新并计算似然、更新模型概率并输出加权结果。听起来步骤多,但每一步都是标准 KF 操作,实现上并不复杂,主要成本是计算量按模型数线性增长。

实际用下来的感受是:IMM 在目标运动模式明确可枚举的场景(比如"匀速"和"转弯"两种模式切换)效果拔群,模型概率的变化曲线非常有解释性,能直接看出目标什么时候开始机动。但如果运动模式是连续渐变的,硬切成几个离散模型就会有量化误差,模型数加多了计算量又吃不消。这时候反而是渐消因子那种连续调节更合适。选型上没有绝对优劣,看你的场景是"跳变"还是"渐变"。

3. 一份能跑起来的新息自适应KF实现

3.1 状态与量测的建模落地

空谈公式容易飘,这里给一个可以直接运行的实现。以二维平面上的匀加速运动目标为例,状态取 [x, y, vx, vy, ax, ay],量测只有位置。采样周期 dt 是唯一需要调的结构参数,写代码时务必让它和实际数据的时间戳严格对应,我见过因为把毫秒当成秒导致整个协方差尺度错了三个数量级的案例。

状态转移矩阵 F 按匀加速模型展开,位置 = 位置 + 速度·dt + 0.5·加速度·dt²,速度 = 速度 + 加速度·dt,加速度保持不变。量测矩阵 H 就是取出位置的两个分量。Q 用连续白噪声加速度模型离散化得到,这是标准做法,比自己瞎填一个对角阵靠谱得多。

import numpy as np def build_model(dt, q_scale): F = np.array([ [1, 0, dt, 0, 0.5*dt*dt, 0], [0, 1, 0, dt, 0, 0.5*dt*dt], [0, 0, 1, 0, dt, 0], [0, 0, 0, 1, 0, dt], [0, 0, 0, 0, 1, 0], [0, 0, 0, 0, 0, 1], ], dtype=float) H = np.array([ [1, 0, 0, 0, 0, 0], [0, 1, 0, 0, 0, 0], ], dtype=float) # 连续白噪声加速度模型离散化 q11, q12, q13 = dt**5/20, dt**4/8, dt**3/6 q22, q23 = dt**3/3, dt**2/2 q33 = dt Q1d = q_scale * np.array([ [q11, q12, q13], [q12, q22, q23], [q13, q23, q33], ]) idx = [0, 2, 4] Q = np.zeros((6, 6)) for a in range(3): for b in range(3): Q[idx[a], idx[b]] = Q1d[a, b] Q[idx[a]+1, idx[b]+1] = Q1d[a, b] return F, H, Q

q_scale 这个标量就是过程噪声强度的总开关,物理含义是"目标加速度的功率谱密度"。它的量纲是 (m/s²)²·s,实际整定时我会先按目标可能的加速度幅值估一个量级,比如机动时加速度可能到 5 m/s²,取 5² = 25 作为起点,再根据 NIS 的判读微调。

3.2 滑窗估计 R 的最小可用写法

在标准 KF 的基础上,增加一个新息历史队列和一个 R 更新步骤。更新 R 的时机很关键:用更新前的 P⁻ 和更新后的新息,顺序错了估计出来的 R 会系统性偏小,这个细节很多实现里都写反了。

class AdaptiveKF: def __init__(self, F, H, Q, R0, x0, P0, win=30, alpha=0.05): self.F, self.H, self.Q = F, H, Q self.R = R0.copy() self.R0 = R0.copy() self.x = x0.copy() self.P = P0.copy() self.win = win self.alpha = alpha self.nu_buf = [] self.r_min = np.diag(R0) * 0.05 self.r_max = np.diag(R0) * 20.0 def predict(self): self.x = self.F @ self.x self.P = self.F @ self.P @ self.F.T + self.Q return self.P def update(self, z, adapt=True): H, R = self.H, self.R P_pred = self.P.copy() nu = z - H @ self.x S = H @ P_pred @ H.T + R K = P_pred @ H.T @ np.linalg.inv(S) self.x = self.x + K @ nu # Joseph 形式,保证正定与对称 I_KH = np.eye(len(self.x)) - K @ H self.P = I_KH @ P_pred @ I_KH.T + K @ R @ K.T self.P = 0.5 * (self.P + self.P.T) if adapt: self.nu_buf.append(nu.copy()) if len(self.nu_buf) > self.win: self.nu_buf.pop(0) if len(self.nu_buf) == self.win: self._adapt_R(P_pred) return self.x, self.P def _adapt_R(self, P_pred): N = len(self.nu_buf) C = np.zeros((self.H.shape[0], self.H.shape[0])) for nu in self.nu_buf: C += np.outer(nu, nu) C /= N R_hat = C - self.H @ P_pred @ self.H.T # 只取对角,逐个钳位 r_new = np.clip(np.diag(R_hat), self.r_min, self.r_max) R_target = np.diag(r_new) # 一阶平滑,避免 R 突变引起增益跳变 self.R = (1 - self.alpha) * self.R + self.alpha * R_target

这里的 r_min 和 r_max 是两道保险,分别取初值的 5% 和 20 倍。上下界的意义在于防止某一段异常数据把 R 推到极端值,之后再也拉不回来。上下界的倍数可以根据传感器在你系统里的可信程度调整,如果量测偶尔会有严重野值,下界要设得更保守一些。

3.3 自适应增益的钳位、平滑与回退

R̂ 的估计必然有噪声,如果每拍都把 R 换成瞬时估计,滤波器输出会跟着一起抖,观感极差。alpha 这个平滑系数就是干这个的,取 0.02 到 0.1 之间比较合适,等价于给 R 加了一个时间常数。alpha 太小响应慢,太大抖动传得进来,我的习惯是从 0.05 起步。

除了平滑,还有一层防守机制值得加上:异常检测与回退。给 NIS 设一个阈值(自由度 2 时,99% 分位数大约是 9.2),连续若干拍超阈值,说明当前不是噪声特性变了,而是出现了模型没覆盖的情况,比如量测野值或者目标做了极端机动。这时候与其让自适应逻辑继续猜,不如临时把 R 放大,降低这次量测的权重,等 NIS 回落之后再恢复正常自适应。这个回退逻辑写起来只有几行,但在野值频发的真实环境里能显著减少恶劣跳变。

注意:回退状态一定要有超时退出机制。我一开始写的版本只进不出,某次连续野值之后滤波器一直卡在回退状态,表现是长时间不跟量测,排查了半天才发现是状态机没有复位。

3.4 数值层面的四个硬性保护

自适应引入了额外的矩阵运算,数值风险比标准 KF 高,下面这四条我现在的实现里是标配。

第一条是协方差更新用 Joseph 形式而不是简单的 (I − KH)P⁻,前者在增益接近最优时对舍入误差更宽容,能显著延缓正定性丢失。

第二条是每拍强制对称化,P = (P + Pᵀ)/2。理论上 P 是对称的,浮点运算会让它慢慢失去对称性,而不对称的 P 在求逆时会产生莫名其妙的结果。

第三条是求逆用解线性方程组而不是显式求逆。S 是 2×2 的时候区别不大,但量测维度高的时候,np.linalg.solve 比 inv 数值更稳,速度也更快。

第四条是定期检查特征值。在调试阶段我会每隔若干拍算一次 P 的最小特征值,一旦出现负值就立刻报警。上线之后这个检查可以降频,但不要完全去掉,它是发现"滤波器正在悄悄腐烂"的唯一可靠手段。

4. 调不通时的排查链路:从现象反推根因

4.1 五种发散形态的对照表

自适应滤波器出问题的表现比标准 KF 更多样,因为多了一层估计逻辑。我按现象整理了一张对照表,实际排查时先对号入座,能快速缩小范围。

现象高概率根因首选验证手段
输出全程抖动,R̂ 曲线剧烈震荡滑窗太短或 alpha 太大把窗口加长一倍,看抖动是否减弱
输出滞后,R̂ 一路涨到上界R 上界设得太低,或 Q 偏小导致新息持续偏大打印 R̂ 与上界的关系,临时放开上界观察
前几十拍正常,之后突然崩掉P 失去正定性,多半是减法形式或未对称化检查 P 的最小特征值序列
R̂ 贴近下界不动新息被人为压制,通常是 Q 过大使 P⁻ 膨胀打印 H P⁻ Hᵀ 与样本协方差的比值
偶发大跳变后缓慢恢复量测野值进入滑窗,污染了 R 估计检查跳变时刻的新息幅值与 NIS

第三行那种"突然崩掉"是最难查的,因为它发生得很突然,日志里前面全是正常的。我第一次遇到时反复怀疑是数据问题,最后是打印 P 的特征值才定位到。教训是:调试阶段一定要把 P 的数值健康度当成一等公民来监控,不要只看输出曲线好不好看。

第五行则提醒我们,滑窗估计对野值天然不鲁棒。一个幅值十倍于正常水平的新息,进入窗口后会把样本协方差拉高一大截,R̂ 跟着变大,滤波器变得不信任量测,接下来几拍就跟不上了。解决办法是在入窗前做限幅,或者干脆用中位数代替均值。

4.2 NIS 曲线的判读方法

NIS 是我最常用的诊断信号,因为它把"滤波器自我感觉"和"现实打脸程度"结合在了一起。判读时不要只看均值,要看形状。

均值长期显著大于量测维数 m,说明新息比滤波器预期的要大。这时候要分两种情况:如果新息序列本身是白的(自相关接近零),问题出在 R 偏小,滤波器对量测的信任过度;如果新息有明显正自相关,问题在模型,Q 偏小或者运动模型选错了,滤波器反应不过来。这个区分很关键,因为两种情况的修法完全相反。

均值长期小于 m,说明滤波器过度保守,K 偏小,把真实的变化当成了噪声。常见于 R 设得过大,或者 Q 设得过小导致 P⁻ 收缩太快。表现出来就是响应迟钝、机动时明显落后。

还有一种形态是均值正常但方差很大,说明大部分时候健康,偶尔出问题。这种最容易被忽略,因为看平均值一切正常。我会额外看 NIS 的超阈值比例,正常情况下 5% 左右,如果超过 15% 就说明有问题需要处理。

4.3 参数整定的先后顺序不能反

调 AKF 有个容易犯的错误:一上来就开自适应,然后所有参数一起调。这样做的后果是永远搞不清是哪个参数在起作用。我现在的顺序是固定的四步。

第一步,先把自适应关掉,用固定 R 把标准 KF 调到一个"基本能用"的状态。判据是 NIS 均值落在 m 附近的一个合理区间内,输出没有明显滞后也没有明显抖动。这一步不做完,后面的自适应毫无意义,因为它是在一个错误的基础上做修正。

第二步,只开 R 自适应,Q 保持第一步定下来的值不动。观察 R̂ 的曲线,如果它在合理范围内平稳变化,说明机制生效了;如果它一直撞上下界,说明初值定得离谱,回去改 R0。这一步要跑足够长的时间,最好覆盖你关心的全部工况。

第三步,如果发现某些工况下响应还是不够快,再考虑引入渐消因子。这时候 Q 依然不动,只是额外加了一个机动检测式的放大。渐消因子的下限设 1,上限一般不超过 5,超过 5 基本等于放弃模型了。

第四步,只有在前面都稳定之后,才考虑是否需要动 Q。而且我建议动的方式是整体缩放 q_scale,而不是估计完整的 Q 矩阵。

这个顺序的核心逻辑是:每次只让一个变量变化,保证你能把观察到的现象归因到具体的改动上。听着很基础,但在实际项目里,赶进度的时候最容易破坏这条纪律,然后花三倍时间返工。

4.4 一次二阶系统的完整复盘

说个具体的。之前做一个二维位置跟踪的小系统,量测是两个方向的位置,采样 20 Hz。用固定 R 的标准 KF 跑,静止段非常好,残差贴着零线;但目标一旦开始做圆周运动,滞后立刻显现,NIS 均值从 2 涨到 8 左右,超阈值比例超过 30%。

按前面的流程,我先看新息自相关,发现明显正相关,说明不是 R 的问题而是模型跟不上。于是我给预测协方差加了渐消因子,NIS 立刻回落到 3 左右,滞后明显改善。但新的问题来了:渐消因子在目标匀速段也偶尔被触发,导致输出出现不该有的抖动。

查下来原因是 λ 的触发判据只用了单拍新息幅值,噪声大的时候会误触发。改成连续三拍超阈值才启用,并且 λ 做成随时间衰减而不是立刻归 1,抖动就消失了。这套组合最终稳定运行,NIS 均值保持在 2.3 左右,超阈值比例 6%。

这个案例的启发是:自适应机制本身也需要平滑和迟滞,它不是一个开关,而是一个需要仔细设计的控制回路。很多教程把这部分一笔带过,但实际工程里,正是这些"元参数"决定了滤波器能不能长期稳定。

5. 该不该上 AKF:代价、边界与替代方案

5.1 自适应带来的三个隐性成本

第一个成本是可观测性要求。R 的估计依赖于新息有足够的统计变化,如果系统长时间处于准静态、量测几乎不变,样本协方差会趋近于零,R̂ 被压到下界,然后滤波器就变得过度自信。这时候一旦出现真实机动,反应会非常糟糕。所以自适应机制必须配一个"激励检测",发现长时间没有足够变化就暂停 R 更新,保持当前值。

第二个成本是调试复杂度的跃升。固定参数的 KF 出问题,原因基本在 Q、R、模型三者之间;自适应版本出问题,还要多考虑窗口长度、平滑系数、上下界、回退逻辑这几个维度。参数空间一下从三维变成七维,没有系统的排查方法很容易陷进去。

第三个成本是可解释性下降。固定参数的滤波器,性能曲线的每一次变化都能对应到具体的物理事件;自适应版本的性能变化可能来自参数本身的自调整,追责时不好归因。这在需要做故障分析的场合是个实实在在的负担,有些团队干脆在正式产品里保留一个"自适应开关",出问题时能快速关掉对比。

5.2 和 EKF、UKF、IMM 的适用边界

这几个方法经常被混在一起讨论,但解决的问题其实不同。EKF 和 UKF 处理的是非线性,它们的自适应变体(比如自适应 EKF)是非线性处理加噪声自适应的叠加,两者正交,可以组合。如果你的系统本身是线性的,上 EKF 没有意义,只会引入线性化误差。

IMM 处理的是模式切换,代价是计算量。它和基于新息的自适应不是互斥的,实际系统里我见过 IMM 每个子模型内部再各自做 R 自适应的组合,效果确实好,但调参工作量也翻倍。

渐消因子处理的是机动响应,它不估计任何噪声参数,只是动态放大预测协方差。如果你的问题主要是"机动时跟不上",而不涉及噪声特性漂移,渐消因子的性价比远高于完整的 AKF,实现只要几行。

选型时我的一般判断是:噪声特性明确且稳定,就用固定 KF;噪声特性会漂移,用 R 自适应;目标会机动,加渐消因子;运动模式可枚举,用 IMM。四种手段可以叠加,但每叠加一层,都要问自己"这一层真的解决了我观察到的问题吗"。

5.3 嵌入式与定点实现里容易翻车的地方

在资源受限的平台上跑 AKF,有几个坑必须提前想到。滑窗估计需要存 N 个新息向量,N 取 50、量测维度 3,就是 150 个浮点数,加上矩阵运算的临时变量,RAM 占用不算小。如果平台紧张,改成指数加权的递推形式,只存一个 R̂ 和一个计数器,内存立刻降下来。

定点实现下,协方差矩阵的动态范围往往极大,位置分量可能是 10⁴ 量级,速度分量是 10² 量级,直接用统一的比例因子会丢精度。我的做法是对状态做归一化,把各个分量缩放到相近的量级再进滤波器,输出时再还原。这一步在多分量系统里几乎是必须的。

还有一点是除零保护。S 矩阵求逆时,如果某一拍量测恰好退化(比如两个传感器读数完全一致),S 可能接近奇异,定点下的截断误差会直接导致除零。加一个基于行列式的检查,低于阈值就跳过这一拍的量测更新,只做预测。这个保护我现在的每个实现里都有,加上之后线上再没出现过因数值问题导致的崩溃。

最后分享一个我个人的习惯:每次改动自适应逻辑,我都会准备三组数据回放——一段静止、一段匀速、一段剧烈机动,跑完对比 NIS 曲线和误差曲线。三组都过了才算改完,只看一组很容易顾此失彼。这个方法笨,但确实避开了不少"改好了 A 弄坏了 B"的情况。

返回列表