Kalman滤波——初阶入门

2023-11-03

概要

​ kalman滤波在机器人控制、数字图像等领域应用非常广泛的一种方法,很多人对其名字不能理解,因为kalman滤波在大多数时候表现出来都是将多个数据进行融合,为什么不叫kalman融合呢?如果你有这个疑问,那就说明你对kalman滤波理解不够,任何的数据融合都是为了将多种途径的数据中的噪声滤波,以达到尽可能接近真实值的目的,从这个角度理解,其融合只是表象,滤除了信号中的噪声才是本质。接下来我将从最简单的角度带领大家入门kalman滤波,保证没有任何基础的人也可以理解什么是卡尔曼滤波。先从原理开始,后面会有C语言的实现代码演示。

思路讲解

​ 首先大家设想一下如下场景:在一个封闭房间里,已知当前的温度,如何尽可能准确的获取下一时刻的温度呢?方法无外乎两种,推断or测量。

1. 根据当前时刻的温度,用经验估计下一时刻的温度。
2. 使用温度计测量下一时刻的温度。

​ 当然,任何估计或者测量,都是有误差的,有没有办法用一种方法,将上述两种方法的值进行融合,很好,看到这里,你已经看到了kalman滤波的关键所在了。在kalman滤波里面,有一个卡尔曼增益(Kalman Gain)或卡尔曼系数来表达两个数据格子的权重,并且有方法让这个Kg根据每次的情况进行迭代,达到无限循环的目的。

核心公式

  1. x^t=Fx^t1+But1 x ^ t − = F x ^ t − 1 + B u t − 1 —— 状态预测方程
    1. F —— 状态转换矩阵,如何从上一时刻状态推测当前状态
    2. B —— 控制矩阵,系统控制量如何作用当前状态
    3. ut1 u t − 1 系统控制量(系统输入量)
  2. Pt=FPt1FT+Q P t − = F P t − 1 F T + Q —— 协方差传递方程
    1. P P —— 预测状态x^tx^t的协方差
    2. Q Q —— 上述预测模型的协方差矩阵
  3. Kt=PtHTHPtHT+RKt=PtHTHPtHT+R —— Kg K g 的求解
    1. zt=Hxt+v z t = H x t + v —— 观察矩阵
      1. H H —— 本身状态和观测状态的转换关系
      2. vv —— 观测噪声
      3. R R —— 观测噪声的协方差
  4. x^t=x^t+Kt(ztHx^t)x^t=x^t+Kt(ztHx^t) —— 当前状态更新
  5. Pt=(IKtH)Pt P t = ( I − K t H ) P t − —— 对预测状态协方差的更新

公式分析

​ 不像别的教程,上来就进行step-by-step的分析。我觉得先将核心的五个公式放上来,让大家先过目一遍,带着问题思考,有助于理解。

​ 我们的分析还是从经典的例子——小车模型开始。

状态预测模型

​ 现在我们假设有一辆小车在二维平面上直线运动如下,

这里写图片描述

如果其当前状态为 P P VV U U ,其中PP为距离, V V 为速度,UU位加速度,那么我们可以得到如下方程组:

Pt=Pt1+Vt1δt+12utδt2 P t = P t − 1 + V t − 1 ∗ δ t + 1 2 u t δ t 2

Vt=Vt1+utδt V t = V t − 1 + u t ∗ δ t

因为卡尔曼滤波只能应用在线性滤波的场景下,所以我们当然认为上述方程组是线性方程,那么其矩阵形式为 这里写图片描述

如果我们令 F F = 10δt11δt01 B B = δt22δtδt22δt

F F 为状态转换矩阵,表示如何从上一时刻状态推测当前状态

BB为控制矩阵,表示系统控制量如何作用当前状态

则上述公式可以写为 x^t=Fx^t1+But1 x ^ t − = F x ^ t − 1 + B u t − 1

这就是卡尔曼滤波的第一个方程——状态预测方程。这里的 x^t1 x ^ t − 1 代表上一时刻估计的最佳状态(当然不是真实值,真实值我们永远也无法准确获取), x^t x ^ t − 代表根据上一时刻对当前时刻的推测值,减号代表还未经过kalman修正。

  • 我们知道,任何的状态估计都是有噪声的,在这里我们用协方差矩阵 P P 来表示状态预测模型的噪声大小。但是仅表示当前的噪声大小还不能让整个系统迭代下去,但是因为有上述的公式,我们根据协方差的定义,可以根据当前时刻的协方差来表示下一时刻的协方差:

