完整的卡尔曼滤波器推导、参数分析以及仿真(带有目标跟踪效果对比)
完整的卡尔曼滤波器推导、参数分析以及仿真(带有目标跟踪效果对比)
1. 引言
之前看过卡尔曼滤波器的介绍,但是学得比较迷惑,只知道它有预测作用,不知道怎么去使用。这
篇博客将从理论、代码、实际效果几个方面解释卡尔曼滤波器,帮助大家在项目中使用卡尔曼滤波器。
比较有启发的几个教程为:
卡尔曼滤波器OpenCV的具体实现:https://blog.csdn.net/gdfsg/article/details/50904811
Matlab的讲解什么是卡尔曼滤波器:https://www.zhihu.com/question/23971601
卡尔曼滤波器里的具体参数的作用:https://zhuanlan.zhihu.com/p/110107365
接下来我将针对各大神的博客以及我个人的理解进行卡尔曼滤波器的介绍,这其中肯定存在一些错
误,希望大家批评指正,能跟大家一起讨论学习进步~
2. 算法效果
直接附上我在跟踪器中加入卡尔曼滤波器后跟踪框的效果:
两个图分别为原始跟踪器的跟踪结果与加上卡尔曼滤波以后的跟踪框效果,发现当加上卡尔曼滤波
以后跟踪框变得非常顺滑~~~,从视觉观感上来说看着更加丝滑了。
3. 卡尔曼滤波的迭代
借用Matlab的例子:当一辆汽车直线匀速,可以使用GPS对其进行定位,但是测量结果存在一定误
差,所以车辆位置不够准确,但是在这个过程中,我们还知道汽车的运行速度,这可以帮助我们得到汽
车的位置的预测值。
通过预测值与测量值的计算我们可以得到汽车位置的最优值。
3.1 问题描述
3.2 卡尔曼滤波器的计算
3.3 卡尔曼滤波器迭代过程:
/////////////////////////////////////////////
同学们可能对F状态矩阵和H观测矩阵有比较大的迷惑,不知道这两个矩阵到底有什么作
用,不要着急,接下来我会用例子对参数进行讲解,哪些参数需要初始化,如何初始化。
////////////////////////////////////////////
3.4 参数分析
我们再来看看这张图,卡尔曼滤波可以用一句话总结:通过汽车预测结果的高斯分布以及测量
值的高斯分布,通过高斯相乘可以得到汽车位置的最佳值。
如果我们有多个传感器,获得了多个位置的测量值,我们仍然可以用高斯滤波进行目标最佳位置估
计:
4. 目标跟踪中卡尔曼滤波器的简单使用
在目标跟踪中,我想对跟踪框的中心点坐标进行卡尔曼滤波,减小跟踪框的“卡顿”现象,状态量包括
中心点x和y坐标,以及x和y的变化率。状态量X为:
实时观测的仅观测中心点的坐标,所以观测矩阵H为:
目标跟踪中假设目标为匀速直线运动,所以状态矩阵F为:
Z矩阵为实际每次的测量矩阵:
P估计量协方差,R测量协方差,Q预估过程协方差的初始化(R和Q可以自行进行初始化),在目标跟踪中我初始化为:
针对R和Q的协方差初始化,不同的初始化有着不同的效果。如果测量协方差的越小,最终的结果更
趋向于测量的结果,反之则趋向于预测结果。这里不再推导公式了~~
(链接https://www.zhihu.com/question/23971601 第4个视频4分钟有结果的数学推导;
链接https://zhuanlan.zhihu.com/p/109859418 对R和Q协方差矩阵的推导)
5. OpenCV中卡尔曼滤波的使用
链接https://blog.csdn.net/gdfsg/article/details/50904811中代码较为清晰,原链接中还有另外的两个
例子。
#include "opencv2/video/tracking.hpp"
#include <opencv2/legacy/legacy.hpp> //#include "cvAux.h"
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/core/core.hpp>
#include <stdio.h>
using namespace cv;
using namespace std;
int main( )
{
float A[10][3] =
{
10, 50, 15.6,
12, 49, 16,
11, 52, 15.8,
13, 52.2, 15.8,
12.9, 50, 17,
14, 48, 16.6,
13.7, 49, 16.5,
13.6, 47.8, 16.4,
12.3, 46, 15.9,
13.1, 45, 16.2
};
const int stateNum=3;
const int measureNum=3;
KalmanFilter KF(stateNum, measureNum, 0);
KF.transitionMatrix = *(Mat_<float>(3, 3) <<1,0,0,0,1,0,0,0,1); //转移矩阵A
setIdentity(KF.measurementMatrix); //测量矩阵H
setIdentity(KF.processNoiseCov, Scalar::all(1e-5)); //系统噪声方差矩阵Q
setIdentity(KF.measurementNoiseCov, Scalar::all(1e-1)); //测量噪声方差矩阵R
setIdentity(KF.errorCovPost, Scalar::all(1));
Mat measurement = Mat::zeros(measureNum, 1, CV_32F);
//初始状态值
KF.statePost = *(Mat_<float>(3, 1) <<A[0][0],A[0][1],A[0][2]);
cout<<"state0="<<KF.statePost<<endl;
for(int i=1;i<=9;i++)
{
//预测
Mat prediction = KF.predict();
//计算测量值
measurement.at<float>(0) = (float)A[i][0];
measurement.at<float>(1) = (float)A[i][1];
measurement.at<float>(2) = (float)A[i][2];
//更新
KF.correct(measurement);
//输出结果
cout<<"predict ="<<"\t"<<prediction.at<float>(0)<<"\t"<<prediction.at<float>(1)<<"\t"<<prediction.at<float>(2)<<endl;
cout<<"measurement="<<"\t"<<measurement.at<float>(0)<<"\t"<<measurement.at<float>(1)<<"\t"<<measurement.at<float>(2)<<endl;
cout<<"correct ="<<"\t"<<KF.statePost.at<float>(0)<<"\t"<<KF.statePost.at<float>(1)<<"\t"<<KF.statePost.at<float>(2)<<endl;
}
system("pause");