卡尔曼滤波笔记+C代码实现

一、前言

        学习视频来自B站up主 DR_CAN的卡尔滤波器的讲解。

二、递归算法

        估计真实数据(求平均值)

         A  =  1/k * (Z1 + Z2 + Z3 + ......+ Zk )

            =  1/k * (Z1 + Z2 + Z3 + ......+ Z(k-1))  + 1/k * Zk

            =  1/k * (k -1)/ (k-1) * (Z1 + Z2 + Z3 + ......+ Z(k-1))  + 1/k * Zk

            =  (k-1)/k * A(k-1)   +  1/k * Zk

            = A(k-1) - 1/k * A(k-1)  + 1/k * Zk

       A  =  A(k-1) + 1/k * (Zk - A(k-1))

        1/k  可以用 Kk  代替。

     公式描述为:

       当前估计值 = 上一次估计值 + 系数 * (当前测量值 - 上一次估计值)

      A  :当前估计值     A(k-1):上一次估计值              1/k (或Kk) : 系数(也称卡尔曼增益)

     Zk :  当前测量值      Z(k-1): 上一次测量值

 求Kk

      Kk =  Eest(k-1) / ( Eest(k-1) + Emea(k))

      Eest(k-1) : 上一次估计误差

      Emea(k) :当前测量误差

 由公式可见 : 当估计误差 远大于 测量误差时    Kk 趋近于  1,当前估计值趋近于当前的测量值

                         当估计误差 远小于 测量误差时    Kk 趋近于  0,当前估计值趋近于上一次估计值

求估计误差

      Eest(k) = (1 - Kk)*   Eest(k-1)                     //不作推导

C代码实现

        执行顺序为 :

               STEP1  : 计算Kk;

               STEP 2 :   计算A;

               STEP 2 :   计算Eest(k);

       假设一个物体真实长度为50,测量工具的误差为3,第0次的估计值为40,第0次的估计误差为5,当前测量的值为51。代码实现如下:

#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include <string.h>

#define         ERR_MEA      3
#define         ACTUAL_VAL   50
typedef struct{
    float ERR_est_last;                 //估计误差
    float ERR_mea;                      //测量误差
    float VAL_est_last;                 //上一次估计值
    float VAL_est_curr;                 //当前估计值    
    float VAL_mea_curr;                 //当前测量值
    float Kk;                           //卡尔曼增益
} KALMAN_PARM;

KALMAN_PARM kalman_val;
void CAL_VALUE_CURRENT(KALMAN_PARM *val);
int main(void){
    memset(&kalman_val, 0,sizeof(KALMAN_PARM));
    kalman_val.ERR_mea          =  ERR_MEA;                      //假设测量误差始终为3          
    kalman_val.ERR_est_last     =  5.0;                          //给定一个初始估计误差
    kalman_val.VAL_est_last     =  40;                           //给定一个初始估计值
    kalman_val.VAL_mea_curr     =  51;                           //给定一个当前的测量值
    for(int i = 0; i < 1000; i++){
        CAL_VALUE_CURRENT(&kalman_val);
    }
    return 0;
}
//计算当前估计值
void CAL_VALUE_CURRENT(KALMAN_PARM *val){
    val->VAL_mea_curr = ACTUAL_VAL + rand()%3;                                                  //制造一个在实际值上下浮动的测量值
    val->Kk           = val->ERR_est_last / (val->ERR_est_last + val->ERR_mea);                 //计算卡尔曼增益
    val->VAL_est_curr = val->VAL_est_last + val->Kk * (val->VAL_mea_curr - val->VAL_est_last);  //计算当前估计值
    val->VAL_est_last = val->VAL_est_curr;
    val->ERR_est_last = (1.0 - val->Kk) * val->ERR_est_last;                                    //更新估计误差
    printf("当前估计值为:%f\n", val->VAL_est_curr);
}

    以上的做法是  基于一个测量工具对一个事物的多次测量,估计结果只于上一次的估计值与当前的测量值相关。

