扩展卡尔曼滤波算法 EKF

扩展卡尔曼滤波的仿真案例,参考书为北航宇航学院王可东老师的Kalman滤波基础及Matlab仿真
一、状态模型:
在这里插入图片描述
二、测量模型:
在这里插入图片描述
状态方程和测量方程中的噪声均为期望为零的白噪声。
三、状态模型和测量模型的线性化(Jacobian矩阵):
在这里插入图片描述
四、状态模型和测量模型的噪声矩阵及初始状态及协方差矩阵:
在这里插入图片描述
五、C++ 仿真源码:
EKF.h

#pragma once
#include <fstream>     
#include <string>   
#include <iostream>
#include <Eigen/Dense>
#include "RandomGenerate.h"

class EKF
{
public:
	EKF();
	virtual ~EKF();
	void Filter();      // 扩展卡尔曼滤波主函数

private:
	void Initialize();             // 初始化相关参数
	void GenerateXreal(int k);     // 生成状态向量真实值
	void GenerateZreal();          // 生成测量向量值
	
	Eigen::Vector2d Nonlinearf(int k);                                                          // 非线性状态方程(非线性映射)
	Eigen::Matrix2d JacobianMatrixF();                                                          // 计算状态方程雅可比矩阵(线性化矩阵)
	void Prediction(const Eigen::Vector2d& f, const Eigen::Matrix2d& F);                        // 一步预测
	Eigen::Vector2d Nonlinearh();                                                               // 非线性测量方程
	Eigen::Matrix2d JacobianMatrixH();                                                          // 计算测量方程雅可比矩阵(线性化矩阵)
	void Update(const Eigen::Vector2d& h, const Eigen::Matrix2d& H);                            // 测量更新

private:
	Eigen::Vector2d xpre;          // 状态向量预测值

	Eigen::Matrix2d Ppre;          // 状态协方差矩阵预测值

	Eigen::Matrix2d Q;             // 状态方程噪声矩阵

	Eigen::Matrix2d R;             // 测量方程噪声矩阵

	Eigen::Matrix2d K;             // 卡尔曼增益矩阵

	Eigen::Vector2d xest;          // 状态向量估计值

	Eigen::Matrix2d Pest;          // 状态协方差矩阵估计值

	Eigen::Vector2d xreal;         // 状态向量真实值

	Eigen::Vector2d zreal;         // 测量向量值

private:
	std::string FileName;          // 文件名

	std::ofstream outFile;         // 文件路径
};

EKF.cpp

#include "EKF.h"

// 构造函数
EKF::EKF() : FileName("./FilterEKF.txt"), outFile(FileName, std::ios::out)
{
	// 初始化相关参数
	Initialize();
}

// 析构函数
EKF::~EKF()
{

}

// 初始化相关参数
void EKF::Initialize()
{
	// 初始化状态方程噪声矩阵
	Q << 0.01, 0,
		 0, 0.1;

	// 初始化测量方程噪声矩阵
	R << 1, 0,
		 0, 0.1;

	// 初始化状态协方差矩阵估计值
	Pest << 10, 0,
			0, 10;

	// 初始化状态向量估计值
	xest << 1, 1;

	// 真实值
	xreal = xest;

	return;
}

// 生成状态向量真实值
void EKF::GenerateXreal(int k)
{
	xreal(0) = xreal(1) * sin(xreal(0)) + 0.1 * k + sqrt(Q(0,0)) * getRandom();
	xreal(1) = xreal(0) + cos(xreal(1)) * cos(xreal(1)) - 0.1 * k + sqrt(Q(1, 1)) * getRandom();

	return;
}

// 生成测量向量值
void EKF::GenerateZreal()
{
	zreal(0) = xreal.norm() + sqrt(R(0, 0)) * getRandom();;
	zreal(1) = atan(xreal(0) / xreal(1)) + sqrt(R(1, 1)) * getRandom();

	return;
}

// 非线性状态方程(非线性映射)
Eigen::Vector2d EKF::Nonlinearf(int k)
{
	Eigen::Vector2d f;
	f(0) = xest(1) * sin(xest(0)) + 0.1 * k;
	f(1) = xest(0) + cos(xest(1)) * cos(xest(1)) - 0.1 * k;
	return f;
}

// 计算状态方程雅可比矩阵(线性化矩阵)
Eigen::Matrix2d EKF::JacobianMatrixF()
{
	Eigen::Matrix2d F;
	F(0, 0) = xest(1) * cos(xest(0));
	F(0, 1) = sin(xest(0));
	F(1, 0) = 1.0;
	F(1, 1) = -sin(2*xest(1));
	return F;
}

