【控制算法笔记】卡尔曼滤波(一)——基本概念和一维卡尔曼估计实现(python,C++)

本文是个人学习笔记,包含个人理解,如有错误欢迎指正。

前言–关于Kalman Filter

在工程实践中卡尔曼滤波器的应用场景非常丰富,尤其是针对需要大量连续数据处理的自动驾驶和工业现场控制场景中,几乎离不开卡尔曼滤波的踪迹。
在多年前刚接触到单片机的时候对各种算法还不是很了解,当时因为一些比赛需要使用到IMU做角度闭环控制,第一次接触到了卡尔曼滤波器。记得印象中当时使用的是MPU6050计算四元数角度,卡尔曼滤波器可以很好的规避传感器在数据读取的过程中随机的噪声信号,保证一定时间段内读取的数据的稳定性。
那么卡尔慢滤波器是如何起作用的?个人感觉这更像是一个符合概率分布条件下的数值估计器,当保证输入数据附加的噪声能满足先验的概率分布,算法就可以实时计算当前输入数据最符合概率分布的结果并输出。为了实现这一效果,卡尔曼滤波器需要获得输入信号噪声的分布情况以及传感器采集数据的误差范围,同时还需要给系统附加一个计算初值,这些参数都会影响到估计的准确性。在实际的生产应用中有时这些噪声和误差的分布难以做到精准建模来获得数据,但是得益于卡尔慢滤波器中的卡尔慢增益,算法其实可以在一定范围内较好的适应噪声情况,只需要一个近似的估计参数,算法会尽最大可能保证估计值的准确性。也正是应为这个原因才使得卡尔慢滤波器及各种扩展的优化算法得到广泛应用。
对于卡尔慢滤波的估计效果,网上有不少的应用演示是使用卡尔慢滤波相关的算法对某个对象的数据进行未来估计,其实这个说法并不靠谱,Kalman Filter(KF)实质只是一个滤波器,是一个当前实际数据的估计器,是站在过去一段时间的数据上估计当前实际值,它并不能预测未来一段时间的数据。这个概念一定要分清,如果说要预测未来的数据,那就得尝试其它方法,要么对数据进行建模得到对象的数学模型,要么尝试一些机器学习的方式来生成对象的网络模型等。在这样的基础上才可以进一步使用KF去估计模型中的参数等,达到使用KF来辅助预测的效果。

最优递归数字信号处理

首先需要认识到在工程物理的测量中信号存在一定程度的不确定性。因为不存在完美的数学建模,实际物理系统也始终存在扰动,并且测量工具本身也会存在测量误差。

统计样本和误差数据

所以定义以下两种误差形式:测量误差,读取误差(估计误差)
当在一个足够大的观测样本中,可以使用样本均值和样本方差来表征采样对象的统计分布规律。对一组样本有均值: x k ^ = 1 k ( z 1 + z 2 + . . . + z n ) = 1 k ∑ i = 1 k z i \hat{x_k} = \frac{1}{k} (z_1+z_2+...+z_n)=\frac{1}{k}\sum_{i=1}^{k} z_i xk^=k1(z1+z2+...+zn)=k1i=1kzi 拆开均值后凑‘1’可得: x k ^ = 1 k ∑ i = 1 k z i = 1 k ∑ i = 1 k − 1 z i + 1 k z k \hat{x_k} = \frac{1}{k}\sum_{i=1}^{k} z_i=\frac{1}{k}\sum_{i=1}^{k-1} z_i+\frac{1}{k}z_k xk^=k1i=1kzi=k1i=1k1zi+k1zk ⇒ x k ^ = 1 k k − 1 k − 1 ∑ i = 1 k − 1 z i + 1 k z k = k − 1 k x k − 1 ^ + 1 k z k \Rightarrow \hat{x_k}=\frac{1}{k}\frac{k-1}{k-1}\sum_{i=1}^{k-1} z_i+\frac{1}{k}z_k=\frac{k-1}{k}\hat{x_{k-1}}+\frac{1}{k}z_k xk^=k1k1k1i=1k1zi+k1zk=kk1xk1^+k1zk ⇒ x k ^ = k − 1 k x ^ k − 1 + 1 k z k = x ^ k − 1 + 1 k ( z k − x ^ k − 1 ) \Rightarrow \hat{x_k}=\frac{k-1}{k}\hat{x}_{k-1}+\frac{1}{k}z_k=\hat{x}_{k-1}+\frac{1}{k}(z_k-\hat{x}_{k-1}) xk^=kk1x^k1+k1zk=x^k1+k1(zkx^k1) 最后可以得到采样数据的最后一个估计值等于上一次的估计值加上当前测量值与上一次估计值之差。
在上面的最后一个公式中: x k ^ = x ^ k − 1 + 1 k ( z k − x ^ k − 1 ) \hat{x_k}=\hat{x}_{k-1}+\frac{1}{k}(z_k-\hat{x}_{k-1}) xk^=x^k1+k1(zkx^k1) 不妨设后一项的系数   1 k \ \frac{1}{k}  k1   K k \ K_k  Kk 称之为卡尔慢增益。并且卡尔慢增益的值为: K k = e E S T K − 1 e E S T K − 1 − e M E A K K_k = \frac{e_{EST_{K-1}}}{e_{EST_{K-1}}-e_{MEA_{K}}} Kk=eESTK1eMEAKeESTK1 其中   e E S T \ e_{EST}  eEST为估计误差   e M E A \ e_{MEA}  eMEA为测量误差。
对卡尔慢增益(Kalman Gain,KG)很容易可以看出:

  • 当上一个时刻的估计误差远远大于当前时刻的测量测量误差是KG趋于1,当前时刻的估计值就等于当前时刻的测量值
  • 当上一个时刻的估计误差远远小于当前时刻的测量误差时,KG趋于0,当前时刻的估计值就等于上一个时刻的估计值

