1.卡尔曼滤波的导论

卡尔曼滤波器(Kalman Filter),是由匈牙利数学家Rudolf Emil Kalman发明,并以其名字命名。卡尔曼出生于1930年匈牙利首都布达佩斯。1953,1954年分别获得麻省理工学院的电机工程学士以及硕士学位。1957年于哥伦比亚大学获得博士学位。卡尔曼滤波器是其在博士期间的研究成果,他的博士论文是《A New Approach to Linear Filtering and Prediction Problem》[1]

卡尔曼滤波器是一个最优化自回归数据处理的算法,在很多时候它是解决大部分最优问题最优且效率最高的方法。广泛应用已经超过了35年,如机器人导航,控制,传感器数据融合甚至军事方面的雷达系统目标跟踪,人脸识别,图像分割和边缘检测等。

1.1 例子

下面以一个关于温度估计的例子(来源于网络)来阐述卡尔曼方法的基本思想。

假设我们要研究的对象是一个房间的温度。根据你的经验判断,这个房间的温度是恒定的,也就是下一分钟的温度等于现在这一份中的温度(假设我们用1分钟来做时间单位)。假设你对你的经验不是100%的相信,可能会有上下偏差几度。我们把这些偏差看成是高斯白噪声(White Gaussian Noise),也就是这些偏差跟前后时间是没有关系的,而且符合高斯分布(Gaussian Distribution)。另外,我们在房间里放一个温度计,但是这个温度计也不准确,测量值会比实际值有偏差。我们也把这些偏差看成是高斯白噪声。

现在对于某一分钟我们有两个有关于该房间的温度值:你根据经验的预测值(系统的预测值)和温度计的值(测量值)。下面我们要用这两个值并结合他们各自的噪声来估计出房间的实际温度值。

假设我们要估计k时刻的时机温度值。首先你要根据k-1时刻的温度值,来预测k时刻的温度。因为你相信温度是恒定的,所以你会得到k时刻的温度预测值是跟k-1时刻一样的,假设是23度,同时该值的噪声偏差是5度(5是这样得到的:如果k-1时刻估计出的最优温度值偏差时3度,你对自己预测不确定度是4度,他们平方相加在开放,就是5)。然后你从温度计那里得到了k时刻 温度值,假设是25度,同时改制存在偏差4度。

由于我们用于估计k时刻的时机温度有两个温度值,分别是23和25度,究竟实际温度是多少呢?相信自己的经验还是相信温度计呢?究竟谁的置信度高,我们可以通过协方差来判断,即,由此我们可以估算出k时刻的实际温度值是:23+0.78*(25-23)=24.56。可以看出,温度计的协方差比较小,因此我们选择相信温度计多些),所以估算出的最优温度值偏向温度计的值。

现在我们已经得到k时刻的最优温度值了,下一步就是要进入k+1时刻,进行新的最优估算。到现在位置,好像还没有看到什么自回归的东西出现。对了,在进入k+1时刻之前,我们还要算出k时刻那个最优值(24.56)的偏差, 算法如下:,这里的5就是上面k时刻你预测的那个23度温度值的偏差(5),得出的2.35就是进入k+1时刻以后k时刻估算出的最优温度值的偏差(对应于上面的3)。

如此循环下去,卡尔曼滤波器就不断的把协方差递归,从而估算出最优的温度值。此方法的运算速度很快,而且它只保留了上一个时刻的协方差。上面的kg,就是卡尔曼增益(Kalman Gain)。他可以随不同时刻而改变他自己的值。

假设k+1时刻温度计读书时24度,那么k+1时刻的最优温度估计为。更新最优值的偏差误差为

1.2 理论

接下来我们来描述卡尔曼滤波器的原理。下面的描述,会涉及一些基本的概念知识,包括概率,随机变量,高斯分布还有状态空间模型等。

首先,引入一个离散控制过程的系统。该系统可用一个线性随机差分方程来描述:

其次,系统的测量值:

以上式子中,X(k)是k时刻的系统状态,U(k)是k时刻对系统的控制量。A和B是系统参数,对于多模型系统,他们为矩阵。Z(k)是k时刻的测量值,H是测量系统的参数,对于多测量系统,H为矩阵。W(k)和V(k)分别表示过程和测量的噪声。他们被假设成高斯白噪声,他们的协方差分别是Q,R(这里我们假设他们不随系统状态的变化而变化)。