cov(Ax,Bx)=Acov(x,x)BTcov(Ax,Bx)=Acov(x,x)BT

Pt=FPt1FT P t = F P t − 1 F T 其中 FT F T 表示状态转换矩阵 F F 的转置。

  • 尽管如此,我们的预测模型也不可能完全准确,它还是会有噪声的存在,同样,这里我们用协方差矩阵QQ来表示其噪声大小。所以上述公式的完全版:

    Pt=FPt1FT+Q P t = F P t − 1 F T + Q

观测模型

​ 我们当然还会有观测,在上述的小车模型里,我们可以观测到距离和速度,或者二者之一即可,令我们观测到的状态为 Zt Z t ,则小车的当前 Xt X t 到观测状态 Zt Z t 的表达关系为:

Zt=HXt+v Z t = H X t + v

  1. Xt X t —— PtVt P t V t

  2. H H —— 本身状态和观测状态的转换关系

  3. vv —— 观测噪声

    ​因为我们这里我们可以观测到一维或多维数据,例如在小车模型里,我们可以观测到速度或者距离或者二者集合,所以这里的矩阵 H H 可以是1010,也可以是 11 1 1

    ​在这里还有一点,我们同样要用一个协方差矩阵 R R 来表示观测噪声vv的波动大小。

状态更新

​ 上述两点,我们讲到了预测模型和观测模型,接下来就是kalman滤波的核心,如何根据这两个模型值来得到一个更接近真实值的修正值。

x^t=x^t+Kt(ztHx^t) x ^ t = x ^ t − + K t ( z t − H x ^ t − )

​ 这里括号里面的是观测值和预测值的残差,其实就是公式③中的观测噪声 v v

KtKt代表 t t 时刻的卡尔曼增益

根据对残差乘上KtKt,则表达了观测模型和预测模型各自的权重。

Kg K g 求解

​ 那么如何求解 Kg K g 呢,这里推导比较复杂,后续的文章再做详细说明,今天就列出公式:

Kt=PtHTHPtHT+R K t = P t − H T H P t − H T + R

Kt K t 除了上述说的衡量两个模型的权重功能外,还能够将数值从 zt z t 转换到 xt x t

什么意思?前面说了,在小车模型中状态预测值 x^t x ^ t 是速度和距离的矩阵集合,而观测值可以是单纯的速度或距离或二者集合。所以他们 xt x t zt z t 两者可能单位都不同,这个时候就要 Kg K g 来进行状态转换。

协方差 P P 更新

​ 为了使得整个系统能够迭代运算下去,我们当然需要对状态预测模型的协方差PP进行更新

Pt=(IKtH)Pt P t = ( I − K t H ) P t −

代码实例

​ 下面我们用C代码来实现以下上述小车模型中的一维kalman滤波

​ 我是在win+Qt5的环境下实现的。

std::vector<int> result, measure;

/* 1 Dimension */
typedef struct {
    float x; //-- @Zuo 状态值
    float F; //-- @Zuo x(n)=F*x(n-1)+u(n),u(n)~N(0,Q)
    float H; //-- @Zuo z(n)=H*x(n)+w(n),w(n)~N(0,R)
    float Q; //-- @Zuo 协方差P传递方程的协方差
    float R; //-- @Zuo 观测噪声协方差
    float P; //-- @Zuo 预测模型的协方差
    float gain; //-- @Zuo 卡尔曼增益
} kalman1_state;

void kalman1_init(kalman1_state *state, float init_x, float init_P)
{
    state->x = init_x;
    state->P = init_P;
    state->F = 1;
    state->H = 1;
    state->Q = 2e2;
    state->R = 5e2;
}

float kalman1_filter(kalman1_state *state, float z_measure)
{
    /* Predict */
    state->x = state->F * state->x;
    state->P = state->F * state->F * state->P + state->Q;

    /* Measurement */
    state->gain = state->P * state->H / (state->P * state->H * state->H + state->R);
    state->x = state->x + state->gain * (z_measure - state->H * state->x);
    state->P = (1 - state->gain * state->H) * state->P;

    return state->x;
}

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    kalman1_state ks;
    kalman1_init(&ks, 0, 1);
    for(int i=0; i<100; i++){
        float m = 40;
        float noise = (std::rand()%200)/10.0;

        if(i==50) noise = -40;
        if(i==70) noise = 35;

        m += noise;

        kalman1_filter(&ks, m);
        result.push_back(ks.x);
        measure.push_back(m);
    }
}

