0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

如何将每个框架插入到SLAM框架中

新机器视觉 来源:古月居 2024-04-30 12:55 次阅读

0. 简介

LinK3D、CSF、BALM这几个都是非常方便去插入到激光SLAM框架的。这里我们会分别从多个角度来介绍如何将每个框架插入到SLAM框架中

1. LinK3D:三维LiDAR点云的

线性关键点表示

LinK3D的核心思想和基于我们的LinK3D的两个LiDAR扫描的匹配结果。绿色线是有效匹配。当前关键点(黑色,CK)的描述符用其相邻关键点来表示。描述符的每个维度对应于扇区区域。第一维度对应于当前关键点的最近关键点所在的扇区区域(蓝色和红色、CK的最近关键点),并且其他维度对应于以逆时针顺序布置的区域。如果在扇区区域中存在关键点,则搜索扇区区域中最近的关键点(紫色和橙色,扇区中CK的最近关键点)并将其用于表示描述符的对应维度。

a735674c-063c-11ef-a297-92fbcf53809c.png

2. Link3D数据植入

这里的Link3D数据植入其实在外围调用就这些内容,当中AggregationKeypoints_LinK3D存储的是存当前点云中的聚类后的关键点。而pCurrentFrame_LinK3D对应的则是点云帧。该函数中利用LinK3D仿函数执行了提取边缘点,聚类,计算描述子的操作。其实主要实现的都是LinK3D提取器。

    //在这里植入LinK3D,把接收到的点云数据用LinK3D提取边缘点和描述子,发布关键点数据,打印输出描述子
    //LinK3D提取器
    BoW3D::LinK3D_Extractor* pLinK3dExtractor(new BoW3D::LinK3D_Extractor(nScans, scanPeriod_LinK3D, minimumRange, distanceTh, matchTh)); 
    //创建点云帧,该函数中利用LinK3D仿函数执行了提取边缘点,聚类,计算描述子的操作
    Frame* pCurrentFrame_LinK3D(new Frame(pLinK3dExtractor, plaserCloudIn_LinK3D));
    //此时pCurrentFrame_LinK3D这个类指针中包含了边缘点,聚类,描述子的信息
//测试 输出关键点数量和第一个关键点信息 正常输出 
// cout << "------------------------" << endl << "关键点数量:" << pCurrentFrame_LinK3D->mvAggregationKeypoints.size();
// cout << "第一个关键点信息x坐标" << pCurrentFrame_LinK3D->mvAggregationKeypoints[0].x;
    //存当前点云中的聚类后的关键点
    AggregationKeypoints_LinK3D->points.insert(AggregationKeypoints_LinK3D->points.end(), pCurrentFrame_LinK3D->mvAggregationKeypoints.begin(), pCurrentFrame_LinK3D->mvAggregationKeypoints.end());
//测试 输出点云中信息 也能正常输出
// cout << "------------------------" << endl << "关键点数量:" << AggregationKeypoints_LinK3D->points.size();
// cout << "第一个关键点信息x坐标" << AggregationKeypoints_LinK3D->points[0].x;
    // 2.对描述子进行匹配 3.使用匹配对进行帧间icp配准 pPreviousFrame是上一个link3d Frame帧 pCurrentFrame_LinK3D是当前link3d Frame帧
    // 获取上一帧和当前帧之间的匹配索引
     vector> vMatchedIndex; 
    pLinK3dExtractor->match(pCurrentFrame_LinK3D->mvAggregationKeypoints, pPreviousFrame->mvAggregationKeypoints, pCurrentFrame_LinK3D->mDescriptors, pPreviousFrame->mDescriptors, vMatchedIndex);
    //仿照BoW3D函数写一个帧间ICP匹配函数求出R,t
    int returnValue = 0;
    // 进行帧间ICP匹配 求当前帧到上一帧的位姿变换
    // 这里求的R t是当前帧点云到上一帧点云的位姿变换
    returnValue = pose_estimation_3d3d(pCurrentFrame_LinK3D, pPreviousFrame, vMatchedIndex, RelativeR, Relativet, pLinK3dExtractor);
    //至此获得了当前帧点云到上一帧点云的位姿变换


    //当前帧Frame用完以后,赋值给上一帧Frame,赋值前先把要丢掉的帧内存释放
    //这里Frame里有成员指针,析构函数里delete成员指针
    delete pPreviousFrame;
    pPreviousFrame = pCurrentFrame_LinK3D;
    //LinK3D 植入结束

