LIO-SAM代码阅读


项目网址:https://github.com/TixiaoShan/LIO-SAM;
参考网址:

  • https://www.zhihu.com/people/gu-du-huan-zhe-90/posts LIO-SAM源码阅读与分析
  • https://zhuanlan.zhihu.com/p/176976425 Lio-sam 流程浅析
  • https://github.com/cuitaixiang/LOAM_NOTED/tree/master/src/loam_velodyne LeGO-LOAM代码注释版
  • https://wykxwyc.github.io/2019/08/01/The-Math-Formula-in-LeGO-LOAM/ LeGO-LOAM公式推导

程序整体框架

roslaunch文件结构

  首先从roslaunch文件来了解程序运行的整体框架。LIO-SAM运行涉及到的roslaunch文件有四个,一个顶层roslaunch文件和其调用的四个roslaunch文件。

  • run.launch
      调用了其他四个launch文件,设置了参数文件地址。
  • module_loam.launch
      类似于LeGO-LOAM启动了四个节点。
  • module_robot_state_publisher.launch
      调用ROS的robot_state_publisher节点,输出机器人参数到tf。
  • module_navsat.launch
      调用ROS中robot_localization包中的ekf_localization_node和navsat_transform_node节点,对机器人进行融合GPS信号的定位。
  • module_rviz.launch
      按照预设的参数运行Rviz。

设置文件

  设置文件包括params.yaml和utility.h两个文件

  • params.yaml
      额外记一个参数,extrinsicRot和extrinsicRPY都是用来把IMU测量转换到lidar坐标系下。之所以要两个是因为在作者使用的IMU中,加速度坐标系和角度坐标系不一样,所以要分开使用。
  • utility.h
      定义了基类供其他函数继承,设置了变量和从yaml文件读取的参数,还有一些工具函数和雷达相关设定。

多线程间消息交互

  去除了gps消息,未使用的消息和用于rviz显示的消息。其中,imu_raw和points_raw是传感器输入。

函数类名称接收消息发布消息
imageProjectionimu_raw
odometry/imu_incremental
points_raw
lio_sam/deskew/cloud_info
TransformFusionlio_sam/mapping/odometry
odometry/imu_incremental
IMUPreintegrationlio_sam/mapping/odometry_incremental
imu_raw
odometry/imu_incremental
FeatureExtractionlio_sam/deskew/cloud_infolio_sam/feature/cloud_info
mapOptimizationlio_sam/feature/cloud_infolio_sam/mapping/odometry
lio_sam/mapping/odometry_incremental

  

tf树结构

  程序tf树结构如下,和一般的机器人导航系统一样使用了map,odom,base_link 和lidar_link四个坐标系,但是目前的代码是为了跑数据集设置的参数,map和odom,base_link 和lidar_link两对坐标系都是重合的。
在这里插入图片描述

imageProjection.cpp

  主函数部分只是创建了imageProjection类对象IP,就开始等待回调函数了。类初始化部分设置了ROS中需要接收和发送的消息,分配控件和设置参数。函数的重点就是里程计,IMU和雷达三个回调函数。

imuHandler和odometryHandler

  这两个回调函数都很简单,用std::lock_guard互斥锁进行保护,将接收的消息加入队列中。

cloudHandler

  按顺序介绍cloudHandler中各个函数的作用。

  • cachePointCloud
      缓存点云,同时在第一次运行是检查激光是否有ring和time两个参数。
  • deskewInfo
      处理IMU和里程计信息,对IMU的速度进行累加。
  • projectPointCloud
      将点投影到range image上,利用IMU的旋转,对点进行去畸变,同时变换到第一帧的坐标系中。
  • cloudExtraction和publishClouds
      按照Range Image的顺序,提取和发布点云。

imuPreintegration.cpp

  imuPreintegration.cpp中包含两个类,IMUPreintegration和TransformFusion。

IMUPreintegration类

  IMUPreintegration类的作用是计算预积分,但是为了计算精度,对IMU数据会做三次处理,预积分,预积分并使用GTSAM优化IMU估计和偏差,最后利用更新的偏差再次进行预积分。在构造函数中,使用imu参数初始化了
GTSAM优化框架。主要的程序处理依然在两个回调函数中。

imuHandler函数

 &emsm;接收imu数据进行预积分,发送预积分结果,imu增量信息odometry/imu_incremental。值得注意的是,在发送的nav_msgs::Odometry类型的数据中,速度在imu坐标系下,而位姿在lidar坐标系下,不知道为什么要这么处理。调查发现代码其他地方并没有使用速度消息。

odometryHandler函数

  优化每进行一百次,去除过去的观测,只保留现在优化的偏差继续用GTSAM进行优化。
  预积分imu,利用GTSAM优化偏差和状态。判断当前偏差和状态,如果误差太大,就重置重新优化。最后利用之前优化的偏差重新进行预积分。