void MainWindow::paintEvent(QPaintEvent *event)
{
    Q_UNUSED(event);

    QPainter painter(this);

    QFont font;
    font.setFamily("Microsoft YaHei");
    font.setPointSize(50);
    font.setItalic(true);
    painter.setFont(font);

    painter.setPen(QColor(255, 0, 0));
    painter.drawLine(QPoint(0,500), QPoint(2000,500));

    for(int i=0; i<measure.size()-1; i++){
        painter.setPen(QColor(0, 255, 0));
        painter.drawLine(QPoint(i*10,result[i]*10), QPoint((i+1)*10,result[i+1]*10));
        painter.setPen(QColor(0, 0, 255));
        painter.drawLine(QPoint(i*10,measure[i]*10), QPoint((i+1)*10,measure[i+1]*10));
    }
}

下面是结果图,其中红色线代表真实值,当然越接近代表滤波效果越好。绿色和蓝色分别代表kalman滤波的输出值和测量值。我给测量值增加了一个20以内的随机误差,但是可以看到我稍微皮了一下,在i=50i=70的时候让误差大幅度的波动了一下,但是看到kalman滤波还是能够较好的将结果往回拉。

这里写图片描述

参考

  1. B站视频讲解
  2. Kalman滤波器从原理到实现

PS

  1. 觉得本文有帮助的可以左上角点赞,或者留言互动。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Kalman滤波——初阶入门 的相关文章

  • MEX 文件中的断言导致 Matlab 崩溃

    我正在使用mxAssert 宏定义为matrix h在我的 C 代码中 mex 可以完美编译 当我调用的 mex 代码中违反断言时 该断言不会导致我的程序崩溃 而是导致 Matlab 本身崩溃 我错过了什么吗 这是有意的行为吗 当我查看 M
  • 使用实体框架从集合中删除项目

    我正在使用DDD 我有一个 Product 类 它是一个聚合根 public class Product IAggregateRoot public virtual ICollection
  • ASP .NET MVC,创建类似路由配置的永久链接

    我需要帮助在 MVC 网站中创建类似 URL 路由的永久链接 Slug 已设置为 www xyz com profile slug 代码为 routes MapRoute name Profile url profile slug defa
  • TextBox 焦点的 WinForms 事件?

    我想添加一个偶数TextBox当它有焦点时 我知道我可以用一个简单的方法来做到这一点textbox1 Focus并检查布尔值 但我不想那样做 我想这样做 this tGID Focus new System EventHandler thi
  • 调试内存不足异常

    在修复我制作的小型 ASP NET C Web 应用程序的错误时 我遇到了 OutOfMemoryException 没有关于在哪里查看的提示 因为这是一个编译时错误 如何诊断此异常 我假设这正是内存分析发挥作用的地方 有小费吗 Thank
  • 获取从属性构造函数内部应用到哪个属性的成员?

    我有一个自定义属性 在自定义属性的构造函数内 我想将属性的属性值设置为属性所应用到的属性的类型 是否有某种方式可以访问该属性所应用到的成员从我的属性类内部 可以从 NET 4 5 using CallerMemberName Somethi
  • VS30063:您无权访问 https://dev.azure.com

    我正在尝试在 asp net core 2 1 mvc 应用程序中使用以下代码连接 Azure DevOps Uri orgUrl new Uri https dev azure com xxxxx String personalAcces
  • 为什么 std::allocator 在 C++17 中丢失成员类型/函数?

    一边看着std 分配器 http en cppreference com w cpp memory allocator 我看到成员 value type pointer const pointer reference const refer
  • 禁用 LINQ 上下文的所有延迟加载或强制预先加载

    我有一个文档生成器 目前包含约 200 个项目的查询 但完成后可能会超过 500 个 我最近注意到一些映射表示延迟加载 这给文档生成器带来了一个问题 因为它需要根据生成的文档来访问所有这些属性 虽然我知道DataLoadOptions可以指
  • “MyClass”的类型初始值设定项引发异常

    以下是我的Windows服务代码 当我调试代码时 我收到错误 异常 CSMessageUtility CSDetails 的类型初始值设定项引发异常 using System using System Collections Generic
  • 在 C 中复制两个相邻字节的最快方法是什么?

    好吧 让我们从最明显的解决方案开始 memcpy Ptr const char a b 2 调用库函数的开销相当大 编译器有时不会优化它 我不会依赖编译器优化 但即使 GCC 很聪明 如果我将程序移植到带有垃圾编译器的更奇特的平台上 我也不
  • C# 搜索目录中包含字符串的所有文件,然后返回该字符串

    使用用户在文本框中输入的内容 我想搜索目录中的哪个文件包含该文本 然后我想解析出信息 但我似乎找不到该字符串或至少返回信息 任何帮助将不胜感激 我当前的代码 private void btnSearchSerial Click object
  • 是否有一个 C++ 库可以从 PDF 文件中提取文本,例如 PDFBox for Java? [关闭]

    Closed 此问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 去年 我使用 PDFBox 在 Java 中创建了一个应用程序来获取某些 PDF 文件中的原始文本 现在
  • gdb查找行号的内存地址

    假设我已将 gdb 附加到一个进程 并且在其内存布局中有一个文件和行号 我想要其内存地址 如何获取文件x中第n行的内存地址 这是在 Linux x86 上 gdb info line test c 56 Line 56 of test c
  • 为什么我使用google'smtp'无法发送电子邮件?

    我有以下程序使用 smtp gmail com 587 发送电子邮件 namespace TestMailServer class Program static void Main string args MailMessage mail
  • 如何在 GCC 5 中处理双 ABI?

    我尝试了解如何克服 GCC 5 中引入的双重 ABI 的问题 但是 我没能做到 这是一个重现错误的非常简单的示例 我使用的GCC版本是5 2 如您所见 我的主要函数 在 main cpp 文件中 非常简单 main cpp include
  • 以编程方式使用自定义元素创建网格

    我正在尝试以编程方式创建一个网格 并将自定义控件作为子项附加到网格中 作为 2x2 矩阵中的第 0 行第 0 列 为了让事情变得更棘手 我使用了 MVVM 设计模式 下面是一些代码可以帮助大家理解这个想法 应用程序 xaml cs base
  • 热重载时调用方法

    我正在使用 Visual Studio 2022 和 C 制作游戏 我想知道当您热重新加载应用程序 当它正在运行时 时是否可以触发一些代码 我基本上有 2 个名为 UnloadLevel 和 LoadLevel 的方法 我想在热重载时执行它
  • 在基类集合上调用派生方法

    我有一个名为 A 的抽象类 以及实现 A 的其他类 B C D E 我的派生类持有不同类型的值 我还有一个 A 对象的列表 abstract class A class B class A public int val get privat
  • WPF/数据集:如何通过 XAML 将相关表中的数据绑定到数据网格列中?

    我正在使用 WPF DataSet 连接到 SQL Server Express XAML 和 C Visual Studio 2013 Express 我从名为 BankNoteBook 的现有 SQL Server Express 数据