在实际处理测量数据时往往分三步实现:
Step1: 计算当前时刻的KG值 K G = K k = e E S T K − 1 e E S T K − 1 − e M E A K KG =K_k= \frac{e_{EST_{K-1}}}{e_{EST_{K-1}}-e_{MEA_{K}}} KG=Kk=eESTK1eMEAKeESTK1
Step2: 计算当前时刻的估计值 x k ^ = x ^ k − 1 + K k ( z k − x ^ k − 1 ) \hat{x_k}=\hat{x}_{k-1}+K_k(z_k-\hat{x}_{k-1}) xk^=x^k1+Kk(zkx^k1)
Step3: 更新当前时刻的估计误差 e E S T k = ( 1 − K k ) e E S T k − 1 e_{EST_{k}}=(1-K_k)e_{EST_{k-1}} eESTk=1KkeESTk1

通过不断递归循环最终可以估计出概率上较为准确的估计值。

程序示例

使用python实现简单的卡尔慢估计

import numpy as np
import matplotlib.pyplot as plt

# 生成一维正态分布的随机数组(测量值)
mean_value = 100
std_dev_measurement = 5

np.random.seed(42)  # 设置随机数种子,保证重复运行的结果不变
# 生成一维正态分布的随机数组
random_array = np.random.normal(mean_value, std_dev_measurement, size=80)

# 初始化卡尔曼滤波器参数
state_estimate = mean_value  # 初始状态估计
state_covariance = 2  # 初始状态协方差
process_noise = 2  # 系统演化的噪声
measurement_noise = std_dev_measurement**2  # 测量误差的噪声
predicted_state = 80 # 预测初值

# 存储估计值
estimated_states = []

# 卡尔曼滤波过程
for measurement in random_array:
    # 1、计算卡尔曼增益
    predicted_covariance = state_covariance + process_noise     # 赋值估计误差,估计误差同时加上了一个系统的动态噪声
    kalman_gain = predicted_covariance / (predicted_covariance + measurement_noise)     # 使用估计误差核测量误差求取卡尔曼增益

    # 2、计算当前时刻的估计值
    state_estimate = predicted_state + kalman_gain * (measurement - predicted_state)    # 计算当前时刻的预测值

    # 3、更新当前时刻的估计误差
    state_covariance = (1 - kalman_gain) * predicted_covariance # 更新估计误差

    estimated_states.append(state_estimate)     # 保存当前时刻的估计值
    predicted_state = state_estimate  # 保存上一个时刻的预测值

# 绘制结果
plt.plot(random_array, label='observed value')
plt.plot(estimated_states, label='Kalman estimated value')
plt.plot([0, 80], [100, 100], label='true value', color='red')
plt.legend()
plt.title('Kalman Filter')
plt.show()

