kalman滤波器C++设计仿真案例

news/2025/1/24 19:44:20/

        很多同学看了我之前的文章,都对kalman滤波器的原理有了理解,但我发现,在具体工程设计过程中,还是很多人都感觉到无从下手,一些参数也不知道如何选取。

        这样吧。我这里举一些简单的例子,并用C++来一步一步的进行设计。然后,咱们一起看看最后实现的效果到底如何好吧。

案例1:室内温度测量

        要求用kalman滤波器对一个房间的温度进行测量估计。这个房间的温度大概是25℃,而由于空气流动、光照等影响,室内温度会有小幅的随机波动。这个波动的方差嘛,就暂定为0.01好了。然后我们 用温度计每分钟进行一次测量,温度计的测量误差大概是0.5℃,也就是方差为0.25。

        这是一个非常简单的案例,过程中所有的矩阵都是一维,也就是标量。

        先定义状态量X(k),就是房间温度。房间温度有波动,所以会有系统噪声W(k)。根据题意,W(k)的协方差矩阵Q就等于0.01。于是状态方程就是:

X(k)=X(k-1)+W(k-1)

Q(k)=0.01

再看量测方程。由于是直接测量,所以H阵等于1,而量测方差阵R根据题意,就等于0.25了。

Z(k)=X(k)+V(k)

R(k)=0.25

        由于涉及矩阵计算,所以这里用到了我自己用C++开发的矩阵类。需要的可以到我的空间,或者下面这个链接去下载:

https://download.csdn.net/download/weixin_38898944/90305952

        开始C++程序设计了啊。嗯,注意是C++11,否则我的有些写法可能会报错。

        先设计一个kalman滤波器的类。为了代码复用程度高,我把之前文章里的提到的基本型kalman滤波器拆分成两个类。一个是模型Model类,一个是kalman类。这样如果换一个需求场景,只要更改模型类就好了,kalman类不用变。先看kalman类。头文件内容:

#ifndef KALMAN_H
#define KALMAN_H#include "matrix.h"
#include "model.h"
class Kalman
{size_t m_ns;    //状态维数size_t m_nm;    //量测维数Matrix Xkf;     //状态估计值Matrix Xpre;    //状态预测值Matrix Z;       //量测Matrix K;       //增益Matrix P;       //状态估计的协方差阵Matrix Ppre;    //状态预测的协方差阵Model *mp;      //模型,包含了状态方程、量测方程等Kalman();       //默认构造函数,私有化,不能调用
public:Kalman(size_t ns, size_t nm);           //构造函数,需要确定状态维数和量测维数void Init(const Matrix &X0, const Matrix &P0, Model *mp0);//初始化,初始化X0和P0,并与模型对象挂钩void SetMeasur(const Matrix &Zk);       //量测量进入滤波器void Iterator();                        //滤波器更新过程Matrix GetX() {return Xkf;}             //获取当前状态估计
};#endif // KALMAN_H

源文件:

#include "kalman.h"Kalman::Kalman()
{}Kalman::Kalman(size_t ns, size_t nm)
{m_ns = ns;m_nm = nm;K = Matrix(ns, nm);Xkf = Matrix(ns);P = Matrix(ns, ns);Ppre = Matrix(ns, ns);Z = Matrix(nm);Xpre = Matrix(ns);
}void Kalman::Init(const Matrix &X0, const Matrix &P0, Model *mp0)
{Xkf = X0;P = P0;mp = mp0;
}void Kalman::SetMeasur(const Matrix &Zk)
{Z = Zk;
}void Kalman::Iterator()
{static Matrix I(Matrix::unit(m_ns));Matrix F(mp->GetF());Matrix H(mp->GetH());Matrix Q(mp->GetQ());Matrix R(mp->GetR());Xpre = mp->StateTrans(Xkf);Ppre = F*P*F.Trans() + Q;K = Ppre*H.Trans()*((H*Ppre*H.Trans() + R).Inv());Xkf = Xpre + K*(Z - H*Xpre);P = (I - K*H)*Ppre;
}

        这几个成员函数都非常直观,尤其注意那个Iterator()函数,滤波器运行需要的转移矩阵F,量测矩阵H以及状态协方差阵 Q和量测方差阵R都是从模型里获取的。所以模型变了该模型就好了,不需要改这个滤波器。一步预测那里并没有写成Xpre=H*X,而是让模型对象去更新,这样写是为了适应以后扩展成非线性kalman滤波器,也就是ekf的时候方便一些。

        下面就来设计模型类。头文件:

#ifndef MODEL_H
#define MODEL_H#include "matrix.h"
class Model
{Matrix Q;Matrix R;
public:Model(): Q(Matrix::unit(1)*0.01), R(Matrix::unit(1)*0.25){}Matrix StateTrans(Matrix &X);         //状态转移,这里就是Xpre=H*Xkfvoid StateUpdate(Matrix &X);          //状态转移后还加入噪声,这里就是X=H*X+wMatrix MeasurPre(const Matrix &X);    //量测预测,没有加入量测噪声Matrix Measur(const Matrix &X);       //量测,加入量测噪声Matrix GetF();                        //获取状态转移矩阵,如果是ekf,可以改写这个函数,//获取状态方程的雅各比矩阵Matrix GetH();                        //获取量测矩阵,如果是ekf,改写这个函数,//获取量测方程的雅各比矩阵Matrix GetQ() {return Q;}                //获取模型的状态方程协方差阵Matrix GetR() {return R;}                //获取模型的量测方程的协方差阵
};#endif // MODEL_H

        Q和R都是一维矩阵,且值分别是0.01和0.25。然后是源文件:

#include "model.h"Matrix Model::StateTrans(Matrix &X)
{Matrix F(Matrix::ones(1, 1));return F*X;
}void Model::StateUpdate(Matrix &X)
{X = StateTrans(X);X = X + Sqrtm(Q)*Matrix::randn(1, 1);
}Matrix Model::MeasurPre(const Matrix &X)
{Matrix H(Matrix::ones(1, 1));return  H*X;
}Matrix Model::Measur(const Matrix &X)
{return MeasurPre(X) + Sqrtm(R)*Matrix::randn(1, 1);
}Matrix Model::GetF()
{return Matrix::ones(1, 1);
}Matrix Model::GetH()
{return Matrix::ones(1, 1);
}

        我的矩阵类里已经实现了矩阵的各类运算,包括矩阵加减乘,矩阵与标量的加减乘以及矩阵转置、矩阵求逆、矩阵元素开根号。并能够生成全零矩阵、全1矩阵、单位矩阵、(0,1)正态分布的随机矩阵等。所以以上代码看上去还是很好理解的。

        有了这两个类,主程序就非常简单了:

#include "model.h"
#include "kalman.h"
#include <stdio.h>
int main()
{FILE *fp;fp = fopen("F:/data/data.txt", "w");Model M;Kalman kf(1, 1);Matrix X(Matrix::ones(1, 1)*25.0);Matrix Z(1);Matrix P0(Matrix::ones(1, 1)*0.01);kf.Init(X, P0, &M);for(size_t i = 0; i < 100; i++){M.StateUpdate(X);Z = M.Measur(X);kf.SetMeasur(Z);kf.Iterator();fprintf(fp, "%lf,%lf,%lf\n", X(0), kf.GetX()(0), Z(0));}fclose(fp);return 0;
}

        初值就设定为25℃,P的初值也取的跟Q一样。用kf.Init()函数初始化,并与模型对象挂钩。

        主程序在运行kalman的同时也对室内温度实际值进行了仿真,并将温度实际值(X),kalman滤波值(kf.GetX()),和量测值(Z)保存到文件里。

        来看看效果吧

        蓝线为实际温度,绿线为测量值,红线为滤波输出。喏,看出效果了吧。

        先这样吧。最近有些忙,回头我再写一个状态变化情况的kalman滤波仿真。


http://www.ppmy.cn/news/1565863.html

相关文章

springboot 调用 c++生成的so库文件

一、创建c文件 SoTest.h #pragma once class SoTest {int Add(int a,int b); };SoTest.cpp #include "SoTest.h"int SoTest::Add(int a, int b) {return a b; }二、创建so文件 /home/ubuntu/projects/SoTest/bin/x64/Debug/libSoTest.so 三、java代码 Maven依…

IP-MS、CoIP-MS及AP-MS助力研究蛋白互作组学

蛋白质相互作用在机体的多种病理生理过程中扮演着至关重要的角色。绝大多数蛋白质分子需要通过与其他蛋白质的相互作用才能实现其生物学功能。因此&#xff0c;深入研究蛋白质相互作用组学对于阐明机体病理和生理过程的发生发展机制具有重要的科学意义。 目前&#xff0c;蛋白…

【Linux入门】2w字详解yum、vim、gcc/g++、gdb、makefile以及进度条小程序

文章目录 Ⅰ. Linux 软件包管理器 yum一、什么是软件包&#xff1f;二、查找软件包三、安装与卸载软件包 拓展&#xff1a;lrzsz简介拓&#xff1a;配置 yum 源路径的方法Ⅱ. Linux开发工具vim编辑器一、vim 的基本概念二、vim 的基本操作三、vim 命令模式的操作四、vim 底行模…

@Async注解

Async是Spring框架提供的一个注解&#xff0c;用于标记一个方法为异步方法。 当你在某个方法上加上这个注解后&#xff0c;Spring会用一个单独的线程去执行这个方法&#xff0c;这样主线程就不会被这个方法阻塞&#xff0c;可以继续执行其他任务。 条件1&#xff1a;开启异步…

Antd React Form使用Radio嵌套多个Select和Input的处理

使用Antd React Form使用Radio会遇到嵌套多个Select和Input的处理&#xff0c;需要多层嵌套和处理默认事件和冒泡&#xff0c;具体实现过程直接上代码。 实现效果布局如下图 代码 <Formname"basic"form{form}labelWrap{...formItemLayoutSpan(5, 19)}onFinish{on…

利用MetaNeighbor验证重复性和跨物种分群

进行跨物种研究时&#xff0c;我们经常需要进行注释结果的比较和归类&#xff0c;或者同一物种不同样本之间的注释验证。R语言中有一个包就可以利用直观的热图展示这一需求。 导入包和环境 library(Seurat) library(ggplot2) library(MetaNeighbor) library(SingleCellExperi…

【LeetCode100】--- 寻找重复数

题目传送门 方法一&#xff1a;暴力解法&#xff08;超时&#xff09; 算法原理 双重循环&#xff0c;每次固定一个数&#xff0c;再遍历别的数。比较这两个数是否相等&#xff0c; 若相等则返回这个数。就是重复数。 复杂度分析 时间复杂度&#xff1a;O&#xff08;N方&…

【Flutter】platform_view之AppKitView在哪个flutter版本添加的

通过一下文件对比判断哪个版本添加的 已添加&#xff1a; https://github.com/flutter/flutter/blob/3.16.0/packages/flutter/lib/src/widgets/platform_view.dart https://github.com/flutter/flutter/blob/3.15.0-0.0.pre/packages/flutter/lib/src/widgets/platform_vie…