5.如何利用ORBSLAM3生成可用于机器人/无人机导航的二维/三维栅格地图--以octomap为例

news2025/1/20 5:54:42

1 octomap的安装及官方文档

        这里我们用ROS自带的安装方式即可:

sudo apt install ros-melodic-octomap-msgs ros-melodic-octomap-ros ros-melodic-
octomap-rviz-plugins ros-melodic-octomap-server

        如上图就是安装成功了:

        如果安装失败了,尝试用小鱼ROS换一下源再去安装:

        一些官方的文档如下,大家感兴趣可以学习一下:https://octomap.github.io/octomap/doc/index.html#gettingstarted_secicon-default.png?t=N7T8https://octomap.github.io/octomap/doc/index.html#gettingstarted_sec

2 如何利用ORBSLAM3生成的地图点通过octomap构造可以用来导航的栅格地图

2.1 octomap节点的编写

        在我们装了octomap后,我们建立一个launch文件,这里都是固定的,我就来给大家解释一下文件的各个参数的含义吧。

        我们建立一个slam.launch文件:

<launch>

    <!-- Octomap Server Node -->
    <node pkg="octomap_server" type="octomap_server_node" name="octomap_server">
        <param name="resolution" value="0.05" />
        <param name="frame_id" type="string" value="/orb_cam_link" />
        <param name="sensor_model/max_range" value="5.0" />
        <remap from="cloud_in" to="/ORB_SLAM3/Point_Clouds" />
        <param name="sensor_model/max_range" value="5000.0" />
        <param name="latch" value="true" />
        <param name="pointcloud_min_z" type="double" value="-1.5" />
        <param name="pointcloud_max_z" type="double" value="10" />
        <param name="occupancy_min_z" type="double" value="0.1" />
        <param name="occupancy_max_z" type="double" value="2" />
        <param name="height_map" type="bool" value="False" />
        <param name="colored_map" value="true" />
    </node>
    <node pkg="tf" type="static_transform_publisher" name="orb_cam_link" args="0 0 0.15 0 0 0 /orb_cam_link /pointCloud 70" />
    <!-- rviz -->
    <node pkg="rviz" type="rviz" name="rviz" args="-d $(find akm_pnc)/rviz/grid.rviz" />
</launch>

        建立一个Octomap Server Node节点。

        这个参数文件是一个ROS launch文件,它定义了启动和配置了几个节点和参数,主要是针对 Octomap Server、静态 TF 变换发布器和 RViz 可视化工具的配置。

让我解释其中的一些关键部分:

1. **Octomap Server Node**:
    - `pkg="octomap_server"` 和 `type="octomap_server_node"` 指定了要运行的节点以及其所在的软件包。
    - `name="octomap_server"` 定义了节点的名称。
    - `param` 标签下的各个 `name` 参数设置了 Octomap Server 的一些参数:
        - `resolution` 设置了地图分辨率为 0.05。
        - `frame_id` 设置了地图的坐标系为 `/orb_cam_link`。
        - `sensor_model/max_range` 设置了传感器模型的最大范围为 5.0。
        - `latch` 设为 `true`,意味着参数会被持久化,即在重新加载时保留先前设置的参数值。
        - 其他参数如 `pointcloud_min_z`、`pointcloud_max_z`、`occupancy_min_z`、`occupancy_max_z` 用于设置点云和占据地图的高度范围等参数。
        - `colored_map` 设置了地图是否包含颜色信息。

2. **TF 静态变换发布器**:
    - `node` 标签下定义了一个 `static_transform_publisher` 节点,用于发布静态的 TF 变换。
    - `name="orb_cam_link"` 定义了发布节点的名称。
    - `args` 包含了发布的静态变换的参数:位置 (0, 0, 0.15)、旋转 (0, 0, 0) 以及目标坐标系和源坐标系的名称 `/orb_cam_link` 和 `/pointCloud`。
 
3. **RViz**:
    - 最后一个节点启动了 RViz 工具,指定了加载一个配置文件 `grid.rviz`。