二、数据融合

        假设有两个称去称一个物体,第一个称的(Z1)30,第二个称得(Z2)32。 且第一个称的标准差为(B1)2,第二个的标准差为(B2)4。测量结果满足正态分布。

        A (两组数据的最优解) = Z1  + K  * (Z2 -Z1),     设X为A的标准差

       X的平方  =  VAR( Z1  + K  * (Z2 -Z1))                 // VAR  代表方差

                      =  VAR( Z1  -  K*Z1   + K * Z2)

                      =  VAR((1- K)*Z1   + K * Z2 )

                      = (1- K) * (1- K) * VAR(Z1) + K * K * (Z2)

                      = (1- K) * (1- K) * B1*B1 + K * K * B2*B2 

         对K求导 令其等于0

           -2(1-K)* B1*B1 + 2 *K *B2 * B2  = 0

            K =  (B1*B1)/ ((B1*B1)  + (B2*B2 ))  = 0.2

      结合上述可      A= 30 + 0.2*(32 - 30) = 30.4    

                              X的平方 = (1-0.2)的平方 * 2的平方   + 0.2的平方 * 4的平方 = 3.2

                              X = 1.79

        c代码实现: 

#include <stdio.h>
#include <math.h>
#include <string.h>

typedef struct{
    double val;                  //测量值
    double devi;                 //标准差
} Normal_distribution;

Normal_distribution  Mea1,Mea2;                    //定义两个正太分布值
double optimal_solution;                            //最优解   
double new_devi;                                 //最优解的标准差    
double  Kk;                                        
//数据融合
void Data_Fusion(Normal_distribution data1,  Normal_distribution data2){
    Kk = pow(2.0, data1.devi) / (pow(2.0, data1.devi) + pow(2.0, data2.devi));
    printf("卡尔曼增益为:%f\n", Kk);    
    optimal_solution = data1.val + Kk * (data2.val - data1.val);
    printf("最优解为:%f\n", optimal_solution);
}

void UPdata_devi(Normal_distribution data1,  Normal_distribution data2){
//    new_devi = (pow(2.0 , (1.0 - Kk)) * pow(2.0, data1.devi))  + (pow(2.0, Kk) * pow(2.0, data2.devi));
    new_devi = (1.0 - Kk) * (1.0 - Kk) * data1.devi * data1.devi +  Kk*Kk * data2.devi*data2.devi;
    new_devi = sqrt(new_devi);
    printf("最优解的标准差为:%f\n", new_devi);
}

int main(void){
    memset(&Mea1, 0,sizeof(Mea1));
    memset(&Mea2, 0,sizeof(Mea2));
    
    Mea1.val   = 30;                  //先给定两个初始值
    Mea1.devi  = 2;
    Mea2.val   = 32;
    Mea2.devi  = 4;
    Data_Fusion(Mea1,Mea2);
    UPdata_devi(Mea1,Mea2);
    return 0;
}

 上述方法为寻求两种测量工具对同一物体测量结果的最优解。 

三、协方差矩阵

        方差与协方差在一个矩阵中表现出来,主要是体现不同变量之间的关联性。

        假设有以下样本:

身高/X体重/Y年龄/z
A1797433
B1878031
C1757128
AVG180.57530.7

              VAR(X)  = 24.89                           协方差(XY) = 18.7

              VAR(Y)  = 14                                协方差(XZ) = 18.7

             VAR(Z)  = 4.22                              协方差(YZ) = 18.7

            则矩阵表示如下:

引用上述样本,C代码实现如下:   

#include <stdio.h>
#include <math.h>
#include <stdlib.h>

double Covariance_Matrix[9] ;
typedef struct{
    double Height;
    double Weight;
    double Age;
} SAMPIE;
SAMPIE p1 = {179,74,33};
SAMPIE p2 = {187,80,31};
SAMPIE p3 = {175,71,28};

