• SR-LIO--手写紧耦合IESKF


    1.ESKF初始化

    1. void eskfEstimator::tryInit(const std::vectordouble, std::pair>> &imu_meas)
    2. { //通过imu测量值初始化均值,协方差;(均值用于初始化零偏,协方差用于判断噪声是否太大)
    3. initialization(imu_meas);
    4. //imu初始化超过10次测量以及累积时间间隔大于3s
    5. if (num_init_meas > MIN_INI_COUNT && imu_meas.back().first - time_first_imu > MIN_INI_TIME)
    6. {
    7. acc_cov *= std::pow(G_norm / mean_acc.norm(), 2);
    8. if (gyr_cov.norm() > MAX_GYR_VAR)
    9. {
    10. LOG(ERROR) << "Too large noise of gyroscope measurements. " << gyr_cov.norm() << " > " << MAX_GYR_VAR;
    11. return;
    12. }
    13. if (acc_cov.norm() > MAX_ACC_VAR)
    14. {
    15. LOG(ERROR) << "Too large noise of accelerometer measurements. " << acc_cov.norm() << " > " << MAX_ACC_VAR;
    16. return;
    17. }
    18. initial_flag = true;
    19. gyr_cov = gyr_cov_scale;
    20. acc_cov = acc_cov_scale;
    21. //初始化陀螺仪零偏
    22. Eigen::Vector3d init_bg = mean_gyr;
    23. //初始化重力加速度
    24. Eigen::Vector3d init_gravity = mean_acc / mean_acc.norm() * G_norm;
    25. setBg(init_bg);
    26. setGravity(init_gravity);
    27. //协方差矩阵初始化
    28. covariance.block<3, 3>(9, 9) *= 0.001;
    29. covariance.block<3, 3>(12, 12) *= 0.0001;
    30. covariance.block<2, 2>(15, 15) *= 0.00001;
    31. //通过参数设定协方差初始化噪声(0.1,0.1,0.0001,0.0001)
    32. initializeNoise();
    33. ROS_INFO("IMU Initialization Done.");
    34. std::cout << "init_gravity = " << init_gravity.transpose() << std::endl;
    35. std::cout << "init_bg = " << init_bg.transpose() << std::endl;
    36. }
    37. else
    38. ROS_INFO("Wait more IMU measurements...");
    39. return;
    40. }

    其中的initialization(imu_meas)函数为:

    1. void eskfEstimator::initialization(const std::vectordouble, std::pair>> &imu_meas)
    2. { //如果为第一个imu测量,怎么判断是否是第一个?(默认就是第一个,之后置为false)
    3. if (is_first_imu_meas)
    4. { //使用读入的第一个imu测量进行初始化
    5. num_init_meas = 1;
    6. is_first_imu_meas = false;
    7. time_first_imu = imu_meas.front().first;
    8. mean_gyr = imu_meas.front().second.first;
    9. mean_acc = imu_meas.front().second.second;
    10. }
    11. //对每一个imu测量进行处理
    12. for (const auto &imu : imu_meas)
    13. { //计算角速度以及加速度的均值,协方差
    14. mean_gyr += (imu.second.first - mean_gyr) / num_init_meas;
    15. mean_acc += (imu.second.second - mean_acc) / num_init_meas;
    16. gyr_cov = gyr_cov * (num_init_meas - 1.0) / num_init_meas
    17. + (imu.second.first - mean_gyr).cwiseProduct(imu.second.first - mean_gyr) * (num_init_meas - 1.0) / (num_init_meas * num_init_meas);
    18. acc_cov = acc_cov * (num_init_meas - 1.0) / num_init_meas
    19. + (imu.second.second - mean_acc).cwiseProduct(imu.second.second - mean_acc) * (num_init_meas - 1.0) / (num_init_meas * num_init_meas);
    20. num_init_meas++;
    21. }
    22. //陀螺仪,加速度的测量值
    23. gyr_0 = imu_meas.back().second.first;
    24. acc_0 = imu_meas.back().second.second;
    25. }

    初始化的目的是使用读入的第一个imu测量值来初始化第一帧imu时间戳、陀螺仪均值及加速度均值。对于后续帧,会继续计算陀螺仪和加速度均值,以及对应的协方差。最后,将最后一个测量值作为下一帧到来时的上一帧测量值。

    2.ESKF预测

      此时已经初始化完毕

    1. //如果已经初始化
    2. if (initial_flag)
    3. {
    4. imuState imu_state_temp;
    5. imu_state_temp.timestamp = current_time;
    6. //imu状态量
    7. //上一时刻无偏加速度
    8. imu_state_temp.un_acc = eskf_pro->getRotation().toRotationMatrix() * (eskf_pro->getLastAcc() - eskf_pro->getBa());
    9. //上一时刻无偏角速度
    10. imu_state_temp.un_gyr = eskf_pro->getLastGyr() - eskf_pro->getBg();
    11. //平移
    12. imu_state_temp.trans = eskf_pro->getTranslation();
    13. //旋转
    14. imu_state_temp.quat = eskf_pro->getRotation();
    15. //速度
    16. imu_state_temp.vel = eskf_pro->getVelocity();
    17. imu_states.push_back(imu_state_temp);
    18. }

    读取ESKF中上一帧末尾时刻的状态值(世界坐标系)作为当前帧起始时刻的状态值:a,w,T,R,v;将其存入imu_states容器中,用于后续畸变校准。

    然后处理当前帧imu读数:

    1. for (auto &imu_msg : measurement.first.first)
    2. {
    3. double time_imu = imu_msg->header.stamp.toSec();
    4. if (time_imu <= time_frame)
    5. {
    6. if(current_time < 0)
    7. //current_time:当前帧起始时刻
    8. current_time = measurement.second.first;
    9. double dt = time_imu - current_time;
    10. if(dt < -1e-6) continue;
    11. assert(dt >= 0);
    12. current_time = time_imu;
    13. dx = imu_msg->linear_acceleration.x;
    14. dy = imu_msg->linear_acceleration.y;
    15. dz = imu_msg->linear_acceleration.z;
    16. rx = imu_msg->angular_velocity.x;
    17. ry = imu_msg->angular_velocity.y;
    18. rz = imu_msg->angular_velocity.z;
    19. imuState imu_state_temp;
    20. imu_state_temp.timestamp = current_time;
    21. //当前时刻无偏加速度中值
    22. imu_state_temp.un_acc = eskf_pro->getRotation().toRotationMatrix() * (0.5 * (eskf_pro->getLastAcc() + Eigen::Vector3d(dx, dy, dz)) - eskf_pro->getBa());
    23. //当前时刻无偏角速度中值
    24. imu_state_temp.un_gyr = 0.5 * (eskf_pro->getLastGyr() + Eigen::Vector3d(rx, ry, rz)) - eskf_pro->getBg();
    25. dt_sum = dt_sum + dt;
    26. //对测量值进行预测
    27. eskf_pro->predict(dt, Eigen::Vector3d(dx, dy, dz), Eigen::Vector3d(rx, ry, rz));
    28. imu_state_temp.trans = eskf_pro->getTranslation();
    29. imu_state_temp.quat = eskf_pro->getRotation();
    30. imu_state_temp.vel = eskf_pro->getVelocity();
    31. imu_states.push_back(imu_state_temp);
    32. }
    33. else
    34. {
    35. double dt_1 = time_frame - current_time;
    36. double dt_2 = time_imu - time_frame;
    37. current_time = time_frame;
    38. assert(dt_1 >= 0);
    39. assert(dt_2 >= 0);
    40. assert(dt_1 + dt_2 > 0);
    41. double w1 = dt_2 / (dt_1 + dt_2);
    42. double w2 = dt_1 / (dt_1 + dt_2);
    43. dx = w1 * dx + w2 * imu_msg->linear_acceleration.x;
    44. dy = w1 * dy + w2 * imu_msg->linear_acceleration.y;
    45. dz = w1 * dz + w2 * imu_msg->linear_acceleration.z;
    46. rx = w1 * rx + w2 * imu_msg->angular_velocity.x;
    47. ry = w1 * ry + w2 * imu_msg->angular_velocity.y;
    48. rz = w1 * rz + w2 * imu_msg->angular_velocity.z;
    49. imuState imu_state_temp;
    50. imu_state_temp.timestamp = current_time;
    51. imu_state_temp.un_acc = eskf_pro->getRotation().toRotationMatrix() * (0.5 * (eskf_pro->getLastAcc() + Eigen::Vector3d(dx, dy, dz)) - eskf_pro->getBa());
    52. imu_state_temp.un_gyr = 0.5 * (eskf_pro->getLastGyr() + Eigen::Vector3d(rx, ry, rz)) - eskf_pro->getBg();
    53. dt_sum = dt_sum + dt_1;
    54. eskf_pro->predict(dt_1, Eigen::Vector3d(dx, dy, dz), Eigen::Vector3d(rx, ry, rz));
    55. imu_state_temp.trans = eskf_pro->getTranslation();
    56. imu_state_temp.quat = eskf_pro->getRotation();
    57. imu_state_temp.vel = eskf_pro->getVelocity();
    58. imu_states.push_back(imu_state_temp);
    59. }
    60. }

    如以上代码所示,具体分为两种情况:

    1)time_imu <= time_frame:

    此时imu的时间戳小于等于雷达扫描帧的时间戳,则可以直接使用imu测量值进行前向预测;

    2)time_imu > time_frame:

    此时imu的时间戳大于雷达扫描帧的时间戳,需要对imu进行插值后才能进行前向预测。

    2.0前向预测函数predict(dt, Eigen::Vector3d(dx, dy, dz), Eigen::Vector3d(rx, ry, rz)):
    1. void eskfEstimator::predict(double dt_, const Eigen::Vector3d &acc_1_, const Eigen::Vector3d &gyr_1_)
    2. {
    3. dt = dt_;
    4. acc_1 = acc_1_;
    5. gyr_1 = gyr_1_;
    6. Eigen::Vector3d avr_acc = 0.5 * (acc_0 + acc_1);
    7. Eigen::Vector3d avr_gyr = 0.5 * (gyr_0 + gyr_1);
    8. Eigen::Vector3d p_before = p;
    9. Eigen::Quaterniond q_before = q;
    10. Eigen::Vector3d v_before = v;
    11. Eigen::Vector3d ba_before = ba;
    12. Eigen::Vector3d bg_before = bg;
    13. Eigen::Vector3d g_before = g;
    14. Eigen::Vector3d un_gyr = 0.5 * (gyr_0 + gyr_1) - bg;
    15. Eigen::Vector3d un_acc = 0.5 * (acc_0 + acc_1) - ba;
    16. //状态累积
    17. q = q * numType::so3ToQuat(un_gyr * dt);
    18. p = p + v * dt;
    19. v = v + q_before.toRotationMatrix() * un_acc * dt - g * dt;
    20. Eigen::Matrix3d R_omega_x, R_acc_x;
    21. //转为反对称矩阵
    22. R_omega_x << 0, -un_gyr(2), un_gyr(1), un_gyr(2), 0, -un_gyr(0), -un_gyr(1), un_gyr(0), 0;
    23. R_acc_x << 0, -un_acc(2), un_acc(1), un_acc(2), 0, -un_acc(0), -un_acc(1), un_acc(0), 0;
    24. Eigen::Matrix<double, 3, 2> B_x = numType::derivativeS2(g);
    25. Eigen::Matrix<double, 17, 17> F_x = Eigen::MatrixXd::Zero(17, 17);
    26. F_x.block<3, 3>(0, 0) = Eigen::Matrix3d::Identity();
    27. F_x.block<3, 3>(0, 6) = Eigen::Matrix3d::Identity() * dt;
    28. F_x.block<3, 3>(3, 3) = Eigen::Matrix3d::Identity() - R_omega_x * dt;
    29. F_x.block<3, 3>(3, 12) = - Eigen::Matrix3d::Identity() * dt;
    30. F_x.block<3, 3>(6, 3) = - q_before.toRotationMatrix() * R_acc_x * dt;
    31. F_x.block<3, 3>(6, 6) = Eigen::Matrix3d::Identity();
    32. F_x.block<3, 3>(6, 9) = - q_before.toRotationMatrix() * dt;
    33. F_x.block<3, 2>(6, 15) = numType::skewSymmetric(g) * B_x * dt;
    34. F_x.block<3, 3>(9, 9) = Eigen::Matrix3d::Identity();
    35. F_x.block<3, 3>(12, 12) = Eigen::Matrix3d::Identity();
    36. F_x.block<2, 2>(15, 15) = - 1.0 / (g.norm() * g.norm()) * B_x.transpose() * numType::skewSymmetric(g) * numType::skewSymmetric(g) * B_x;
    37. Eigen::Matrix<double, 17, 12> F_w = Eigen::MatrixXd::Zero(17, 12);
    38. F_w.block<3, 3>(6, 0) = - q_before.toRotationMatrix() * dt;
    39. F_w.block<3, 3>(3, 3) = - Eigen::Matrix3d::Identity() * dt;
    40. F_w.block<3, 3>(9, 6) = - Eigen::Matrix3d::Identity() * dt;
    41. F_w.block<3, 3>(12, 9) = - Eigen::Matrix3d::Identity() * dt;
    42. covariance = F_x * covariance * F_x.transpose() + F_w * noise * F_w.transpose();
    43. acc_0 = acc_1;
    44. gyr_0 = gyr_1;
    45. }

    在预测过程中,首先获取本次imu的测量值x_{1}(w_{1},a_{1}),然后结合上一次imu的测量值x_{0}(w_{0},a_{0})求均值(以下所有表达式皆以下标1表示当前时刻,下标0表示上一时刻):

    \dot{w}=\frac{1}{2}\cdot (w_{0}+w_{1})-b_{g}

    \dot{a}=\frac{1}{2}\cdot (a_{0}+a_{1})-b_{a}

    2.1计算名义状态的预测,即对IMU进行积分(此处使用的是中值积分):

    p_{1}=p_{0}+v_{0}\cdot \Delta _{t}

    v_{1}=v_{0}+q_{0}\cdot((\frac{_{1}}{2}\cdot (a_{0}+a_{1})-b_a{})\cdot\Delta _{t})-g\cdot\Delta_{t}

    q_{1}=q_{0}\cdot((\frac{1}{2}\cdot(w_{0}+w_{1})-b_{g})\cdot\Delta_{t})

    此时完成了名义状态的计算。需要注意的是,这里名义状态的递推忽略了一些量,完整的递推形式应该是这样的(加粗代表上式缺少的部分):

    p_{1}=p_{0}+v_{0}\cdot \Delta _{t}\mathbf{+\frac{1}{2}\cdot(q_{0}\cdot(\frac{1}{2}\cdot(a_{0}+a_{1})-b_{a}))\cdot\Delta_{t}^{2}-\frac{1}{2}\cdot{g}\cdot\Delta_{t}^{2}}

    v_{1}=v_{0}+q_{0}\cdot((\frac{_{1}}{2}\cdot (a_{0}+a_{1})-b_a{})\cdot\Delta _{t})-g\cdot\Delta_{t}

    q_{1}=q_{0}\cdot((\frac{1}{2}\cdot(w_{0}+w_{1})-b_{g})\cdot\Delta_{t})

    \boldsymbol{b_{g}(t+\Delta_{t})=b_{g}(t)}

    \boldsymbol{b_{a}(t+\Delta_{t})=b_{a}(t)}

    \boldsymbol{g_{g}(t+\Delta_{t})=g_{g}(t)}

    2.2计算误差状态的预测(加粗代表矩阵形式):

    \delta x_{pred}=\boldsymbol{F}\delta x\Leftrightarrow \delta x_{1}=\boldsymbol{F}\delta x _{0}

    \boldsymbol{P_{pred}}=\boldsymbol{F}\boldsymbol{P}\boldsymbol{F}^{T}+\boldsymbol{Q}\Leftrightarrow \boldsymbol{P_{1}}=\boldsymbol{F}\boldsymbol{P_{0}}\boldsymbol{F}^{T}+\boldsymbol{Q}

    计算协方差矩阵还有更完善的形式:

    \boldsymbol{P_{pred}}=\boldsymbol{F_{x}}\boldsymbol{P}\boldsymbol{F_{x}}^{T}+\boldsymbol{F_{w}}\boldsymbol{Q}\boldsymbol{F_{w}}^{T}\Leftrightarrow \boldsymbol{P_{1}}=\boldsymbol{F_{x}}\boldsymbol{P_{0}}\boldsymbol{F_{x}}^{T}+\boldsymbol{F_{w}}\boldsymbol{Q}\boldsymbol{F_{w}}^{T}

    需要注意的是,一般ESKF的误差状态在每次更新后会被重置为0,即\delta x _{0}=0。因此只需关注协方差部分即可。

    第一个重头戏来了!!!计算\boldsymbol{F}矩阵:

    首先贴一下高博书里的计算形式:

    高博这里没有考虑到重力的处理,因此贴出完整形式:

    3.ESKF更新

    存储完imu读数后进入处理函数:

    1. void lioOptimization::process(std::vector &cut_sweep, double timestamp_begin, double timestamp_offset)
    2. {
    3. state *cur_state = new state();//p,v,q,ba,bg
    4. stateInitialization(cur_state);
    5. std::vector const_frame;
    6. const_frame.insert(const_frame.end(), cut_sweep.begin(), cut_sweep.end());
    7. cloudFrame *p_frame = buildFrame(const_frame, cur_state, timestamp_begin, timestamp_offset);
    8. optimizeSummary summary = stateEstimation(p_frame);
    9. std::cout << "after solution: " << std::endl;
    10. std::cout << "rotation: " << p_frame->p_state->rotation.x() << " " << p_frame->p_state->rotation.y() << " "
    11. << p_frame->p_state->rotation.z() << " " << p_frame->p_state->rotation.w() << std::endl;
    12. std::cout << "translation: " << p_frame->p_state->translation.x() << " " << p_frame->p_state->translation.y() << " " << p_frame->p_state->translation.z() << std::endl;
    13. std::cout << "gravity = " << G.x() << " " << G.y() << " " << G.z() << std::endl;
    14. dt_sum = 0;
    15. publish_odometry(pub_odom, p_frame);
    16. publish_path(pub_path, p_frame);
    17. if(debug_output)
    18. {
    19. pcl::PointCloud::Ptr p_cloud_temp;
    20. p_cloud_temp.reset(new pcl::PointCloud());
    21. point3DtoPCL(p_frame->point_frame, p_cloud_temp);
    22. std::string pcd_path(output_path + "/cloud_frame/" + std::to_string(index_frame) + std::string(".pcd"));
    23. saveCutCloud(pcd_path, p_cloud_temp);
    24. }
    25. int num_remove = 0;
    26. if (initial_flag)
    27. {
    28. if (index_frame > sweep_cut_num && index_frame % sweep_cut_num == 0)
    29. {
    30. while (all_cloud_frame.size() > std::max(2, sweep_cut_num))
    31. {
    32. recordSinglePose(all_cloud_frame[0]);
    33. all_cloud_frame[0]->release();
    34. all_cloud_frame.erase(all_cloud_frame.begin());
    35. num_remove++;
    36. }
    37. assert(all_cloud_frame.size() == std::max(2, sweep_cut_num));
    38. }
    39. }
    40. else
    41. {
    42. while (all_cloud_frame.size() > options.num_for_initialization)
    43. {
    44. recordSinglePose(all_cloud_frame[0]);
    45. all_cloud_frame[0]->release();
    46. all_cloud_frame.erase(all_cloud_frame.begin());
    47. num_remove++;
    48. }
    49. }
    50. for(int i = 0; i < all_cloud_frame.size(); i++)
    51. all_cloud_frame[i]->id = all_cloud_frame[i]->id - num_remove;
    52. while (v_cut_sweep.size() > sweep_cut_num - 1)
    53. {
    54. std::vector().swap(v_cut_sweep.front());
    55. assert(v_cut_sweep.front().size() == 0);
    56. v_cut_sweep.erase(v_cut_sweep.begin());
    57. }
    58. }

    首先进入stateInitialization(cur_state)函数对状态进行初始化:

    1. //状态初始化,1:匀速模型;2:IMU
    2. void lioOptimization::stateInitialization(state *cur_state)
    3. {
    4. if (index_frame <= sweep_cut_num + 1)
    5. {
    6. cur_state->rotation = Eigen::Quaterniond::Identity();
    7. cur_state->translation = Eigen::Vector3d::Zero();
    8. }
    9. else if (index_frame == sweep_cut_num + 2)
    10. { //匀速模型初始化
    11. if (options.initialization == INIT_CONSTANT_VELOCITY)
    12. {
    13. //q(k) = [q(k-1) * q(k-2)^-1] * q(k-1)
    14. Eigen::Quaterniond q_next_end = all_cloud_frame[all_cloud_frame.size() - 1]->p_state->rotation *
    15. all_cloud_frame[all_cloud_frame.size() - 2]->p_state->rotation.inverse() * all_cloud_frame[all_cloud_frame.size() - 1]->p_state->rotation;
    16. //t(k) = t(k-1) + [q(k-1) * q(k-2)^-1] * [t(k-1) - t(k-2)]
    17. Eigen::Vector3d t_next_end = all_cloud_frame[all_cloud_frame.size() - 1]->p_state->translation +
    18. all_cloud_frame[all_cloud_frame.size() - 1]->p_state->rotation *
    19. all_cloud_frame[all_cloud_frame.size() - 2]->p_state->rotation.inverse() *
    20. (all_cloud_frame[all_cloud_frame.size() - 1]->p_state->translation -
    21. all_cloud_frame[all_cloud_frame.size() - 2]->p_state->translation);
    22. cur_state->rotation = q_next_end;
    23. cur_state->translation = t_next_end;
    24. }
    25. //IMU初始化
    26. else if (options.initialization == INIT_IMU)
    27. {
    28. if (initial_flag)
    29. {
    30. cur_state->rotation = eskf_pro->getRotation();
    31. cur_state->translation = eskf_pro->getTranslation();
    32. }
    33. else
    34. { //q(k) = [q(k-1) * q(k-2)^-1] * q(k-1)
    35. Eigen::Quaterniond q_next_end = all_cloud_frame[all_cloud_frame.size() - 1]->p_state->rotation *
    36. all_cloud_frame[all_cloud_frame.size() - 2]->p_state->rotation.inverse() * all_cloud_frame[all_cloud_frame.size() - 1]->p_state->rotation;
    37. //t(k) = t(k-1) + [q(k-1) * q(k-2)^-1] * [t(k-1) - t(k-2)]
    38. Eigen::Vector3d t_next_end = all_cloud_frame[all_cloud_frame.size() - 1]->p_state->translation +
    39. all_cloud_frame[all_cloud_frame.size() - 1]->p_state->rotation *
    40. all_cloud_frame[all_cloud_frame.size() - 2]->p_state->rotation.inverse() *
    41. (all_cloud_frame[all_cloud_frame.size() - 1]->p_state->translation -
    42. all_cloud_frame[all_cloud_frame.size() - 2]->p_state->translation);
    43. cur_state->rotation = q_next_end;
    44. cur_state->translation = t_next_end;
    45. }
    46. }
    47. else
    48. {
    49. cur_state->rotation = all_cloud_frame[all_cloud_frame.size() - 1]->p_state->rotation;
    50. cur_state->translation = all_cloud_frame[all_cloud_frame.size() - 1]->p_state->translation;
    51. }
    52. }
    53. else
    54. {
    55. if (options.initialization == INIT_CONSTANT_VELOCITY)
    56. {
    57. Eigen::Quaterniond q_next_end = all_cloud_frame[all_cloud_frame.size() - 1]->p_state->rotation *
    58. all_cloud_frame[all_cloud_frame.size() - 2]->p_state->rotation.inverse() * all_cloud_frame[all_cloud_frame.size() - 1]->p_state->rotation;
    59. Eigen::Vector3d t_next_end = all_cloud_frame[all_cloud_frame.size() - 1]->p_state->translation +
    60. all_cloud_frame[all_cloud_frame.size() - 1]->p_state->rotation *
    61. all_cloud_frame[all_cloud_frame.size() - 2]->p_state->rotation.inverse() *
    62. (all_cloud_frame[all_cloud_frame.size() - 1]->p_state->translation -
    63. all_cloud_frame[all_cloud_frame.size() - 2]->p_state->translation);
    64. cur_state->rotation = q_next_end;
    65. cur_state->translation = t_next_end;
    66. }
    67. else if (options.initialization == INIT_IMU)
    68. {
    69. if (initial_flag)
    70. {
    71. cur_state->rotation = eskf_pro->getRotation();
    72. cur_state->translation = eskf_pro->getTranslation();
    73. }
    74. else
    75. {
    76. Eigen::Quaterniond q_next_end = all_cloud_frame[all_cloud_frame.size() - 1]->p_state->rotation *
    77. all_cloud_frame[all_cloud_frame.size() - 2]->p_state->rotation.inverse() * all_cloud_frame[all_cloud_frame.size() - 1]->p_state->rotation;
    78. Eigen::Vector3d t_next_end = all_cloud_frame[all_cloud_frame.size() - 1]->p_state->translation +
    79. all_cloud_frame[all_cloud_frame.size() - 1]->p_state->rotation *
    80. all_cloud_frame[all_cloud_frame.size() - 2]->p_state->rotation.inverse() *
    81. (all_cloud_frame[all_cloud_frame.size() - 1]->p_state->translation -
    82. all_cloud_frame[all_cloud_frame.size() - 2]->p_state->translation);
    83. cur_state->rotation = q_next_end;
    84. cur_state->translation = t_next_end;
    85. }
    86. }
    87. else
    88. {
    89. cur_state->rotation = all_cloud_frame[all_cloud_frame.size() - 1]->p_state->rotation;
    90. cur_state->translation = all_cloud_frame[all_cloud_frame.size() - 1]->p_state->translation;
    91. }
    92. }
    93. }

    这里的初始化形式主要分为匀速模型及直接使用imu初始化两种形式,其中匀速模型初始化即当前时刻状态等于上一时刻状态加上上上时刻状态到上一时刻状态的增量。

    随后进入主要部分,即状态估计stateEstimation(p_frame)

    1. optimizeSummary lioOptimization::stateEstimation(cloudFrame *p_frame)
    2. {
    3. icpOptions optimize_options = options.optimize_options;
    4. const double kSizeVoxelInitSample = options.voxel_size;//1.5
    5. const double kSizeVoxelMap = optimize_options.size_voxel_map;//1.0
    6. const double kMinDistancePoints = options.min_distance_points;//0.1
    7. const int kMaxNumPointsInVoxel = options.max_num_points_in_voxel;//20
    8. optimizeSummary optimize_summary;
    9. if(p_frame->frame_id > sweep_cut_num)
    10. {
    11. bool good_enough_registration = false;
    12. double sample_voxel_size = p_frame->frame_id < options.init_num_frames ? options.init_sample_voxel_size : options.sample_voxel_size;
    13. double min_voxel_size = std::min(options.init_voxel_size, options.voxel_size);
    14. optimize_summary = optimize(p_frame, optimize_options, sample_voxel_size);
    15. if(!optimize_summary.success)
    16. {
    17. return optimize_summary;
    18. }
    19. }
    20. else
    21. {
    22. p_frame->p_state->translation = eskf_pro->getTranslation();
    23. p_frame->p_state->rotation = eskf_pro->getRotation();
    24. p_frame->p_state->velocity = eskf_pro->getVelocity();
    25. p_frame->p_state->ba = eskf_pro->getBa();
    26. p_frame->p_state->bg = eskf_pro->getBg();
    27. G = eskf_pro->getGravity();
    28. G_norm = G.norm();
    29. }
    30. //将p_frame点云添加到地图
    31. addPointsToMap(voxel_map, p_frame, kSizeVoxelMap, kMaxNumPointsInVoxel, kMinDistancePoints);
    32. const double kMaxDistance = options.max_distance;
    33. const Eigen::Vector3d location = p_frame->p_state->translation;
    34. removePointsFarFromLocation(voxel_map, location, kMaxDistance);
    35. return optimize_summary;
    36. }

    状态估计函数中主要调用optimize(p_frame, optimize_options, sample_voxel_size)函数:

    1. optimizeSummary lioOptimization::optimize(cloudFrame *p_frame, const icpOptions &cur_icp_options, double sample_voxel_size)
    2. {
    3. std::vector keypoints;
    4. //从point_frame中采样关键点keypoints(雷达坐标系)
    5. gridSampling(p_frame->point_frame, keypoints, sample_voxel_size);
    6. optimizeSummary optimize_summary;
    7. optimize_summary = updateIEKF(cur_icp_options, voxel_map, keypoints, p_frame);
    8. if (!optimize_summary.success) {
    9. return optimize_summary;
    10. }
    11. Eigen::Quaterniond q_end = p_frame->p_state->rotation;
    12. Eigen::Vector3d t_end = p_frame->p_state->translation;
    13. //将所有雷达坐标系下的点转到世界坐标系
    14. for (auto &point_temp: p_frame->point_frame) {
    15. transformPoint(point_temp, q_end, t_end, R_imu_lidar, t_imu_lidar);
    16. }
    17. return optimize_summary;
    18. }

    该函数首先对输入点云进行下采样出关键点keypoints,然后通过updateIEKF函数进行卡尔曼滤波的更新:

    1. optimizeSummary lioOptimization::updateIEKF(const icpOptions &cur_icp_options, const voxelHashMap &voxel_map_temp, std::vector &keypoints, cloudFrame *p_frame)
    2. {
    3. int max_num_iter = p_frame->frame_id < cur_icp_options.init_num_frames ?
    4. std::max(15, cur_icp_options.num_iters_icp) : cur_icp_options.num_iters_icp;
    5. std::cout<<"@@@max_num_iter:"<//begin15 later5
    6. //获取预测状态变量
    7. Eigen::Vector3d p_predict = eskf_pro->getTranslation();
    8. Eigen::Quaterniond q_predict = eskf_pro->getRotation();
    9. Eigen::Vector3d v_predict = eskf_pro->getVelocity();
    10. Eigen::Vector3d ba_predict = eskf_pro->getBa();
    11. Eigen::Vector3d bg_predict = eskf_pro->getBg();
    12. Eigen::Vector3d g_predict = eskf_pro->getGravity();
    13. optimizeSummary summary;
    14. for (int i = -1; i < max_num_iter; i++)
    15. {
    16. std::vector plane_residuals;
    17. double loss_old = 0.0;
    18. //构建点到面残差
    19. summary = buildPlaneResiduals(cur_icp_options, voxel_map_temp, keypoints, plane_residuals, p_frame, loss_old);
    20. if (summary.success == false)
    21. return summary;
    22. int num_plane_residuals = plane_residuals.size();
    23. Eigen::Matrix<double, Eigen::Dynamic, 6> H_x;
    24. Eigen::Matrix<double, Eigen::Dynamic, 1> h;
    25. //H_x:距离残差对状态变量的雅可比
    26. H_x.resize(num_plane_residuals, 6);
    27. //h:距离残差
    28. h.resize(num_plane_residuals, 1);
    29. for (int i = 0; i < num_plane_residuals; i++)
    30. {
    31. H_x.block<1, 6>(i, 0) = plane_residuals[i].jacobians;
    32. h.block<1, 1>(i, 0) = Eigen::Matrix<double, 1, 1>(plane_residuals[i].distance * plane_residuals[i].weight);
    33. }
    34. //计算误差状态变量
    35. Eigen::Vector3d d_p = eskf_pro->getTranslation() - p_predict;
    36. Eigen::Quaterniond d_q = q_predict.inverse() * eskf_pro->getRotation();
    37. Eigen::Vector3d d_so3 = numType::quatToSo3(d_q);
    38. Eigen::Vector3d d_v = eskf_pro->getVelocity() - v_predict;
    39. Eigen::Vector3d d_ba = eskf_pro->getBa() - ba_predict;
    40. Eigen::Vector3d d_bg = eskf_pro->getBg() - bg_predict;
    41. Eigen::Vector3d g = eskf_pro->getGravity();
    42. Eigen::Vector3d g_predict_normalize = g_predict;
    43. Eigen::Vector3d g_normalize = g;
    44. g_predict_normalize.normalize();
    45. g_normalize.normalize();
    46. //归一化后的重力向量的叉乘积
    47. Eigen::Vector3d cross = g_predict_normalize.cross(g_normalize);
    48. //归一化后的重力向量的点乘积
    49. double dot = g_predict_normalize.dot(g_normalize);
    50. Eigen::Matrix3d R_dg;
    51. //根据点乘积的值,计算旋转矩阵R_dg。如果点乘积接近1,矩阵设为单位矩阵。否则,使用叉乘积计算一个Skew对称矩阵,并使用Rodrigues'公式计算旋转矩阵
    52. if (fabs(1.0 - dot) < 1e-6)
    53. R_dg = Eigen::Matrix3d::Identity();
    54. else
    55. {
    56. Eigen::Matrix3d skew = numType::skewSymmetric(cross);
    57. R_dg = Eigen::Matrix3d::Identity() + skew + skew * skew * (1.0 - dot)
    58. / (cross(0) * cross(0) + cross(1) * cross(1) + cross(2) * cross(2));
    59. }
    60. //SO3旋转 δθ_dg
    61. Eigen::Vector3d so3_dg = numType::rotationToSo3(R_dg);
    62. //计算导数矩阵B
    63. Eigen::Matrix<double, 3, 2> B_x_predict = numType::derivativeS2(g_predict);
    64. //B^T * δθ_dg
    65. Eigen::Vector2d d_g = B_x_predict.transpose() * so3_dg;
    66. //17维误差状态变量
    67. Eigen::Matrix<double, 17, 1> d_x;
    68. d_x.head<3>() = d_p;
    69. d_x.segment<3>(3) = d_so3;
    70. d_x.segment<3>(6) = d_v;
    71. d_x.segment<3>(9) = d_ba;
    72. d_x.segment<3>(12) = d_bg;
    73. d_x.tail<2>() = d_g;
    74. //I - 1/2 * δθ (3*3)
    75. Eigen::Matrix3d J_k_so3 = Eigen::Matrix3d::Identity() - 0.5 * numType::skewSymmetric(d_so3);
    76. //J0_gn = I + 1/2 * B^T * δθ_dg * B (2*2)
    77. Eigen::Matrix2d J_k_s2 = Eigen::Matrix2d::Identity() + 0.5 * B_x_predict.transpose() * numType::skewSymmetric(so3_dg) * B_x_predict;
    78. //整个操作就是计算J0_n * dx
    79. Eigen::Matrix<double, 17, 1> d_x_new = d_x;
    80. //(I - 1/2 * δθ) * δθ 在旋转误差状态左乘了(I - 1/2 * δθ)
    81. d_x_new.segment<3>(3) = J_k_so3 * d_so3;
    82. //J0_gn * (B^T * δθ_dg) 在重力状态分量上左乘了J0_gn:(I + 1/2 * B^T * δθ_dg * B)
    83. d_x_new.segment<2>(15) = J_k_s2 * d_g;
    84. Eigen::Matrix<double, 17, 17> covariance = eskf_pro->getCovariance();
    85. //计算J0_n * P * J0_n^T
    86. for (int j = 0; j < covariance.cols(); j++)
    87. covariance.block<3, 1>(3, j) = J_k_so3 * covariance.block<3, 1>(3, j);
    88. for (int j = 0; j < covariance.cols(); j++)
    89. covariance.block<2, 1>(15, j) = J_k_s2 * covariance.block<2, 1>(15, j);
    90. for (int j = 0; j < covariance.rows(); j++)
    91. covariance.block<1, 3>(j, 3) = covariance.block<1, 3>(j, 3) * J_k_so3.transpose();
    92. for (int j = 0; j < covariance.rows(); j++)
    93. covariance.block<1, 2>(j, 15) = covariance.block<1, 2>(j, 15) * J_k_s2.transpose();
    94. //(J0_n * (P/V) * J0_n^T)^-1
    95. Eigen::Matrix<double, 17, 17> temp = (covariance/laser_point_cov).inverse();
    96. //H^T * H
    97. Eigen::Matrix<double, 6, 6> HTH = H_x.transpose() * H_x;
    98. //H^T * H + (J0_n * (P/V) * J0_n^T)^-1
    99. temp.block<6, 6>(0, 0) += HTH;
    100. //(H^T * V^-1 * H + (J0_n * (P/V) * J0_n^T)^-1)^-1
    101. Eigen::Matrix<double, 17, 17> temp_inv = temp.inverse();
    102. //计算Kh
    103. Eigen::Matrix<double, 17, 1> K_h = temp_inv.block<17, 6>(0, 0) * H_x.transpose() * h;
    104. Eigen::Matrix<double, 17, 17> K_x = Eigen::Matrix<double, 17, 17>::Zero();
    105. //计算KH
    106. K_x.block<17, 6>(0, 0) = temp_inv.block<17, 6>(0, 0) * HTH;
    107. //dx = -Kh - (I - KH) * J0_n * dx
    108. d_x = - K_h + (K_x - Eigen::Matrix<double, 17, 17>::Identity()) * d_x_new;
    109. Eigen::Vector3d g_before = eskf_pro->getGravity();
    110. if ((d_x.head<3>()).norm() > 100.0 || AngularDistance(d_x.segment<3>(3)) > 100.0)
    111. {
    112. continue;
    113. }
    114. eskf_pro->observe(d_x);
    115. //状态更新
    116. p_frame->p_state->translation = eskf_pro->getTranslation();
    117. p_frame->p_state->rotation = eskf_pro->getRotation();
    118. p_frame->p_state->velocity = eskf_pro->getVelocity();
    119. p_frame->p_state->ba = eskf_pro->getBa();
    120. p_frame->p_state->bg = eskf_pro->getBg();
    121. G = eskf_pro->getGravity();
    122. G_norm = G.norm();
    123. bool converage = false;
    124. if (p_frame->frame_id > sweep_cut_num &&
    125. (d_x.head<3>()).norm() < cur_icp_options.threshold_translation_norm && //0.001
    126. AngularDistance(d_x.segment<3>(3)) < cur_icp_options.threshold_orientation_norm) //0.0001
    127. {
    128. converage = true;
    129. }
    130. //协方差更新
    131. if (converage || i == max_num_iter - 1)
    132. {
    133. Eigen::Matrix<double, 17, 17> covariance_new = covariance;
    134. Eigen::Matrix<double, 3, 2> B_x_before = numType::derivativeS2(g_before);
    135. J_k_so3 = Eigen::Matrix3d::Identity() - 0.5 * numType::skewSymmetric(d_x.segment<3>(3));
    136. J_k_s2 = Eigen::Matrix2d::Identity() + 0.5 * B_x_before.transpose() * numType::skewSymmetric(B_x_before * d_x.tail<2>()) * B_x_before;
    137. for (int j = 0; j < covariance.cols(); j++)
    138. covariance_new.block<3, 1>(3, j) = J_k_so3 * covariance.block<3, 1>(3, j);
    139. for (int j = 0; j < covariance.cols(); j++)
    140. covariance_new.block<2, 1>(15, j) = J_k_s2 * covariance.block<2, 1>(15, j);
    141. for (int j = 0; j < covariance.rows(); j++)
    142. {
    143. covariance_new.block<1, 3>(j, 3) = covariance.block<1, 3>(j, 3) * J_k_so3.transpose();
    144. covariance.block<1, 3>(j, 3) = covariance.block<1, 3>(j, 3) * J_k_so3.transpose();
    145. }
    146. for (int j = 0; j < covariance.rows(); j++)
    147. {
    148. covariance_new.block<1, 2>(j, 15) = covariance.block<1, 2>(j, 15) * J_k_s2.transpose();
    149. covariance.block<1, 2>(j, 15) = covariance.block<1, 2>(j, 15) * J_k_s2.transpose();
    150. }
    151. for (int j = 0; j < 6; j++)
    152. K_x.block<3, 1>(3, j) = J_k_so3 * K_x.block<3, 1>(3, j);
    153. for (int j = 0; j < 6; j++)
    154. K_x.block<2, 1>(15, j) = J_k_s2 * K_x.block<2, 1>(15, j);
    155. covariance = covariance_new - K_x.block<17, 6>(0, 0) * covariance.block<6, 17>(0, 0);
    156. eskf_pro->setCovariance(covariance);
    157. break;
    158. }
    159. }
    160. return summary;
    161. }

    这里主要涉及到点到面残差的计算、雅可比的计算、卡尔曼增益的计算以及协方差矩阵的更新。这里为了简便直接将公式贴出来:

    1)计算点到面残差:

    2)计算雅可比:

    3)计算卡尔曼增益:

    其中H矩阵的计算为残差对状态量的雅可比:

    4)更新协方差矩阵及状态量:

    协方差矩阵的更新是卡尔曼滤波迭代结束才进行更新:

    而状态量是每次迭代都会进行更新:

    至此,IESKF状态估计的全部过程结束。

  • 相关阅读:
    【工具】SSH端口转发管理器,专门管理SSH Port Forwarding
    Langchain-chatchat本地部署
    SQL 优化
    【开源】JAVA+Vue.js实现APK检测管理系统
    数据结构与算法之排序: Leetcode 922. 按奇偶排序数组 II (Typescript版)
    UE4 材质多张图片拼接成一张图片(此处用2×2拼接)
    Ubuntu系统升级
    能链科技完成重点项目签约
    HummerRisk V0.6.0发布:升级列表高级搜索功能,扩充对象存储和操作审计支持范围等
    《宝塔面板教程1》:宝塔面板安装命令及安装要求
  • 原文地址:https://blog.csdn.net/qq_43527694/article/details/134466229