opencv卡尔曼滤波详解

1.问题描述

假设下面曲线
y = k x + b + w y=kx+b+w y=kx+b+w
其中a,b为曲线的参数,w为高斯噪声。假设我们有N个关于x,y的观测数据点,想根据这些数据点求出直线的参数。

2.卡尔曼滤波拟合点斜式直线

2.1状态变量

状态变量是需要求解的参数。对于拟合一条直线 y = k x + b y=kx+b y=kx+b,状态变量为待求的参数 k , b ​ k,b​ k,b

2.2状态方程

状态方程可以表示为
x k = x k − 1 + b + w k x_{k}=x_{k-1}+b+w_{k} xk=xk1+b+wk

2.3观测方程

虽然点(x,y)是二维的,转换为观测其实只有一维。表示如下
h ( x k ) = k x + b − y + v k h(x_{k})=kx+b-y+v_{k} h(xk)=kx+by+vk

卡尔曼滤波观测方程中H矩阵如下
H k = ∂ h ∂ x k ∣ x ˉ k H k = [ ∂ h ∂ k ∂ h ∂ k ] = [ x 1 ] H_{k}=\frac{\partial h}{\partial x_{k}}|_{\bar{x}_{k}}\\ H_{k}=\begin{bmatrix} \frac{\partial h}{\partial k}&\frac{\partial h}{\partial k} \end{bmatrix} =\begin{bmatrix} x &1 \end{bmatrix} Hk=xkhxˉkHk=[khkh]=[x1]
设第k次观测的坐标值为 ( x 0 , y 0 ) (x_{0},y_{0}) (x0,y0)
将观测值代入得到
第k次观测的 H k H_{k} Hk矩阵为
H k = [ x 0 1 ] H_{k}=\begin{bmatrix} {x}_{0}&1 \end{bmatrix} Hk=[x01]

3使用opencv库实现

#include "opencv2/video/tracking.hpp"
#include "opencv2/highgui.hpp"

#include 