//构建矩阵
void COV_MAT_CAL(void){
    double avg_X, avg_Y,avg_Z;                                          //三个平均值  分别对应Height,Weight,Age
    double COV_X_2, COV_Y_2, COV_Z_2, COV_X_Y,COV_X_Z, COV_Y_Z;         //6个协方差的值
    avg_X  = (p1.Height + p2.Height + p3.Height) / 3.0;
    avg_Y  = (p1.Weight + p2.Weight + p3.Weight) / 3.0;
    avg_Z  = (p1.Age    + p2.Age    + p3.Age)    / 3.0;

    COV_X_2 = ((p1.Height - avg_X) * (p1.Height - avg_X) + (p2.Height - avg_X) * (p2.Height - avg_X) + (p3.Height - avg_X) * (p3.Height - avg_X)) / 3.0;
    COV_Y_2 = ((p1.Weight - avg_Y) * (p1.Weight - avg_Y) + (p2.Weight - avg_Y) * (p2.Weight - avg_Y) + (p3.Weight - avg_Y) * (p3.Weight - avg_Y)) / 3.0;
    COV_Z_2 = ((p1.Age - avg_Z)*(p1.Age - avg_Z) + (p2.Age - avg_Z)*(p2.Age - avg_Z) + (p3.Age - avg_Z)*(p3.Age - avg_Z))/3.0;
    COV_X_Y = ((p1.Height - avg_X) * (p1.Weight - avg_Y) + (p2.Height - avg_X) * (p2.Weight - avg_Y) + (p3.Height - avg_X) * (p3.Weight - avg_Y)) / 3.0;
    COV_X_Z = ((p1.Height - avg_X) * (p1.Age - avg_Z) + (p2.Height - avg_X) * (p2.Age - avg_Z) + (p3.Height - avg_X) * (p3.Age - avg_Z)) / 3.0;
    COV_Y_Z = ((p1.Weight - avg_Y) * (p1.Age - avg_Z) + (p2.Weight - avg_Y) * (p2.Age - avg_Z) + (p3.Weight - avg_Y) * (p3.Age - avg_Z)) / 3.0;

    Covariance_Matrix[0] = COV_X_2;
    Covariance_Matrix[1] = COV_X_Y;
    Covariance_Matrix[2] = COV_X_Z;
    Covariance_Matrix[3] = COV_X_Y;
    Covariance_Matrix[4] = COV_Y_2;
    Covariance_Matrix[5] = COV_Y_Z;
    Covariance_Matrix[6] = COV_X_Z;
    Covariance_Matrix[7] = COV_Y_Z;
    Covariance_Matrix[8] = COV_Z_2;

    printf("%f  %f  %f\n%f  %f  %f\n%f  %f  %f\n", Covariance_Matrix[0],Covariance_Matrix[1],Covariance_Matrix[2],\
                                                   Covariance_Matrix[3],Covariance_Matrix[4],Covariance_Matrix[5],\
                                                   Covariance_Matrix[6],Covariance_Matrix[7],Covariance_Matrix[8] );
}



int main(void){
    COV_MAT_CAL();
    return 0;
}

四、 状态空间表达

        大抵如下:

 

                 X上的一个点,两个点分别为对时间的一阶求导和二阶求导。

五、视频中,二阶卡尔曼滤波器的C实现

        矩阵的运算包括加、减、乘、数乘、转置、行列式、逆矩阵、代数余子式。

        数据来源视频中提供的excel表。自行获取。

 C代码如下:

#include <stdio.h>
#include <math.h>
#include <stdlib.h>

typedef struct{
    double A11;
    double A12;
    double A21;
    double A22;
} TWO_DIM_Matrix;                      //定义2x2的矩阵

typedef struct{
    double Position;                   //位置
    double Velocity;                   //速度
}Statue;

typedef struct{
    double W1;                        //状态位置误差
    double W2;                        //状态速度误差
}Statue_ERR;

typedef struct{
    double V1;                        //测量位置误差
    double V2;                        //测量速度误差
}MEA_ERR;

//状态位置过程误差
double E1[30] = { 0.097,  0.150,  0.108,  -0.293,  0.214, -0.024, -0.097, -0.414, -0.466, -0.287,\
                  0.316, -0.165,  0.222,   0.314, -0.532, -0.083, -0.144, -0.305, -0.499, -0.194,\
                  0.177, -0.310, -0.446,  -0.024,  0.239,  0.353, -0.026, -0.621,  0.167,  0.164};
//状态速度过程误差
double E2[30] = { 0.159, -0.418,  0.044,  -0.115, -0.375, -0.165, -0.390, -0.162,  0.061, -0.024,\
                 -0.309,  0.060,  0.201,   0.303, -0.305, -0.325,  0.227,  0.176, -0.182,  0.265,\
                 -0.179,  0.140,  0.051,  -0.761,  0.146, -0.030,  0.340,  0.399, -0.421, -0.473};