总体而言,这个 launch 文件配置了 Octomap Server 用于构建地图,并设置了一些传感器模型、地图分辨率以及静态 TF 变换的发布,最后启动了 RViz 工具以可视化地图和其他相关数据。

        这里需要注意的!!非常重要的参数有两个!!

        第一个是:to后面要放入自己的点云话题

        <remap from="cloud_in" to="/ORB_SLAM3/Point_Clouds" />

        第二个是frame_id:看一下ROS官方给的说明(“地图将被发布的静态全局坐标系。在动态构建地图时,需要从传感器数据到该坐标系的变换信息可用。”,也就是说,地图会被发布到一个固定的全局坐标系中。在创建地图的过程中,需要能够获得传感器数据与这个全局坐标系之间的转换信息。)

octomap_server - ROS Wikiicon-default.png?t=N7T8http://wiki.ros.org/octomap_server

        <param name="frame_id" type="string" value="/orb_cam_link" />

        下面我们来看ORB-SLAM3的部分怎么修改吧!

2.2 ORB-SLAM3发布栅格地图数据

2.2.1 理解坐标系/orb_cam_link、/odom

        我们控制仿真程序向前走。

        这是初始的状态:

        目前的坐标系为orb_cam_link。我们控制仿真程序向前走一段距离。

        我们发现,栅格地图生成了一部分。有尾部的绿线是我们的轨迹。它的话题为/RGBD/Path

        但是我们如果换成坐标系为odom呢??一直在原点不动了。

        因此,我们估计到的Tcw其实就是orb_cam_link到odom坐标系的变换矩阵。

        这里的track_point和all_point是追踪的地图点和所有的地图点,如上图彩色的部分和白色的部分。

