视觉SLAM ch12 建图(RGB-D)

news2025/6/19 15:48:43

一、RGB-D稠密建图

RGB-D相机通结构光和飞行时间获取深度。

稠密重建方法:根据估计的相机位姿,将RGB-D数据转化为点云,然后进行拼接,最终得到由离散的点组成的点云地图

在此基础上,如果希望估计物体的表面,可以用三角网格(Mesh)和面片(Surfel)进行建图;如果希望知道地图的障碍物信息,可以通过体素(Voxel)建立占据网格地图(Occupancy Map)。

二、点云地图

对点云地图还需要进行滤波处理:外点去除滤波器和体素网格的降采样滤波器(Voxel grid filter)

1.PCL点云库头文件,参考【1】

#include <boost/make_shared.hpp>                            //boost指针相关头文件
#include <pcl/point_types.h>                                //点类型定义头文件
#include <pcl/point_cloud.h>                                //点云类定义头文件
#include <pcl/features/normal_3d.h>                         //法线特征头文件
#include <pcl/features/normal_3d_omp.h>                     //法线特征加速头文件    
#include <pcl/features/fpfh.h>                              //fpfh类特征头文件
#include <pcl/features/fpfh_omp.h>                          //fpfh加速类头文件
#include <pcl/features/boundary.h>                          //边界提取头文件 
#include <pcl/search/kdtree.h>                              //kdtree搜索对象类头文件
#include <pcl/point_representation.h>                       //点表示相关的头文件
#include <pcl/io/pcd_io.h>                                  //PCD文件打开存储类头文件
#include <pcl/filters/passthrough.h>                        //直通滤波器头文件
#include <pcl/filters/voxel_grid.h>                         //用于体素网格化的滤波类头文件
#include <pcl/filters/filter.h>                             //滤波相关头文件 
#include <pcl/filters/project_inliers.h>                    //点云投影头文件
#include <pcl/filters/extract_indices.h>                    //索引提取头文件
#include <pcl/filters/statistical_outlier_removal.h>        //统计离群点类头文件 
#include <pcl/segmentation/sac_segmentation.h>              //RanSaC分割
#include <pcl/segmentation/region_growing.h>                //区域生长
#include <pcl/segmentation/region_growing_rgb.h>            //基于颜色的区域生长
#include <pcl/segmentation/supervoxel_clustering.h>         //超体聚类
#include <pcl/segmentation/lccp_segmentation.h>             //基于凹凸性分割
#include <pcl/surface/mls.h>                                //点云平滑类头文件
#include <pcl/registration/icp.h>                           //ICP类相关头文件
#include <pcl/registration/icp_nl.h>                        //非线性ICP 相关头文件
#include <pcl/registration/transforms.h>                    //变换矩阵类头文件
#include <pcl/registration/ia_ransac.h>                     //sac-ia类头文件 
#include <pcl/registration/correspondence_estimation.h>     //直方图配准
#include <pcl/registration/correspondence_rejection_features.h>//特征的错误对应关系去除
#include <pcl/registration/correspondence_rejection_sample_consensus.h>//随机采样一致性去除 
#include <pcl/visualization/pcl_visualizer.h>               //可视化类头文件
 
typedef pcl::PointXYZ  Pxyz;
typedef pcl::PointXYZRGB Prgb;
typedef pcl::PointCloud<Pxyz> PXYZ;
typedef pcl::PointCloud<Prgb> PRGB;
 
typedef pcl::Normal Pnl;
typedef pcl::PointNormal Pnt;
typedef pcl::PointCloud<Pnt> PNT;

2.点云的创建、访问与转换以及读写

//创建点云指针对象
//PointXYZRGB是单个点包含xyz以及rgb信息
pcl::PointCloud< pcl::PointXYZRGB>::Ptr 
                        pointCloud (new  pcl::PointCloud< pcl::PointXYZRGB>);


//访问点云
pcl::PointXYZRGB  p;
p.x = xx;
p.y = xx;
p.z = xx;
p.b = xx;
p.g = xx;
p.r = xx;