//测量位置过程误差
double E3[30] = {-0.208,  0.911, -0.899,  -0.232, -0.114,  0.399, -0.493,  0.400,  0.208, -0.878,\
                  1.933,  0.161, -1.906,  -1.473,  0.417, -0.744, -0.576, -1.120,  0.838, -1.633,\
                 -0.261, -0.214, -0.026,   0.015,  0.230,  1.321,  1.287, -1.508, -1.194,  1.420};
//测量速度过程误差 
double E4[30] = { 0.272,  0.414,  0.438,  -0.985, -0.183, -0.382,  1.181, -0.731, -0.235, -0.639,\
                  1.352,  1.352,  1.187,  -0.200,  1.082,  1.364,  0.180, -0.610, -0.443,  0.301,\
                  0.041,  0.165, -0.164,   0.399, -0.923, -0.613,  0.131,  0.475,  0.428, -0.185};    

Statue_ERR        Process_Err[30];                                 //过程误差
MEA_ERR           measure_Err[30];                                 //测量误差
Statue            X[30], Z[30], X_Hat[30], X_Hat_PRE[30];              //
TWO_DIM_Matrix    Q, R, A, H, E, P_k[30], P_k_PRE[30],K_k[30];

void Status_Param_init(void){
    for(int i = 0; i<30;i++){                             //将样本误差数据存入数组
        Process_Err[i].W1 = E1[i];
        Process_Err[i].W2 = E2[i];
    }
    X[0].Position = 0.0;                                  //给定初始位置和速度值
    X[0].Velocity = 1.0;

    for(int j = 1 ; j<30; j++){
        X[j].Position = A.A11*X[j-1].Position + A.A12*X[j-1].Velocity + Process_Err[j-1].W1;
        X[j].Velocity = A.A22*X[j-1].Velocity + Process_Err[j-1].W2;                              //A.A21  的值为0  可以不写
//        printf("%f\n",X[j].Velocity); 
    }
}

void Mea_Param_init(void){
    for(int i = 0; i<30; i++){
        measure_Err[i].V1 = E3[i];
        measure_Err[i].V2 = E4[i];
    }

    for(int j =1; j<30; j++){
        Z[j].Position = H.A11*X[j].Position + H.A12*X[j-1].Velocity + measure_Err[j].V1;
        Z[j].Velocity = H.A22*X[j].Velocity + measure_Err[j].V2;                               //A.A21 的值为0   可以不写
//        printf("%f\n",Z[j].Velocity); 
    }
}

void TWO_DIM_Matrix_PARAM_init(void){
    //Q Matrix init
    Q.A11 = 0.1;    Q.A12 = 0.0;
    Q.A21 = 0.0;    Q.A22 = 0.1;
   //R Matrix init
    R.A11 = 1.0;    R.A12 = 0.0;
    R.A21 = 0.0;    R.A22 = 1.0;
    //A Matrix init
    A.A11 = 1.0;    A.A12 = 1.0;
    A.A21 = 0.0;    A.A22 = 1.0;
    //H Matrix init
    H.A11 = 1.0;    H.A12 = 0.0;
    H.A21 = 0.0;    H.A22 = 1.0;
    //E Matrix init
    E.A11 = 1.0;    E.A12 = 0.0;
    E.A21 = 0.0;    E.A22 = 1.0;

    P_k[0].A11 = 1.0;   P_k[0].A12 = 0.0;
    P_k[0].A21 = 0.0;   P_k[0].A22 = 1.0;
}
//矩阵的转置运算
TWO_DIM_Matrix Matrix_Transpose(TWO_DIM_Matrix input_matrix){
    TWO_DIM_Matrix Result;
    Result.A11 = input_matrix.A11;
    Result.A12 = input_matrix.A21;
    Result.A21 = input_matrix.A12;
    Result.A22 = input_matrix.A22;
    return Result;
}
//矩阵的代数余子式
TWO_DIM_Matrix Matrix_Congruent(TWO_DIM_Matrix input_matrix){
    TWO_DIM_Matrix Result;
    Result.A11 = input_matrix.A22;
    Result.A12 = input_matrix.A21*(-1);
    Result.A21 = input_matrix.A12*(-1);
    Result.A22 = input_matrix.A11; 
    return Result;
}
//矩阵相乘
TWO_DIM_Matrix Matrix_Multi(TWO_DIM_Matrix input_matrix1 , TWO_DIM_Matrix input_matrix2){
    TWO_DIM_Matrix Result;
    Result.A11 = input_matrix1.A11*input_matrix2.A11 + input_matrix1.A12*input_matrix2.A21;
    Result.A12 = input_matrix1.A11*input_matrix2.A12 + input_matrix1.A12*input_matrix2.A22;
    Result.A21 = input_matrix1.A21*input_matrix2.A11 + input_matrix1.A22*input_matrix2.A21;
    Result.A22 = input_matrix1.A21*input_matrix2.A12 + input_matrix1.A22*input_matrix2.A22;
    return Result;
}
//矩阵相加
TWO_DIM_Matrix Matrix_ADD(TWO_DIM_Matrix input_matrix1 , TWO_DIM_Matrix input_matrix2){
    TWO_DIM_Matrix Result;
    Result.A11 = input_matrix1.A11 + input_matrix2.A11;
    Result.A12 = input_matrix1.A12 + input_matrix2.A12;
    Result.A21 = input_matrix1.A21 + input_matrix2.A21;
    Result.A22 = input_matrix1.A22 + input_matrix2.A22;
    return Result;
}