2.2.2 稠密建图代码详解 如何发送全部稠密点云给octomap

        <remap from="cloud_in" to="/ORB_SLAM3/Point_Clouds" />

        这里我们接收/ORB_SLAM3/Point_Clouds类型的点云进行稠密重建,那么需要稠密点云进行输入。

        我们在稠密建图的线程中新添加一个话题:

        pclPoint_pub = n.advertise<sensor_msgs::PointCloud2>("/ORB_SLAM3/Point_Clouds",1000000);
        pclPoint_local_pub = n.advertise<sensor_msgs::PointCloud2>("/ORB_SLAM3/Point_local_Clouds",1000000);

        我们把所有帧的稠密点云赋予给octomap:

    /**
     * @brief 根据关键帧生成点云
     * @param kf
     * @param imRGB
     * @param imD
     * @param pose
     * @return
     */
    pcl::PointCloud< PointCloudMapping::PointT >::Ptr PointCloudMapping::generatePointCloud(KeyFrame *kf,const cv::Mat& imRGB, const cv::Mat& imD, const cv::Mat& pose)
    {
        std::chrono::steady_clock::time_point t1 = std::chrono::steady_clock::now();
        PointCloud::Ptr current(new PointCloud);
        PointCloud::Ptr loop_points(new PointCloud);
        for(size_t v = 0; v < imRGB.rows ; v+=3){
            for(size_t u = 0; u < imRGB.cols ; u+=3){
                cv::Point2i pt(u,v);
                bool isDynamic = false;
                float d = imD.ptr<float>(v)[u];
                if(d < 0.1 || d>15)
                    continue;
                PointT p;
                p.z = d;
                p.x = ( u - mCx) * p.z / mFx;
                p.y = ( v - mCy) * p.z / mFy;
                p.b = imRGB.ptr<uchar>(v)[u*3];
                p.g = imRGB.ptr<uchar>(v)[u*3+1];
                p.r = imRGB.ptr<uchar>(v)[u*3+2];
                current->points.push_back(p);
                loop_points->points.push_back(p);
            }
        }
        Eigen::Isometry3d T = Converter::toSE3Quat( pose );
        PointCloud::Ptr tmp(new PointCloud);
        // tmp为转换到世界坐标系下的点云
        pcl::transformPointCloud(*current, *tmp, T.inverse().matrix());

        // depth filter and statistical removal,离群点剔除
        statistical_filter.setInputCloud(tmp);
        statistical_filter.filter(*current);
        (*mPointCloud) += *current;

        pcl::transformPointCloud(*mPointCloud, *tmp, T.inverse().matrix());
        // 加入新的点云后,对整个点云进行体素滤波
        voxel.setInputCloud(mPointCloud);
        voxel.filter(*tmp);
        mPointCloud->swap(*tmp);
        mPointCloud->is_dense = false;
        return loop_points;
    }
    /**
     * @brief 显示点云
     */
    void PointCloudMapping::NormalshowPointCloud()
    {
         0.PointCloude数据结构中含有什么
        // typedef pcl::PointXYZRGBA PointT;
        // typedef pcl::PointCloud<PointT> PointCloud;
        // PointCloud::Ptr pcE;
        // Eigen::Isometry3d T;
        // int pcID;
        PointCloude pointcloude;
        ros::NodeHandle n;
        pclPoint_pub = n.advertise<sensor_msgs::PointCloud2>("/ORB_SLAM3/Point_Clouds",1000000);
        pclPoint_local_pub = n.advertise<sensor_msgs::PointCloud2>("/ORB_SLAM3/Point_local_Clouds",1000000);
        ros::Rate r(5);
        /// 一直在执行
        while(true)
        {
            KeyFrame* kf;
            cv::Mat colorImg, depthImg;
            {
                std::unique_lock<std::mutex> locker(mKeyFrameMtx);
                 1.如果没有关键帧(还没有进入追踪线程,等待关键帧的加入)
                while(mvKeyFrames.empty() && !mbShutdown)
                {
                    mKeyFrameUpdatedCond.wait(locker);
                }
                {
                    unique_lock<mutex> lck( keyframeMutex );
                }
                 2.更新点云(这里代码逻辑有问题)
                if(lastKeyframeSize  == LoopKfId)
                    updatecloud();
                if (!(mvDepthImgs.size() == mvColorImgs.size() && mvKeyFrames.size() == mvColorImgs.size())) {
                    std::cout << RED << "这是不应该出现的情况!" << std::endl;
                    continue;
                }

                if (mbShutdown && mvColorImgs.empty() && mvDepthImgs.empty() && mvKeyFrames.empty()) {
                    break;
                }

                 3.取出我们应该去处理的数据
                kf = mvKeyFrames.front();
                colorImg = mvColorImgs.front();
                depthImg = mvDepthImgs.front();
                mvKeyFrames.pop();
                mvColorImgs.pop();
                mvDepthImgs.pop();
            }

            if (mCx==0 || mCy==0 || mFx==0 || mFy==0)
            {
                mCx = kf->cx;
                mCy = kf->cy;
                mFx = kf->fx;
                mFy = kf->fy;
            }

            {
                std::unique_lock<std::mutex> locker(mPointCloudMtx);
                 4.获得关键帧的位姿
                cv::Mat mTcw_Mat = kf->GetPoseMat();
                 5.pcE中存放点云数据,已经被转化到世界坐标系下了
                pointcloude.pcE=generatePointCloud(kf,colorImg, depthImg, mTcw_Mat);
                 6.存放关键帧的ID
                pointcloude.pcID = kf->mnId;
                 7.存放关键帧的位姿
                pointcloude.T = ORB_SLAM3::Converter::toSE3Quat(mTcw_Mat);

                pointcloud.push_back(pointcloude);
                if(pointcloude.pcE->empty())
                    continue;

                 8.这帧的点云
                pcl_cloud_local_kf = *pointcloude.pcE;
                 9.所有的点云
                pcl_cloud_kf = *mPointCloud;

                 10.转换到ROS坐标系下
                Cloud_transform(pcl_cloud_local_kf,pcl_local_filter);
                Cloud_transform(pcl_cloud_kf,pcl_filter);

                 11.转化为ROS格式的点云
                pcl::toROSMsg(pcl_local_filter, pcl_local_point);

                // TODO 发布给octomap
                pcl::toROSMsg(pcl_filter, pcl_point);

                 12.pclPoint_pub (/ORB_SLAM3/Point_Clouds)
                pcl_local_point.header.frame_id = "/pointCloud_local";
                pcl_point.header.frame_id = "/pointCloud";
                pclPoint_local_pub.publish(pcl_local_point);

                // TODO 发布给octomap
                pclPoint_pub.publish(pcl_point);
                std::cout << YELLOW << "show point cloud, size=" << mPointCloud->points.size() << std::endl;
                lastKeyframeSize++;
            }
        }
        {
            if(!mPointCloud->empty())
            {
                // 存储点云
                string save_path = "./VSLAMRGBD.pcd";
                pcl::io::savePCDFile(save_path, *mPointCloud);
                cout << GREEN << "save pcd files to :  " << save_path << endl;
            }

        }
        mbFinish = true;
    }

        自适应场景跑,雷达也是一样,建立好了栅格地图:

        我们调用命令去保存:

rosrun map_server map_saver map:=/projected_map

        在主目录下就可以看到我们的导航地图啦!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1269132.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

机器学习——决策树

1.决策树 2.熵&#xff08;不确定程度&#xff09; 3.信息增益 & 信息增益比 3.1 信息增益 & 信息增益比 的 概念 3.2 案例解释说明 &#xff13;.&#xff12;.&#xff11;数据集说明 &#xff13;.&#xff12;.&#xff12;计算 &#xff14;&#xff0e;&#x…

VUE语法-ref和reactive响应式数据引用

1、响应式概述 在vue中定义一个参数&#xff0c;当这个参数在使用中发生了变化&#xff0c;在页面中对这个数据应用的地方都会同步的发生变化&#xff0c;这个就是数据响应式。 2、创建一个非响应式的参数 该程序中采用的是VUE3的用法&#xff1a; 1、在程序中定义了一个局…

【内网安全】搭建网络拓扑,CS内网横向移动实验

文章目录 搭建网络拓扑 ☁环境CS搭建,木马生成上传一句话&#xff0c;获取WebShellCS上线reGeorg搭建代理&#xff0c;访问内网域控IIS提权信息收集横向移动 实验拓扑结构如下&#xff1a; 搭建网络拓扑 ☁ 环境 **攻击者win10地址&#xff1a;**192.168.8.3 dmz win7地址&…

开源免费跨平台数据同步工具-Syncthing

Syncthing是一款开源免费跨平台的文件同步工具&#xff0c;是基于P2P技术实现设备间的文件同步&#xff0c;所以它的同步是去中心化的&#xff0c;即你并不需要一个服务器&#xff0c;故不需要担心这个中心的服务器给你带来的种种限制&#xff0c;而且类似于torrent协议&#x…

视图层、模板(补充)

视图层 响应对象 响应---》本质都是 HttpResponse HttpResponse---》字符串render----》放个模板---》模板渲染是在后端完成 js代码是在客户端浏览器里执行的模板语法是在后端执行的redirect----》重定向 字符串参数不是是空的状态码是 3开头JsonResponse---》json格式数据 …

软著项目推荐 深度学习卷积神经网络的花卉识别

文章目录 0 前言1 项目背景2 花卉识别的基本原理3 算法实现3.1 预处理3.2 特征提取和选择3.3 分类器设计和决策3.4 卷积神经网络基本原理 4 算法实现4.1 花卉图像数据4.2 模块组成 5 项目执行结果6 最后 0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 基…

JVM——产生内存溢出原因

目录 1.产生内存溢出原因一 &#xff1a;代码中的内存泄漏1.案例1&#xff1a;equals()和hashCode()导致的内存泄漏问题&#xff1a;**正常情况**&#xff1a;**异常情况&#xff1a;**解决方案&#xff1a; 2.案例2&#xff1a;内部类引用外部类问题&#xff1a;解决方案&…

振南技术干货集:各大平台串口调试软件大赏(2)

注解目录 &#xff08;串口的重要性不言而喻。为什么很多平台把串口称为 tty&#xff0c;比如 Linux、MacOS 等等&#xff0c;振南告诉你。&#xff09; 1、各平台上的串口调试软件 1.1Windows 1.1.1 STCISP &#xff08;感谢 STC 姚老板设计出 STCISP 这个软件。&#xf…

MySQL 中的锁(一)

MySQL 中的锁 按照 MySQL 官方的说法&#xff0c;InnoDB 中锁可以分为&#xff1a; 可见&#xff0c;InnoDB 中锁非常多&#xff0c;总的来说&#xff0c;可以如下分类&#xff1a; 这些锁都是做什么的&#xff1f;具体含义是什么&#xff1f;我们现在来一一学习。 8.1. 解…

Nginx性能调优策略