点云帧配置如下,其实可以看到这里面没有太多的内容,主要还是调用Link3D中的void LinK3D_Extractor::Ptr pLaserCloudIn, vector &keyPoints, cv::Mat &descriptors, ScanEdgePoints &validCluster)函数。并获取关键点、描述子还有聚类信息。具体的实现与具体论文保持一致,可以看Github内容

  //静态(全局?)变量要在这里初始化
  long unsigned int Frame::nNextId = 0;


  Frame::Frame(LinK3D_Extractor* pLink3dExtractor, pcl::PointCloud::Ptr pLaserCloudIn):mpLink3dExtractor(pLink3dExtractor)
  {
    mnId = nNextId++; 


    (*mpLink3dExtractor)(pLaserCloudIn, mvAggregationKeypoints, mDescriptors, mClusterEdgeKeypoints);
  }

2. CSF“布料”滤波算法

然后下面就是CSF的处理,这里其实可以将内容加在BALM当中,Github。因为CSF其实作用是区分地面点的作用

a75834b6-063c-11ef-a297-92fbcf53809c.png

 // 
 CSF csf;
 csf.params.iterations = 600;
 csf.params.time_step = 0.95;
 csf.params.cloth_resolution = 3;
 csf.params.bSloopSmooth = false;


 csf.setPointCloud(*laserCloudSurfLast);
 // pcl::savePCDFileBinary(map_save_directory, *SurfFrame);


 std::vector groundIndexes, offGroundIndexes;
 // 输出的是vector类型的地面点和非地面点索引
 pcl::PointCloud::Ptr groundFrame(new pcl::PointCloud);
 pcl::PointCloud::Ptr offGroundFrame(new pcl::PointCloud);
 csf.do_filtering(groundIndexes, offGroundIndexes);
 pcl::copyPointCloud(*laserCloudSurfLast, groundIndexes, *groundFrame);
 pcl::copyPointCloud(*laserCloudSurfLast, offGroundIndexes, *offGroundFrame);

实际用一句话:把点云翻过来,罩上一块不同材质的布料,就可以得到地面了。参考实际物理的布,布料上的点之间存在不同的作用力,详细可以参考这篇文章:https://www.guyuehome.com/40977

3. BALM

对于BALM其实主要分为三块,balm_front、scan2map、balm_back这三个流程。我们一开始使用的是ALOAM的前端来提取激光里程计信息,然后scan2map中加入CSF来进一步区分地面点。最后分成三类传入到BA约束当中https://blog.csdn.net/lovely_yoshino/article/details/133940976。

a7677ad4-063c-11ef-a297-92fbcf53809c.png

