最新消息:蔓草札记的微信公众号开通了,赶紧在微信通讯录公众号中搜索“蔓草札记”关注下吧 :)

图解卡尔曼滤波是如何工作的

图像处理 xhhjin 4611浏览 1评论

本文是国外博主 Bzarg 在 2015 年写的一篇图解。卡尔曼滤波可以被认为是一种数据融合算法,已有 50 多年的历史,是当今使用最重要和最常见的数据融合算法之一,它解决的是如何从多个不确定数据中提取相对精确的数据,而这包含了两个前提条件:
1,实践前提:这些数据满足高斯分布;
2,理论前提:一个高斯分布乘以另一个高斯分布可以得到第三个高斯分布,第三个高斯分布即为提取到相对精确的数据范围。

我必须要告诉你一些关于卡尔曼滤波的知识,因为它的作用是非常惊人的。

令人惊讶的是,似乎很少有软件工程师和科学家了解它,这让我有点小失望,因为在一些含有不确定因素的场景里,如何去综合获取有效的信息,卡尔曼滤波是一个通用并且强有力的算法,有时候它提取精确信息的能力看上去就像是“见证奇迹的时刻”。如果看到这里你认为我说的话里有夸大的水分,你可以看下我之前发布的效果视频,在这个 demo 里我通过检测角速度来获取一个自由物体的姿态,效果奇佳。

什么是卡尔曼滤波?

你可以在任何含有不确定因素的动态系统里使用卡尔曼滤波,而且你应该可以通过某种数学建模对系统下一步动向做一个大概的预测。尽管系统总是会受到一些未知因素的干扰,但是卡尔曼滤波总是可以用来提高系统预估的精确度,这样你就可以更加准确地知道到底发生了什么事情。而且它可以有效利用多个粗糙数据之间的关系,而单独面对这些数据你可能都无从下手。

卡尔曼滤波尤其适合动态系统,它对于内存要求极低(它仅需要保留系统上一个状态的数据,而不是一段跨度很长的历史数据),并且它运算很快,这使得它非常适合解决实时问题和应用于嵌入式系统。

如果你尝试去谷歌搜索相关资料,对于卡尔曼滤波的数学表达总是很枯燥并且难理解。这增加了大家的学习成本甚至打击了大家的学习兴趣,因为卡尔曼滤波真的是超级简单,当然前提是你用正确的方式去理解它。因此这就形成了一个很有意义的学术话题,我将会通过很多清晰、漂亮的图片以及颜色标注来阐述这个话题。对学习者的预备知识要求很简单,你只需要对概率论和矩阵运算有一些简单的基础知识。

我们从一个简单的例子入手,看下卡尔曼滤波可以解决什么问题。如果你想直接看公式推导,可以跳到下一节

利用卡尔曼滤波我们可以做什么?

我们举一个玩具的例子:你开发了一款小型机器人,它可以在树林里自主移动,并且这款机器人需要明确自己的位置以便进行导航。

我们可以通过一组状态变量 $\vec{x_k}$ 来描述机器人的状态,包括位置和速度:

$$
\vec{x_k} = (\vec{p}, \vec{v})
$$

注意这个状态仅仅是系统所有状态中的一部分,你可以选取任何数据变量作为观测的状态。在我们这个例子中选取的是位置和速度,它也可以是水箱中的水位,汽车引擎的温度,一个用户的手指在平板上划过的位置,或者任何你想要跟踪的数据。

我们的机器人同时拥有一个 GPS 传感器,精度在 10m。这已经很好了,但是对我们的机器人来说它需要以远高于 10m 的这个精度来定位自己的位置。在机器人所处的树林里有很多溪谷和断崖,如果机器人对位置误判了哪怕只是几步远的距离,它就有可能掉到坑里,所以仅靠 GPS 是不够的。

同时我们可以获取到一些机器人的运动的信息:驱动轮子的电机指令对我们也有用处。如果没有外界干扰,仅仅是朝一个方向前进,那么下一个时刻的位置只是比上一个时刻的位置在该方向上移动了一个固定距离。当然我们无法获取影响运动的所有信息:机器人可能会受到风力影响,轮子可能会打滑,或者碰到了一些特殊的路况;所以轮子转过的距离并不能完全表示机器人移动的距离,这就导致通过轮子转动预测机器人位置不会非常准确。

