Kalman filtering method in OpenCV

Keywords: OpenCV

1, Class definition in OpenCV

The definition of KalmanFilter class

class CV_EXPORTS_W KalmanFilter  
{  
public:      
    CV_WRAP KalmanFilter();                                                                           //Construct the default Kalman filter object  
    CV_WRAP KalmanFilter(int dynamParams, int measureParams, int controlParams=0, int type=CV_32F);  //The method of constructing KalmanFilter object completely  
    void init(int dynamParams, int measureParams, int controlParams=0, int type=CV_32F);              //Initializing the KalmanFilter object will replace the original KF object  
    
    CV_WRAP const Mat& predict(const Mat& control=Mat());           //Calculate predicted status value      
    CV_WRAP const Mat& correct(const Mat& measurement);             //Update status values based on measurements  
  
    Mat statePre;            //Predicted value (x'(k)): x(k)=A*x(k-1)+B*u(k)  
    Mat statePost;           //State value (x(k)): x(k)=x'(k)+K(k)*(z(k)-H*x'(k))  
    Mat transitionMatrix;    //State transition matrix  
    Mat controlMatrix;       //Control matrix B   
    Mat measurementMatrix;   //Measurement matrix H  
    Mat processNoiseCov;     //System error Q  
    Mat measurementNoiseCov; //Measurement error R  
    Mat errorCovPre;         //Minimum mean square error (P'(k)): P'(k)=A*P(k-1)*At + Q)  
    Mat gain;                //Kalman gain (K(k)): K(k)=P'(k)*Ht*inv(H*P'(k)*Ht+R)  
    Mat errorCovPost;        //Corrected minimum mean square error (P(k)): P(k)=(I-K(k)*H)*P'(k)  
  
    // Temporary matrix  
    Mat temp1;  
    Mat temp2;  
    Mat temp3;  
    Mat temp4;  
    Mat temp5;  
};  

The implementation of correlation function,

#include "precomp.hpp"

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);         //Predicted value x(k)=A*x(k-1)+B*u(k)  
    statePost = Mat::zeros(DP, 1, type);        //Corrected state value x(k)=x'(k)+K(k)*(z(k)-H*x'(k))  
    transitionMatrix = Mat::eye(DP, DP, type);  //State transition matrix

    processNoiseCov = Mat::eye(DP, DP, type);     //System error Q
    measurementMatrix = Mat::zeros(MP, DP, type); //Measurement matrix
    measurementNoiseCov = Mat::eye(MP, MP, type); //measurement error

    errorCovPre = Mat::zeros(DP, DP, type);    //Minimum mean square error (P'(k)): P'(k)=A*P(k-1)*At + Q) 
    errorCovPost = Mat::zeros(DP, DP, type);   //Corrected minimum mean square error (P(k)): P(k)=(I-K(k)*H)*P'(k)  
    gain = Mat::zeros(DP, MP, type);             //Kalman gain 

    if( CP > 0 )
        controlMatrix = Mat::zeros(DP, CP, type);  //Control matrix
    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);//GEMM? 2? T indicates to transpose the second parameter.

    // 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);//Calculate measurement covariance

    // temp4 = inv(temp3)*temp2 = Kt(k)
    solve(temp3, temp2, temp4, DECOMP_SVD);//Solve function, used to solve the linear equation temp3*temp4=temp2

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

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

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

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

    return statePost;
}

}

2, What needs to be explained

The relevant formulas of Kalman filter will not be pasted out. The above update and prediction functions can be compared with those formulas, and the following are some key points to explain.

(1) gemm() function

gemm() is the generalized multiplication of matrix

void gemm(const GpuMat& src1, constGpuMat& src2, double alpha, const GpuMat& src3, double beta,GpuMat& dst, int flags=0, Stream& stream=Stream::Null())

Corresponding:

   dst = alpha*src1*src2 +beta* src3

It should be noted that the last parameter given in the program is GEMM ﹣ 2 ﹣ T, which means to transpose the second parameter.

(2) solve() function

bool solve(InputArray src1, InputArray src2, OutputArray dst, int flags=DECOMP_LU)

It is used to solve linear equation A*X=B, left side of src1 linear system (equivalent to A above), right side of src2 linear system (equivalent to B above), solution of dst output (equivalent to X requiring solution), and flag is the method used.