Nginx是一个高性能的Web服务器和反向代理服务器&#xff0c;常用于处理高并发的请求。以下是一些常见的Nginx性能调优策略&#xff1a; 一、调整worker_processes和worker_connections 在Nginx配置文件中&#xff0c;可以通过worker_processes和worker_connections参数来调整w…

CLIPTokenizer.from_pretrained本地加载

以"openai/clip-vit-large-patch14"为例&#xff0c;原代码为&#xff1a; self.tokenizer CLIPTokenizer.from_pretrained(“openai/clip-vit-large-patch14”) self.transformer CLIPTextModel.from_pretrained(“openai/clip-vit-large-patch14”) 但我连不到外…

ArkTS-取消标题与自定义标题栏

文章目录 取消标头自定义标题栏导入Resources自定义跳转动画关于底部tabBar导航文本输入(TextInput/TextArea)自定义样式添加事件可以是onChange可以是onSubmit List列表组件设置主轴方向 网格布局服务卡片-获取地理位置页面获取地理位置服务卡片获取地理位置 可以先看看&#…

移民同步进行|企业高管自费赴美国奥本大学访学

K经理申请了美国杰出人才移民&#xff0c;已经获批I-140&#xff0c;正在排期中&#xff0c;尚未获得绿卡。为了使孩子同步美国学制&#xff0c;K经理希望先以访问学者身份带孩子出国接受免费公立教育。最终我们落实了奥本大学的职位&#xff0c;申请人及孩子顺利获签出国&…

VSCODE+QEMU+WSL调试RISCV代码(SBI、kernel)

前言 最近在对RISC-V架构比较感兴趣&#xff0c;正好手头有《RISC-V体系结构编程与实践》的书籍&#xff0c;就打算跟随笨叔将这块的知识学习起来&#xff0c;最开始当然是需要搭建一个基础的实验平台&#xff0c;本来笨叔是贴心的提供了VMare的环境&#xff0c;奈何天生叛逆的…

Matlab下载许可证文件 教程(在账号有许可证的前提下)

文章目录 Part.I IntroductionPart.II 许可证文件过期解决方案Chap.I 使用 Internet 自动激活Chap.II 在不使用 Internet 的情况下手动激活 Part.I Introduction 本文主要介绍&#xff0c;在 Mathwork 账号有许可证的前提下&#xff0c;下载许可证的操作流程。 好久没有用 Mat…

OSCP系列靶场-Esay-1

总结 getwebshell : ftp可匿名登录 → 发现隐藏文件夹 → 发现ssh密钥 → 猜解ssh用户名 → ssh密钥登录 提 权 思 路 : 发现suid权限文件 → cpulimit提权 准备工作 启动VPN 获取攻击机IP → 192.168.45.191 启动靶机 获取目标机器IP → 192.168.179.130 信息收集-端口扫…

Android自定义瀑布流文字展示

在历史搜索功能中&#xff0c;我们常用到一个瀑布流展示控件&#xff0c;用来展示我们的搜索记录&#xff0c;所以就自定义一个吧&#xff01; 布局中代码示例 <com.example.mymodularization.measure.LinearCustomandroid:id"id/ll"android:layout_width"wr…

基于深度学习的点云三维目标检测方法综述

论文标题&#xff1a;基于深度学习的点云三维目标检测方法综述 作者&#xff1a;郭毅锋&#xff11;&#xff0c;&#xff12;†&#xff0c;吴帝浩&#xff11;&#xff0c;魏青民&#xff11; 发表日期&#xff1a; 2023 1 阅读日期 &#xff1a;2023 11 29 研究背景&…

一、Gradle 手动创建一个项目

文章目录 Gradle 介绍Gradle Wrapper Gradle 使用手动安装 Gradle初始化 Gradle 介绍 Gradle 是一个快速的、可信的、适应性强的自动化构建工具&#xff0c;它是开源的。它使用优雅的并且可扩展的描述性语言。其他的介绍在官网可以了解。 Gradle Wrapper 官方建议使用 Gradl…

vue3实现元素拖拽移动功能

效果图 实现拖拽移动 首先我们给需要实现功能的元素加一个draggable"true"让元素能够被拖拽 先来认识两个搭配draggable属性一起使用的事件——ondragstart和ondragend&#xff0c;它们的定义分别为&#xff1a; ①. ondragstart 事件在用户开始拖动元素或选择的文…