从零入门激光SLAM(二十)——IESKF代码实现

大家好呀,我是一个SLAM方向的在读博士,深知SLAM学习过程一路走来的坎坷,也十分感谢各位大佬的优质文章和源码。随着知识的越来越多,越来越细,我准备整理一个自己的激光SLAM学习笔记专栏,从0带大家快速上手激光SLAM,也方便想入门SLAM的同学和小白学习参考,相信看完会有一定的收获。如有不对的地方欢迎指出,欢迎各位大佬交流讨论,一起进步。博主创建了一个科研互助群Q:772356582,欢迎大家加入讨论。

源码是高博大佬的,网址如下 

slam_in_autonomous_driving/src/ch8 at master · gaoxiang12/slam_in_autonomous_driving · GitHub

下面对代码的逻辑和主要函数进行解析

一、IESKF结构

  • 参数配置/设置状态变量

    struct Options {
          Options() = default;
          /// IEKF配置
          int num_iterations_ = 3;  // 迭代次数
          double quit_eps_ = 1e-3;  // 终止迭代的dx大小
          /// IMU 测量与零偏参数
          double imu_dt_ = 0.01;         // IMU测量间隔
          double gyro_var_ = 1e-5;       // 陀螺测量标准差
          double acce_var_ = 1e-2;       // 加计测量标准差
          double bias_gyro_var_ = 1e-6;  // 陀螺零偏游走标准差
          double bias_acce_var_ = 1e-4;  // 加计零偏游走标准差
    
          /// RTK 观测参数
          double gnss_pos_noise_ = 0.1;                   // GNSS位置噪声
          double gnss_height_noise_ = 0.1;                // GNSS高度噪声
          double gnss_ang_noise_ = 1.0 * math::kDEG2RAD;  // GNSS旋转噪声
    
          /// 其他配置
          bool update_bias_gyro_ = true;  // 是否更新bias
          bool update_bias_acce_ = true;  // 是否更新bias};
          // nominal state
          SO3 R_;
          VecT p_ = VecT::Zero();
          VecT v_ = VecT::Zero();
          VecT bg_ = VecT::Zero();
          VecT ba_ = VecT::Zero();
          VecT g_{0, 0, -9.8};
          // error state
          Vec18T dx_ = Vec18T::Zero();
          // covariance
          Mat18T cov_ = Mat18T::Identity();
          // noise
          MotionNoiseT Q_ = MotionNoiseT::Zero();
          GnssNoiseT gnss_noise_ = GnssNoiseT::Zero();
          Options options_;
    
  • 设置初始条件
    void SetInitialConditions(Options options, const VecT& init_bg, const VecT& init_ba,
                                const VecT& gravity = VecT(0, 0, -9.8)) {
          BuildNoise(options);
          options_ = options;
          bg_ = init_bg;
          ba_ = init_ba;
          g_ = gravity;
          cov_ = 1e-4 * Mat18T::Identity();
          cov_.template block<3, 3>(6, 6) = 0.1 * math::kDEG2RAD * Mat3T::Identity();
      }
    //构建噪声模型
    void BuildNoise(const Options& options) {
          double ev = options.acce_var_;
          double et = options.gyro_var_;
          double eg = options.bias_gyro_var_;
          double ea = options.bias_acce_var_;
          double ev2 = ev;  // * ev;
          double et2 = et;  // * et;
          double eg2 = eg;  // * eg;
          double ea2 = ea;  // * ea;
          // set Q
          Q_.diagonal() << 0, 0, 0, ev2, ev2, ev2, et2, et2, et2, eg2, eg2, eg2, ea2, ea2, ea2, 0, 0, 0;
          double gp2 = options.gnss_pos_noise_ * options.gnss_pos_noise_;
          double gh2 = options.gnss_height_noise_ * options.gnss_height_noise_;
          double ga2 = options.gnss_ang_noise_ * options.gnss_ang_noise_;
          gnss_noise_.diagonal() << gp2, gp2, gh2, ga2, ga2, ga2;
    }
    
  • 使用IMU预测

    bool IESKF<S>::Predict(const IMU& imu) {
      /// Predict 部分与ESKF完全一样,不再解释
      assert(imu.timestamp_ >= current_time_);
    
      double dt = imu.timestamp_ - current_time_;
      if (dt > (5 * options_.imu_dt_) || dt < 0) {
          LOG(INFO) << "skip this imu because dt_ = " << dt;
          current_time_ = imu.timestamp_;
          return false;
      }
    
      VecT new_p = p_ + v_ * dt + 0.5 * (R_ * (imu.acce_ - ba_)) * dt * dt + 0.5 * g_ * dt * dt;
      VecT new_v = v_ + R_ * (imu.acce_ - ba_) * dt + g_ * dt;
      SO3 new_R = R_ * SO3::exp((imu.gyro_ - bg_) * dt);
      R_ = new_R;
      v_ = new_v;
      p_ = new_p;
      Mat18T F = Mat18T::Identity();
      F.template block<3, 3>(0, 3) = Mat3T::Identity() * dt;
      F.template block<3, 3>(3, 6) = -R_.matrix() * SO3::hat(imu.acce_ - ba_) * dt;
      F.template block<3, 3>(3, 12) = -R_.matrix() * dt;
      F.template block<3, 3>(3, 15) = Mat3T::Identity() * dt;
      F.template block<3, 3>(6, 6) = SO3::exp(-(imu.gyro_ - bg_) * dt).matrix();
      F.template block<3, 3>(6, 9) = -Mat3T::Identity() * dt;
      cov_ = F * cov_ * F.transpose() + Q_;
      current_time_ = imu.timestamp_;
      return true;
    }
    
  • 迭代观测模型

    using CustomObsFunc = std::function<void(const SE3& input_pose, Eigen::Matrix<S, 18, 18>& HT_Vinv_H,
                                               Eigen::Matrix<S, 18, 1>& HT_Vinv_r)>;
    // 使用自定义观测函数更新滤波器
    bool IESKF<S>::UpdateUsingCustomObserve(IESKF::CustomObsFunc obs) {
      // H阵由用户给定
       // 保存当前的旋转矩阵
      SO3 start_R = R_;
      Eigen::Matrix<S, 18, 1> HTVr;
      Eigen::Matrix<S, 18, 18> HTVH;
      Eigen::Matrix<S, 18, Eigen::Dynamic> K;
      Mat18T Pk, Qk;
      //进入残差更新循环
      for (int iter = 0; iter < options_.num_iterations_; ++iter) {
          // 调用用户提供的观测函数
          //GetNominalSE3()获取当前名义状态位姿
    //SE3 GetNominalSE3() const { return SE3(R_, p_); }
          //HTVH卡尔曼更新步骤中用于修正预测的协方差矩阵
          //HTVr卡尔曼更新步骤中用于修正预测的状态变量
          obs(GetNominalSE3(), HTVH, HTVr);
          // 投影协方差矩阵P
          Mat18T J = Mat18T::Identity();
          J.template block<3, 3>(6, 6) = Mat3T::Identity() - 0.5 * SO3::hat((R_.inverse() * start_R).log());
          Pk = J * cov_ * J.transpose();
          // 卡尔曼更新
          Qk = (Pk.inverse() + HTVH).inverse();   // 计算更新后的协方差矩阵Qk
          dx_ = Qk * HTVr; // 计算状态增量dx_
          // LOG(INFO) << "iter " << iter << " dx = " << dx_.transpose() << ", dxn: " << dx_.norm();
          //将增量dx_合并到名义变量中
          Update();
          // 检查增量的范数是否小于终止阈值
          if (dx_.norm() < options_.quit_eps_) {
              break;
          }
      }
      // 更新协方差矩阵update P
      cov_ = (Mat18T::Identity() - Qk * HTVH) * Pk;
    
      // 再次投影协方差矩阵P
      Mat18T J = Mat18T::Identity();
      Vec3d dtheta = (R_.inverse() * start_R).log();
      J.template block<3, 3>(6, 6) = Mat3T::Identity() - 0.5 * SO3::hat(dtheta);
      cov_ = J * cov_ * J.inverse();
      // 重置状态增量
      dx_.setZero();
      return true;
    }

详情请见...

 从零入门激光SLAM(二十)——IESKF代码解释 - 古月居 (guyuehome.com)

  • 11
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
【资源说明】 基于迭代误差状态卡尔曼滤波(IESKF)的Livox-IMU车载SLAM系统C++源码.zip 基于迭代误差状态卡尔曼滤波(IESKF)的Livox-IMU车载SLAM系统C++源码.zip 基于迭代误差状态卡尔曼滤波(IESKF)的Livox-IMU车载SLAM系统C++源码.zip 基于迭代误差状态卡尔曼滤波(IESKF)的Livox-IMU车载SLAM系统C++源码.zip 基于迭代误差状态卡尔曼滤波(IESKF)的Livox-IMU车载SLAM系统C++源码.zip基于迭代误差状态卡尔曼滤波(IESKF)的Livox-IMU车载SLAM系统C++源码.zip基于迭代误差状态卡尔曼滤波(IESKF)的Livox-IMU车载SLAM系统C++源码.zip基于迭代误差状态卡尔曼滤波(IESKF)的Livox-IMU车载SLAM系统C++源码.zip基于迭代误差状态卡尔曼滤波(IESKF)的Livox-IMU车载SLAM系统C++源码.zip基于迭代误差状态卡尔曼滤波(IESKF)的Livox-IMU车载SLAM系统C++源码.zip基于迭代误差状态卡尔曼滤波(IESKF)的Livox-IMU车载SLAM系统C++源码.zip 【备注】 1、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用! 2、本项目适合计算机相关专业(如计科、人工智能、通信工程、自动化、电子信息等)的在校学生、老师或者企业员工下载使用,也适合小白学习进阶,当然也可作为毕设项目、课程设计、作业、项目初期立项演示等。 3、如果基础还行,也可在此代码基础上进行修改,以实现其他功能,也可直接用于毕设、课设、作业等。 欢迎下载,沟通交流,互相学习,共同进步!

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

桦树无泪

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值