TransformFusion类

  这个类的主要作用是向rviz发送两个位姿变换,当前位姿和imu path。
  首先,发送odom到map的固定变换,代码中是重合的。接收mapOptimization发出的里程计信息,结合IMUPreintegration发出的IMU预积分增量,发送odom到lidar的位姿变换和odom的里程计信息。同时根据里程计信息,发布path消息。

featureExtraction.cpp

  类似于LOAM的特征提取测量,在判断遮挡之后,对每一个SCAN分六段根据smooth选取线特征和面特征,对面特征进行降采样。

mapOptmization.cpp

  代码运行分为三个线程,mapOptimization的回调函数,回环检测,以及地图输出。先从后两个比较简单的说起。在代码里还可以看到,作者还计划引入通过用户交互来设置回环。

loopClosureThread

  通过将关键帧构建KD tree来搜寻临近的关键帧,选择与最新的关键帧位姿临近而且时间差超过阈值的关键帧作为回环候选。
  使用候选帧附近的帧构建局部地图,对局部地图和当前帧进行降采样。使用icp估计当前帧到局部地图的位姿变换,并设置因子图相关参数。

visualizeGlobalMapThread

  发布全局地图和在程序运行结束之后保存点云和轨迹。

laserCloudInfoHandler

  每隔一段时间,对地图进行优化,分别介绍调用的几个函数的功能。

  • updateInitialGuess
      根据imu预积分的结果设置优化初值。
  • extractSurroundingKeyFrames和downsampleCurrentScan
      通过构建kd tree找到临近帧和时间最近的几帧构建局部地图,对局部地图和当前帧进行降采样。
  • scan2MapOptimization
      迭代优化当前帧到局部地图的位姿变化。每次迭代通过构建kd tree找到距离当前帧线和面特征最近的五个线和面特征点,根据这五个点做直线和平面拟合,计算点到直线和平面的距离,同时计算距离的单位方向向量。通过高斯牛顿法优化位姿变换。
  • saveKeyFramesAndFactor
      如果角度或者距离变化较大,将当前帧选为关键帧,同时根据里程计和回环节点在ISAM中进行全局优化。保存关键帧位姿。
  • correctPoses
      修正回环导致的位姿变化。

代码细节

  • respawn参数
      在roslaunch文件运行节点时 ,设置节点在退出后重新启动。
    respawn="true"(optional, default: False)
    Restart the node automatically if it quits.
  • ros::NodeHandle::param函数
      从参数服务器获取参数,不能获取的情况下设置为默认值,返回是否成功获取参数。
  • Eigen::Map函数
      通过指针从C++数组构造Eigen数组,也可以像EIgen其他数据结构一样使用Map。
  • tcpNoDelay参数
      If a TCP transport is used, specifies whether or not to use TCP_NODELAY to provide a potentially lower-latency connection.
  • ros回调函数
      参考:http://wiki.ros.org/roscpp/Overview/Callbacks%20and%20Spinning
      ROS::spin()和ROS::spinonce都是为了单线程设置的,如果要同时处理多个消息,应该使用MultiThreadedSpinner或者AsyncSpinner。spin和spinonce的区别在于spin有一个内置的等待时间。
  • 使用imu修正位姿
      在进行位姿估计和发布增量位姿的时候,都会和imu进行融合更新,但是权重很低。
  • 3
    点赞
  • 37
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
lio-sam是一个开源项目,是LIO(Linux内核iSCSI target)模块的一个分支。它是专门为高性能和可扩展性而设计的iSCSI目标代码lio-sam项目的主要目标是提供一个高性能的iSCSI目标,同时保持Linux kernel的稳定性和可靠性。它在传输层使用Scst(SCSI target实现)和LIO(Linux iSCSI实现)的组合,并有一些优化以提高性能。它还支持各种iSCSI功能,如CHAP认证、数据压缩和IPsec等。 代码阅读lio-sam对Linux内核和iSCSI有一定的了解是很有帮助的。lio-sam使用了一些Linux内核的机制,如工作队列和内存管理。了解这些机制将有助于理解lio-sam的实现原理和性能优化技巧。 在阅读lio-sam代码时,可以关注以下几个方面: 1. LIO模块的初始化和配置:lio-sam在加载模块时进行一些初始化工作,包括创建Scst的实例和配置iSCSI target。了解这些步骤可以帮助理解lio-sam的工作流程和配置方式。 2. iSCSI连接管理:lio-sam负责管理iSCSI连接,包括连接的建立、维护和中断。了解连接管理的实现原理可以帮助理解lio-sam如何处理多个客户端的连接和请求。 3. SCSI命令处理:lio-sam的核心功能是处理SCSI命令。了解lio-sam如何解析SCSI命令、调用底层存储设备和返回响应可以帮助理解其工作原理和性能优化方法。 4. 性能优化技巧:lio-sam的设计目标之一是提高性能。代码中可能包含一些性能优化技巧,如批量处理、IO调度和缓存管理等。了解这些技巧可以帮助优化自己的应用程序。 需要注意的是,代码阅读是一项耗时耗力的工作,需要具备一定的编程和系统知识。在阅读代码时,可以结合官方文档、论坛和社区来获取更多的信息和帮助。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值