3d激光SLAM:LIO-SAM框架---位姿融合输出

这篇具有很好参考价值的文章主要介绍了3d激光SLAM:LIO-SAM框架---位姿融合输出。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

前言

LIO-SAM的全称是:Tightly-coupled Lidar Inertial Odometry via Smoothing and Mapping

从全称上可以看出,该算法是一个紧耦合的雷达惯导里程计(Tightly-coupled Lidar Inertial Odometry),借助的手段就是利用GT-SAM库中的方法。

LIO-SAM 提出了一个利用GT-SAM的紧耦合激光雷达惯导里程计的框架。
实现了高精度、实时的移动机器人的轨迹估计和建图。

在之前的博客讲解了imu如何进行预积分,最终以imu的频率发布了imu的预测位姿里程计。
3d激光SLAM:LIO-SAM框架---位姿融合输出

本篇博客主要讲解,最终是如何进行位姿融合输出的

3d激光SLAM:LIO-SAM框架---位姿融合输出

Eigen::Affine3f

其中功能的核心在于 位姿间的变换,所以要了解 Eigen::Affine3f 部分的内容
Affine3f 是eighen库的 仿射变换矩阵
实际上就是:平移向量+旋转变换组合而成,可以同时实现旋转,缩放,平移等空间变换。

Eigen库中,仿射变换矩阵的大致用法为:

  1. 创建Eigen::Affine3f 对象a。
  2. 创建类型为Eigen::Translation3f 对象b,用来存储平移向量;
  3. 创建类型为Eigen::Quaternionf 四元数对象c,用来存储旋转变换;
  4. 最后通过以下方式生成最终Affine3f变换矩阵: a=b*c.toRotationMatrix();
  5. 一个向量通过仿射变换时的方法是result_vector=test_affine*test_vector;

仿射变换包括:

  • 平移
  • 旋转
  • 放缩
  • 剪切
  • 反射

平移(translation)和旋转(rotation)顾名思义,两者的组合称之为欧式变换(Euclidean transformation)或刚体变换(rigid transformation);放缩(scaling)可进一步分为uniform scaling和non-uniform scaling,前者每个坐标轴放缩系数相同(各向同性),后者不同;如果放缩系数为负,则会叠加上反射(reflection)——reflection可以看成是特殊的scaling;刚体变换+uniform scaling 称之为,相似变换(similarity transformation),即平移+旋转+各向同性的放缩;

位姿融合输出

在imu预积分的节点中,在main函数里面 还有一个类的实例对象,那就是

TransformFusion TF

其主要功能是做位姿融合输出

最终输出imu的预测结果,与上节中的imu预测结果的区别就是:

该对象的融合输出是基于全局位姿的基础上再进行imu的预测输出。全局位姿就是 经过回环检测后的lidar位姿。

该对象的本质功能如下图
3d激光SLAM:LIO-SAM框架---位姿融合输出

建图优化会输出两种激光雷达的位姿:

  • lidar 增量位姿
  • lidar 全局位姿

其中lidar 增量位姿就是 通过 lidar的匹配功能,优化出的帧间的相对位姿,通过相对位姿的累积,形成世界坐标系下的位姿
lidar全局位姿 则是在 帧间位姿的基础上,通过 回环检测,再次进行优化的 世界坐标系下的位姿,所以对于增量位姿,全局位姿更加精准

在前面提到的发布的imu的预测位姿是在lidar的增量位姿上基础上预测的,那么为了更加准确,本部分功能就预测结果,计算到基于全局位姿的基础上面。