// 一步预测
void EKF::Prediction(const Eigen::Vector2d &f, const Eigen::Matrix2d &F)
{
	// 状态向量预测值
	xpre = f;

	Eigen::Matrix2d Pxx = F * Pest * F.transpose();

	// 状态协方差矩阵预测值
	Ppre = Pxx + Q;

	return;
}

// 非线性测量方程
Eigen::Vector2d EKF::Nonlinearh()
{
	Eigen::Vector2d h;
	h(0) = xpre.norm();
	h(1) = atan(xpre(0) / xpre(1));
	return h;
}

// 计算测量方程雅可比矩阵(线性化矩阵)
Eigen::Matrix2d EKF::JacobianMatrixH()
{
	Eigen::Matrix2d H;
	double a = xpre.norm();
	H(0, 0) = xpre(0) / a;
	H(0, 1) = xpre(1) / a;
	H(1, 0) = xpre(1) / (a * a);
	H(1, 1) = -xpre(0) / (a * a);
	return H;
}

// 测量更新
void EKF::Update(const Eigen::Vector2d& h, const Eigen::Matrix2d& H)
{
	Eigen::Matrix2d Pzz = H * Ppre * H.transpose();
	Eigen::Matrix2d Pvv = Pzz + R;
	Eigen::Matrix2d Pxz = Ppre * H.transpose();

	K = Pxz * Pvv.inverse();
	xest = xpre + (K * (zreal - h));
	Pest = Ppre - (K * Pvv * K.transpose());
	return;
}

// 扩展卡尔曼滤波主函数
void EKF::Filter()
{
	std::cout << "请输入采样点个数:" << std::endl;
	int num;
	std::cin >> num;

	for (int i = 0; i != num; i++)
	{
		// 生成状态向量真实值
		GenerateXreal(i);

		// 生成测量向量值
		GenerateZreal();

		// 非线性状态方程(非线性映射)
		Eigen::Vector2d f = Nonlinearf(i);

		// 计算状态方程雅可比矩阵(线性化矩阵)
		Eigen::Matrix2d F = JacobianMatrixF();

		// 一步预测
		Prediction(f, F);

		// 非线性测量方程
		Eigen::Vector2d h = Nonlinearh();

		// 计算测量方程雅可比矩阵(线性化矩阵)
		Eigen::Matrix2d H = JacobianMatrixH();

		// 测量更新
		Update(h, H);

		// 保存到文件
		outFile << xreal(0) << ", " << xreal(1) << ", " << xest(0) << ", " << xest(1) << ", " << xpre(0) << ", " << xpre(1) << std::endl;

		// 输出到控制台
		//std::cout<< i <<":  " << abs(xreal(0)-xest(0)) << ", " << abs(xreal(1) - xest(1)) << std::endl;
	}
	return;
}

demo.cpp

#include "UKF.h"
#include "EKF.h"

int main()
{
	UKF ukf;
	//ukf.Filter();

	EKF ekf;
	ekf.Filter();

	system("pause");
	return 0;
}

六、仿真结果:

%% 测试 C++ 程序的可行性。
clear;
clc;

%% 读入C++数据
x = dlmread('Filter.txt');
n = length(x(:,1));
t = 1 : n;

%% 状态
figure;
plot(t, x(:,1), '*-');
hold on;
plot(t, x(:,2), 'o-');
legend('real1','real2');
title('状态');
xlabel('时间/s');
grid on;

%% 状态1
figure;
plot(t, x(:,1), 's-', t, x(:,3), 'o-', t, x(:,5),'*-');
legend('real1','est1','pre1');
title('状态1');
xlabel('时间/s');
grid on;

%% 状态2
figure;
plot(t, x(:,2), 's-', t, x(:,4), 'o-', t, x(:,6),'*-');
legend('real2','est2','pre2');
title('状态2');
xlabel('时间/s');
grid on;

%% 状态1误差
figure;
plot(t, x(:,1)-x(:,3), 'o-', t, x(:,1)-x(:,5),'*-');
legend('est1','pre1');
title('状态1误差');
xlabel('时间/s');
grid on;

%% 状态2误差
figure;
plot(t, x(:,2)-x(:,4), 'o-', t, x(:,2)-x(:,6),'*-');
legend('est2','pre2');
title('状态2误差');
xlabel('时间/s');
grid on;

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

由图 1、图 2 可知,这两个状态虽然是非线性的,但是其总体趋势都呈线性变化,即非线性程度较弱,因此采用 EKF 滤波算法有望实现较好的估计结果;由图 3、图4 来看,EKF 滤波效果较好,对状态 1 和状态 2 均能很好的估计,滤波误差较小,且滤波(估计)的精度比预测的精度高。

  • 2
    点赞
  • 41
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值