随机推荐

  • 实现数据字典的缓存、加载、刷新和映射的集成框架

    前言 在业务开发的过程中 总是会遇到字典打交道 比如说 性别 类型 状态等字段 都可以归纳为字典的范围 字典的组成分成 字典类型 字典数据 其中 字典数据 归属于一类的 字典类型 可以通过 字典类型 获取 字典数据 例如开头提到的 性别就为
  • 蓝桥杯python基础练习报时助手

    这道题比较简单我们可以直接用字典和if语句来完成 按照题目意思创建一个字典1 20和30 40 50 因为创建全部的字典太麻烦 我们可以将不存在字典的建转化为字典中的建 第二步可以运用if语句进行判断 m 0时直接 输出即可 m h gt
  • centos mail报错:550 Mailbox not found or access denied

    运行了几年的发邮件脚本 最近体发邮件都发生了报错 无法发出 smtp server 550 Mailbox not found or access denied 报错信息提示邮箱找不到 但是接收人邮箱确定没有错误 因为一直正常运行 网上说5
  • 【Unity XR】Unity开发OpenXR

    Unity开发OpenXR 介绍OpenXR相关依赖插件 OpenXR OpenXR Plugin XR Interaction Toolkit XR Plugin Management 安装OpenXR相关依赖插件 Package Man
  • qt MVD 框架入门教学归纳实例:QListView + QAbstractItemModel + QStyledItemDelegate 实现自定义进度条(同时显示文件名 + 实时跟新进度)

    前置理论基础 关于QT的MVD框架这里就不做赘述 通篇介绍的话得占不少版面 其实作为qt开发者 基本上只要有个能跑起来的demo 相关的技术点不难理解 新手学习mvd的难点在于没有一个小型的 直观的demo能直接梳理出三者的关系 关于MVD
  • Ubuntu显示美化 优化 常用插件

    本文不再更新 已迁移到MD文档 参见 Ubuntu显示美化 优化 常用插件 神奏的博客 CSDN博客 1 安装 Extension Manager ubuntu snap商店或者deb商店打开 搜索 Extension Manager 安装
  • vue实现穿梭框功能

  • 路由器的几种模式

    1 AP 无线热点模式 路由器的WAN口接入网线 在其他设备通过路由器的无线连接上网 2 Client 客户端的模式 将无线路由器作为无线网卡来使用 通过无线的方式连接到其他路由上 然后设备通过网线连接上路由器 3 Router 无线路由模
  • Linux的cat命令

    1 cat 显示文件连接文件内容的工具 cat 是一个文本文件查看和连接工具 查看一个文件的内容 用cat比较简单 就是cat 后面直接接文件名 比如 de gt root localhost cat etc fstab de gt 为了便
  • Linux下SVN的安装及SVN常用命令

    SVN的介绍 SVN是一个开源的版本控制系統 svn版本管理工具管理随时间改变的各种数据 这些数据放置在一个中央资料档案库 repository 中 这个档案库很像一个普通的文件服务器 它能记住你每次的修改 查看所有的修改记录 恢复到任何历
  • vue前端项目的结构以及组成部分

    本文结构 在初入前端的时候 一个项目中的文件夹会非常多 与Vue官网的简单demo相差非常大 这也是对前端项目文件组成和几个大的模块的一些介绍 文末还有一些不成文的代码规范 本文目录 项目代码组成 前端项目组成 前端的几大模块 项目编写规范
  • RPC框架的网络线程模型

    一 RPC的网络IO模型 1 连接独占线程或进程 在这个模型中 线程 进程处理来自绑定连接的消息 在连接断开前不退也不做其他事情 当连接数逐渐增多时 线程 进程占用的资源和上下文切换成本会越来越大 性能很差 这就是C10K问题的来源 这种方
  • FOTA升级差分包编译服务器搭建

    奈何公司测试组电脑没有Linux系统 每次测试FOTA升级用的差分包都需要找我来制作 实在麻烦 本想搞个QT界面弄得专业点 后面有时间再去搞吧 现在打算先临时写一个应急 一 Ubuntu端先搭建FTP服务器 参考之前搭建的记录 Ubuntu
  • 【网络】https单向认证和双向认证

    前言 之前面试的时候 面试官问我了解过https的双向认证吗 当时 的确不理解 不过没关系 现在就来补上 正文 1 单向认证 还是有必要先说下单向认证 单向认证是我刚开始接触https的时候就了解到的 下面看一下执行流程图 图是网上找的 再
  • 什么网站适合使用cdn?

    高防cdn的主要功能就是加速和防御 通过建设多个cdn节点来防御各种ddos cc等网络攻击 保证网站的稳定运行 那么高防cdn是如何进行防御的呢 1 高防cdn的防御机制是智能多样的 不是单打独斗类型 起内部的智能机制可以应对各种类型的攻
  • 高阶数据结构之图论

    文章目录 图是什么 图的存储 邻接矩阵 邻接表 无向图邻接表存储 有向图邻接表存储 图的遍历 广度优先遍历 BFS 深度优先遍历 DFS 最小生成树 Prim算法 Kruskal算法 最短路径问题 Dijkstra算法 求单源最短路径 Be
  • C++基础:继承,子类继承父类有规范和要求

    文章目录 1 语法 2 成员的访问权限 3 继承关系的构造顺序 4 同名隐藏 5 多种情况 6 派生对象给基类对象的传递 7 多重继承 8 菱形继承 9 对象构造顺序总结 1 语法 继承关系 A is a B 父类 子类 基类 派生类 继承
  • 【C进阶】指针(二)

    六 函数指针数组 数组是一个存放相同类型数据的存储空间 我们已经学习了指针数组 eg int arr 10 整形指针数组 数组 存放的是整形指针 char arr 5 字符指针数组 数组 存放的是字符指针 那么把函数的地址存到一个数组中 那
  • Laravel 使用数组条件查询时 in和or 的用法

    laravel给出了whereIn的用法 users DB table users gt whereIn id 1 2 3 gt get 或者在闭包中使用whereIn ids 1 2 list User where function qu
  • Kalman滤波——初阶入门

    概要 kalman滤波在机器人控制 数字图像等领域应用非常广泛的一种方法 很多人对其名字不能理解 因为kalman滤波在大多数时候表现出来都是将多个数据进行融合 为什么不叫kalman融合呢 如果你有这个疑问 那就说明你对kalman滤波理