在上面的程序中通过随机生成了一组均值为100,方差为5的数据,并且给测量数据附加了大小为12(5*2+2)的扰动数据。令卡卡尔曼滤波器的初始参数为:卡尔曼增益为4,初始估计值为80。根据结果可以看出最后随着估计次数的增加,估计的精度不断提高。
在这里插入图片描述

使用C++/C实现卡尔曼滤波估计

通常卡尔曼滤波需要设计到很多嵌入式系统中,下面同时给出了cpp形式的代码,主要的计算部分将存储数据的vector修改为数组就是c兼容的代码。注意由于c++没有python那样便利的可视化和调包机制,这里画图软件使用GNU Plot,可以自行搜索安装该程序。

#include <iostream>
#include <vector>

#include<stdio.h>
#include<string.h>
#include <fstream>
#include <sstream>
#include <cmath>
int main() {
    const int size = 80;                    // 生成的数据长度
    double mean_value = 100.0;              // 待生成的随机数的均值
    double std_dev_measurement = 5.0;       // 随机数的方差
    double process_noise = 2.0;             // 系统的扰动误差
    double predicted_state = 80.0;          // 预测初值k-1步
    double state_estimate = mean_value;     // 预测初值k步
    double state_covariance = 2.0;          // 初始的估计误差
    double measurement_noise = std_dev_measurement * std_dev_measurement;   // 测量误差

    // 设置随机数种子,保证重复运行的结果不变
    std::srand(42);
    // 生成一维正态分布的随机数组
    std::vector<double> random_array;
    // Box-Muller算法利用均匀分布产生高斯分布随机数
    for (int i = 0; i < 80; ++i) {
        double U1 = rand() * 1.0f / RAND_MAX; // 0~1均匀分布
        double U2 = rand() * 1.0f / RAND_MAX; // 0~1均匀分布
        double Z = sqrt(-2 * log(U1)) * cos(2 * 3.1415 * U2);// 均值为0,方差为1的正态分布
        double Y = 100 + 3 * Z; // 均值为100,方差为9的正态分布
        random_array.push_back(Y);
    }

    // 存储估计值
    std::vector<double> estimated_states;

    // 卡尔曼滤波过程
    for (const auto& measurement : random_array) {
        // 1、计算卡尔曼增益
        double predicted_covariance = state_covariance + process_noise;     // 计算估计误差
        double kalman_gain = predicted_covariance / (predicted_covariance + measurement_noise); // 计算卡尔曼增益

        // 2、计算当前时刻的估计值
        state_estimate = predicted_state + kalman_gain * (measurement - predicted_state);

        // 3、更新当前时刻的估计误差
        state_covariance = (1 - kalman_gain) * predicted_covariance;

        estimated_states.push_back(state_estimate);
        predicted_state = state_estimate;   // 保存当前时刻的估计值
    }

    // 输出当前估计值到文件
    std::ofstream dataFile("data.txt");
    // 输出估计值
    std::cout << "Estimated States:" << std::endl;
    int index = 0;
    for (const auto& estimate : estimated_states) {
        std::cout << estimate << " ";
        dataFile << index << " " << (double)estimate << " " << index << " "  << mean_value << " "<< index<<" "<< random_array[index] <<std::endl;
        index++;
    }
    std::cout << std::endl;
    dataFile.close();

    //  =======================    下面的代码都是为了画图     ======================
    // 创建一个GNU Plot进程
    FILE* gnuplotPipe = _popen("gnuplot -persist", "w");
    // 设置GNU Plot的属性
    if (gnuplotPipe) {
        fprintf(gnuplotPipe, "set size 1,1\n");
        fprintf(gnuplotPipe, "set title 'Kalman Filter'\n");
        fprintf(gnuplotPipe, "set xlabel 'Iteration'\n");
        fprintf(gnuplotPipe, "set ylabel 'Value'\n");

        // 发送GNUplot命令绘制动态图
        fprintf(gnuplotPipe, "plot 'data.txt' using 1:2 with lines linestyle 6 title 'Kalman estimated value', \
                          'data.txt' using 3:4 with lines linestyle 3 title 'true value', \
                          'data.txt' using 5:6 with lines linestyle 3 title 'observed value'\n");
        fflush(gnuplotPipe);
        fprintf(gnuplotPipe, "pause mouse\n");
        _pclose(gnuplotPipe);
    }
    else {
        std::cerr << "Error opening GNUplot pipe" << std::endl;
    }
    return 0;
}