GPS 传感器也会告知我们一些关于机器人状态的信息,但是会包含一些不确定性因素。我们通过轮子转动可以预知机器人是如何运动的,同样也有一定的不准确度。

如果我们综合两者的信息呢?可以得到比只依靠单独一个信息来源更精确的结果么?答案当然是 YES,这就是卡尔曼滤波要解决的问题。

卡尔曼滤波如何看待你的问题

我们再来看下需要解决的问题,同样是上边的系统,系统状态包括位置和速度。

\begin{aligned}
\vec{x} = \begin{bmatrix}
p\\
v
\end{bmatrix}
\end{aligned}

我们不知道位置和速度的准确值;但是我们可以列出一个准确数值可能落在的区间。在这个范围里,一些数值组合的可能性要高于另一些组合的可能性。

卡尔曼滤波假设所有的变量(在我们的例子中为位置和速度)是随机的且符合高斯分布。每个变量有一个平均值 $\mu$,代表了随机分布的中心值(也表示这是可能性最大的值),和一个方差 $\sigma^2$,代表了不确定度。

在上图中位置和速度是无关联的,即系统状态中的一个变量并不会告诉你关于另一个变量的任何信息。

下图则展示了一些有趣的事情:在现实中,速度和位置是有关联的,如果已经确定位置的值,那么某些速度值存在的可能性更高。

假如我们已知上一个状态的位置值,现在要预测下一个状态的位置值。如果我们的速度值很高,我们移动的距离会远一点。相反,如果速度慢,机器人不会走的很远。

这种关系在跟踪系统状态时很重要,因为它给了我们更多的信息:一个测量值告诉我们另一个测量值可能是什么样子。这就是卡尔曼滤波的目的,我们要尽量从所有不确定信息中提取有价值的信息!

这种关系可以通过一个称作协方差的矩阵来表述,简而言之,矩阵中的每个元素 $\Sigma_{ij}$ 表示了第 $i$ 个状态变量和第 $j$ 个状态变量之间的关系(你可能猜到了协方差矩阵是对称的,即交换下标 $i$ 和 $j$ 并无任何影响)。协方差矩阵通常表示为 $\Sigma$,它的元素则表示为 $\Sigma_{ij}$ 。

利用矩阵描述问题

我们对系统状态的分布建模为高斯分布,所以在 $k$ 时刻我们需要两个信息:最佳预估值 $\mathbf{\hat{x}_k}$(平均值,有些地方也表示为 $\mathbf{\mu}$),和它的协方差矩阵 $\mathbf{P_k}$

\begin{equation} \label{eq:statevars}
\begin{aligned}
\mathbf{\hat{x}}_k &= \begin{bmatrix}
\text{position}\\
\text{velocity}
\end{bmatrix}\\
\mathbf{P}_k &=
\begin{bmatrix}
\Sigma_{pp} & \Sigma_{pv} \\
\Sigma_{vp} & \Sigma_{vv} \\
\end{bmatrix}
\end{aligned}
\end{equation}

(这里我们只记录了位置和速度,但是如果需要的话我们可以把任何数据变量放进我们的系统状态里)

下一步,我们需要通过当前阶段($k-1$ 时刻)的状态来预测下一阶段($k$ 时刻)的状态。请注意,我们不知道状态的准确值,但是我们的预测函数并不在乎,它仅仅是对 $k-1$ 时刻所有可能值的范围进行预测转移,然后得出一个 $k$ 时刻新值的范围。

我们可以通过一个状态转移矩阵 $\mathbf{F_k}$ 来描述这个转换

它把 $k-1$ 时刻所有可能的状态值转移到一个新的范围内,这个新的范围代表了系统新的状态值可能存在的范围,如果 $k-1$ 时刻估计值的范围是准确的话。

通过一个运动公式来表示这种预测下个状态的过程:

\begin{split}
\color{deeppink}{p_k} &= \color{royalblue}{p_{k-1}} + \Delta t &\color{royalblue}{v_{k-1}} \\
\color{deeppink}{v_k} &= &\color{royalblue}{v_{k-1}}
\end{split}

