02.2|从两个不确定数字推导卡尔曼滤波,而不是背五条公式
输入是预测值及其方差、独立观测及其方差;输出是融合后的估计与剩余不确定度。本课从标量推导到矩阵。假设预测误差与当前观测噪声零均值、互不相关,协方差已知。高斯假设让结果等于后验均值;即使不是高斯,这一推导仍给出线性无偏估计中的最小均方误差解。这里的误差定义为真实值减估计值,不是控制中的目标减实际。
| 符号/对象 | 含义与维度 | 单位与约定 |
|---|---|---|
| 真实、预测、更新后状态 | 本例速度为m/s;上标不是数的正负 | |
| 维观测、创新、观测矩阵 | ;负责状态到观测的单位映射 | |
| 预测、后验、观测协方差 | 对角项是单位平方,非标准差 | |
| 增益、创新协方差 | 为状态/观测单位,为观测单位平方 | |
| 状态转移、输入矩阵、已知输入、过程增量协方差 | 与同单位,不是质量q |
需要补的数学只读这些: 矩阵如何乘向量和平方目标如何最小化。先能口头解释本页小例,再进入完整推导;不必预先背下矩阵公式。
开始推导前:均值、方差、协方差究竟在记什么
一次测量只有一个数,但“这个数可能错多少”需要另一本账。概率不是质量评分,而是模型给不同可能结果分配的权重;期望 是按这些权重求平均,不是取最大概率的那一个结果。均值表示中心,方差表示围绕中心的平方散布,标准差才恢复原来的物理单位。
用一个可以手算的速度误差模型建立直觉:误差 等概率取 与 。目的不是声称真实噪声只取两个值,而是看懂平均运算:
- 均值为零不等于每次误差为零:正负误差互相抵消了。
- 平方避免抵消,所以方差记录误差规模;把标准差误填为方差会改变信任程度。
- 如果误差均值不为零,。下文把 写作方差,依赖零均值假设;偏置未处理时不能悄悄使用这个等号。
协方差是在记“两项误差是否一起偏”。 两个零均值误差的协方差为 ,单位是两者单位的乘积。若位置误差为正时速度误差也常为正,乘积平均为正;若常一正一负,则为负。负协方差不是负方差,非对角元素允许为负。协方差矩阵的对角是各自方差,非对角保存这种关联。噪声是误差来源,协方差是我们对其统计规模的描述,两者不是同一件东西。
不带矩阵,先猜融合结果。 预测速度为 ,观测为 。预测标准差 ,观测标准差 :预测更可靠,所以新值应更接近 。下一节推导的 正是“从预测走向观测的比例”,不是传感器质量百分比。
两只时钟动作:预测让时间前进,更新让知识改变
在时刻 融合完毕,保存 。拿这一段已知输入预测到时刻 ,得到 ;等时刻 的观测到来,再得到 。上标 与 是“本次观测使用前/后”,不表示负数/正数。更新前后描述的是同一物理时刻,不是又走了一个采样周期。
例如 到 的IMU推进一次,随后用 的DVL改正同一状态;若没有新DVL,就只有预测,不能拿上一帧反复更新。过程协方差 给新的一段模型误差记账,观测协方差 给这一次读数误差记账;它们都不是为了“曲线好看”而随意填的平滑旋钮。
微练习: 两个预测具有相同均值,但方差分别是 与 ;收到同一个新速度观测,哪个应该移动得更多?
概念答案:中心相同,信心不同
后者。固定正的 时, 随先验方差增大而增大。均值相同不代表知识相同,所以状态估计必须同时保存均值与协方差。到标量KF练习用相同观测验证,再到二维KF练习观察位置与速度之间的互协方差。
标量:为什么权重是方差之比
1.先提出一个可调整的估计器,而不是直接写增益
同一瞬间,预测告诉我们速度约为 ,DVL告诉我们速度约为 。为什么不取简单平均?因为二者精度不同。我们先允许估计器沿着“观测与预测的差”走任意比例 :
| 量 | 含义 | 本例单位 |
|---|---|---|
| 不能直接知道的真实速度 | ||
| 更新前、更新后的速度估计 | ||
| DVL测量; 是零均值测量噪声 | ||
| 我们接下来要优化的权重 | 无量纲 | |
| 预测误差,是真实减估计 |
表示不动预测; 表示完全采用观测。我们不预先规定哪个更好,而是计算更新后还剩多少均方误差。把新估计代入 :
因为 ,所以
这一步说明新误差有两个来源:保留下来的预测误差,以及观测噪声被增益放大的部分。不能只顾把预测误差系数变小,因为 越大,测量噪声贡献也越大。定义 、,两者是方差,单位为 。展开平方:
假设当前测量噪声与预测误差互不相关,最后一项为零,才得到待优化的二次函数:
这也是为什么重复融合同一旧样本会有问题:第一次融合后,预测已经含有它的噪声,第二次再声称 就错了。
2.让均方误差最小,增益自然出现
现在只剩一个标量未知数 。对上式求导时, 与 都是已知常量。链式法则给出
令导数等于零,先展开,再把所有含 的项移到一侧:
只要 ,便有
二阶导数是 ,所以这确实是最小值,不只是一个驻点。如果预测方差远大于观测方差, 接近1;反之接近0。特别地,二者相同才得到简单平均的 。
再把最优 代回,而不是把后验方差凭经验减小。注意 :
两边取倒数,有 。这说明独立高斯信息的精度相加。极限也很有用: 时,、;非常差的观测不会使状态突然变得精确。 且测量模型正确时,,直接测到的这一维不确定性趋近零。
项目质量q=50的完整手算
3.把每个单位带入同一帧DVL算例
取项目质量分 。先由质量得到速度标准差,注意这里的 不是四元数,也不是过程噪声 :
预测为 、,观测为 。创新使用“观测减预测”:
负创新意味着预测过大,应向下修正。创新自身也不确定,因为它包含预测误差和观测噪声;二者独立,所以创新方差是
先乘再加:速度修正量 ,因此
检查三个结果:新估计应位于预测与观测之间;质量较差时移动应较小;后验方差应小于原方差但不能负。只比较速度是否接近DVL而不看方差,会误以为滤波器“响应太慢”,其实它正在按给定的不确定度正确权衡。
矩阵:一条速度观测为何能修正偏置
4.为什么多维预测要左右各乘一次状态转移
令 为状态, 为已知输入。离散模型为
为 状态转移, 为 输入矩阵, 是零均值过程增量,。过程噪声描述这一段模型无法精确预测的变化,不是当前DVL的测量误差。将旧状态用估计替换,并使用 ,均值预测是
两式相减,已知输入正好抵消,得到 。协方差的定义是误差外积的期望,所以
展开有四项:旧误差外积、过程噪声外积、两项交叉项。假设旧误差与新过程噪声独立,两项交叉项为零,剩下
左右各乘一次,是因为协方差既要变换外积左边的向量,也要变换右边的转置向量。只写 会破坏对称性与单位。
当前传感器只看状态的一部分,写为 。,,。例如 、速度观测时 ; 的作用不是“告诉滤波器有个传感器”,而是明确状态如何生成可与读数比较的预测。
5.从误差外积完整推导矩阵增益与Joseph形式
先沿用标量估计器,只把 变成 矩阵:,其中 。把 代入可得 ,于是
是 ;它描述观测修正了哪些状态误差方向。对外积取期望,且使用不相关假设,直接得到
这就是Joseph形式,不需要等到知道最优增益才成立。第一项是剩余先验误差,第二项是引入的测量噪声。两项都是某个协方差的线性变换,因此在精确算术下均为半正定;相加仍半正定。
创新方差也从定义得到,而非一个额外猜测:
的尺寸为 。接下来为了优化 ,将Joseph式逐项展开:
后两项共有左右的 ,因此合并为 。取迹得到标量目标 。利用迹的循环性质及矩阵转置不改变迹,两个线性项相同:
为什么对矩阵求导会得到下面的结果?可从单个元素 看:线性项对它的导数为 ;二次项为 ,求导分别打到左右两个 ,由于 对称,两份相同,合成 。汇总为
令它为零得到 。若 正定,唯一最优增益为
“最优”还可以不依赖记忆求导规则来检查。记 ,把平方配齐,有
最后一项半正定,所以任何其他增益都不会让协方差在某个方向比最优值更小。实际程序不计算 ,而解方程 ;对称正定时可用Cholesky分解。小型标量观测只需除以一个正数。
6.两维状态的一帧数值:观测速度,修正偏置
令 ,,。各元素单位不相同:左上为速度方差,右下为加速度偏置方差,非对角为二者单位的乘积。取 ,则 ,。所以 。创新 使速度下降 ,使偏置上升 。
这符合物理:若过去惯性速度比DVL大,可能是加速度偏置扣少了;把偏置估计增大,以后积分的加速度就会减小。但这个判断来自历史传播形成的互协方差,不是这帧DVL直接测到了偏置。
假设状态是 ,观测只看速度,。增益第一项含速度方差,第二项含偏置与速度的互协方差。长期传播中偏置造成速度漂移,二者建立相关性,于是速度创新也能改变偏置。这不是凭一帧速度“直接测到”偏置,而是运动模型把过去证据连接了起来。不同状态单位不同,互协方差的单位就是两个状态单位的乘积。
可运行教学KF:先跑一维,再扩展状态
先独立手写:不要跳过P的更新
- 完成标量
update(x,P,z,R),先用 算例核对,再把 改成0.01。 - 完成两状态、单观测的Joseph更新,使用上面的速度—偏置例;输出两个状态和四个协方差元素。
- 给同一个观测重复更新十次,解释为什么数值会变小,但在现实中这种“精度提升”是假的。
在线练习:标量KF;进阶的匀速位置—速度KF使用状态 ,不要与本课例子的 混淆。
参考实现与运行检查(C++17,先完成练习再展开)
用 g++ -std=c++17 -O2 main.cpp -o main && ./main 编译运行。以下是离线教学实现,不连接传感器或执行器。
#include <array>
#include <cassert>
#include <cmath>
#include <iomanip>
#include <iostream>
struct Scalar { double x, P; };
void scalar_update(Scalar& s, double z, double R) {
assert(s.P>=0 && R>0);
const double prior=s.P, r=z-s.x, S=prior+R, K=prior/S;
s.x+=K*r;
s.P=(1-K)*(1-K)*prior+K*K*R; // Joseph,不复用更新后的P
}
using V=std::array<double,2>;
using M=std::array<std::array<double,2>,2>;
void update(V& x, M& P, const V& H, double z, double R) {
const M old=P;
V PH{}, K{};
for(int i=0;i<2;++i) for(int j=0;j<2;++j) PH[i]+=old[i][j]*H[j];
double S=R, r=z;
for(int i=0;i<2;++i) { S+=H[i]*PH[i]; r-=H[i]*x[i]; }
assert(S>0);
M A{}, next{};
for(int i=0;i<2;++i) {
K[i]=PH[i]/S;
x[i]+=K[i]*r;
for(int j=0;j<2;++j) A[i][j]=(i==j?1.:0.)-K[i]*H[j];
}
for(int i=0;i<2;++i) for(int j=0;j<2;++j) {
for(int a=0;a<2;++a) for(int b=0;b<2;++b)
next[i][j]+=A[i][a]*old[a][b]*A[j][b];
next[i][j]+=K[i]*R*K[j];
}
P=next;
}
int main() {
Scalar s{.30,.04}; scalar_update(s,.22,.25);
assert(std::abs(s.x-.2889655172413793)<1e-12);
V x{.30,0}; M P{{{.04,-.01},{-.01,.09}}};
update(x,P,V{1,0},.22,.25);
assert(std::abs(x[1]-.0027586206896551726)<1e-12);
assert(P[0][0]>=0 && P[1][1]>=0);
assert(P[0][0]*P[1][1]-P[0][1]*P[1][0]>=-1e-12);
std::cout<<std::setprecision(10)<<s.x<<' '<<s.P<<'\n';
std::cout<<x[0]<<' '<<x[1]<<'\n';
}
算法顺序:按真实时间间隔做预测;每个新观测先构造 、 与创新;求 ;检查有效性与创新门限;通过后求增益、改均值、用Joseph式改协方差。门限拒绝观测时不要仍然缩小。对称化只消除浮点舍入的不对称,不能修复错误噪声模型、负方差或错误雅可比。
ArduSub映射、练习与答案要点
补充练习答案与阅读提示
在项目链路里,writeBodyFrameOdom负责封装与时间安排,FuseBodyVel才是观测创新、门限与状态修正所在。项目笔记的创新写成预测减观测,而本教材坚持观测减预测;相应修正符号必须一起变化,不能只翻转日志符号。练习一:把改成0.01,算新与速度。答案:=0.8,速度0.236 m/s,后验方差0.008。练习二:取 、、,算偏置修正。答案:第二个增益−0.034483,乘创新−0.08得到偏置增加0.002759(按偏置状态单位)。精读 Solà 第6节时,先把每个更新符号映射回本课,再进入非线性几何。TB02-S1
对照真实C++:公式等价,不代表数值实现必须写成同一种形式
在本模块DVL源码精读所述同一上游提交中,FuseBodyVel 使用稀疏结构构造协方差修正,并检查方差是否会变负。下面为 AP_NavEKF3_PosVelFusion.cpp 第1854—1865行的独立短摘录;ArduPilot贡献者,GNU GPL v3,许可与版本来源同上。
P[i][j] = P[i][j] - KHP[i][j];
ForceSymmetry();
ConstrainVariances();
KHP 是 的矩阵乘积,因此第一行对应简式 。把最优关系 代回Joseph展开式,可以证明两者在精确算术下等价。但简式包含相近数相减,舍入可能破坏对称或非负性;后两行分别强制对称并约束方差。上游围绕性能和完整状态实现了自己的数值保护,本课手写版本采用Joseph,便于直接看到“两个半正定项相加”的来源。
不要从这一行推断“协方差不用考虑测量噪声”:已经进入增益和创新方差,简式只是代数消元之后没有显式出现它。反过来,也不要在更新后用新重新计算同一帧的;所有这一帧的增益都应由更新前的先验构造。