(3) Why can we use solve() function to solve Kalman gain

The significance of Kalman gain K is to minimize the posterior estimation error covariance, and bring K into the expression of the posterior estimation error covariance,

By derivation, the optimal K value can be calculated. General expression:

The basis of using SOLVE() function is the red line part above, which is equivalent to directly solving the linear equation.

For specific derivation, please refer to:
https://wenku.baidu.com/view/a5a6068619e8b8f67c1cb98b.html

(4) A typical example -- tracking mouse position

    #include "opencv2/video/tracking.hpp"  
    #include "opencv2/highgui/highgui.hpp"  
    #include <stdio.h>  
    using namespace cv;  
    using namespace std;  
      
    const int winHeight=600;  
    const int winWidth=800;  
      
      
    Point mousePosition= Point(winWidth>>1,winHeight>>1);  
      
    //mouse event callback  
    void mouseEvent(int event, int x, int y, int flags, void *param )  
    {  
        if (event==CV_EVENT_MOUSEMOVE) {  
            mousePosition = Point(x,y);  
        }  
    }  
      
    int main (void)  
    {  
        RNG rng;  
        //1.kalman filter setup  
        const int stateNum=4;                                      //State value 4 × 1 vector (x,y, △ x, △ y)  
        const int measureNum=2;                                    //Measured value 2 × 1 vector (x,y)    
        KalmanFilter KF(stateNum, measureNum, 0);     
      
        KF.transitionMatrix = *(Mat_<float>(4, 4) <<1,0,1,0,0,1,0,1,0,0,1,0,0,0,0,1);  //Transfer matrix A  
        setIdentity(KF.measurementMatrix);                                             //Measurement matrix H  
        setIdentity(KF.processNoiseCov, Scalar::all(1e-5));                            //System noise variance matrix Q  
        setIdentity(KF.measurementNoiseCov, Scalar::all(1e-1));                        //Measurement noise variance matrix R  
        setIdentity(KF.errorCovPost, Scalar::all(1));                                  //Posterior error estimate covariance matrix P  
        rng.fill(KF.statePost,RNG::UNIFORM,0,winHeight>winWidth?winWidth:winHeight);   //Initial state value x(0)  
        Mat measurement = Mat::zeros(measureNum, 1, CV_32F);                           //The initial measurement value x'(0) must be defined first because this value will be updated later  
          
        namedWindow("kalman");  
        setMouseCallback("kalman",mouseEvent);  
              
        Mat image(winHeight,winWidth,CV_8UC3,Scalar(0));  
      
        while (1)  
        {  
            //2.kalman prediction  
            Mat prediction = KF.predict();  
            Point predict_pt = Point(prediction.at<float>(0),prediction.at<float>(1) );   //Predicted value (x',y')  
      
            //3.update measurement  
            measurement.at<float>(0) = (float)mousePosition.x;  
            measurement.at<float>(1) = (float)mousePosition.y;          
      
            //4.update  
            KF.correct(measurement);  
      
            //draw   
            image.setTo(Scalar(255,255,255,0));  
            circle(image,predict_pt,5,Scalar(0,255,0),3);    //predicted point with green  
            circle(image,mousePosition,5,Scalar(255,0,0),3); //current position with red          
              
            char buf[256];  
            sprintf_s(buf,256,"predicted position:(%3d,%3d)",predict_pt.x,predict_pt.y);  
            putText(image,buf,Point(10,30),CV_FONT_HERSHEY_SCRIPT_COMPLEX,1,Scalar(0,0,0),1,8);  
            sprintf_s(buf,256,"current position :(%3d,%3d)",mousePosition.x,mousePosition.y);  
            putText(image,buf,cvPoint(10,60),CV_FONT_HERSHEY_SCRIPT_COMPLEX,1,Scalar(0,0,0),1,8);  
              
            imshow("kalman", image);  
            int key=waitKey(3);  
            if (key==27){//esc     
                break;     
            }         
        }  
    }  

Other examples can refer to:
https://blog.csdn.net/haima1998/article/details/80641628

37 original articles published, 33 praised, 30000 visitors+
Private letter follow

Posted by Frederick on Mon, 02 Mar 2020 21:47:56 -0800