pointCloud->points.push_back(p);
//points是个vector变量
//转换
pcl::PointCloud<pcl::PointXYZRGB> Cloud    //点云对象

pcl::PointCloud< pcl::PointXYZRGB>::Ptr 
                   pointCloud (new  pcl::PointCloud< pcl::PointXYZRGB>);   //点云指针对象


cloud = * pointCloud ;
pointCloud = cloud.makeShared();
//读
pcl::PointCloud< pcl::PointXYZRGB>::Ptr 
                   pointCloud(new  pcl::PointCloud< pcl::PointXYZRGB>);
if (pcl::io::loadPCDFile<pcl::PointXYZRGB>("map.pcd", *pointCloud) == -1)
    {
        cout<<("读取pcd失败");
        return 1;
    }
    cout << "读取到 "<< pointCloud_read->width * pointCloud_read->height << " 个点"<<endl;


//写
pcl::io::savePCDFileBinary("map.pcd", *pointCloud);   //二进制形式写入文件
pcl::io::savePCDFileASCII("map.pcd", *pointCloud);    //以ascii格式写入文件

3.统计滤波器

StatisticalOutlierRemoval 滤波器主要用于剔除点云中的离群点。滤波思想为:对每一个点的利用 KD-tree 进行一个统计分析,利用KD-tree得到的结果,计算该点到所有临近点的平均距离。假设每个点与临近点之间的距离是一个高斯分布,那么平均距离就是均值,并且可以根据均值和每一个距离求得标准差,那么平均距离在标准范围之外的点,可以被定义为离群点并从数据中去除。【2】

// 新建临时点云
pcl::PointCloud< pcl::PointXYZRGB>::Ptr 
                   tmp(new  pcl::PointCloud< pcl::PointXYZRGB>);   //点云指针对象
// 创建滤波器对象
pcl::StatisticalOutlierRemoval<pcl::PointXYZ> statistical_filter;

statistical_filter.setMeanK(50);//设置在进行统计时考虑查询点邻居点数
statistical_filter.setStddevMulThresh(1.0);//设置判断是否为离群点的阈值
statistical_filter.setInputCloud(current);//设置待滤波的点云(导入当前点云)

//将滤波结果保存在临时点云中,并将临时点云存入总点云
statistical_filter.filter(*tmp);
(*pointCloud) += *tmp;

4.体素滤波器

利用体素网格滤波器进行降采样。由于多个视角存在视野重叠,在重叠区域会存在大量的位置十分相近的点。会占据内存空间。体素滤波器保证了在某个一定大小的立方体(体素)内仅有一个点,相当于对三维空间进行了降采样,从而节省了存储空间。
在体素滤波器中,把分辨率设成0.03,表示每个0.03x0.03x0.03的立方体中只有一个点,这个分辨率较高,所以实际中感觉不到地图的差异,单程序输出中点数明显减少。

// 体素滤波器
pcl::VoxelGrid<PointT> voxel_filter;

//体素网格分辨率为0.03 * 0.03 * 0.03,一个立方体晶格存放一个点
double resolution = 0.03;
voxel_filter.setLeafSize(resolution, resolution, resolution);       // resolution

//临时点云
pcl::PointCloud< pcl::PointXYZRGB>::Ptr 
                   tmp(new  pcl::PointCloud< pcl::PointXYZRGB>);

// 输入总点云,降采样结果保存到tmp
voxel_filter.setInputCloud(pointCloud);
voxel_filter.filter(*tmp);
// 交换总点云与临时点云
tmp->swap(*pointCloud);

5.点云可视化

#include <pcl/visualization/cloud_viewer.h>

pcl::visualization::CloudViewer viewer ("点云");
viewer.showCloud (pointCloud);
while (!viewer.wasStopped ())
{
    
}

Cloudviewer只是一个简单的可视化工具,其功能只有有限的showCloud,以及注册键盘、鼠标的回调函数。更加复杂的工具是PCLVisualizer,参考【3】