在这里插入图片描述
通过可是化输出可知系统有良好的估计能力。
对于matlab程序可以参考上面的python代码,很容易可以改写出来相同效果程序。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/354830.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

类和对象 第五部分第二小节:左移运算符重载

作用&#xff1a;可以输出自定义数据类型 代码案例&#xff1a; 1.成元函数重载&#xff1a; 利用成员函数重载写出来的代码为 void operate<<(cout)等于p<<cout&#xff0c;与预期效果不符。因此我们不会利用成员函数重载<<运算符&#xff0c;因为无法实现c…

06.领域驱动设计:使用DDD分层架构,可以有效降低层与层之间的依赖

目录 1、概述 2、什么是DDD分层架构 1.用户接口层 2.应用层 3.领域层 4.基础层 3、DDD分层架构最重要的原则是什么 4、DDD分层架构如何推动架构演进 1.微服务架构的演进 2.微服务内服务的演进 5、三层架构如何演进到DDD分层架构 我们该怎样转向DDD分层架构 6、总结…

0127-2-Vue深入学习5—Vue-Router路由模式

1、Vue-Router三种路由模式&#xff1a; hash&#xff1a;#️⃣使用URL hash 值来做路由&#xff0c;支持所有路由器&#xff1b;history:&#x1f4d6;依赖HTML5 History API和服务器配置&#xff1b;abstract:⛓支持所有JS运行环境&#xff0c;Node.js服务端&#xff1b; 1.1…

陪诊小程序开发:让医疗服务更贴心

随着社会的发展和人口老龄化的加剧&#xff0c;医疗服务的需求日益增长。在这个背景下&#xff0c;陪诊小程序的开发应运而生&#xff0c;为医疗服务提供了更加便捷、高效的解决方案。本文将探讨陪诊小程序开发的意义、功能、优势以及未来发展趋势。 一、陪诊小程序开发的意义…

ES -倒排索引

倒排索引 在学习ES中的映射之前&#xff0c;我们先学习一下ES中的倒排索引。 定义 倒排索引就是单词到文档id的关系&#xff0c;如下所示&#xff0c;左边是一个正排索引&#xff0c;右边就是一个单词到文档id的倒排索引&#xff1a; 倒排表以字或词为关键字进行索引&#x…

XCTF:Normal_RSA[WriteUP]

从题目中获取到两个文件 flag.enc内容是通过rsa加密了的密文 pubkey.pem是rsa公钥&#xff0c;加密者利用这个文件对flag原文进行了加密 如果对rsa加密算法不了解的可以补一下教学视频 数学不好也能听懂的算法 - RSA加密和解密原理和过程_哔哩哔哩_bilibili 使用openssl对公…

【前端web入门第二天】02 表单-input标签-单选框-多选框

表单 文章目录: 1.input标签基本使用 1.1 input标签占位文本1.2 单选框 radio 1.3 多选框 checkbox 作用:收集用户信息。 使用场景: 登录页面注册页面搜索区域 1.input标签基本使用 input标签type属性值不同&#xff0c;则功能不同。 <input type"..."&g…

BGP同步规则

BGP同步规则:开启同步下,从IBGP收到一条路由不会传给任何EBGP邻居(实验效果IBGP邻居和EBGP邻居都不传),除非从自身的IGP中也学到这条路由。目的是防止AS内部出现路由黑洞,向外部通告了一个本AS不可达的虚假的路由。 同步规则只影响从IBGP邻居收到的路由,不影响从EBGP邻居收…

伊恩·斯图尔特《改变世界的17个方程》相对论笔记

它告诉我们什么&#xff1f; 物质包含的能量等于其质量乘以光速的平方。 为什么重要&#xff1f; 光的速度很快&#xff0c;它的平方绝对是一个巨大的数。1千克的物质释放出的能量相当于史上最大的核武器爆炸所释放能量的约40%。一系列相关的方程改变了我们对空间、时间、物质和…

C语言 unicode 字符串处理Demo

概述 做个笔录 1、示例1 #include <stdio.h> #include <string.h> #include "main.h"struct strStruct {uint16_t phone_num[16];uint16_t message[400]; };void filterSpaces(char* src, char* dst) {uint8_t i 0;uint8_t flag 0;char* p NULL; fo…

