0. 简介

上一节我们将while内部的IKD-Tree部分全部讲完,下面将是最后一部分,关于后端优化更新的部分。

1. 迭代更新

最后一块主要做的就是,拿当前帧与IKD-Tree建立的地图算出的残差,然后去计算更新自己的位置,并将更新后的结果通过map_incremental函数插入到由ikd-Tree表示的映射中。

// 外参,旋转矩阵转欧拉角V3D ext_euler = SO3ToEuler(state_point.offset_R_L_I);fout_pre << setw(20) << Measures.lidar_beg_time - first_lidar_time << " " << euler_cur.transpose() << " " << state_point.pos.transpose() << " " << ext_euler.transpose() << " " << state_point.offset_T_L_I.transpose() << " " << state_point.vel.transpose()<< " " << state_point.bg.transpose() << " " << state_point.ba.transpose() << " " << state_point.grav << endl; //输出预测的结果if (0) // If you need to see map point, change to "if(1)"{// 释放PCL_Storage的内存PointVector().swap(ikdtree.PCL_Storage);// 把树展平用于展示ikdtree.flatten(ikdtree.Root_Node, ikdtree.PCL_Storage, NOT_RECORD);featsFromMap->clear();featsFromMap->points = ikdtree.PCL_Storage;}pointSearchInd_surf.resize(feats_down_size); //搜索索引Nearest_Points.resize(feats_down_size);      //将降采样处理后的点云用于搜索最近点int rematch_num = 0;bool nearest_search_en = true; //t2 = omp_get_wtime();/*** 迭代状态估计 ***/double t_update_start = omp_get_wtime();double solve_H_time = 0;//迭代卡尔曼滤波更新,更新地图信息kf.update_iterated_dyn_share_modified(LASER_POINT_COV, solve_H_time);state_point = kf.get_x();euler_cur = SO3ToEuler(state_point.rot);pos_lid = state_point.pos + state_point.rot * state_point.offset_T_L_I;geoQuat.x = state_point.rot.coeffs()[0];geoQuat.y = state_point.rot.coeffs()[1];geoQuat.z = state_point.rot.coeffs()[2];geoQuat.w = state_point.rot.coeffs()[3];double t_update_end = omp_get_wtime();/******* 发布里程计 *******/publish_odometry(pubOdomAftMapped);/*** 向映射kdtree添加特性点 ***/t3 = omp_get_wtime();map_incremental();t5 = omp_get_wtime();/******* 发布轨迹和点 *******/publish_path(pubPath);if (scan_pub_en || pcd_save_en)publish_frame_world(pubLaserCloudFull);if (scan_pub_en && scan_body_pub_en){publish_frame_body(pubLaserCloudFull_body);publish_frame_lidar(pubLaserCloudFull_lidar);}// publish_effect_world(pubLaserCloudEffect);// publish_map(pubLaserCloudMap);/*** Debug 参数 ***/if (runtime_pos_log){frame_num++;kdtree_size_end = ikdtree.size();aver_time_consu = aver_time_consu * (frame_num - 1) / frame_num + (t5 - t0) / frame_num;aver_time_icp = aver_time_icp * (frame_num - 1) / frame_num + (t_update_end - t_update_start) / frame_num;aver_time_match = aver_time_match * (frame_num - 1) / frame_num + (match_time) / frame_num;aver_time_incre = aver_time_incre * (frame_num - 1) / frame_num + (kdtree_incremental_time) / frame_num;aver_time_solve = aver_time_solve * (frame_num - 1) / frame_num + (solve_time + solve_H_time) / frame_num;aver_time_const_H_time = aver_time_const_H_time * (frame_num - 1) / frame_num + solve_time / frame_num;T1[time_log_counter] = Measures.lidar_beg_time;s_plot[time_log_counter] = t5 - t0;                         //整个流程总时间s_plot2[time_log_counter] = feats_undistort->points.size(); //特征点数量s_plot3[time_log_counter] = kdtree_incremental_time;        // kdtree增量时间s_plot4[time_log_counter] = kdtree_search_time;             // kdtree搜索耗时s_plot5[time_log_counter] = kdtree_delete_counter;          // kdtree删除点数量s_plot6[time_log_counter] = kdtree_delete_time;             // kdtree删除耗时s_plot7[time_log_counter] = kdtree_size_st;                 // kdtree初始大小s_plot8[time_log_counter] = kdtree_size_end;                // kdtree结束大小s_plot9[time_log_counter] = aver_time_consu;                //平均消耗时间s_plot10[time_log_counter] = add_point_size;                //添加点数量time_log_counter++;printf("[ mapping ]: time: IMU + Map + Input Downsample: %0.6f ave match: %0.6f ave solve: %0.6f  ave ICP: %0.6f  map incre: %0.6f ave total: %0.6f icp: %0.6f construct H: %0.6f \n", t1 - t0, aver_time_match, aver_time_solve, t3 - t1, t5 - t3, aver_time_consu, aver_time_icp, aver_time_const_H_time);ext_euler = SO3ToEuler(state_point.offset_R_L_I);fout_out << setw(20) << Measures.lidar_beg_time - first_lidar_time << " " << euler_cur.transpose() << " " << state_point.pos.transpose() << " " << ext_euler.transpose() << " " << state_point.offset_T_L_I.transpose() << " " << state_point.vel.transpose()<< " " << state_point.bg.transpose() << " " << state_point.ba.transpose() << " " << state_point.grav << " " << feats_undistort->points.size() << endl;dump_lio_state_to_log(fp);}}status = ros::ok();rate.sleep();}/**************** save map ****************//* 1. make sure you have enough memories/* 2. pcd save will largely influence the real-time performences **/if (pcl_wait_save->size() > 0 && pcd_save_en){string file_name = string("scans.pcd");string all_points_dir(string(string(ROOT_DIR) + "PCD/") + file_name);pcl::PCDWriter pcd_writer;cout << "current scan saved to /PCD/" << file_name << endl;pcd_writer.writeBinary(all_points_dir, *pcl_wait_save);}fout_out.close();fout_pre.close();if (runtime_pos_log){vector<double> t, s_vec, s_vec2, s_vec3, s_vec4, s_vec5, s_vec6, s_vec7;FILE *fp2;string log_dir = root_dir + "/Log/fast_lio_time_log.csv";fp2 = fopen(log_dir.c_str(), "w");fprintf(fp2, "time_stamp, total time, scan point size, incremental time, search time, delete size, delete time, tree size st, tree size end, add point size, preprocess time\n");for (int i = 0; i < time_log_counter; i++){fprintf(fp2, "%0.8f,%0.8f,%d,%0.8f,%0.8f,%d,%0.8f,%d,%d,%d,%0.8f\n", T1[i], s_plot[i], int(s_plot2[i]), s_plot3[i], s_plot4[i], int(s_plot5[i]), s_plot6[i], int(s_plot7[i]), int(s_plot8[i]), int(s_plot10[i]), s_plot11[i]);t.push_back(T1[i]);s_vec.push_back(s_plot9[i]);s_vec2.push_back(s_plot3[i] + s_plot6[i]);s_vec3.push_back(s_plot4[i]);s_vec5.push_back(s_plot[i]);}fclose(fp2);}return 0;
}