#include <pcl/visualization/cloud_viewer.h>
#include <thread>
#include <chrono>

//初始化
pcl::visualization::PCLVisualizer::Ptr viewer (new pcl::visualization::PCLVisualizer ("3D Viewer"));

//设置背景颜色
viewer->setBackgroundColor (0, 0, 0);
//添加坐标系(即红绿蓝三色轴,放置在原点)
viewer->addCoordinateSystem (3.0);//3.0指轴的长度
//viewer->addCoordinateSystem (3.0,1,2,3);一个重载函数,3.0指轴的长度,放置在(1,2,3)位置
//初始化默认相机参数
viewer->initCameraParameters ();

//创建一个点云的句柄
pcl::visualization::PointCloudColorHandlerRGBField<pcl::PointXYZRGB> rgb(pointCloud);
//将点云加入到viewer,并定义一个唯一的字符串作为ID号,利用此字符串在其他成员方法中也能表示引用该点云
viewer->addPointCloud<pcl::PointXYZRGB> (pointCloud, rgb, "label_pc");

//用于改变显示点云的尺寸,可以利用该方法控制点云在视窗中的显示方式
viewer->setPointCloudRenderingProperties (pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 1, "label_pc");
while (!viewer->wasStopped ())
{
   viewer->spinOnce (100);   //更新显示
   boost::this_thread::sleep (boost::posix_time::microseconds (100));
}

点云地图提高了基本的可视化地图,但不能满足对于稠密地图的需求。

1)定位:取决与前端视觉里程计的处理方式。如果是基于特征点的视觉里程计,由于点云中没有存储特征点的信息,则无法用于基于特征点的定位方式。如果前端是点云的ICP,那么可以将局部点云对全局点云进行ICP以估计位姿。
2)导航与避障:点云无法直接用于避障,纯粹的点云无法表示“是否有障碍物",不过,可以在点云的基础上加工,得到更适合导航避障的地图形式。
3)可视化和交互:点云只有离散的点,没有物体表面的信息,所以不符合可视化要求。


点云是基础的,初级的,接近传感器读取的原始数据。更高级的功能,如导航避障,可以从点云出发,构建占据网格地图,以供导航算法查询某点是否能通过。SfM(三维重建)中常使用的柏松重建,就能通过基本的点云重建物体网格地图,得到物体的表面信息。还有Surfel也是一种表达物体表面的方式,以面元作为地图的基本单位,能建立可视化地图。
 

三、点云重建网格

思路:先计算点云的法线,在从法线计算网格。

//
// Created by gaoxiang on 19-4-25.
//

#include <pcl/point_cloud.h>                           //点云类定义头文件
#include <pcl/point_types.h>                          //点类型定义头文件
#include <pcl/io/pcd_io.h>                                 //PCD文件打开存储类头文件
#include <pcl/visualization/pcl_visualizer.h>   //可视化类头文件
#include <pcl/kdtree/kdtree_flann.h>             //用于对点云进行Kd树搜索
#include <pcl/surface/surfel_smoothing.h>  //对点云进行平滑处理
#include <pcl/surface/mls.h>                              //点云平滑类头文件
#include <pcl/surface/gp3.h>                         //对点云进行三角化处理
#include <pcl/surface/impl/mls.hpp>

// typedefs
typedef pcl::PointXYZRGB PointT;
typedef pcl::PointCloud<PointT> PointCloud;
typedef pcl::PointCloud<PointT>::Ptr PointCloudPtr;

typedef pcl::PointXYZRGBNormal SurfelT;
typedef pcl::PointCloud<SurfelT> SurfelCloud;
typedef pcl::PointCloud<SurfelT>::Ptr SurfelCloudPtr;