整理为矩阵:

\begin{align}
\color{deeppink}{\mathbf{\hat{x}}_k} &= \begin{bmatrix}
1 & \Delta t \\
0 & 1
\end{bmatrix} \color{royalblue}{\mathbf{\hat{x}}_{k-1}} \\
&= \mathbf{F}_k \color{royalblue}{\mathbf{\hat{x}}_{k-1}} \label{statevars}
\end{align}

我们现在有了一个状态转移矩阵,可以简单预测下个状态,但仍不知道如何更新协方差矩阵。

这里我们需要另一个公式。如果我们对每个点进行矩阵 $\color{firebrick}{\mathbf{A}}$ 转换,它的协方差矩阵  $\Sigma$ 会发生什么变化呢?

这个简单,直接告诉你结果。

\begin{equation}
\begin{split}
Cov(x) &= \Sigma\\
Cov(\color{firebrick}{\mathbf{A}}x) &= \color{firebrick}{\mathbf{A}} \Sigma \color{firebrick}{\mathbf{A}}^T
\end{split} \label{covident}
\end{equation}

结合 \eqref{covident} 和 \eqref{statevars}:

\begin{equation}
\begin{split}
\color{deeppink}{\mathbf{\hat{x}}_k} &= \mathbf{F}_k \color{royalblue}{\mathbf{\hat{x}}_{k-1}} \\
\color{deeppink}{\mathbf{P}_k} &= \mathbf{F_k} \color{royalblue}{\mathbf{P}_{k-1}} \mathbf{F}_k^T
\end{split}
\end{equation}

外界作用力

我们并没有考虑到所有影响因素。系统状态的改变并不只依靠上一个系统状态,外界作用力可能会影响系统状态的变化。

例如,跟踪一列火车的运动状态,火车驾驶员可能踩了油门使火车提速。同样,在我们机器人例子中,导航软件可能发出一些指令启动或者制动轮子。如果我们知道这些额外的信息,我们可以通过一个向量 $\color{darkorange}{\vec{\mathbf{u}_k}}$ 来描述这些信息,把它添加到我们的预测方程里作为一个修正。

假如我们通过发出的指令得到预期的加速度 $\color{darkorange}{a}$,上边的运动方程可以变化为:

\begin{split}
\color{deeppink}{p_k} &= \color{royalblue}{p_{k-1}} + {\Delta t} &\color{royalblue}{v_{k-1}} + &\frac{1}{2} \color{darkorange}{a} {\Delta t}^2 \\
\color{deeppink}{v_k} &= &\color{royalblue}{v_{k-1}} + & \color{darkorange}{a} {\Delta t}
\end{split}

矩阵形式:

\begin{equation}
\begin{split}
\color{deeppink}{\mathbf{\hat{x}}_k} &= \mathbf{F}_k \color{royalblue}{\mathbf{\hat{x}}_{k-1}} + \begin{bmatrix}
\frac{\Delta t^2}{2} \\
\Delta t
\end{bmatrix} \color{darkorange}{a} \\
&= \mathbf{F}_k \color{royalblue}{\mathbf{\hat{x}}_{k-1}} + \mathbf{B}_k \color{darkorange}{\vec{\mathbf{u}_k}}
\end{split}
\end{equation}

$\mathbf{B}_k$ 称作控制矩阵, $\color{darkorange}{\vec{\mathbf{u}_k}}$ 称作控制向量(没有任何外界动力影响的系统,可以忽略该项)。

我们增加另一个细节,假如我们的预测转换矩阵不是 100% 准确呢,会发生什么?

外界不确定性

如果状态只会根据系统自身特性演变那将不会有任何问题。如果我们可以把所有外界作用力对系统的影响计算清楚那也不会有任何问题。

但是如果有些外力我们无法预测呢?假如我们在跟踪一个四轴飞行器,它会受到风力影响。如果我们在跟踪一个轮式机器人,轮子可能会打滑,或者地面上的突起会使它降速。我们无法跟踪这些因素,并且这些事情发生的时候上述的预测方程可能会失灵。

我们可以把“世界”中的这些不确定性统一建模,在预测方程中增加一个不确定项。