对于满足上面的条件,卡尔曼滤波器是最优的信息处理器。下面我们来用他们结合他们的协方差来估算系统的最优输出。

首先我们要利用系统的过程模型,来预测下一个状态的系统。假设现在的系统状态是k,根据系统的模型,可以基于系统的上一个状态而预测出现在的状态:

式子(a)中,是利用上一状态预测的结果,是上一状态最优的结果,U(k)为现在状态的控制量,如果没有控制量,它可以为0.

到现在位置,我们的系统结果已经更新了,可是,对应于的协方差还没有更新。我们用P表示协方差:

式(b)中,对应的协方差,P(k-1|k-1)是X(k-1|k-1)对应的协方差,A'表示A的转置矩阵,Q表示系统过程的协方差。式子a,b就是卡尔曼滤波器五条公式中的2条,也就是对系统的预测。

现在我们有了现在状态的预测结果,然后我们再收集现在状态的测量值。结合预测值和测量值,我们可以得到现在状态(k)的最优化估计值X(k|k):

其中Kg为卡尔曼增益(Kalman Gain):

到现在为止,我们已经得到了k状态下最优的估算值X(k|k)。但是为了要卡尔曼滤波器不断的运行下去知道系统结束,我们还要更新k状态下X(k|k)的协方差:

其中I为1的矩阵,对于单模型单测量,I=1,。当系统进入k+1状态时,P(k|k)就是式子(b)的.这样算法就可以递归下去了。

卡尔曼滤波器的原理就是由式子(a)-(e)五条基本公式组成。下面以卡尔曼滤波器在目标边框预测中的应用进行建模和代码实现

2. 卡尔曼滤波在目标边框预测中的应用

2.1 建模

假设已知t-1时刻,目标边框, 分别表示t-1时刻边框左上角x,y坐标点,右下角x,y坐标点。假设每条边以均匀速度变化,即。同时知道t时刻下,的观测量。现在我们需要预测t时刻下,边框的最优边框预测。边框如下图所示

系统状态表示为,其元素分别表示边框的左上角顶点的x,y和右下角顶点的x,y,以及它们的变化量。系统状态方程中的A可表示为:

由于没有控制量,所以B=0,W为高斯白噪声,设置为0。则系统状态方程(a)可表示为:

(a')

先验协方差可表示为:

(b')

当k=1时,P(0)我们设置为:

是系统过程的协方差,我们设置为:

接下来可以计算卡尔曼增益:

其中

得到卡尔曼增益后,那么最优系统状态应为:

接下来更新X(k)的协方差,更新公式为:

如此循环下去,就可以不断的预测目标边框的值了。

  2.2基于OpenCV的实现

MotionFilter.h

#pragma once

#include "opencv2/opencv.hpp"

#include "opencv2/highgui/highgui.hpp"

using namespace cv;

enum MotionFilterType{KALMAN_FILTER = 0, CONDENSATION_FILTER = 1};//滤波器类型

#ifndef CSST_CAMMOV_STATE

#define CSST_CAMMOV_STATE

enum CamMovStat{CMS_MOVING, CMS_STOP, CMS_UNCERTEN};

#endif

//相机状态

#ifndef CSST_CAMSTATE

#define CSST_CAMSTATE

class CamState

{

public:

float x;//x移动方向

float y;//y移动方向

float step;//移动补偿

CamMovStat state;//相机状态

CamState(float x=0., float y=0., float step=0.,CamMovStat state=CamMovStat::CMS_STOP)

{

this->x = x;

this->y = y;

this->state = state;

this->step = step;

}

};

#endif

class CMotionFilter

{

public:

CMotionFilter();

virtual ~CMotionFilter(void);

// 初始化运动滤波跟踪

virtual bool Init(cv::Rect& inBBox) = 0;

// 更新运动滤波器,获得新的目标边框

virtual int Update(cv::Rect& inBBox, const CamState& inCamState) = 0;

// 获取滤波器类型,返回卡尔曼 或者Cendensation

virtual MotionFilterType GetFilterType(void) = 0;

};

MothonFilter.cpp

#include "MotionFilter.h"

CMotionFilter::CMotionFilter()

{

}

CMotionFilter::~CMotionFilter(void)

{

}

KalmanMotionFilter.h

#pragma once

#include "motionfilter.h"

class CKalmanMotionFilter :

public CMotionFilter

{

public:

CKalmanMotionFilter();

~CKalmanMotionFilter(void);

// 初始化运动滤波跟踪

virtual bool Init(cv::Rect& inBBox);

// 更新运动滤波器,获得新的目标边框

virtual int Update(cv::Rect& inBBox, const CamState& inCamState);

// 获取滤波器类型,返回卡尔曼

virtual MotionFilterType GetFilterType(void);

void CalcCorrectPos(const CamState camst, float &xC, float &yC);

private:

KalmanFilter m_Kalman;

int m_KalmanIsInit;

cv::Mat m_Measurement ;//Z(k)

cv::Mat m_Realposition ;//real X(k)

cv::Mat m_ControlMatrix;

int m_LastIdx;

Rect m_LastBox;

int m_StopFrameCount;

CamState m_LastCamState;//相机上一帧的状态

int m_CamStateCount;//用于计算相依移动修正量的权重 ,与mLastCamState变量 和 CalcCorrectPos函数配套使用

};

KalmanMotionFilter.cpp

#include "KalmanMotionFilter.h"

CKalmanMotionFilter::CKalmanMotionFilter(){

}

CKalmanMotionFilter::~CKalmanMotionFilter(void)

{

}

// 初始化运动滤波跟踪

bool CKalmanMotionFilter::Init(cv::Rect& inBBox)

{

m_Kalman.init(8,4,0);

m_Kalman.transitionMatrix = *(Mat_<float>(8, 8) << 1,0,0,0,1,0,0,0, 0,1,0,0,0,1,0,0, 0,0,1,0,0,0,1,0, 0,0,0,1,0,0,0,1,

0,0,0,0,1,0,0,0, 0,0,0,0,0,1,0,0, 0,0,0,0,0,0,1,0, 0,0,0,0,0,0,0,1);

setIdentity(m_Kalman.measurementMatrix,Scalar::all(1));

setIdentity(m_Kalman.processNoiseCov,Scalar::all(1e-5));

setIdentity(m_Kalman.measurementNoiseCov,Scalar::all(1e-1));

setIdentity(m_Kalman.errorCovPost,Scalar::all(1));

m_Measurement = cvCreateMat( 4, 1, CV_32FC1 );//Z(k)

m_Realposition = cvCreateMat( 8, 1, CV_32FC1 );//real X(k)

m_ControlMatrix = cvCreateMat( 8, 1, CV_32FC1 );//real X(k)

m_LastBox = inBBox;

m_Kalman.statePost = *(Mat_<float>(8,1) << inBBox.x, inBBox.y, inBBox.br().x-1, inBBox.br().y-1,0 ,0 , 0, 0);

m_KalmanIsInit = 1;

m_LastCamState = CamMovStat::CMS_STOP;

return false;

}

// 更新运动滤波器,获得新的目标边框

int CKalmanMotionFilter::Update(cv::Rect& inBBox, const CamState& inCamState = CamState())

{

float xCorrect = 0.0;

float yCorrect = 0.0;

CalcCorrectPos(inCamState,xCorrect, yCorrect);

//当相机运动的时,后验状态概率进行修正,修正量为相机的移动速度。

m_Kalman.statePost.at<float>(4,0)=m_Kalman.statePost.at<float>(4,0)+xCorrect;

m_Kalman.statePost.at<float>(5,0)=m_Kalman.statePost.at<float>(5,0)+yCorrect;

m_Kalman.statePost.at<float>(6,0)=m_Kalman.statePost.at<float>(6,0)+xCorrect;

m_Kalman.statePost.at<float>(7,0)=m_Kalman.statePost.at<float>(7,0)+yCorrect;

//相机运动的话会输入控制矩阵进行修正。

Mat PredictPoint = m_Kalman.predict();//预测

int obj_x = cvRound(PredictPoint.at<float>(0,0));

int obj_y = cvRound(PredictPoint.at<float>(1,0));

int obj_x1 = cvRound(PredictPoint.at<float>(2,0));

int obj_y1 = cvRound(PredictPoint.at<float>(3,0));

Rect meansureBox = Rect(inBBox.x,inBBox.y,inBBox.br().x - 1,inBBox.br().y - 1);

m_Realposition.at<float>(0,0)=meansureBox.x;

m_Realposition.at<float>(1,0)=meansureBox.y;

m_Realposition.at<float>(2,0)=meansureBox.width;

m_Realposition.at<float>(3,0)=meansureBox.height;

m_Realposition.at<float>(4,0)=meansureBox.x - m_LastBox.x + 1;

m_Realposition.at<float>(5,0)=meansureBox.y- m_LastBox.y + 1;

m_Realposition.at<float>(6,0)=meansureBox.width- m_LastBox.br().x + 1;

m_Realposition.at<float>(7,0)=meansureBox.height- m_LastBox.br().y + 1;

m_Measurement = m_Kalman.measurementMatrix*m_Realposition;

m_Kalman.correct(m_Measurement);//修正误差

//mLastBox = meansureBox;

inBBox.x = obj_x;

inBBox.y = obj_y;

inBBox.width = obj_x1 - obj_x + 1;

inBBox.height = obj_y1 - obj_y + 1;

m_LastBox = inBBox;

return 0;

}

// 获取滤波器类型,返回卡尔曼

MotionFilterType CKalmanMotionFilter::GetFilterType(void)

{

return MotionFilterType::KALMAN_FILTER;

}

//计算对目标的位置补偿,用于对卡尔曼预测结果进行运动补偿

void CKalmanMotionFilter::CalcCorrectPos(const CamState camst, float &xC, float &yC)

{

if (CamMovStat::CMS_MOVING == camst.state)

{

if (CamMovStat::CMS_STOP ==m_LastCamState.state && CamMovStat::CMS_MOVING == camst.state)

{//开始对bbox进行向后运动补偿计算

m_CamStateCount = 0;

m_LastCamState.x = camst.x;

m_LastCamState.y = camst.y;

}

double sigm = 1;

double weight = exp(-1.0*m_CamStateCount*m_CamStateCount/(sigm*sigm));

xC = m_LastCamState.x*weight;

yC = m_LastCamState.y*weight;

m_CamStateCount++;

}

if (CamMovStat::CMS_STOP == camst.state)

{

if (CamMovStat::CMS_MOVING ==m_LastCamState.state && CamMovStat::CMS_STOP == camst.state)

{//开始对bbox进行向后运动补偿计算

m_CamStateCount = 0;

}

double sigm = 1;

double weight = exp(-1.0*m_CamStateCount*m_CamStateCount/(sigm*sigm));

xC = -m_LastCamState.x*weight;

yC = -m_LastCamState.y*weight;

m_CamStateCount++;

}

m_LastCamState.state = camst.state;

}

word版本:卡尔曼滤波(KalmanFilter)在目标边框预测中的应用.zip

参考文献

1. Rudolf Emil Kalman. "A New Approach to Linear Filtering and Prediction Problems" .http://www.cs.unc.edu/~welch/kalman/media/pdf/Kalman1960.pdf. 1960

2. http://www.cs.unc.edu/~welch/kalman/

卡尔曼滤波(Kalman Filter)在目标边框预测中的应用的更多相关文章

  1. GMM+Kalman Filter+Blob 目标跟踪

    转 http://www.cnblogs.com/YangQiaoblog/p/5462453.html ==========图片版================================== ...

  2. 关于卡尔曼滤波(Kalman Filter)的很好讲解

    http://bilgin.esme.org/BitsAndBytes/KalmanFilterforDummies C#代码: double[] Data = new double[] { 0.39 ...

  3. Kalman Filter、Extended Kalman Filter以及Unscented Kalman Filter介绍

    模型定义 如上图所示,卡尔曼滤波(Kalman Filter)的基本模型和隐马尔可夫模型类似,不同的是隐马尔科夫模型考虑离散的状态空间,而卡尔曼滤波的状态空间以及观测空间都是连续的,并且都属于高斯分布 ...

  4. 卡尔曼滤波—Simple Kalman Filter for 2D tracking with OpenCV

    之前有关卡尔曼滤波的例子都比较简单,只能用于简单的理解卡尔曼滤波的基本步骤.现在让我们来看看卡尔曼滤波在实际中到底能做些什么吧.这里有一个使用卡尔曼滤波在窗口内跟踪鼠标移动的例子,原作者主页:http ...

  5. 卡尔曼滤波(Kalman Filter) 的进一步讨论

    我们在上一篇文章中通过一个简单的样例算是入门卡尔曼滤波了.本文将以此为基础讨论一些技术细节. 卡尔曼滤波(Kalman Filter) http://blog.csdn.net/baimafujinj ...

  6. 一文搞懂 SLAM 中的Extension Kalman Filter 算法编程

    作者 | Doreen 01 问题描述 预先知道事物未来的状态总是很有价值的! √ 预知台风的路线可以避免或减轻重大自然灾害的损失. √ 敌国打过来的导弹,如果能够高精度预测轨迹,就能有效拦截. √ ...

  7. kalman filter卡尔曼滤波器- 数学推导和原理理解-----网上讲的比较好的kalman filter和整理、将预测值和观测值融和

    = 参考/转自: 1 ---https://blog.csdn.net/u010720661/article/details/63253509 2----http://www.bzarg.com/p/ ...

  8. [Math]理解卡尔曼滤波器 (Understanding Kalman Filter) zz

    1. 卡尔曼滤波器介绍 卡尔曼滤波器的介绍, 见 Wiki 这篇文章主要是翻译了 Understanding the Basis of the Kalman Filter Via a Simple a ...

  9. [Math]理解卡尔曼滤波器 (Understanding Kalman Filter)

    1. 卡尔曼滤波器介绍 卡尔曼滤波器的介绍, 见 Wiki 这篇文章主要是翻译了 Understanding the Basis of the Kalman Filter Via a Simple a ...

随机推荐

  1. Metlnfo CMS全版本漏洞收集

    根据https://www.seebug.org/appdir/MetInfo 进行书写. [版本:Metlnfo 4.0] 漏洞标题:Metlnfo cms任意用户密码修改 漏洞文件:member/ ...

  2. Linux内核同步原语之原子操作

    避免对同一数据的并发访问(通常由中断.对称多处理器.内核抢占等引起)称为同步. ——题记 内核源码:Linux-2.6.38.8.tar.bz2 目标平台:ARM体系结构 原子操作确保对同一数据的“读 ...

  3. springBoot单元测试-模拟MVC测试

    1)模拟mvc测试,和基础测试是一样的, 都需要在pom文件中引入junit的支持. 略 2)编写测试类 Application1TestMVC 在类头上除啦加入之前的@RunWith(SpringR ...

  4. Linux阵列 RAID详解 (转)

    原文链接:http://molinux.blog.51cto.com/2536040/516008   一. RAID详解   二. mdadm工具介绍   三. 创建一个RAID的基本过程   四. ...

  5. Mysql授权允许远程访问

    MySQL Community Edition(GPL) 在我们使用mysql数据库时,有时我们的程序与数据库不在同一机器上,这时我们需要远程访问数据库.缺省状态下,mysql的用户是没有远程访问的权 ...

  6. ASP.NET Core 2.0 MVC 发布部署--------- ASP.NET Core 发布的具体操作

    ASP.NET Core 发布的具体操作 下面使用C# 编写的ASP.NET Core Web项目示例说明发布的全过程. 1.创建项目 选择“文件” > “新建” > “项目”. 在“添加 ...

  7. 构建基于TCP的应用层通信模型

    各层的关系如下图,表述的是两个应用或CS间通信的过程:   通常使用TCP构建应用时,需要考虑传输层的通信协议,以便应用层能够正确识别消息请求.比如,一个请求的内容很长(如传文件),那肯定要分多次发送 ...

  8. 浅谈Linux系统中如何查看进程 ——ps,pstree,top,w,全解

    进程是一个其中运行着一个或多个线程的地址空间和这些线程所需要的系统资源.一般来说,Linux系统会在进程之间共享程序代码和系统函数库,所以在任何时刻内存中都只有代码的一份拷贝. 1,ps命令 作用:p ...

  9. 洛谷P1420 最长连号 题解

    题目传送门 这道题我是打暴力的...(尴尬) 所以直接是O(N2)的时间,但好像没有炸,数据很水... #include<bits/stdc++.h> using namespace st ...

  10. Bootstrap – 1.认识

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8&quo ...