using namespace cv;
using namespace std;
int main(int, char**)
{
    double theta=-M_PI/4;
    double c=10;
    double k=-tan(theta);
    double b=-c/sin(theta);
    cout<<"ori theta ="<(2, 2) << 1, 0, 0, 1);

    setIdentity(KF.measurementMatrix);
    setIdentity(KF.processNoiseCov, Scalar::all(1e-5));
    setIdentity(KF.measurementNoiseCov, Scalar::all(1e-1));
    setIdentity(KF.errorCovPost, Scalar::all(1));

    randn(KF.statePost, Scalar::all(0), Scalar::all(0.1));
    for ( int i=0; i(0,0)=x_data[i];
        KF.measurementMatrix.at(0,1)=1;
        measurement=y_data[i];
        KF.correct(measurement);
         cout<<" k= "<(0,0)<<" b= "<(1,0)<

注意:噪声一定不能设置得太大,否则不会收敛,得不得想要的结果。ps:被坑惨了...

4.opencv卡尔曼滤波实现详解

4.1 opencv卡尔曼滤波实现源码

namespace cv
{

KalmanFilter::KalmanFilter() {}
KalmanFilter::KalmanFilter(int dynamParams, int measureParams, int controlParams, int type)
{
    init(dynamParams, measureParams, controlParams, type);
}

void KalmanFilter::init(int DP, int MP, int CP, int type)
{
    CV_Assert( DP > 0 && MP > 0 );
    CV_Assert( type == CV_32F || type == CV_64F );
    CP = std::max(CP, 0);

    statePre = Mat::zeros(DP, 1, type);
    statePost = Mat::zeros(DP, 1, type);
    transitionMatrix = Mat::eye(DP, DP, type);

    processNoiseCov = Mat::eye(DP, DP, type);
    measurementMatrix = Mat::zeros(MP, DP, type);
    measurementNoiseCov = Mat::eye(MP, MP, type);

    errorCovPre = Mat::zeros(DP, DP, type);
    errorCovPost = Mat::zeros(DP, DP, type);
    gain = Mat::zeros(DP, MP, type);

    if( CP > 0 )
        controlMatrix = Mat::zeros(DP, CP, type);
    else
        controlMatrix.release();

    temp1.create(DP, DP, type);
    temp2.create(MP, DP, type);
    temp3.create(MP, MP, type);
    temp4.create(MP, DP, type);
    temp5.create(MP, 1, type);
}

const Mat& KalmanFilter::predict(const Mat& control)
{
    CV_INSTRUMENT_REGION()

    // update the state: x'(k) = A*x(k)
    statePre = transitionMatrix*statePost;

    if( !control.empty() )
        // x'(k) = x'(k) + B*u(k)
        statePre += controlMatrix*control;

    // update error covariance matrices: temp1 = A*P(k)
    temp1 = transitionMatrix*errorCovPost;

    // P'(k) = temp1*At + Q
    gemm(temp1, transitionMatrix, 1, processNoiseCov, 1, errorCovPre, GEMM_2_T);

    // handle the case when there will be measurement before the next predict.
    statePre.copyTo(statePost);
    errorCovPre.copyTo(errorCovPost);

    return statePre;
}

const Mat& KalmanFilter::correct(const Mat& measurement)
{
    CV_INSTRUMENT_REGION()

    // temp2 = H*P'(k)
    temp2 = measurementMatrix * errorCovPre;

    // temp3 = temp2*Ht + R
    gemm(temp2, measurementMatrix, 1, measurementNoiseCov, 1, temp3, GEMM_2_T);

    // temp4 = inv(temp3)*temp2 = Kt(k)
    solve(temp3, temp2, temp4, DECOMP_SVD);

    // K(k)
    gain = temp4.t();

    // temp5 = z(k) - H*x'(k)
    temp5 = measurement - measurementMatrix*statePre;

    // x(k) = x'(k) + K(k)*temp5
    statePost = statePre + gain*temp5;

    // P(k) = P'(k) - K(k)*temp2
    errorCovPost = errorCovPre - gain*temp2;

    return statePost;
}

}

以下为公式对应源码部分

4.2预测

预测对应predict函数
状态方程更新
x ˉ k = F k x ^ + u k \bar{x}_{k}=F_{k}\hat{x}+u_{k} xˉk=Fkx^+uk
对应

// update the state: x'(k) = A*x(k)
statePre = transitionMatrix*statePost;
 if( !control.empty() )
 // x'(k) = x'(k) + B*u(k)
   statePre += controlMatrix*control;

P更新
P ˉ k = F P ^ k F T + R k \bar{P}_{k}=F\hat{P}_{k}F^T+R_{k} Pˉk=FP^kFT+Rk
对应

  // update error covariance matrices: temp1 = A*P(k)
    temp1 = transitionMatrix*errorCovPost;
    // P'(k) = temp1*At + Q
    gemm(temp1, transitionMatrix, 1, processNoiseCov, 1, errorCovPre, GEMM_2_T);

4.3更新

(1)先计算卡尔曼增益 K K K
K = P ˉ k H k T ( H k P ˉ k H k T + Q ) − 1 K=\bar{P}_{k}H_{k}^T(H_{k}\bar{P}_{k}H_{k}^T+Q)^{-1} K=PˉkHkT(HkPˉkHkT+Q)1
对应

  // temp2 = H*P'(k)
 temp2 = measurementMatrix * errorCovPre;
// temp3 = temp2*Ht + R
gemm(temp2, measurementMatrix, 1, measurementNoiseCov, 1, temp3, GEMM_2_T);
// temp4 = inv(temp3)*temp2 = Kt(k)
solve(temp3, temp2, temp4, DECOMP_SVD);
// K(k)
gain = temp4.t();

其中H矩阵是对应measurementMatrix是需要手动输入的
H = ∂ h ∂ x k ∣ x ˉ k H=\frac{\partial h}{\partial x_{k}}|_{\bar{x}_{k}}\\ H=xkhxˉk

(2)计算后验分布
计算增量
x ^ k = x ˉ k + K ( z k − H k x ˉ k ) \hat{x}_{k}=\bar{x}_{k}+K(z_{k}-H_{k}\bar{x}_{k}) x^k=xˉk+K(zkHkxˉk)
对应

// temp5 = z(k) - H*x'(k)
    temp5 = measurement - measurementMatrix*statePre;
 // x(k) = x'(k) + K(k)*temp5
    statePost = statePre + gain*temp5;

更新P后验
P ^ k = ( I − K H k ) P ˉ k \hat{P}_{k}=(I-KH_{k})\bar{P}_{k} P^k=(IKHk)Pˉk
对应

// P(k) = P'(k) - K(k)*temp2
    errorCovPost = errorCovPre - gain*temp2;

5Eigen实现

这是我自己按照公式实现的代码,对应关系更加明显

#include "ceres/ceres.h"
#include 
#include
using namespace std;
using Eigen::MatrixXd;
using Eigen::VectorXd;
const int STATE_NUMBER=2;
const int MEASUREMENT_NUMBER=1;
const double SIGMA=0.01;
int main(int argc, char** argv)
{
  double k=1.0, b=2.0;         // 真实参数值
  int N=100;                          // 数据点
  double w_sigma=1;                 // 噪声Sigma值
  cv::RNG rng;                        // OpenCV随机数产生器
  vector x_data, y_data;      // 数据
  cout<<"generating data: "<

你可能感兴趣的:(SLAM,opencv,kalman,卡尔曼滤波)