首先看构造函数

    TransformFusion()
    {
        if(lidarFrame != baselinkFrame)
        {
            try
            {   
                tfListener.waitForTransform(lidarFrame, baselinkFrame, ros::Time(0), ros::Duration(3.0));
                tfListener.lookupTransform(lidarFrame, baselinkFrame, ros::Time(0), lidar2Baselink);
            }
            catch (tf::TransformException ex)
            {
                ROS_ERROR("%s",ex.what());
            }
        }

判断lidar帧和baselink帧是不是同一个坐标系
通常baselink指车体系
如果不是,
查询 一下 lidar 和baselink 之间的 tf变换 ros::Time(0) 表示最新的
等待两个坐标系有了变换
更新两个的变换 lidar2Baselink

        subLaserOdometry = nh.subscribe<nav_msgs::Odometry>("lio_sam/mapping/odometry", 5, &TransformFusion::lidarOdometryHandler, this, ros::TransportHints().tcpNoDelay());
        subImuOdometry   = nh.subscribe<nav_msgs::Odometry>(odomTopic+"_incremental",   2000, &TransformFusion::imuOdometryHandler,   this, ros::TransportHints().tcpNoDelay());

订阅地图优化的节点的全局位姿 和预积分节点的 增量位姿

        pubImuOdometry   = nh.advertise<nav_msgs::Odometry>(odomTopic, 2000);
        pubImuPath       = nh.advertise<nav_msgs::Path>    ("lio_sam/imu/path", 1);

发布两个信息 odomTopic ImuPath

然后看第一个回调函数 lidarOdometryHandler

    void lidarOdometryHandler(const nav_msgs::Odometry::ConstPtr& odomMsg)
    {
        std::lock_guard<std::mutex> lock(mtx);
        lidarOdomAffine = odom2affine(*odomMsg);
        lidarOdomTime = odomMsg->header.stamp.toSec();
    }

将全局位姿保存下来

  • 将ros的odom格式转换成 Eigen::Affine3f 的形式
  • 将最新帧的时间保存下来

第二个回调函数是 imuOdometryHandler
imu预积分之后所发布的imu频率的预测位姿

    void imuOdometryHandler(const nav_msgs::Odometry::ConstPtr& odomMsg)
    {
        static tf::TransformBroadcaster tfMap2Odom;
        static tf::Transform map_to_odom = tf::Transform(tf::createQuaternionFromRPY(0, 0, 0), tf::Vector3(0, 0, 0));

建图的话 可以认为 map坐标系和odom坐标系 是重合的(初始化时刻)

tfMap2Odom.sendTransform(tf::StampedTransform(map_to_odom, odomMsg->header.stamp, mapFrame, odometryFrame));

发布静态tf,odom系和map系 他们是重合的

imuOdomQueue.push_back(*odomMsg);

imu得到的里程计结果送入到这个队列中

        if (lidarOdomTime == -1)
            return;

如果没有收到lidar位姿 就returen

        while (!imuOdomQueue.empty())
        {
            if (imuOdomQueue.front().header.stamp.toSec() <= lidarOdomTime)
                imuOdomQueue.pop_front();
            else
                break;
        }

弹出时间戳 小于 最新 lidar位姿时刻之前的imu里程计数据

        Eigen::Affine3f imuOdomAffineFront = odom2affine(imuOdomQueue.front());
        Eigen::Affine3f imuOdomAffineBack = odom2affine(imuOdomQueue.back());
        Eigen::Affine3f imuOdomAffineIncre = imuOdomAffineFront.inverse() * imuOdomAffineBack;

计算最新队列里imu里程计的增量

Eigen::Affine3f imuOdomAffineLast = lidarOdomAffine * imuOdomAffineIncre;

增量补偿到lidar的位姿上去,就得到了最新的预测的位姿

        float x, y, z, roll, pitch, yaw;
        pcl::getTranslationAndEulerAngles(imuOdomAffineLast, x, y, z, roll, pitch, yaw);

分解成平移 + 欧拉角的形式

        nav_msgs::Odometry laserOdometry = imuOdomQueue.back();
        laserOdometry.pose.pose.position.x = x;
        laserOdometry.pose.pose.position.y = y;
        laserOdometry.pose.pose.position.z = z;
        laserOdometry.pose.pose.orientation = tf::createQuaternionMsgFromRollPitchYaw(roll, pitch, yaw);
        pubImuOdometry.publish(laserOdometry);

发送全局一致位姿的 最新位姿

        static tf::TransformBroadcaster tfOdom2BaseLink;
        tf::Transform tCur;
        tf::poseMsgToTF(laserOdometry.pose.pose, tCur);
        if(lidarFrame != baselinkFrame)
            tCur = tCur * lidar2Baselink;

更新tf

        tf::StampedTransform odom_2_baselink = tf::StampedTransform(tCur, odomMsg->header.stamp, odometryFrame, baselinkFrame);
        tfOdom2BaseLink.sendTransform(odom_2_baselink);

更新odom到baselink的tf

        static nav_msgs::Path imuPath;
        static double last_path_time = -1;
        double imuTime = imuOdomQueue.back().header.stamp.toSec();
        // 控制一下更新频率,不超过10hz
        if (imuTime - last_path_time > 0.1)
        {
            last_path_time = imuTime;
            geometry_msgs::PoseStamped pose_stamped;
            pose_stamped.header.stamp = imuOdomQueue.back().header.stamp;
            pose_stamped.header.frame_id = odometryFrame;
            pose_stamped.pose = laserOdometry.pose.pose;
            // 将最新的位姿送入轨迹中
            imuPath.poses.push_back(pose_stamped);
            // 把lidar时间戳之前的轨迹全部擦除
            while(!imuPath.poses.empty() && imuPath.poses.front().header.stamp.toSec() < lidarOdomTime - 1.0)
                imuPath.poses.erase(imuPath.poses.begin());

            // 发布轨迹,这个轨迹实践上是可视化imu预积分节点输出的预测值
            if (pubImuPath.getNumSubscribers() != 0)
            {
                imuPath.header.stamp = imuOdomQueue.back().header.stamp;
                imuPath.header.frame_id = odometryFrame;
                pubImuPath.publish(imuPath);
            }
        }
    }

发布imu里程计的轨迹

  • 控制一下更新频率,不超过10hz
  • 将最新的位姿送入轨迹中
  • 把lidar时间戳之前的轨迹全部擦除
  • 发布轨迹,这个轨迹实践上是可视化imu预积分节点输出的预测值

result

3d激光SLAM:LIO-SAM框架---位姿融合输出
其中粉色的部分就是imu的位姿融合输出path文章来源地址https://www.toymoban.com/news/detail-419191.html

到了这里,关于3d激光SLAM:LIO-SAM框架---位姿融合输出的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • LIO-SAM学习与运行测试数据集

    环境配置: ubuntu18.04, ros1(ros-melodic) 注: 在ros1的kinetic, melodic, noetic(https://github.com/TixiaoShan/LIO-SAM/issues/206)上被测试过; ros1的github代码: https://github.com/TixiaoShan/LIO-SAM/tree/master ros2的github代码: https://github.com/TixiaoShan/LIO-SAM/tree/ros2 论文:LIO-SAM:Tightly-coupled Lidar Inertial Odometry vis Smoothin

    2023年04月16日
    浏览(42)
  • LIO-SAM从0到1运行自己的数据集

    ​ 笔者在学习LIO_SAM时踩了不少坑,在此记录从开始到最后整个踩坑过程。文中参考了很多大佬的文章,我只是个搬运工。 可以直接跳到第二部分从0到1实现 有疑问可以随时联系我,欢迎交流。 ⼀种激光惯导紧耦合的SLAM框架,可在室内和室外实现效果不错的建图。 (1) Image

    2024年02月02日
    浏览(40)
  • 《LIO-SAM阅读笔记》-为何要引入增量式里程计?

    前言: LIO-SAM在后端中同时维护着两个里程计,一个是增量式里程计,一个是优化后的里程计,其中优化后的里程计是经过imu、回环、gps因子图联合优化后的结果,是整个系统中最准确的位姿估计,那么为什么还需要维护增量式里程计呢? 以下是我的理解 ,不一定正确,如有

    2024年01月22日
    浏览(46)
  • Ubuntu20.04安装LeGO-LOAM和LIO-SAM

    Ubuntu20.04安装LIO-SAM真是挺折磨人的,填了一路的坑,在此记录分享一下,为大家安装编译算法提供一个全面的参考。 目录 1. GTSAM安装 1.1 GTSAM安装准备 1.1.1 目录/usr/local/lib下清理gatsam 1.1.2 目录/opt/ros/noetic/lib/下清理gtsam 1.2 GTSAM安装过程 2. LIO-SAM算法依赖项安装 3. LeGO-LOAM算法编

    2024年04月28日
    浏览(83)
  • Ubuntu20.04 ROS noetic中编译和运行LIO-SAM

    本文是对自己学习过程的一个记录和总结,如果内容有误,请大家指点,感谢。         本文是在已经安装好ROS环境中进行的,不需要提前安装其他库,只需按照步骤进行操作,便能完成LIO-SAM的编译和运行,并且每一步都有我执行时的截图进行参考。 1.【创建工作空间】

    2024年03月24日
    浏览(57)
  • Ubuntu 20.04 与 ROS noetic安装 gtsam 编译 LIO-SAM 的适配版本

    本文简介在 Ubuntu 20.04 下以 ROS noetic 为基础安装 GTSAM 并成功编译 LIO-SAM 的适配版本。 安装前请检查cmake 和boost版本,Ubuntu 20.04.06自带cmake(= 3.0) 和libboost-all-dev(= 1.65)已满足要求。编译LIO-SAM适配版本的其它依赖包也已满足要求(主要是PCL, Eigen和OpenCV等, 详见其CMakeList)。 当前

    2024年02月13日
    浏览(55)
  • LOAM、Lego-liom、Lio-sam轨迹保存,与Kitti数据集真值进行评估

            首先需要保存轨迹,轨迹保存参考下面的代码,最好自己 添加一个节点 (如下图),用新节点来订阅和保存轨迹至txt文件,因为直接在算法的线程中加入此步骤我试了好像保存不了,好像是在不同线程间的参数传递格式的问题(也可能是我个人的问题)。     

    2023年04月08日
    浏览(66)
  • 在Ubuntu20.04系统上LIO-SAM跑KITTI数据集和自己数据集代码修改

    参考我的另一篇文章: Ubuntu20.04下的编译与运行LIO-SAM【问题解决】 因为liosam 要求输入的点云每个点都有ring 信息和相对时间time信息,目前的雷达驱动基本具备这些信息,但是早期的KITTI数据集不具备,所以代码要自己计算一下 ring和time。方法可以参考lego-loam中这部分内容,

    2024年02月01日
    浏览(45)
  • 6.如何利用LIO-SAM生成可用于机器人/无人机导航的二维/三维栅格地图--以octomap为例

    目录 1 octomap的安装 2 二维导航节点的建立及栅格地图的构建 3 三维栅格地图的建立         这里采用命令安装:         这样子就是安装好了。         我们进入liosam的工作空间下的launch文件夹:         新建一个launch文件,就叫octomap2D.launch         将下面的

    2024年01月16日
    浏览(49)
  • 激光SLAM:Faster-Lio 算法编译与测试

    Faster-LIO是基于FastLIO2开发的。FastLIO2是开源LIO中比较优秀的一个,前端用了增量的kdtree(ikd-tree),后端用了迭代ESKF(IEKF),流程短,计算快。Faster-LIO则把ikd-tree替换成了iVox,顺带优化了一些代码逻辑,实现了更快的LIO。在典型的32线激光雷达中可以取得100-200Hz左右的计算频

    2024年02月02日
    浏览(37)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包