这样,原始状态中的每一个点可以都会预测转换到一个范围,而不是某个确定的点。可以这样描述: $\color{royalblue}{\mathbf{\hat{x}}_{k-1}}$ 中的每个点移动到一个符合方差 $\color{mediumaquamarine}{\mathbf{Q}_k}$ 的高斯分布里。另一种说法,我们把这些不确定因素描述为方差为 $\color{mediumaquamarine}{\mathbf{Q}_k}$ 的高斯噪声。

这会产生一个新的高斯分布,方差不同,但是均值相同。

对 ${\color{mediumaquamarine}{\mathbf{Q}_k}}$ 简单叠加,可以拿到扩展的方差,这样就得到了完整的预测转换方程。

\begin{equation}
\begin{split}
\color{deeppink}{\mathbf{\hat{x}}_k} &= \mathbf{F}_k \color{royalblue}{\mathbf{\hat{x}}_{k-1}} + \mathbf{B}_k \color{darkorange}{\vec{\mathbf{u}_k}} \\
\color{deeppink}{\mathbf{P}_k} &= \mathbf{F_k} \color{royalblue}{\mathbf{P}_{k-1}} \mathbf{F}_k^T + \color{mediumaquamarine}{\mathbf{Q}_k}
\end{split}
\label{kalpredictfull}
\end{equation}

新的预测转换方程只是引入了已知的可以预测的外力影响因素。

新的不确定性可以通过老的不确定性计算得到,通过增加外界无法预测的、不确定的因素成分。

到这里,我们得到了一个模糊的估计范围,一个通过 $\color{deeppink}{\mathbf{\hat{x}}_k}$ 和 $\color{deeppink}{\mathbf{P}_k}$ 描述的范围。如果再结合我们传感器的数据呢?

通过测量值精炼预测值

我们可能还有一些传感器来测量系统的状态。目前我们不用太关心所测量的状态变量是什么。也许一个测量位置一个测量速度。每个传感器可以提供一些关于系统状态的数据信息,每个传感器检测一个系统变量并且产生一些读数。

注意传感器测量的范围和单位可能与我们跟踪系统变量所使用的范围和单位不一致。我们需要对传感器做下建模:通过矩阵 $\mathbf{H}_k$

我们可以得到传感器读数分布的范围:

\begin{equation}
\begin{aligned}
\vec{\mu}_{\text{expected}} &= \mathbf{H}_k \color{deeppink}{\mathbf{\hat{x}}_k} \\
\mathbf{\Sigma}_{\text{expected}} &= \mathbf{H}_k \color{deeppink}{\mathbf{P}_k} \mathbf{H}_k^T
\end{aligned}
\end{equation}

卡尔曼滤波也可以处理传感器噪声。换句话说,我们的传感器有自己的精度范围,对于一个真实的位置和速度,传感器的读数受到高斯噪声影响会使读数在某个范围内波动。

我们观测到的每个数据,可以认为其对应某个真实的状态。但是因为存在不确定性,某些状态的可能性比另外一些可能性更高。

我们将这种不确定性(如传感器噪声)的协方差表示为 $\color{mediumaquamarine}{\mathbf{R}_k}$,读数的分布均值等于我们观察到传感器的读数,我们将其表示为 $\color{yellowgreen}{\vec{\mathbf{z}_k}}$。

所以现在我们有了两个高斯分布,一个来自于我们通过状态转移预测的预测值,另一个来自于我们实际传感器读数的测量值。

我们必须尝试去把两者的数据预测值(粉色)与观测值(绿色)融合起来。

所以我们得到的新的数据会长什么样子呢? 对于任何可能的读数 $(z_1,z_2)$,我们都有两个相关的概率:(1)我们的传感器读数 $\color{yellowgreen}{\vec{\mathbf{z}_k}}$ 是 $(z_1,z_2)$ 的测量值的概率,以及(2)先前估计值被认为是我们应该看到的读数的概率。

如果我们有两个概率,并且想知道两个概率都为真的机会,则将它们相乘。因此,我们对两个高斯分布进行了相乘处理:

相乘之后得到的即为重叠部分,这个区域同时属于两个高斯分布。并且比单独任何一个区域都要精确。这个区域的平均值取决于我们更取信于哪个数据来源,这样我们也通过我们手中的数据得到了一个最好的估计值。

唔~ 这看上去像另一个高斯分布

事实证明,两个独立的高斯分布相乘之后会得到一个新的具有其均值和协方差矩阵的高斯分布!新高斯分布的均值和方差均可以通过老的均值方差求得。下面开始推公式。

高斯分布相乘

首先考虑一维高斯情况:一个均值为 $\mu$,方差为 $\sigma^2$ 的高斯分布的形式为:

\begin{equation} \label{gaussformula}
\mathcal{N}(x, \mu,\sigma) = \frac{1}{ \sigma \sqrt{ 2\pi } } e^{ -\frac{ (x – \mu)^2 }{ 2\sigma^2 } }
\end{equation}

我们想知道两个高斯分布相乘会发生什么。蓝色曲线代表了两个高斯分布的交集部分。

\begin{equation} \label{gaussequiv}
\mathcal{N}(x, \color{fuchsia}{\mu_0}, \color{deeppink}{\sigma_0}) \cdot \mathcal{N}(x, \color{yellowgreen}{\mu_1}, \color{mediumaquamarine}{\sigma_1}) \stackrel{?}{=} \mathcal{N}(x, \color{royalblue}{\mu’}, \color{mediumblue}{\sigma’})
\end{equation}

将公式 $\eqref{gaussformula}$ 代入公式 $\eqref{gaussequiv}$,我们可以得到新的高斯分布的均值和方差如下所示:

\begin{equation} \label{fusionformula}
\begin{aligned}
\color{royalblue}{\mu’} &= \mu_0 + \frac{\sigma_0^2 (\mu_1 – \mu_0)} {\sigma_0^2 + \sigma_1^2}\\
\color{mediumblue}{\sigma’}^2 &= \sigma_0^2 – \frac{\sigma_0^4} {\sigma_0^2 + \sigma_1^2}
\end{aligned}
\end{equation}

我们将其中的一小部分重写为 $\color{purple}{\mathbf{k}}$:

\begin{equation} \label{gainformula}
\color{purple}{\mathbf{k}} = \frac{\sigma_0^2}{\sigma_0^2 + \sigma_1^2}
\end{equation}

\begin{equation}
\begin{split}
\color{royalblue}{\mu’} &= \mu_0 + &\color{purple}{\mathbf{k}} (\mu_1 – \mu_0)\\
\color{mediumblue}{\sigma’}^2 &= \sigma_0^2 – &\color{purple}{\mathbf{k}} \sigma_0^2
\end{split} \label{update}
\end{equation}

这样一来,公式的形式就简单多了!我们顺势将公式 $\eqref{gainformula}$ 和 $\eqref{update}$ 的矩阵形式写在下面:

\begin{equation} \label{matrixgain}
\color{purple}{\mathbf{K}} = \Sigma_0 (\Sigma_0 + \Sigma_1)^{-1}
\end{equation}

\begin{equation}
\begin{split}
\color{royalblue}{\vec{\mu}’} &= \vec{\mu_0} + &\color{purple}{\mathbf{K}} (\vec{\mu_1} – \vec{\mu_0})\\
\color{mediumblue}{\Sigma’} &= \Sigma_0 – &\color{purple}{\mathbf{K}} \Sigma_0
\end{split} \label{matrixupdate}
\end{equation}

$\color{purple}{\mathbf{K}}$ 被称为卡尔曼增益,待会会用到。

简单,我们快结束了。

公式汇总

我们有两个高斯分布,一个是我们的预测值 $(\color{fuchsia}{\mu_0}, \color{deeppink}{\Sigma_0}) = (\color{fuchsia}{\mathbf{H}_k \mathbf{\hat{x}}_k}, \color{deeppink}{\mathbf{H}_k \mathbf{P}_k \mathbf{H}_k^T})$,另外一个是实际的测量值 $(\color{yellowgreen}{\mu_1}, \color{mediumaquamarine}{\Sigma_1}) = (\color{yellowgreen}{\vec{\mathbf{z}_k}}, \color{mediumaquamarine}{\mathbf{R}_k})$,我们将这两个高斯分布带入公式 $\eqref{matrixupdate}$ 中就可以得到二者的重叠区域:

\begin{equation}
\begin{aligned}
\mathbf{H}_k \color{royalblue}{\mathbf{\hat{x}}_k’} &= \color{fuchsia}{\mathbf{H}_k \mathbf{\hat{x}}_k} & + & \color{purple}{\mathbf{K}} ( \color{yellowgreen}{\vec{\mathbf{z}_k}} – \color{fuchsia}{\mathbf{H}_k \mathbf{\hat{x}}_k} ) \\
\mathbf{H}_k \color{royalblue}{\mathbf{P}_k’} \mathbf{H}_k^T &= \color{deeppink}{\mathbf{H}_k \mathbf{P}_k \mathbf{H}_k^T} & – & \color{purple}{\mathbf{K}} \color{deeppink}{\mathbf{H}_k \mathbf{P}_k \mathbf{H}_k^T}
\end{aligned} \label {kalunsimplified}
\end{equation}

从公式 $\eqref{matrixgain}$ 我们可以知道,卡尔曼增益是:

\begin{equation} \label{eq:kalgainunsimplified}
\color{purple}{\mathbf{K}} = \color{deeppink}{\mathbf{H}_k \mathbf{P}_k \mathbf{H}_k^T} ( \color{deeppink}{\mathbf{H}_k \mathbf{P}_k \mathbf{H}_k^T} + \color{mediumaquamarine}{\mathbf{R}_k})^{-1}
\end{equation}

然后我们将公式 $\eqref{kalunsimplified}$ 与公式 $\eqref{eq:kalgainunsimplified}$ 中的 $\mathbf{H}_k$ 去除,同时将 $\color{royalblue}{\mathbf{P}_k’}$ 后面的 $\mathbf{H}_k^T$ 去除,我们可以得到最终的化简形式的更新方程:

\begin{equation}
\begin{split}
\color{royalblue}{\mathbf{\hat{x}}_k’} &= \color{fuchsia}{\mathbf{\hat{x}}_k} & + & \color{purple}{\mathbf{K}’} ( \color{yellowgreen}{\vec{\mathbf{z}_k}} – \color{fuchsia}{\mathbf{H}_k \mathbf{\hat{x}}_k} ) \\
\color{royalblue}{\mathbf{P}_k’} &= \color{deeppink}{\mathbf{P}_k} & – & \color{purple}{\mathbf{K}’} \color{deeppink}{\mathbf{H}_k \mathbf{P}_k}
\end{split}
\label{kalupdatefull}
\end{equation}

\begin{equation}
\color{purple}{\mathbf{K}’} = \color{deeppink}{\mathbf{P}_k \mathbf{H}_k^T} ( \color{deeppink}{\mathbf{H}_k \mathbf{P}_k \mathbf{H}_k^T} + \color{mediumaquamarine}{\mathbf{R}_k})^{-1}
\label{kalgainfull}
\end{equation}

至此,我们得到了每个状态的更新步骤,$\color{royalblue}{\mathbf{\hat{x}}_k’}$ 是我们最佳的预测值,接下来我们可以持续进行预测(通过 $\color{royalblue}{\mathbf{P}_k’}$),然后更新,重复上述过程!。

总结

在上述所有数学公式中,你需要实现的只是公式 $\eqref{kalpredictfull}, \eqref{kalupdatefull}$ 和 $\eqref{kalgainfull}$(或者,如果你忘记了这些,可以从等式 $\eqref{covident}$ 和 $\eqref{matrixupdate}$ 重新推导所有内容。)

这将使你能够准确地对任何线性系统建模。对于非线性系统,我们使用扩展卡尔曼滤波器,该滤波器通过简单地线性化预测和测量值的均值进行工作。

如果我讲的还不错的话,希望读者也可以认识到卡尔曼滤波有多酷,并且在某个新的领域使用它。


欢迎转载,转载请注明出处:蔓草札记 » 图解卡尔曼滤波是如何工作的

♥ 喜欢 0 赞赏
发表我的评论
取消评论

表情

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

网友最新评论 (1)

  1. 不错,听懂了一点
    舟舟1年前 (2023-05-08)回复