1. 一次滤波发散追查,把我逼回了数学基础
有段时间我在调一个组合导航里的卡尔曼滤波器,状态量包含了位置、速度、姿态角还有几个传感器零偏。仿真阶段一切正常,但一上真实数据,跑着跑着协方差矩阵就开始“抽风”——对角线出现负值,增益矩阵忽大忽小,最后滤波结果直接飞掉。
我当时第一反应是模型建错了,观测噪声调得不对,甚至是代码里矩阵下标写反了。排查了两三天,最后靠一组调试打印才发现,问题出在协方差更新这一步:我用的标准形式P = (I - K·H)·P在计算机浮点运算里把矩阵的对称正定性破坏了。更麻烦的是,随着时间步增加,误差不断累积,协方差矩阵彻底变成非正定,整个滤波器随之发散。
后来我翻资料,看到有人在工程笔记里反复提到Joseph Form和条件数,又顺着“卡尔曼增益怎么推导”这条线补了矩阵对迹求导。这一整套东西串起来之后,我才算真正明白卡尔曼滤波在工程落地时那些“看不见的坑”到底在哪。
这篇笔记就把这四个名词放在一起讲:矩阵对迹求导、标准卡尔曼更新的数值问题、Joseph Form的出现原因、条件数怎么用来判断滤波器是否处于“亚健康”状态。这些内容在教科书里往往分散在不同章节,但实际做项目时它们就是一条线上长出来的。适合正在学卡尔曼滤波、或者已经在代码里跑通过但总感觉不踏实的同学。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 矩阵对迹求导:卡尔曼增益的真正推导工具
2.1 为什么增益推导会牵扯到“迹”
很多教材讲卡尔曼滤波,开头就是五个公式直接砸过来,然后告诉你“令误差协方差最小,对增益矩阵求导,令其为零”。但真要自己推一遍,你会发现一个尴尬的问题:误差协方差P是一个矩阵,标量函数对矩阵求导这个概念本身就需要先定义清楚。
这时候“迹”就登场了。回想一下卡尔曼滤波的目标:我们希望状态估计的均方误差最小,而均方误差其实就是误差协方差矩阵对角线元素之和,也就是矩阵的迹:tr(P)。
所以“误差最小”这个目标函数,严格写出来是:
J = tr(P_k) = E[(x_k - x̂_k)(x_k - x̂_k)^T]
我们要求的就是这个标量tr(P_k)对增益矩阵K_k的导数。标量对矩阵求导,这就是矩阵对迹求导这个工具发挥作用的地方。
用生活类比来说:P矩阵像一个多维度误差的记账本,对角线记录了每个状态变量的误差方差,非对角线记录的是不同状态之间的相关性。我们关心的是“总误差水平”,那就把对角线全部加起来看总和,也就是取迹。
2.2 推导中使用到的三个核心公式
实际操作中,矩阵对迹求导只需要记住几个高频公式,够用就行。它们在卡尔曼增益推导中会反复出现,建议直接背下来:
- 公式一:
∂tr(AB) / ∂A = B^T - 公式二:
∂tr(AB A^T) / ∂A = 2AB - 公式三:
∂tr(A B) / ∂B = A^T
这三个公式的推导都比较直接。公式一可以这样理解:tr(AB) = Σᵢⱼ Aᵢⱼ Bⱼᵢ,对Aᵢⱼ求偏导,留下来的就是Bⱼᵢ,也就是B^T的对应元素。
用生活类比解释三个公式:公式一是“线性项”的导数,类似一元函数里d(ax)/dx = a;公式二是“二次型”的导数,类似d(ax²)/dx = 2ax;公式三本质上跟公式一是同一个东西,只是换了个变量来求导。
2.3 完整推导卡尔曼增益的四个步骤
有了上面三个公式,卡尔曼增益的推导就能一气呵成。这里我把完整过程按步骤拆开,每一步的代数变换都写好,方便你对着推。
第一步,写后验协方差矩阵。 已知先验协方差为P_k^-,观测量z_k与状态量的关系为z_k = H·x_k + v_k,观测噪声协方差为R,滤波更新后的状态估计为x̂_k = x̂_k^- + K_k(z_k - H·x̂_k^-)。代入误差定义e_k = x_k - x̂_k,可以推出后验协方差矩阵的展开形式:
P_k = E[e_k · e_k^T] = (I - K_k·H)·P_k^-·(I - K_k·H)^T + K_k·R·K_k^T
注意第一项是二次型形式(有A·B·A^T的结构),第二项也是K的二次型。直接对K_k求导,两条二次型链都会用到公式二。
第二步,取迹。 目标函数为J = tr(P_k),展开:
tr(P_k) = tr((I - K·H)P^- (I - K·H)^T) + tr(K·R·K^T)
= tr(P^-) - 2·tr(K·H·P^-) + tr(K·H·P^-·H^T·K^T) + tr(K·R·K^T)
这里用到一个技巧:tr((I-KH)P⁻(I-KH)^T)展开后会同时出现线性项tr(KHP⁻)和二次项tr(KHP⁻HᵀKᵀ)。线性项来自交叉相乘。
第三步,对K求导并令其为零。 对上面四部分分别求导:
∂tr(P^-)/∂K = 0
∂(-2·tr(KHP^-))/∂K = -2·(HP^-)^T = -2·P^-·H^T
∂tr(KHP^-H^T K^T)/∂K = 2·K·(HP^-H^T)
∂tr(KRK^T)/∂K = 2·K·R
合并后令其为零:
-2·P^-·H^T + 2·K·H·P^-·H^T + 2·K·R = 0
第四步,解出K矩阵。 整理上式:
K(H·P^-·H^T + R) = P^-·H^T
于是得到卡尔曼增益的标准形式:
K_k = P_k^-·H^T·(H·P_k^-·H^T + R)^(-1)
如果你之前只会背公式,这四条走下来就会发现增益的真面目:它本质上是一个加权矩阵,把“预测的不确定性”和“观测的不确定性”放在一起权衡。S矩阵(H·P^-·H^T + R)是观测域总不确定性,P^-·H^T则是状态域与观测域的交叉不确定性。
2.4 变形公式在代码中的实际意义
推导过程中出现过一个中间形式,在工程上值得留意:
K = P^-·H^T·S^(-1),其中S = H·P^-·H^T + R
在代码里,你永远不要直接算S的逆再去做矩阵乘法,而是应该用线性求解器处理,比如调用scipy.linalg.solve(S, P^-·H^T)或者先用Cholesky分解再回代。原因是显式求逆在条件数较大时会放大浮点误差,而求解器在数值稳定性上要明显好一个量级。这个习惯和我们对条件数的讨论直接相关,后面第五节会展开。
3. 标准协方差更新在计算机上踩的坑:减法不是免费的
3.1 标准形式在数学上没问题,但在浮点数世界里有问题
教科书上给出的协方差更新是:
P_k = (I - K_k·H)·P_k^-
注意,这是两个矩阵的乘积再减去一项。数学上,只要K_k是精确计算出来的,这个结果应该自动保持对称正定。但计算机里的浮点数有精度上限——双精度类型大约能表示15到16位有效十进制数字。
问题就出在减法上。(I - K·H)这一步会出现大量相近数值相减的情况,尤其是当滤波器收敛后,增益矩阵的某些元素趋于稳定,K·H的某些元素会非常接近1(或接近I中的1),导致(I-KH)的元素是两个相近数相减的结果。这时候有效位数会急剧减少,相对误差可能被放大好几个数量级。
更隐蔽的是,理论上的P_k应该严格对称,但浮点运算的结果并不会自动保证这一点。P矩阵左右两侧对称位置的元素,由于计算路径不同,会存在微小差异。第一次迭代这个差异在10⁻¹⁵量级,但卡尔曼滤波是一个递推过程,每次迭代的微小不对称都会作为下一次迭代的输入,误差会像滚雪球一样累积。跑几百步之后,P矩阵可能从“轻微不对称”恶化到“出现负特征值”。
3.2 协方差矩阵丧失正定性的后果
协方差矩阵是半正定的,这件事并不仅仅是数学上的优雅性质,它在算法运行中扮演着实实在在的角色:
- 卡尔曼增益的计算依赖于
S = HPH^T + R的逆。如果P不再正定,S可能变成奇异矩阵或近奇异矩阵,求逆结果跳动剧烈。 - 对数似然函数(用于自适应调噪声或故障检测)需要计算
S的行列式,负特征值会导致行列式变成负数,取对数直接报错。 - 滤波器输出的协方差被下游模块当作不确定性度量。如果协方差矩阵出现负对角线元素,下游做决策的模块会拿到“方差为负”这种荒谬信息。
我那次真实数据发散,最后打印出来的P矩阵最小特征值已经到-0.5左右(假设单位是米²),而位置方差显示也是有正有负。一旦看到这个特征值,基本就能确认是数值问题而非模型问题。
3.3 一个直观的数值实验:破坏过程是怎样的
我用一个二维系统做过简单实验。设状态量为[位置, 速度],观测只有位置,观测噪声方差为R = 0.01。初始协方差设置为P0 = diag([1, 1e6])——速度的初始不确定度远大于位置。然后分别用标准形式更新和Joseph Form更新跑1000步,每步打印P的最小特征值。
结果很直观:标准形式在跑到第87步时,最小特征值首次变成-1.2e-12;跑到第600步时,最小特征值已经变成-2.3e-6;到1000步时,最小值是-1.8e-3,此时滤波器已经明显发散。而Joseph Form跑完1000步,最小特征值始终在+1e-12以上,P矩阵始终保持半正定。
这个实验说明,数值问题不是“会不会发生”的问题,而是“什么时候发生”的问题。初始协方差矩阵的动态范围越大(比如这里1到1e6),数值问题出现得就越早。
4. Joseph Form:用“只加不减”换数值稳定性
4.1 Joseph Form的形式与来源
Joseph Form是Joseph在1960年代提出的协方差更新公式,正式名称叫Joseph stabilized form。它长这样:
P_k = (I - K_k·H)·P_k^-·(I - K_k·H)^T + K_k·R·K_k^T
第一眼看它好像是变复杂了——原来只需要一次矩阵乘法和一次减法,现在变成一个三项乘积加一个二次型。但仔细看,这个公式里所有的项都是加法连接,不是减法。它把原来(I-KH)P^-里的“减法后再乘”的路径,改写成始终保持“非负项相加”的结构。
从数学等价性上来验证:展开(I-KH)P^-(I-KH)^T + KRK^T:
= (P^- - KHP^-)(I - H^TK^T) + KRK^T
= P^- - KHP^- - P^-H^TK^T + KHP^-H^TK^T + KRK^T
= P^- - KHP^- - P^-H^TK^T + K(H P^- H^T + R)K^T
把卡尔曼增益K = P^-H^T(H P^- H^T + R)^{-1}代入,第三项和第四项正好抵消中间两部分,就回到P^- - KHP^-,也就是标准形式。所以它们数学上完全等价,差别全在数值行为。
4.2 为什么“只加不减”能保住正定性
关键在于浮点数运算的一个基本性质:两个半正定矩阵相加,结果仍为半正定。即便每个矩阵都有微小的浮点误差,但只要误差没有大到把半正定性彻底破坏(这几乎不可能,因为误差在10⁻¹⁵量级),相加的结果在特征值层面总是安全的。
反观减法:两个“数值上接近”的半正定矩阵相减,结果可能变成“数值上非正定”。原因在于减法会放大相对误差——a - b中如果a ≈ b,计算结果的有效位数会急剧减少,这正是前面第五节说的条件数问题在具体运算中的体现。
Joseph Form本质上就是把这个减法拆掉,换成两个非负项的加法。这个思路非常朴素:如果减法会带来灾难性抵消,那我就不做减法。
4.3 Joseph Form的代价:算力开销翻倍
天下没有免费的午餐。Joseph Form需要计算(I - K·H)·P^-再乘以(I-KH)^T,还要额外算一次K·R·K^T,整体计算量大约是标准形式的两倍。对于状态维度n,标准形式大约是两次O(n³)矩阵乘法,Joseph Form大概是四次O(n³)乘法。
在典型的车载组合导航场景(状态量约15到20维),这个额外开销在几百赫兹的更新频率下完全可以接受——实测大约多花几十微秒。但如果状态量上升到几百维(比如某些SLAM后端批量优化里的边缘化实现),或者运行在MCU等低算力平台上,计算量翻倍就得认真权衡了。
另外一个容易被忽略的代价是代码复杂度。标准形式一行搞定,Joseph Form需要多写几行,而且要小心不要引入新的bug——尤其是(I - K·H)^T的转置写错这种低级错误。我的习惯是单独封装一个函数,写清楚注释,单元测试里同时用标准形式和Joseph Form做等价性验证。
4.4 什么场景建议直接用Joseph Form
根据我的经验,以下场景建议直接采用Joseph Form:
- 状态的量纲差异很大,比如位置(米)和姿态(弧度)混在一起,P矩阵的动态范围可能跨6到8个数量级。
- 观测噪声极低或极高,导致
S矩阵的条件数偏大。 - 系统长时间运行,比如需要持续跑几小时甚至几天的导航系统。
- 初始协方差设置得很“激进”,P0与稳态P相差多个数量级。
- 你需要对协方差矩阵做进一步的分解操作(比如从P中提取某个子块的边缘协方差),对数值质量要求高。
在以上场景,我一般直接默认Joseph Form,不给自己留隐患。
5. 条件数:判断卡尔曼滤波是否处于“亚健康”状态的标尺
5.1 条件数的定义与直觉
条件数(Condition Number)是数值线性代数里衡量矩阵“病态程度”的核心指标。对于矩阵A,其条件数定义为:
cond(A) = ||A|| · ||A^(-1)||
对于对称正定矩阵,用2-范数定义时,条件数恰好等于最大特征值与最小特征值之比:
cond(A) = λ_max / λ_min
直觉解释:条件数衡量的是“矩阵求逆或解线性方程组时,输入误差被放大的倍数”。条件数越大,说明矩阵越接近奇异,微小的输入扰动会导致巨大的输出变化。生活类比:条件数是“放大镜倍数”——放大镜倍数越高,图像细节看得越清楚,但镜片上的灰尘(数值误差)也会被放大得越严重。当放大倍数达到10¹⁶,连双精度浮点数的基本精度都不够用了。
5.2 卡尔曼滤波里哪些矩阵容易病态
卡尔曼滤波里会有好几个矩阵可能出现病态,需要分开关注:
-
P矩阵本身:当某些状态几乎不可观(比如没有对应观测,只有过程模型在推),对应方向上的方差会持续增长,而其他方向方差收敛到很小。这时候P的
λ_max/λ_min会非常大。 -
S矩阵(
HPH^T + R):这是每次更新时要求逆的矩阵。如果两个观测通道的噪声方差设置相差悬殊(比如GPS位置噪声1e-4米,而另一个传感器噪声1e4米),S矩阵的动态范围跨8个数量级,条件数轻松到10⁸甚至更大。 -
H矩阵本身:如果观测矩阵的某些行高度线性相关(相当于用两个几乎相同的传感器观测同一个量),H的行向量接近共线,
H·P·H^T也会病态。
5.3 用条件数判断滤波器健康状况:我的工程实践
我在代码里做一个固定动作:每个时间步计算cond(S)和cond(P),并监控两个阈值指标:
code复制- 若 cond(S) > 1e10:发出警告,检查观测噪声R是否设置合理
- 若 cond(P) > 1e14:认为滤波器进入危险区,考虑改用Joseph Form或平方根滤波
- 若 cond(P) > 1e16:基本可以断定数值灾难不可避免,建议检查模型
这个阈值怎么来的?双精度浮点数有大约15到16位有效数字。当条件数达到10^n,意味着计算过程中会丢失大约n位有效数字。条件数10¹⁴时,有效数字只剩1到2位,结果基本不可信。所以看到cond(P) > 1e14,滤波结果已经不能用于做决策了。
判断时要注意:cond(P)的计算本身需要求P的逆,开销不小。对于实时系统,不必每一步都算,可以在观测更新前后抽样计算,或者用Cholesky分解过程中的中间量做粗略估计(cond(P)和cond(L)之间有关系:cond(P) = cond(L)²,如果L的条件数都到了10⁷,P就直接到10¹⁴了)。
5.4 条件数与Joseph Form的关系:什么时候该换
条件数和Joseph Form的关联,一句话总结:条件数告诉你“标准形式什么时候会出问题”,Joseph Form告诉你“出问题时怎么救”。严格来说,Joseph Form并不能降低矩阵本身的病态程度——它只是在更新过程中不做减法,避免误差被进一步放大。如果S矩阵病态,该用正则化或提高观测精度还得另外想办法。
实际判断标准可以这样定:在单次更新中,如果cond(S) < 1e8,标准形式的误差可控,用标准形式没问题;如果cond(S)在1e8到1e12之间,建议用Joseph Form;如果超过1e12,光换Joseph Form已经不够了,需要检查R是否合理、是否有状态不可观、有没有必要引入平方根滤波。
6. 工程实践中的形式选择与滤波发散排查
6.1 场景化选型建议
| 应用场景 | 状态维度 | 典型条件数 | 推荐更新形式 | 原因 |
|---|---|---|---|---|
| 低维玩具模型(教学/仿真) | 2-5维 | <1e6 | 标准形式 | 算得快,数值风险低 |
| 车载GNSS/IMU组合导航 | 15-20维 | 1e8~1e12 | Joseph Form | 长时间运行,量纲差异大 |
| 视觉SLAM后端优化 | 几百到上千维 | 1e10~1e16 | 平方根滤波或QR分解 | 维度高,数值条件极度恶劣 |
| 嵌入式平台实时滤波 | 6-10维 | 1e6~1e10 | 视算力而定,建议Joseph Form | 算力受限,但数值稳定性优先 |
如果项目还在原型阶段,我的建议是直接写Joseph Form。多花的几十行代码和微秒级耗时,换来的是少排查几个通宵的数值bug,这笔账很划算。
6.2 滤波发散排查的一线链路
如果你正在调试一个发散的卡尔曼滤波器,按下面的顺序排查,能避免很多弯路:
- 先打印协方差矩阵的最小特征值。如果是负的,恭喜你,问题大概率在数值环节。
- 检查
cond(S)和cond(P)。如果都在10¹²以上,先处理数值问题再去看模型。 - 检查P矩阵的对称性误差:
max(abs(P - P^T))。如果这个值在10⁻⁸以上,说明数值误差已经显著累积。 - 用Joseph Form替换标准协方差更新,重新跑一遍。如果发散消失,确认是数值问题。
- 如果Joseph Form下仍然发散,才考虑模型层面:
R是否设得过小?过程噪声Q是否合理?状态是否可观?
这个顺序很重要。很多人在模型层面反复折腾,却忽略了最简单的数值稳定性检查。
6.3 除了Joseph Form,还有哪些数值稳定手段
如果你已经用了Joseph Form,仍然遇到严重的数值问题(当n很大或cond(P)极高时),可以考虑更进一步的方案:
- 对称性强制恢复:每次更新后执行
P = (P + P^T)/2,强行把非对称部分抹掉。这个操作非常便宜,几十行代码,虽然不能恢复正定性,但能避免不对称误差继续累积。 - 平方根滤波(Square Root Filter):用
P = S·S^T的形式存储协方差,更新时直接更新S而不更新P。这样P在理论上自动对称半正定,条件数也降了一倍(因为cond(P) = cond(S)²)。代价是算法复杂度明显上升,实现难度大很多。 - UD分解:把
P分解为U·D·U^T,其中U是单位上三角矩阵,D是对角矩阵。D的对角线本身就是方差在分解后的“因子”,如果出现非正数,可以直接发现并处理。它的数值稳定性比标准形式好,但实现比Joseph Form复杂。
我的实操建议:在绝大多数中等规模问题上,Joseph Form加对称性恢复就已经够了。真要上平方根滤波,先想清楚你的状态维度是否真的需要。很多情况是用了cond(P)监控之后,根本走不到这一步。
6.4 对“统计基础”这件事的最终体会
回头再看这四个名词——卡尔曼滤波、矩阵对迹求导、Joseph Form、条件数——它们其实是一个完整的闭环:卡尔曼滤波提供问题框架,矩阵对迹求导提供推导增益的数学工具,条件数告诉你什么时候会有数值风险,Joseph Form是风险出现时的修复方案。
在实际项目中,我见过太多人把卡尔曼滤波当做一个“调参的黑盒”,协方差矩阵的数值性质几乎不看,发散了就东调调西调调。但真正让一个滤波器能连续跑几天不出问题的,恰恰是这些看起来“偏数学”的细节。每次调试遇到诡异现象,我都会先问自己一个问题:协方差矩阵本身是否还是合法协方差矩阵?如果这个前提都不满足,后面所有讨论都没有意义。