//把基本点云转化成光滑的带有法线信息的点云
SurfelCloudPtr reconstructSurface(
        const PointCloudPtr &input, float radius, int polynomial_order) {

     // 定义对象 (第二种定义类型是为了存储法线       
    pcl::MovingLeastSquares<PointT, SurfelT> mls;
    //由于MLS算法是基于KDTree实现的,所以需要创建KDTree对象
    pcl::search::KdTree<PointT>::Ptr tree(new pcl::search::KdTree<PointT>);
    mls.setSearchMethod(tree);
    //搜索半径
    mls.setSearchRadius(radius);
    //计算法线
    mls.setComputeNormals(true);
     // 设置近邻高斯权重系数,一般为搜索半径平方
    mls.setSqrGaussParam(radius * radius);
   
    //设置是否使用多项式拟合提高精度
    mls.setPolynomialFit(polynomial_order > 1);
     // 设置阶数
    mls.setPolynomialOrder(polynomial_order);
    // 输入点云
    mls.setInputCloud(input);

    SurfelCloudPtr output(new SurfelCloud);
     // 处理结果保存到输出点云并放返回
    mls.process(*output);
    return (output);
}

// 为点云表面添加三角网格面元
pcl::PolygonMeshPtr triangulateMesh(const SurfelCloudPtr &surfels) {
    // 创建搜索树
    pcl::search::KdTree<SurfelT>::Ptr tree(new pcl::search::KdTree<SurfelT>);
    tree->setInputCloud(surfels);

    // Initialize objects
    pcl::GreedyProjectionTriangulation<SurfelT> gp3;
    pcl::PolygonMeshPtr triangles(new pcl::PolygonMesh);

    // Set the maximum distance between connected points (maximum edge length)
    gp3.setSearchRadius(0.05);

    // Set typical values for the parameters
    gp3.setMu(2.5);
    gp3.setMaximumNearestNeighbors(100);
    gp3.setMaximumSurfaceAngle(M_PI / 4); // 45 degrees
    gp3.setMinimumAngle(M_PI / 18); // 10 degrees
    gp3.setMaximumAngle(2 * M_PI / 3); // 120 degrees
    gp3.setNormalConsistency(true);

    // Get result
    gp3.setInputCloud(surfels);
    gp3.setSearchMethod(tree);
    gp3.reconstruct(*triangles);

    return triangles;
}

int main(int argc, char **argv) {

    // Load the points
    PointCloudPtr cloud(new PointCloud);
    if (argc == 0 || pcl::io::loadPCDFile(argv[1], *cloud)) {
        cout << "failed to load point cloud!";
        return 1;
    }
    cout << "point cloud loaded, points: " << cloud->points.size() << endl;

    // Compute surface elements
    cout << "computing normals ... " << endl;
    double mls_radius = 0.05, polynomial_order = 2;
    auto surfels = reconstructSurface(cloud, mls_radius, polynomial_order);

    // Compute a greedy surface triangulation
    cout << "computing mesh ... " << endl;
    pcl::PolygonMeshPtr mesh = triangulateMesh(surfels);

    cout << "display mesh ... " << endl;
    pcl::visualization::PCLVisualizer vis;
    vis.addPolylineFromPolygonMesh(*mesh, "mesh frame");
    vis.addPolygonMesh(*mesh, "mesh");
    vis.resetCamera();
    vis.spin();
}

四、八叉树地图

点云地图与八叉树地图对比:点云地图规模大,而且无法处理运动物体;八叉树地图占用空间很小,而且可以用于导航。

八叉树octomap参考【4】

 五、如何在八叉树地图中进行导航或路径规划吗?

在起点与终点之间,选择概率值最小的路径。若概率值最小的路径仍超过某一阈值,则说明无法到达终点。若有多条概率值小的路径,可以根据高斯分布选择路最“宽”的路径。

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

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

相关文章

Python 实验二 Python语言基础

1.运用输入输出函数编写程序&#xff0c;将华氏温度转换成摄氏温度。换算公式&#xff1a;C(F-32)*5/9,其中 C为摄氏温度&#xff0c;F为华氏温度。 Ffloat(input("请输入你要转换的华氏温度&#xff1a;")) C(F-32)*5/9 print("转换为摄氏温度为&#xff1a;&…

opencv:介绍 SIFT(尺度不变特征变换)及其使用(一)