前面获取的传播状态xk^\hat{x_k}xk​^​和协方差Pk^\hat{P_k}Pk​^​对未知状态xkx_kxk​施加了一个先验高斯分布。具体来说,Pk^\hat{P_k}Pk​^​表示以下误差状态的协方差:

式中,Jκ为(x^kπ田x^kκ)日x^k( \widehat {x}_ {k}^ {\pi } 田 \widehat {x}_ {k}^ {\kappa } )日 \widehat {x} k(xkπ​田xkκ​)日xk对x^kk\hat{x}^k_kx^kk​=0处的偏微分。

式中,A(.)−1A(.)^{-1}A(.)−1 定义于公式(6),如下。

如下的δGθIkδ^Gθ_{I_k}δGθIk​​和δIθLkδ^Iθ{L_k}δIθLk​分别为IMU的姿态和转动的误差状态。


对于第一次迭代(以拓展的卡尔曼滤波器为例),有 xkκ^=xk\hat{x^κ_k} =x_kxkκ​^​=xk​, Jκ=IJ_κ =IJκ​=I。

除了先验分布外,我们也有一个源于测量(8)的状态分布:

结合(10)的先验分布和(12)的测量模型,可得到状态xkx_kxk​的后验分布,其等价表示为xkkx^k_kxkk​及其最大后验估计:

式中,有∣∣x∣∣M2=xTM−1x||x||^2_M = x^TM^{−1} x∣∣x∣∣M2​=xTM−1x。该最大后验估计问题可由下面的迭代卡尔曼滤波器解决:

需注意,卡尔曼增益K计算需要对状态维数矩阵求逆,而不是在之前的工作中使用的测量维数矩阵。上述过程将重复进行,直到收敛(即∣∣xκ(k+1)日xkκ∣∣<||x_κ^{(k+1)} 日 x^κ_k||<∣∣xκ(k+1)​日xkκ​∣∣<无穷小)。收敛后的最优状态和协方差估计为:

通过状态更新xkx_kxk​,第k次扫描中的每个LiDAR点(Lpj)将通过(16)被转换到全局框架:

转换后的LiDAR点{Gp¯j}将被插入到由ikd-Tree表示的映射中。我们的状态估计在算法1中进行了总结。

…详情请参照古月居

FAST-LIO2代码解析(六)相关推荐

  1. 视觉SLAM开源算法ORB-SLAM3 原理与代码解析

    来源:深蓝学院,文稿整理者:何常鑫,审核&修改:刘国庆 本文总结于上交感知与导航研究所科研助理--刘国庆关于[视觉SLAM开源算法ORB-SLAM3 原理与代码解析]的公开课. ORB-SLA ...

  2. Linux-0.00 代码解析(四)

    Linux 0.00 的编译.运行.源码下载: http://blog.csdn.net/longintchar/article/details/78757065 Linux 0.00 Makefil ...

  3. iOS 编写高质量Objective-C代码(六)

    级别: ★★☆☆☆ 标签:「iOS」「Block」「Objective-C」 作者: MrLiuQ 审校: QiShare团队 前言: 这几篇文章是小编在钻研<Effective Objecti ...

  4. Celery 源码解析六:Events 的实现

    序列文章: Celery 源码解析一:Worker 启动流程概述 Celery 源码解析二:Worker 的执行引擎 Celery 源码解析三: Task 对象的实现 Celery 源码解析四: 定时 ...

  5. Mina Kimchi SNARK 代码解析

    1. 引言 Mina系列博客有: Mina概览 Mina的支付流程 Mina的zkApp Mina中的Pasta(Pallas和Vesta)曲线 Mina中的Schnorr signature Min ...

  6. Compact Multi-Signatures for Smaller Blockchains代码解析

    1. 引言 Boneh等人2018年论文<Compact Multi-Signatures for Smaller Blockchains>,论文解读参见博客 Compact Multi- ...

  7. STARK中的FRI代码解析

    1. 引言 前序博客有: STARK入门知识 STARKs and STARK VM: Proofs of Computational Integrity Fast Reed-Solomon Inte ...

  8. 番外篇15:libevent简单理解(附libevent官方代码解析,和跨平台服务器、客户端链接代码)

    文章目录 一.事件event和事件管理器event_base介绍 二.libevent流程简介(注册->检测->分派) 三.libevent的好处 四.代码比较 4.1 原来reactor ...

  9. [从零手写VIO|第五节]——后端优化实践——单目BA求解代码解析

    长篇警告⚠⚠⚠ 目录 solver 全流程回顾 Solver三要素 Solver求解中的疑问 核心问题 代码解析 1. TestMonoBA.cpp 2. 后端部分: 2.1 顶点 2.2 边(残差) ...

最新文章

  1. 为什么匿名内部类参数必须为final类型
  2. Beautiful Soup-4.2.0
  3. android开发常见的设计模式,Android开发有哪些常用设计模式?
  4. 发掘VS2005 SP1 (一)
  5. JS ajax请求参数格式( formData 、serialize)
  6. Echars折线配置详解
  7. 八款JS框架介绍及比较
  8. java中after什么意思_Java中的即时isAfter()方法
  9. java中使用nextLine(); 没有输入就自动跳过的问题?
  10. Linux下用C获取so库所在路径
  11. 全球十大数据治理解决方案提供商
  12. 使用Word制作签名电子版
  13. SNAP 4. 使用snap进行地物光谱分析
  14. 米勒-拉宾素数检测法(判断一个极大的数是否为质数)——算法解析
  15. 安装Win8跳过密钥方法
  16. 2021 年年度蕞佳开源软件!
  17. 黑客入侵Wishbone窃取上万邮箱及手机号码
  18. 复旦大学计算机研究生初试分数线,复旦大学计算机研究生复试分数线
  19. SQL对时间的操作,比如在当前时间上增加减少一天,在当前的时间上增加减少一个月
  20. dnf服务器炸团门票怎么找回,dnf补票小技巧 再也不怕掉线炸团制裁

热门文章

  1. Windbg调试学习
  2. 输出字符的 ASCII 码
  3. python数据分析:会员数据化运营(中)——RMF分析
  4. mysql innodb文件存储_MySQL数据库和InnoDB存储引擎文件
  5. 【Excel函数】隔行取数并取最大值
  6. 01.基于Irises的springboot项目框架(简版)
  7. 向量叉乘算子、点乘算子与矩阵运算的关系
  8. docker daemon调试
  9. Python爬取酷狗音乐歌手信息
  10. 如何解读链式中介作用分析结果?