Quaternion kinematics for error state kalman filter实现GPS+IMU融合,(附源码)

最近在学习kalman滤波相关的知识,恰好工作可能需要使用ESKF算法,因此将Joan Sola大神的书看了一遍,同时推导了相关的公式。俗话说得好:“Talk is cheap, show me the code。"因此我就想着如何将文章里面的算法实现一遍,加深对算法的记忆。

在网上查阅资料时,看到了博主利用高精度IMU模型实现了GPS+IMU的ESKF融合,这不巧了,数据啥的就都有了,只需要将代码的核心部分修改一下就可以了。

首先就是将源代码看了一遍,搞懂了输入输出,于是对代码进行了改写。附上github地址:GitHub - Shelfcol/gps_imu_fusion at Qk-ESKF-dev

(引流我的EKF实现GPS+IMU的融合

先上效果图:(感觉还不错,gnss-ins-sim使用得low-accuracy IMU)

 下面对算法进行讲解,并给出相应得代码段。

本文使用的名义状态量[p,v,q,a_b,\omega_b,g]^T_{19 \times 1}, 误差状态量为\delta x = [\delta p,\delta v,\delta q,\delta a_b,\delta \omega_b,\delta g]^T_{18 \times 1},这里需要注意一下,名义状态量多一维是因为利用四元数表示的旋转。

名义状态运动学方程为: 

使用上述公式在IMU数据到来的预测阶段进行状态的预测:(需要注意的是,这里使用的IMU的加速度和角速度都是使用的IMU局部坐标系下的数值)

bool ESKFQK::UpdateOdomEstimation(const Eigen::Vector3d& a_m, const Eigen::Vector3d& w_m, const double dt) {
    // 计算位置
    Eigen::Matrix3d R = q_.toRotationMatrix();
    
    pose_ += velocity_*dt+0.5*(R*(a_m-accel_bias_)+g_)*dt*dt;
    velocity_ += (R*(a_m-accel_bias_)+g_)*dt;

    Eigen::Vector3d d_theta = (w_m-gyro_bias_)*dt;
    Eigen::AngleAxisd dq(d_theta.norm(),d_theta.normalized());//!
    q_ = q_*Eigen::Quaterniond(dq);

    return true;
}

(矩阵块太难打,所以直接截图)

误差状态动力学公式为:

 这里需要注意的就是\delta \theta的更新:书上使用的是(\omega_m-\omega_b) \Delta t生成的旋转矩阵的转置来旋转原本的\delta \theta。而我根据s上的公式(155)

\dot{ \delta \theta} = -[\omega _m- \omega_b]_{\times} \delta \theta-\delta \omega_b-\omega_n

所以对\delta \theta 的更新使用的公式为 

 \delta \theta \leftarrow \delta \theta +\dot{ \delta \theta} \delta t \leftarrow (I -[\omega _m- \omega_b]_{\times} \Delta t) \delta \theta-\delta \omega_b \Delta t-\Theta_i

公式157c的推导没搞明白,如果有理解的仁兄还望不吝赐教。

F_x完全是利用误差状态动力学公式推导而来,这里不再赘述,直接贴图。需要注意的是误差状态的预测方程(164)是无用的,因为在只有IMU数据时,误差状态为0,所以(164)没有实质作用。

 对应的代码块:

bool ESKFQK::UpdateErrorState(const Eigen::Vector3d& a_m, const Eigen::Vector3d& w_m, const double dt) {
    Eigen::Matrix3d A_M = BuildSkewMatrix(a_m-accel_bias_);

    // Fx
    Fx_.setZero(); // 初始化为零矩阵
    Fx_.block<3,3>(0,0) = Fx_.block<3,3>(3,3) = Fx_.block<3,3>(9,9) = Fx_.block<3,3>(12,12) = Fx_.block<3,3>(15,15)= Eigen::Matrix3d::Identity();
  
    Eigen::Matrix3d R = q_.toRotationMatrix();
    Fx_.block<3,3>(INDEX_STATE_POSI,INDEX_STATE_VEL) =  Eigen::Matrix3d::Identity()*dt;
    Fx_.block<3,3>(INDEX_STATE_VEL,INDEX_STATE_ORI) =  -R*A_M*dt;
    Fx_.block<3,3>(INDEX_STATE_VEL,INDEX_STATE_ACC_BIAS) =  -R*dt;
    Fx_.block<3,3>(INDEX_STATE_VEL,INDEX_STATE_GYRO_BIAS) = Eigen::Matrix3d::Identity()*dt;
    

    Eigen::AngleAxisd  AngleAxis_w(((w_m-gyro_bias_)*dt).norm(),((w_m-gyro_bias_)*dt).normalized());
    Eigen::Matrix3d R_w(AngleAxis_w);
    Fx_.block<3,3>(INDEX_STATE_ORI,INDEX_STATE_ORI) = R_w.transpose();//公式157c
    // Fx_.block<3,3>(INDEX_STATE_ORI,INDEX_STATE_ORI) = Eigen::Matrix3d::Identity()-BuildSkewMatrix(w_m-gyro_bias_)*dt;//自己使用的公式
    
    Fx_.block<3,3>(INDEX_STATE_ORI,INDEX_STATE_GYRO_BIAS) =  -Eigen::Matrix3d::Identity()*dt;

    P_ = Fx_*P_*Fx_.transpose()+Fi_*Qi_*Fi_.transpose();
    return true;
}

当有GPS数据到来的时候,则进行观测更新。因为GPS只有位置,所以可观测的数据只有位置。则对应的H_x = [I_{3 \times 3} \ 0_{3 \times 16}]_{3 \times 19}

 其中的H矩阵不是关于名义状态量,而是关于误差状态量的,同时是在真实状态\hat x_t = x+\hat {\delta x}处获得。因为当前还没有进行观测,所以误差状态的均值为0,所以\hat x_t = x

其中 H_x = [I_{3 \times 3} \ 0_{3 \times 16}]_{3 \times 19},这也是标准的EKF步骤中使用的观测矩阵。

X_{\delta x}求解如下

 对应的代码如下:


bool ESKFQK::ObservationOfErrorState()
{
    Hx_.setZero();
    Hx_.block<3,3>(0,0) = Eigen::Matrix3d::Identity();
    // X_dx_;
    X_dx_.block<6,6>(0,0) = Eigen::Matrix<double,6,6>::Identity();
    X_dx_.block<9,9>(10,9) = Eigen::Matrix<double,9,9>::Identity();
    double qx = q_.x();
    double qy = q_.y();
    double qz = q_.z();
    double qw = q_.w();
    X_dx_.block<4,3>(6,6) = 0.5*(Eigen::Matrix<double,4,3>()<<-qx,-qy,-qz,
                                                                qw,-qz,qy,
                                                                qz,qw,-qx,
                                                                -qy,qx,qw).finished();
    H_ = Hx_*X_dx_;

    K_ = P_ * H_.transpose() * (H_ * P_ * H_.transpose() +V_).inverse(); // kalman增益
    X_ = K_*(curr_gps_data_.position_ned - pose_);

    P_ = (TypeMatrixP::Identity() - K_ * H_) * P_*(TypeMatrixP::Identity() - K_ * H_).transpose()+K_*V_*K_.transpose();
    
}

下一步就需要将误差量加入到名义状态里面:(需要注意的是这里使用的是右乘,因为是绕动轴旋转)

// 估计值=估计值+误差量
void ESKFQK::EliminateError() {
    pose_ +=X_.block<3,1>(INDEX_STATE_POSI,0);
    velocity_ += X_.block<3,1>(INDEX_STATE_VEL, 0);
    Eigen::Matrix3d C_nn = Sophus::SO3d::exp(X_.block<3,1>(INDEX_STATE_ORI, 0)).matrix();
    q_ = q_*Eigen::Quaterniond(C_nn); 
    accel_bias_ += X_.block<3,1>(INDEX_STATE_ACC_BIAS, 0);
    gyro_bias_ += X_.block<3,1>(INDEX_STATE_GYRO_BIAS, 0);
    g_ += X_.block<3,1>(INDEX_STATE_G,0);
}

最后还需要重置ESKF:

void ESKFQK::ResetState() {
    X_.setZero();
    // P = G'PG'^T
    Eigen::Matrix<double,DIM_STATE,DIM_STATE>  G;
    G.setIdentity();
    G.block<3,3>(6,6) -= BuildSkewMatrix(0.5*X_.block<3,1>(INDEX_STATE_ORI, 0)); 
    P_ = G*P_*G.transpose();
}

这样就大功告成啦!

说一点调参的经验,需要注意状态量的方差设置要与实际的传感器的精度对应。

参考文献:[1]J Solà. Quaternion kinematics for the error-state Kalman filter[J].  2017.

                  [2]【附源码+代码注释】误差状态卡尔曼滤波(error-state Kalman Filter),扩展卡尔曼滤波,实现GPS+IMU融合,EKF ESKF GPS+IMU_一点儿也不萌的萌萌的博客-CSDN博客_误差卡尔曼

            

  • 8
    点赞
  • 84
    收藏
    觉得还不错? 一键收藏
  • 9
    评论
四元数运动学是错误状态卡尔曼滤波器中的一种重要方法。在四元数运动学中,我们使用四元数表示刚体的旋转姿态。错误状态卡尔曼滤波器是一种滤波算法,用于估计系统的状态,特别是旋转姿态的状态,并根据输入信号对估计的状态进行修正。 在错误状态卡尔曼滤波器中,我们通过使用四元数来表示旋转姿态的状态,并定义一个误差状态来描述实际姿态与估计姿态之间的差异。然后,我们使用卡尔曼滤波器的观测方程和状态方程,更新估计的状态,以减小误差状态。 四元数运动学提供了一种方便的方法来表示旋转姿态,它具有良好的数学特性和计算效率。通过使用四元数运动学,我们可以使用简洁的数学公式来描述旋转操作,避免了矩阵和欧拉角等其他旋转表示方法的复杂性。 在错误状态卡尔曼滤波器中,我们使用四元数运动学来更新估计的旋转姿态状态。通过将观测值与估计值之间的差异与卡尔曼增益相乘,我们可以得到一个修正项,用于更新估计的姿态状态。这种方式可以有效地融合观测数据和先验信息,提高对旋转姿态的估计精度。 总之,四元数运动学是错误状态卡尔曼滤波器中用于估计旋转姿态的一种重要方法。通过使用四元数来表示姿态状态,并结合卡尔曼滤波算法进行状态估计,我们可以实现更精确的姿态估计,并应用于各种导航和控制系统中。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值