kalman_filter_code - yuhannah/skills_map GitHub Wiki

KF卡尔曼滤波

代码实例:KF融合imu和odo的yaw和角速度wz

  • KF: ($\mu_{t-1},\sum_{t-1},u_t,z_t$)

    $2. \overline{\mu}t=A_t\mu{t-1}+B_tu_t $--状态转移矩阵,预测值

    $ 3. \overline{\sum}t=A_t\sum\nolimits{t-1}A_t^T+R_t $

    $ 4. K_t=\overline{\sum}_tC_t^T(C_t \overline{\sum}_tC_t^T+Q_t)^{-1} $--卡尔曼增益

    $ 5. \mu_t=\overline{\mu}_t+K_t(z_t-C_t\overline{\mu}_t) $

    $ 6. \sum \nolimits{_t}=(I-K_tC_t)\overline{\sum}{_t} $

    return $ \mu_t,\sum \nolimits{_t} $

imu: 50Hz, odo: 50Hz

参与KF融合的状态量是$ (w_z,yaw) $,二维状态;没有控制量;预测来自imu,观测来自里程计。

u8 yaw_fusion_new(imu_elements *_down_sample_del_imu, T_odo *_pro_odo, T_sensor_valid_flag *_sensor_valid_flag) {
    float timediff = _down_sample_del_imu->delta_dt;
    if (_down_sample_del_imu->delta_dt <= 0.0f) return yaw;

    float wz_cov = 1.0e-5f;//z轴角速度的方差
    float odo_R[2] = {1.0f, 1.0f};//里程计的方差
    float SK[2] = {0.0f, 0.0f};//更新卡尔曼增益的中间量
    float Kg[2][2];//卡尔曼增益

    float odo_Z[2] = {0.0f, 0.0f};//里程计的观测值,(wz, yaw)
    float predict_wz = 0.0f;//预测wz,来自imu
    float predict_yaw = 0.0f;//预测yaw,来自imu和上次的融合结果
    float predict_P[2][2] = {1.0f, 0.0f, 0.0f, 1.0f};//预测方差
    static float P[2][2] = {1.0f, 0.0f, 0.0f, 1.0f};//卡尔曼滤波后的方差
    static float wz = 0.0f;//卡尔曼滤波后的z轴角速度

    predict_wz = _down_sample_del_imu->delta_ang.z / _down_sample_del_imu->delta_dt;//预测的角速度wz
    predict_yaw = fusion_yaw + wz * _down_sample_del_imu->delta_dt;//预测的yaw

$2. \overline{\mu}t=A_t\mu{t-1}+B_tu_t $--状态转移矩阵,预测值

$ predict_w_z=\frac{\Delta yaw}{\Delta t} $

$ predict_yaw=fusion_yaw+w_z \cdot \Delta t $

$ w_z $是t-1时刻的角速度,$fusion_yaw$是t-1时刻的$yaw$。

    if (1) {
        predict_P[0][0] = P[0][0] + wz_cov;
        predict_P[0][1] = P[0][1] + P[0][0] * timediff + wz_cov * timediff;
        predict_P[1][0] = P[0][1] + P[0][0] * timediff + wz_cov * timediff;
        predict_P[1][1] = P[1][1] + P[0][1] * timediff + wz_cov * timediff * timediff + P[1][0] * timediff + P[0][0] * timediff * timediff;

$ 3. \overline{\sum}t=A_t\sum\nolimits{t-1}A_t^T+R_t $--预测方差 $$ predict_P= \begin{bmatrix} 1 & 0 \ \Delta t & 1 \end{bmatrix} \begin{bmatrix} P_{00}+wz_cov & P_{01} \ P_{10} & P_{11} \end{bmatrix} \begin{bmatrix} 1 & \Delta t \ 0 & 1 \end{bmatrix} \ = \begin{bmatrix} P_{00}+wz_cov & P_{01} \ (P_{00}+wz_cov)\Delta t+P_{10} & P_{01}\Delta t +P_{11} \ \end{bmatrix} \begin{bmatrix} 1 & \Delta t \ 0 & 1 \end{bmatrix} \ = \begin{bmatrix} P_{00}+wz_cov & (P_{00}+wz_cov)\Delta t+P_{01} \ (P_{00}+wz_cov)\Delta t+P_{10} & ((P_{00}+wz_cov)\Delta t+P_{10})\Delta t+ P_{01}\Delta t +P_{11} \ \end{bmatrix} $$ 计算观测值里程计增量$z_t$,此处试图用反向里程计增量补偿imu增量,注意,正常的计算为(odo_r-odo_l)*K。

        float delta_yaw = (_pro_odo->delta_odo_l - _pro_odo->delta_odo_r) * 2.0f * M_PI * WHEEL_RADIUS / PULSE_PER_CIRCLE / WHEEL_BASE;//!!!
        if (fabs(delta_yaw) > 0.03f) {
            wz = _down_sample_del_imu->delta_ang.z / _down_sample_del_imu->delta_dt;
            fusion_yaw = predict_yaw;
            if (fusion_yaw < -PI) {
                fusion_yaw += 2 * PI;
            } else if (fusion_yaw > PI) {
                fusion_yaw -= 2 * PI;
            }
            yaw = fusion_yaw;
            return 0;
        }
        odo_Z[0] = delta_yaw / _down_sample_del_imu->delta_dt;//!!!
        odo_Z[1] = fusion_yaw + delta_yaw; //注意imu和odo分别累积的航向是不同的!
        if (odo_Z[1] < -PI) {
            odo_Z[1] += 2 * PI;
        } else if (odo_Z[1] > PI) {
            odo_Z[1] -= 2 * PI;
        }

考虑打滑或者里程计数据无效,则使用imu数据。

 if (_sensor_valid_flag->odo_valid == 0 ||
            _sensor_valid_flag->odo_slip == 1)
        {
            wz = _down_sample_del_imu->delta_ang.z / _down_sample_del_imu->delta_dt;
            fusion_yaw = predict_yaw;
            if (fusion_yaw < -PI) {
                fusion_yaw += 2 * PI;
            } else if (fusion_yaw > PI) {
                fusion_yaw -= 2 * PI;
            }
            yaw = fusion_yaw;
            return 0;
        }

$ 4. K_t=\overline{\sum}_tC_t^T(C_t \overline{\sum}_tC_t^T+Q_t)^{-1} $--卡尔曼增益

$SK=\frac{1}{predict_P+odo_R}$中间系数

$Kg=predict_P \cdot SK$卡尔曼增益系数

$Q_t=odo_R$

按照公式来准确推算增益系数,如下:(用P暂时代替predict_P简化) $$ (predict_P+odo_R)^{-1}=( \begin{bmatrix} P_{00} & P_{01} \ P_{10} & P_{11} \end{bmatrix}+ \begin{bmatrix} odo_R[0] & 0 \ 0 & odo_R[1] \end{bmatrix} )^{-1} \ = \begin{bmatrix} \frac{-(P_{11}+odo_R[1])}{P_{01}P_{10}-(P_{00}+odo_R[0])(P_{11}+odo_R[1])} & \frac{P_{01}}{P_{01}P_{10}-(P_{00}+odo_R[0])(P_{11}+odo_R[1])} \ \frac{P_{10}}{P_{01}P_{10}-(P_{00}+odo_R[0])(P_{11}+odo_R[1])} & \frac{-(P_{00}+odo_R[0])}{P_{01}P_{10}-(P_{00}+odo_R[0])(P_{11}+odo_R[1])} \end{bmatrix} $$

$$ predict_P(predict_P+odo_R)^{-1}= \ \begin{bmatrix} P_{00} & P_{01} \ P_{10} & P_{11} \end{bmatrix} \begin{bmatrix} \frac{-(P_{11}+odo_R[1])}{P_{01}P_{10}-(P_{00}+odo_R[0])(P_{11}+odo_R[1])} & \frac{P_{01}}{P_{01}P_{10}-(P_{00}+odo_R[0])(P_{11}+odo_R[1])} \ \frac{P_{10}}{P_{01}P_{10}-(P_{00}+odo_R[0])(P_{11}+odo_R[1])} & \frac{-(P_{00}+odo_R[0])}{P_{01}P_{10}-(P_{00}+odo_R[0])(P_{11}+odo_R[1])} \end{bmatrix} \ = \begin{bmatrix} \frac{P_{01}P_{10}-P_{00}(P_{11}+odo_R[1])}{P_{01}P_{10}-(P_{00}+odo_R[0])(P_{11}+odo_R[1])} & \frac{P_{00}P_{01}-P_{01}(P_{00}+odo_R[0])}{P_{01}P_{10}-(P_{00}+odo_R[0])(P_{11}+odo_R[1])} \ \frac{P_{10}P_{11}-P_{10}(P_{11}+odo_R[1])}{P_{01}P_{10}-(P_{00}+odo_R[0])(P_{11}+odo_R[1])} & \frac{P_{01}P_{10}-P_{11}(P_{00}+odo_R[0])}{P_{01}P_{10}-(P_{00}+odo_R[0])(P_{11}+odo_R[1])} \end{bmatrix} $$

假设$P_{01}P_{10}$很小(非对角线元素,近似为0???),则以上两式依次简化为: $$ SK=(predict_P+odo_R)^{-1}=( \begin{bmatrix} P_{00} & P_{01} \ P_{10} & P_{11} \end{bmatrix}+ \begin{bmatrix} odo_R[0] & 0 \ 0 & odo_R[1] \end{bmatrix} )^{-1} \ = \begin{bmatrix} \frac{-(P_{11}+odo_R[1])}{P_{01}P_{10}-(P_{00}+odo_R[0])(P_{11}+odo_R[1])} & \frac{P_{01}}{P_{01}P_{10}-(P_{00}+odo_R[0])(P_{11}+odo_R[1])} \ \frac{P_{10}}{P_{01}P_{10}-(P_{00}+odo_R[0])(P_{11}+odo_R[1])} & \frac{-(P_{00}+odo_R[0])}{P_{01}P_{10}-(P_{00}+odo_R[0])(P_{11}+odo_R[1])} \end{bmatrix} \ \approx \begin{bmatrix} \frac{1}{P_{00}+odo_R[0]} & \frac{P_{01}}{P_{01}P_{10}-(P_{00}+odo_R[0])(P_{11}+odo_R[1])} \ \frac{P_{10}}{P_{01}P_{10}-(P_{00}+odo_R[0])(P_{11}+odo_R[1])} & \frac{1}{P_{11}+odo_R[1]} \end{bmatrix} \ = \begin{bmatrix} SK[0] & * \

  • & SK[1] \end{bmatrix} $$

$$ K_g=predict_P(predict_P+odo_R)^{-1}= \ \begin{bmatrix} \frac{P_{00}}{SK[0]} & \frac{P_{01}}{SK[1]} \ \frac{P_{10}}{SK[0]} & \frac{P_{11}}{SK[1]} \end{bmatrix} \ = \begin{bmatrix} \frac{P_{00}}{P_{00}+odo_R[0]} & \frac{P_{01}}{P_{11}+odo_R[1]} \ \frac{P_{10}}{P_{00}+odo_R[0]} & \frac{P_{11}}{P_{11}+odo_R[1]} \end{bmatrix} $$

for (int index = 0; index < 2; index++) {
            SK[index] = 1.0 / (double) (predict_P[index][index] + odo_R[index]);
            for (uint8_t i = 0; i <= 1; i++) {
                Kg[i][index] = predict_P[i][index] * SK[index];
            }
        }

$ 5. \mu_t=\overline{\mu}_t+K_t(z_t-C_t\overline{\mu}_t) $--更新t时刻的融合结果 $$ K_g(odo_Z-predict)= \begin{bmatrix} K_g[00] & K_g[01] \ K_g[10] & K_g[11] \end{bmatrix} \begin{bmatrix} odo_Z[0]-predict_w_z \ odo_Z[1]-predict_yaw \end{bmatrix} \ = \begin{bmatrix} K_g00+K_g01 \ K_g10+K_g11 \end{bmatrix} $$

        float innov_wz = odo_Z[0] - predict_wz;
        float innov_yaw = odo_Z[1] - predict_yaw;
        if (innov_yaw < -PI) {
            innov_yaw += 2 * PI;
        } else if (innov_yaw > PI) {
            innov_yaw -= 2 * PI;
        }
        wz = predict_wz + Kg[0][0] * innov_wz + Kg[0][1] * innov_yaw;
        fusion_yaw = predict_yaw + Kg[1][0] * innov_wz + Kg[1][1] * innov_yaw;
        //		wz = predict_wz + Kg[0][0]*innov_wz;
        //		yaw = predict_yaw + Kg[1][1]*innov_yaw;

$ 6. \sum \nolimits{_t}=(I-K_tC_t)\overline{\sum}{_t} $--更新方差 $$ P=(I-K_g)predict_P \ = \begin{bmatrix} 1-K_g[00] & -K_g[01] \ -K_g[10] & 1-K_g[11] \end{bmatrix} \begin{bmatrix} P[00] & P[01] \ P[10] & P[11] \end{bmatrix} \ = \begin{bmatrix} (1-K_g[00])P[00]-K_g[01]P[10] & (1-K_g[00])P[01]-K_g[01]P[11] \ -K_g[10]P[00]+(1-K_g[11])P[10] & -K_g[10]P[01]+(1-K_g[11])P[11] \end{bmatrix} $$ 根据方差对称性,P[01]=P[10]=(P[01]+P[10])/2

        P[0][0] = (1.0f - Kg[0][0]) * P[0][0] - Kg[0][1] * P[1][0];
        P[0][1] = (1.0f - Kg[0][0]) * P[0][1] - Kg[0][1] * P[1][1];
        P[1][0] = -Kg[1][0] * P[0][0] + (1.0f - Kg[1][1]) * P[1][0];
        P[1][1] = -Kg[1][0] * P[0][1] + (1.0f - Kg[1][1]) * P[1][1];
        P[0][1] = 0.5f * (P[0][1] + P[1][0]);
        P[1][0] = P[0][1];
        if (fabs(P[0][0]) > 1.0e5f || fabs(P[1][1]) > 1.0e3f) {
            wz = _down_sample_del_imu->delta_ang.z / _down_sample_del_imu->delta_dt;
            fusion_yaw = predict_yaw;
            P[0][0] = 1.0f;
            P[0][1] = 0.0f;
            P[1][0] = 0.0f;
            P[1][1] = 1.0f;
        }

        if (fusion_yaw < -PI) {
            fusion_yaw += 2 * PI;
        } else if (fusion_yaw > PI) {
            fusion_yaw -= 2 * PI;
        }

最后,如果不进行融合,全部采用imu的数据,如下:

    } else {//没有融合,累加yaw
        wz = _down_sample_del_imu->delta_ang.z / _down_sample_del_imu->delta_dt;
        fusion_yaw = predict_yaw;
        if (fusion_yaw < -PI) {
            fusion_yaw += 2 * PI;
        } else if (fusion_yaw > PI) {
            fusion_yaw -= 2 * PI;
        }
    }
    yaw = fusion_yaw;

    return 1;
}