在本章中 我们将了解 SIFT 算法的概念 我们将学习如何找到 SIFT 关键点和描述符。 理论 在过去的几章中,我们了解了一些角点检测器,如 Harris 等。它们具有旋转不变性,这意味着即使图像旋转,我们也可以找到相同的角点。这是显而易见的,因为旋转后的图像中的角点仍然是角点…

Cairo 1 实战入门:编写测试部署ERC-20代币智能合约

概述 读者可以前往 我的博客 获得更好的阅读体验。 Cairo 是 ZK Rollup 的领域专用语言&#xff0c;目前仅用于 StarkNet 项目。随着 Rollup 叙事的发展&#xff0c;我们认为 cairo 在未来一定会成为智能合约开发的核心语言。 本文类似我之前编写的 Foundry教程&#xff1a;…

BART模型简单介绍

目录 一、概要 二、深入扩展 2.1 预训练任务 2.2 模型精调 一、概要 BART&#xff08;Bidirectional and Auto-Regressive Transformers&#xff09;模型使用标准的基于Transformer的序列到序列结构&#xff0c;主要区别在于用GeLU&#xff08;Gaussian Error Linerar Units…

西安石油大学C++上机实验 上机二:友元和运算符重载程序设计(2学时)

上机二&#xff1a;友元和运算符重载程序设计&#xff08;2学时&#xff09; 实验目的&#xff1a; 了解友元函数的概念。 掌握友元函数的定义和使用方法。 了解运算符重载的概念和使用方法。 掌握几种常用的运算符重载的方法。 实验内容 编写一个时间类&#xff0c;实现…

【面试】记一次中小公司某一次面试题

文章目录1. MySQL中explain执行计划你比较关注哪些字段&#xff1f;2.char、varchar 和 text的区别&#xff1f;3. int(3)和int(11)查询的区别&#xff1f;4. 字段里NULL和空值的区别&#xff1f;5. spring中怎么解决循环依赖问题&#xff1f;5.1 重新设计5.2 使用注解 Lazy5.3…

ESP32设备驱动-HDC2080温湿度传感器驱动

HDC2080温湿度传感器驱动 文章目录 HDC2080温湿度传感器驱动2、硬件准备3、软件准备4、驱动实现HDC2080 器件是一款集成式湿度和温度传感器,可在小型 DFN 封装中以极低的功耗提供高精度测量。基于电容的传感器包括新的集成数字功能和用于消散冷凝和湿气的加热元件。 HDC2080 数…

虹科新闻 | 冲击记录仪ASPION G-Log 2荣获行业领袖奖-Best of Industry Award

虹科ASPION G-Log 2 荣获 行业领袖奖 “2022 Best of Industry Award” 创新的运输冲击数据记录仪虹科ASPION G-Log 2荣获2022年行业领袖奖。经过5个月的投票和近2万张选票&#xff0c;G-Log 2在传感器 由冲击或振动&#xff0c;甚至是温度或相对湿度等气候因素引起的运输损坏…

ERTEC200P-2 PROFINET设备完全开发手册(5-3)

5.3 标识和维护数据&#xff08;I&M&#xff09; 标识和维护数据是一类特殊的数据记录&#xff0c;其中 “I&M0”&#xff08;“16#AFF0”&#xff09;用于有关模块或设备的常规信息。包含的信息例如&#xff1a;订货号/Order-ID, 硬件软件版本/hard- and software v…

强化学习之蒙特卡罗(MC)、动态规划(DP)、时间差分(TD)

强化学习笔记1.马尔可夫决策过程(MDP)1.马尔可夫性质2.马尔可夫过程3.马尔可夫奖励过程(MRP)4.马尔可夫决策过程(MDP)2.蒙特卡罗&#xff08;MC&#xff09;、动态规划&#xff08;DP&#xff09;、时间差分&#xff08;TD&#xff09;1.蒙特卡罗(MC)2.动态规划(DP)3.时间差分(…

【C】Function