//矩阵相减
TWO_DIM_Matrix Matrix_Reduce(TWO_DIM_Matrix input_matrix1 , TWO_DIM_Matrix input_matrix2){
    TWO_DIM_Matrix Result;
    Result.A11 = input_matrix1.A11 - input_matrix2.A11;
    Result.A12 = input_matrix1.A12 - input_matrix2.A12;
    Result.A21 = input_matrix1.A21 - input_matrix2.A21;
    Result.A22 = input_matrix1.A22 - input_matrix2.A22;
    return Result;
}
//矩阵的逆矩阵
TWO_DIM_Matrix Inverse_Matrix(TWO_DIM_Matrix input_matrix){
    TWO_DIM_Matrix Result;
    double A;
    A = input_matrix.A11*input_matrix.A22 - input_matrix.A12*input_matrix.A21;
    A = 1.0/A;  
    Result = Matrix_Congruent(input_matrix);
    Result.A11 = A * Result.A11;
    Result.A12 = A * Result.A12;
    Result.A21 = A * Result.A21;
    Result.A22 = A * Result.A22; 
    return Result;
}

void Kalman_Calc(void){
    TWO_DIM_Matrix buffer;
    X_Hat[0].Position = 0.0;
    X_Hat[0].Velocity = 1.0;
    for(int i = 1 ; i<30; i++){
        X_Hat_PRE[i].Position = A.A11 * X_Hat[i-1].Position + A.A12*X_Hat[i-1].Velocity;
        X_Hat_PRE[i].Velocity = A.A22 * X_Hat[i-1].Velocity;
        buffer = Matrix_Multi(A, P_k[i-1]);
        buffer = Matrix_Multi(buffer, Matrix_Transpose(A));
        P_k_PRE[i] = Matrix_ADD(buffer, Q);
        buffer = Matrix_ADD(P_k_PRE[i], R);
        buffer = Inverse_Matrix(buffer);
        K_k[i] = Matrix_Multi(P_k_PRE[i], buffer);
        X_Hat[i].Position = X_Hat_PRE[i].Position + K_k[i].A11 * (Z[i].Position - X_Hat_PRE[i].Position) + K_k[i].A12 * (Z[i].Velocity - X_Hat_PRE[i].Velocity);
        X_Hat[i].Velocity = X_Hat_PRE[i].Velocity + K_k[i].A21 * (Z[i].Position - X_Hat_PRE[i].Position) + K_k[i].A22 * (Z[i].Velocity - X_Hat_PRE[i].Velocity);
        P_k[i] = Matrix_Multi(Matrix_Reduce(E,K_k[i]), P_k_PRE[i]);
    }

}
int main(void){
    int i;
    TWO_DIM_Matrix_PARAM_init();
    Status_Param_init();
    Mea_Param_init();
    Kalman_Calc();
    for(i = 0;i<30;i++){
        printf("%f\n",X_Hat_PRE[i].Velocity);
    }
    for(i = 0;i<30;i++){
        printf("%f\n",X_Hat[i].Velocity);
    }
    return 0;
}

评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值