【保姆级教程】Windows11下go-zero的etcd安装与初步使用

【Go-Zero】Windows11下etcd的安装与初步使用 大家好 我是寸铁&#x1f44a; 总结了一篇Windows11下etcd的安装与初步使用的文章✨ 喜欢的小伙伴可以点点关注 &#x1f49d; 前言&#xff1a; 在使用etcd 前&#xff0c;我们需要了解一下etcd 是什么&#xff0c;为什么使用etcd…

C++ STL中list迭代器的实现

list 的模拟实现中&#xff0c;重难点在于迭代器功能的实现&#xff0c;因此本文只围绕 iterator 及 const_iterator 的设计进行介绍&#xff0c;其余如增删查改则不再赘述——在C语言的基础上&#xff0c;这些都非常简单。 与 string / vector 不同&#xff0c;list 的节点原生…

基于springboo校园社团信息管理系统

摘要 随着高校规模的扩大和学生社团活动的日益丰富多彩&#xff0c;校园社团信息管理成为一个备受关注的问题。为了更有效地组织、管理和推动校园社团的发展&#xff0c;本文设计并实现了一套基于Spring Boot的校园社团信息管理系统。本系统以实现社团信息的集中管理和高效运营…

Android Automotive:在路上释放 Android 操作系统的力量

Android Automotive&#xff1a;在路上释放 Android 操作系统的力量 Android 在汽车行业的历程车载信息娱乐系统 (IVI) 的演变汽车中的 Android&#xff1a;演变和进步Android 汽车操作系统的崛起Polestar 2&#xff1a;开创 Android 汽车体验Android 开源项目 (AOSP) 及其他项…

不确定优化入门:用简单实例讲明白随机规划、鲁棒优化和分布鲁棒优化

文章目录 1 引言2 学习动机3 经典问题4 解决方案4.1 忽略不确定性4.2 随机规划4.3 鲁棒优化4.4 分布鲁棒优化 5 总结相关阅读 1 引言 按2024的原定计划&#xff0c;今年开始要学习不确定优化了。 粗略翻阅了一些相关的书籍和教程&#xff0c;大都包含许多数学公式&#xff0c…

网络安全科普:SSL证书保护我们的网上冲浪安全

当我们在线上愉快冲浪时&#xff0c;各类网站数不胜数&#xff0c;但是如何判定该站点是安全还是有风险呢&#xff1f; 当当当&#xff0c;SSL数字证书登场&#xff01;&#xff01; SSL证书也称为数字证书&#xff0c;是一种用于保护网站和用户之间通信安全的加密协议。由权…

Steam游戏免费玩 gamebox 一起来玩幻兽帕鲁吧

steam大作免费畅玩 幻兽帕鲁也有资源 UI设计精美 还有补票链接&#xff0c;点击一下&#xff0c;就能跳转至Steam商店 可以自定义安装位置 下载链接 gamebox&#xff1a;https://rssm666.lanzn.com/b039g6dqj

Windows XP x86 sp3 安装 Google Chrome 49.0.2623.112 (正式版本) (32 位)

1 下载地址&#xff1b; https://dl.google.com/release2/h8vnfiy7pvn3lxy9ehfsaxlrnnukgff8jnodrp0y21vrlem4x71lor5zzkliyh8fv3sryayu5uk5zi20ep7dwfnwr143dzxqijv/49.0.2623.112_chrome_installer.exe 2 直接 双击 49.0.2623.112_chrome_installer.exe 安装&#xff1b; 3 …

算法学习之每日一题Day4

题目 费解的开关 一、有关题目&#xff08;涉及算法&#xff1a;递推&#xff0c;模拟&#xff09; 1.题目来源&#xff1a;《算法竞赛进阶指南》 Acwing 95 2.题目链接 https://www.acwing.com/problem/content/description/97/ 3.题目描述 你玩过“拉灯”游戏吗&…

范仲淹大直男逆袭,先天下之忧而忧

人在最艰苦时&#xff0c;最能体现英雄本色。 天底下最苦的是读书。读书要眼到、手到、心到&#xff0c;专心致志&#xff0c;灵活运用。 范仲淹读书很用功&#xff0c;每天煮一锅粥。等到第二天&#xff0c;粥凝固了&#xff0c;范仲淹把隔夜粥划为四块&#xff0c;早上吃两块…