系列连载 【python / C / C】 参考 《C语言程序设计&#xff08;第四版&#xff09;谭浩强》【C语言】C语言视频教程《郝斌 C 语言自学教程》 文章目录为什么需要函数什么叫函数如何定义函数函数的分类注意的问题常用的系统函数为什么需要函数 避免重复性操作 有利于程序模…

23种设计模式之建造者模式(黑马程序员)

建造者模式一、概述二、结构三、实现四、总结五、模式扩展在最后一、概述 将一个复杂对象的构建与表示分离&#xff0c;使得同样的构建过程可以创建不同的表示。 分离了部件的构造&#xff08;由Build来负责&#xff09;和装配&#xff08;Director负责&#xff09;。从而可以…

Spring 的创建和使用

目录 一. 创建 Spring项目 二. 存储 Bean 对象到Spring中 1. 添加Spring配置文件 2. 创建一个 Bean 对象 3. 将 Bean 存储到 Spring 容器中 三. 从 Spring 中获取并使用 Bean 对象 1. 创建 Spring 上下文 1.1 使用 ApplicationContext 作为Spring上下文 1.2 使用 Bea…

ONLYOFFICE 邀请您一起创建插件

文档编辑器中的插件具有高度的便捷性和实用性&#xff0c;这对工作效率的提高至关重要&#xff0c;ONLYOFFICE 功能的强大也离不开丰富的插件资源。 现在&#xff0c;您可以自己制作插件并上架到 ONLYOFFICE&#xff0c;一起帮助广大用户更高效地办公&#xff0c;并获得相应的…

KDMP真实案例之主机 I/O 请求没有正常结束导致系统重启

目录 查看kdump文件 导致系统崩溃的寄存器指向的内存地址 导致重启的汇编指令 导致重启的源码 block/blk-core.c文件简介 导致重启的blk_finish_request函数源码 函数源码解读 系统重启原因 处理意见 查看kdump文件 各字段意思说明 KERNEL&#xff1a;显示内核文件的路…

你真的会用iPad吗,如何使iPad秒变生产力工具?在iPad上用vscode写代码搞开发

目录 前言 视频教程 1. 本地环境配置 2. 内网穿透 2.1 安装cpolar内网穿透(支持一键自动安装脚本) 2.2 创建HTTP隧道 3. 测试远程访问 4. 配置固定二级子域名 4.1 保留二级子域名 4.2 配置二级子域名 5. 测试使用固定二级子域名远程访问 6. iPad通过软件远程vscode…

通俗举例讲解动态链接】静态链接

参考动态链接 - 知乎 加上我自己的理解&#xff0c;比较好懂&#xff0c;但可能在细节方面有偏差,但总体是一致的 静态链接的背景 静态链接使得不同的程序开发者和部门能够相对独立的开发和测试自己的程序模块&#xff0c;从某种意义上来讲大大促进了程序开发的效率&#xf…

Mybatis【环境搭建】

目录 一、Maven 环境配置 1、配置 pom.xml 1.1、依赖的 jar包 1.2、防止资源导出失败 2、在resources下编写 Mybatis核心配置文件 二、搭建结构 1、编写mybatis工具类&#xff08;utils&#xff09; 2、编写实体类&#xff08;pojo&#xff09; 3、Mybatis 的实现&…

一篇搞定Lambda和Stream流

一、Lambda表达式 jdk8中的语法糖&#xff0c;优化某些匿名内部类的写法&#xff0c;函数式编程的重要体现&#xff0c;不再关注对象是什么&#xff0c;更关注数据进行了什么操作 1、练习 练习1 练习2 练习3 练习4 练习5 2、省略规则 参数类型可以省略方法体只有一句代码时…

【Leetcode】消失的数字 [C语言实现]

&#x1f47b;内容专栏&#xff1a;《Leetcode刷题专栏》 &#x1f428;本文概括&#xff1a; 面试17.04.消失的数字 &#x1f43c;本文作者&#xff1a;花 碟 &#x1f438;发布时间&#xff1a;2023.4.10 目录 思想1&#xff1a;先排序再查找 思想2&#xff1a;异或运算 代…