while(n.ok())
  {
    ros::spinOnce();
    if(corn_buf.empty() || ground_buf.empty() || odom_buf.empty() || offground_buf.empty())
    {
      continue;
    }


    mBuf.lock();
    uint64_t time_corn = corn_buf.front()->header.stamp.toNSec();
    uint64_t time_ground = ground_buf.front()->header.stamp.toNSec();
    uint64_t time_odom = odom_buf.front()->header.stamp.toNSec();
    uint64_t time_offground = offground_buf.front()->header.stamp.toNSec();
    if(time_odom != time_corn)
    {
      time_odom < time_corn ? odom_buf.pop() : corn_buf.pop();
            mBuf.unlock();
            continue;
        }


        if(time_odom != time_ground)
        {
            time_odom < time_ground ? odom_buf.pop() : ground_buf.pop();
            mBuf.unlock();
            continue;
        }


        if(time_odom != time_offground)
        {
            time_odom < time_ground ? odom_buf.pop() : ground_buf.pop();
            mBuf.unlock();
            continue;
        }


        ros::Time ct(ground_buf.front()->header.stamp);
    pcl::PointCloud::Ptr pl_ground_temp(new pcl::PointCloud);
    pcl::PointCloud::Ptr pl_edge_temp(new pcl::PointCloud);
    pcl::PointCloud::Ptr pl_offground_temp(new pcl::PointCloud);


    rosmsg2ptype(*ground_buf.front(), *pl_ground);
    rosmsg2ptype(*corn_buf.front(), *pl_corn);
    rosmsg2ptype(*offground_buf.front(), *pl_offground);


    //pcl::savePCDFileBinary("/home/wb/FALOAMBA_WS/wb/Map/map.pcd", *pl_ground);


    //pl_ground还有用,所以这里复制出一个新点云
    *pl_ground_temp = *pl_ground;
    *pl_edge_temp = *pl_corn;
    *pl_offground_temp = *pl_offground;
    corn_buf.pop(); ground_buf.pop(); offground_buf.pop();


    q_odom.w() = odom_buf.front()->pose.pose.orientation.w;
    q_odom.x() = odom_buf.front()->pose.pose.orientation.x;
    q_odom.y() = odom_buf.front()->pose.pose.orientation.y;
    q_odom.z() = odom_buf.front()->pose.pose.orientation.z;
    t_odom.x() = odom_buf.front()->pose.pose.position.x;
    t_odom.y() = odom_buf.front()->pose.pose.position.y;
    t_odom.z() = odom_buf.front()->pose.pose.position.z;
    odom_buf.pop();
    mBuf.unlock();


    // T_curr2last = T_curr2w * T_last2w¯¹
    Eigen::Vector3d delta_t(q_last.matrix().transpose()*(t_odom-t_last));
    Eigen::Quaterniond delta_q(q_last.matrix().transpose() * q_odom.matrix());
    q_last = q_odom;
    t_last = t_odom;


    // T_curr2last * I
    t_gather_pose = t_gather_pose + q_gather_pose * delta_t;
    q_gather_pose = q_gather_pose * delta_q;
    if(jump_flag < skip_num)
        {
            jump_flag++;
            continue;
        }
        jump_flag = 0;


        if(plcount == 0)// 第一帧
        {
            // 第一帧:T_curr2w = T_curr2last
            q_poses.push_back(q_gather_pose);
            t_poses.push_back(t_gather_pose);
        }
        else// 第二帧
        {
            // T_1_2_0 * T_2_2_1 = T_2_2_0
            // T_2_2_0 * T_3_2_2 = T_3_2_0
            q_poses.push_back(q_poses[plcount-1]*q_gather_pose);
            t_poses.push_back(t_poses[plcount-1] + q_poses[plcount-1] * t_gather_pose);
        }


        parray.header.stamp = ct;
        geometry_msgs::Pose apose;
        apose.orientation.w = q_poses[plcount].w();
        apose.orientation.x = q_poses[plcount].x();
        apose.orientation.y = q_poses[plcount].y();
        apose.orientation.z = q_poses[plcount].z();
        apose.position.x = t_poses[plcount].x();
        apose.position.y = t_poses[plcount].y();
        apose.position.z = t_poses[plcount].z();


        // ---------------------------- 当前帧位姿(优化前的)--------------------------------
        nav_msgs::Odometry laser_odom;
        laser_odom.header.frame_id = "camera_init";
        laser_odom.child_frame_id = "aft_BA";
        laser_odom.header.stamp = ct;
        laser_odom.pose.pose.orientation.x = apose.orientation.x;
        laser_odom.pose.pose.orientation.y = apose.orientation.y;
        laser_odom.pose.pose.orientation.z = apose.orientation.z;
        laser_odom.pose.pose.orientation.w = apose.orientation.w;
        laser_odom.pose.pose.position.x = apose.position.x;
        laser_odom.pose.pose.position.y = apose.position.y;
        laser_odom.pose.pose.position.z = apose.position.z;
        //发布优化前的位姿
        pub_odom.publish(laser_odom);
        //发布坐标关系
        static tf::TransformBroadcaster br;
        tf::Transform transform;
        tf::Quaternion q;
        transform.setOrigin(tf::Vector3(apose.position.x, apose.position.y, apose.position.z));
        q.setW(apose.orientation.w);
        q.setX(apose.orientation.x);
        q.setY(apose.orientation.y);
        q.setZ(apose.orientation.z);
        transform.setRotation(q);
        br.sendTransform(tf::StampedTransform(transform, laser_odom.header.stamp, "camera_init", "aft_BA"));
        parray.poses.push_back(apose);


        // 发布优化前的位姿
        pub_pose.publish(parray);


        pl_ground_buf.push_back(pl_ground_temp);
        pl_edge_buf.push_back(pl_edge_temp);
        pl_offground_buf.push_back(pl_offground_temp);

审核编辑:黄飞

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • SLAM
    +关注

    关注

    23

    文章

    419

    浏览量

    31797
  • 滤波算法
    +关注

    关注

    2

    文章

    88

    浏览量

    13717

原文标题:如何插入LinK3D、CSF、BALM来直接插入各个SLAM框架中

文章出处:【微信号:vision263com,微信公众号:新机器视觉】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    GSLAM:一套通用的SLAM框架与基准

    其中,SLAM算法插件提供了SLAM领域流行的优秀算法,包括DSO,ORBSLAM,SVO和TheiaSFM等,这些插件可以直接集成自己的代码,研究人员也可以基于这些插件进行进一步
    的头像 发表于 03-07 09:42 8560次阅读
    GSLAM:一套通用的<b class='flag-5'>SLAM</b><b class='flag-5'>框架</b>与基准

    基于多模态语义SLAM框架

    本文提出了一个鲁棒且快速的多模态语义 SLAM 框架,旨在解决复杂和动态环境SLAM 问题。具体来说,仅几何聚类和视觉语义信息相结合
    的头像 发表于 08-31 09:39 1683次阅读

    Hadoop的整体框架组成

    。Hadoop旨在从单个服务器扩展数千个机器,每个都提供本地计算和存储。Hadoop框架包括以下四个模块:HadoopCommon: 这些是其他Hadoop模块所需的Java库和实用程序。这些库提供文件系统
    发表于 05-11 16:00

    框架设计的常用模式有哪些

    1. 模板方法模式模板方法模式是框架中最常用的设计模式。其根本的思路是算法由框架固定,而将算法具体的操作交给二次开发者实现。例如一个设备初始化的逻辑,
    发表于 12-17 16:44

    什么是框架?为什么要有框架

    代码结构体逻辑是一样的,同时有大量相似或者共同的地方。我们可以这些共同的地方抽出来形成一个固定的程序框架,那么我们再开发新的同一种类型的程序时就可以套用这套框架。这样会大大提高我们的开发效率,同时由于这个
    发表于 11-09 07:38

    HOOFR-SLAM的系统框架及其特征提取

    Intelligent Vehicles Applications1. 介绍2. HOOFR-SLAM2.1 系统框架2.2 HOOFR特征提取2.3 映射线程2.3.1 特征匹配1. 介绍提出一种HOOFR-...
    发表于 12-21 06:35

    如何将CubeMX生成的程序框架与BSP驱动程序快速集成?

    一些经验如何将 CubeMX 生成的程序框架与 BSP 驱动程序快速集成。目标是拥有无编译/链接错误的应用程序框架。x-nucleo 板的入门文档侧重于 HAL 和 BSP 层的一般概念。我正在寻找
    发表于 01-30 08:48

    ACE代码框架总结

    .ACE_Engine框架模块划分对于类Web开发范式组件,根据组件从前端后端的过程,可以整个框架划分为JsFrameWork,DomNode, ComPonent, Render
    发表于 03-22 09:11

    如何将应用程序移植运行在基于Arm的设备上的Windows?

    本指南介绍如何将应用程序移植运行在基于Arm的设备上的Windows。该指南首先回顾了一般指南,然后展示了不同框架的示例:Tweeten应用程序的Electron移植、StaffPad应用程序
    发表于 08-02 06:06

    spring mvc框架介绍

    。使用 Spring 可插入的 MVC 架构,可以选择是使用内置的 Spring Web 框架还是 Struts 这样的 Web 框架。通过策略接口,Spring 框架是高度可配置的,
    发表于 11-17 16:28 2335次阅读
    spring mvc<b class='flag-5'>框架</b>介绍

    从单片机基础程序框架

    单片机应用的核心技术是什么?是按键,数码管,流水灯,串口。是它们的程序框架。按键和数码管是 输入是人机界面,把它们的程序框架研究透了,以后做彩屏或者更花销的显示界面,程序框架也可以通用。 流水
    发表于 09-07 14:45 9次下载

    LINS算法的框架与代码分析

    , 由于网上已经有些同学对算法做了介绍,一些基础的知识本文不再赘述,本文详细围绕以下两个问题介绍,希望对读者理解算法有所帮助: 1、LINS 是如何将激光观测融入滤波框架的? 2、滤波框架
    的头像 发表于 10-09 14:57 3180次阅读

    基于LeGo-LOAM框架的3D激光SLAM技术

    能力也过硬,一般企业年薪至少30W起步了。 学习SLAM主要需要攻克三大难关: 扎实的数学基础 对整个SLAM框架及细节部分理解一定深度 比较高度的编程能力 这三点都绝非易事,需要
    的头像 发表于 06-29 15:28 637次阅读
    基于LeGo-LOAM<b class='flag-5'>框架</b>的3D激光<b class='flag-5'>SLAM</b>技术

    视觉SLAM开源方案汇总 视觉SLAM设备选型

    SLAM至今已历经三十多年的研究,这里给出经典视觉SLAM框架,这个框架本身及其包含的算法已经基本定型,并且已经在许多视觉程序库和机器人程序库中提供。
    发表于 08-10 14:15 1023次阅读
    视觉<b class='flag-5'>SLAM</b>开源方案汇总 视觉<b class='flag-5'>SLAM</b>设备选型

    视觉SLAM是什么?视觉SLAM的工作原理 视觉SLAM框架解读

    近年来,SLAM技术取得了惊人的发展,领先一步的激光SLAM已成熟的应用于各大场景,视觉SLAM虽在落地应用上不及激光SLAM,但也是目前
    的头像 发表于 09-05 09:31 3831次阅读
    视觉<b class='flag-5'>SLAM</b>是什么?视觉<b class='flag-5'>SLAM</b>的工作原理 视觉<b class='flag-5'>SLAM</b><b class='flag-5'>框架</b>解读