本文首发于微信公众号「3D视觉工坊」:3D点云配准(二多幅点云配准)

在上一篇文章 点云配准(一 两两配准)中我们介绍了两两点云之间的配准原理。本篇文章,我们主要介绍一下PCL中对于多幅点云连续配准的实现过程,重点请关注代码行的注释。

对于多幅点云的配准,它的主要思想是对所有点云进行变换,使得都与第一个点云在统一坐标系中。在每个连贯的、有重叠的点云之间找到最佳的变换,并累积这些变换到全部的点云。能够进行ICP算法的点云需要进行粗略的预匹配,并且一个点云与另一个点云需要有重叠部分

此处我们以郭浩主编的《点云库PCL从入门到精通》提供的示例demo来介绍一下多幅点云进行配准的过程。

/* ---[ */
int main (int argc, char** argv)
{// 加载数据
std::vector<PCD, Eigen::aligned_allocator<PCD> > data; //存储管理所有打开的点云
loadData (argc, argv, data); //加载所有点云文件到data//检查用户输入
if (data.empty ()){PCL_ERROR ("Syntax is: %s <source.pcd> <target.pcd> [*]", argv[0]);PCL_ERROR ("[*] - multiple files can be added. The registration results of (i, i+1) will be registered against (i+2), etc");PCL_INFO ("Example: %s `rospack find pcl`/test/bun0.pcd `rospack find pcl`/test/bun4.pcd", argv[0]);return (-1);}PCL_INFO ("Loaded %d datasets.", (int)data.size ());//创建一个PCL可视化对象
p = new pcl::visualization::PCLVisualizer (argc, argv, "Pairwise Incremental Registration example"); //创建一个PCL可视化对象
p->createViewPort (0.0, 0, 0.5, 1.0, vp_1); //用左半窗口创建视口vp_1
p->createViewPort (0.5, 0, 1.0, 1.0, vp_2); //用右半窗口创建视口vp_2
PointCloud::Ptr result (new PointCloud), source, target;Eigen::Matrix4f GlobalTransform = Eigen::Matrix4f::Identity (), pairTransform;for (size_t i = 1; i < data.size (); ++i)  //循环处理所有点云
{source = data[i-1].cloud; //连续配准
target = data[i].cloud;   //相邻两组点云//添加可视化数据
showCloudsLeft(source, target); //可视化为配准的源和目标点云
PointCloud::Ptr temp (new PointCloud);PCL_INFO ("Aligning %s (%d) with %s (%d).n", data[i-1].f_name.c_str (), source->points.size (), data[i].f_name.c_str (), target->points.size ());//调用子函数完成一组点云的配准,temp返回配准后两组点云在第一组点云坐标下的点云,pairTransform返回从目标点云target到源点云source的变换矩阵。//现在我们开始进行实际的匹配,由子函数pairAlign具体实现,//其中参数有输入一组需要配准的点云,以及是否进行下采样的设置项,其他参数输出配准后的点云及变换矩阵。
pairAlign (source, target, temp, pairTransform, true);//把当前的两两配对转换到全局变换//把当前的两两配对后的点云temp转换到全局坐标系下(第一个输入点云的坐标系)返回result
pcl::transformPointCloud (*temp, *result, GlobalTransform);//update the global transform更新全局变换//用当前的两组点云之间的变换更新全局变换
GlobalTransform = pairTransform * GlobalTransform;//保存转换到第一个点云坐标下的当前配准后的两组点云result到文件i.pcd
std::stringstream ss;ss << i << ".pcd";pcl::io::savePCDFile (ss.str (), *result, true);}
}

对于上述过程中的核心函数pairAlign(),我们重点介绍如下:

/**匹配一对点云数据集并且返还结果*参数 cloud_src 是源点云*参数 cloud_src 是目标点云*参数output输出的配准结果的源点云*参数final_transform是在来源和目标之间的转换*/
void pairAlign (const PointCloud::Ptr cloud_src, const PointCloud::Ptr cloud_tgt, PointCloud::Ptr output, Eigen::Matrix4f &final_transform, bool downsample = false)
{//下采样//为了一致性和高速的下采样//注意:为了大数据集需要允许这项
PointCloud::Ptr src (new PointCloud); //存储滤波后的源点云
PointCloud::Ptr tgt (new PointCloud); //存储滤波后的目标点云
pcl::VoxelGrid<PointT> grid; //滤波处理对象
if (downsample){grid.setLeafSize (0.05, 0.05, 0.05); //设置滤波处理时采用的体素大小
grid.setInputCloud (cloud_src);grid.filter (*src);grid.setInputCloud (cloud_tgt);grid.filter (*tgt);}else
{src = cloud_src;tgt = cloud_tgt;}//计算曲面法线和曲率
PointCloudWithNormals::Ptr points_with_normals_src (new PointCloudWithNormals);PointCloudWithNormals::Ptr points_with_normals_tgt (new PointCloudWithNormals);pcl::NormalEstimation<PointT, PointNormalT> norm_est; //点云法线估计对象
pcl::search::KdTree<pcl::PointXYZ>::Ptr tree (new pcl::search::KdTree<pcl::PointXYZ> ());norm_est.setSearchMethod (tree); //设置估计对象采用的搜索对象
norm_est.setKSearch (30); //设置估计时进行搜索用的k数
norm_est.setInputCloud (src);norm_est.compute (*points_with_normals_src); //下面分别估计源和目标点云法线
pcl::copyPointCloud (*src, *points_with_normals_src);norm_est.setInputCloud (tgt);norm_est.compute (*points_with_normals_tgt);pcl::copyPointCloud (*tgt, *points_with_normals_tgt);//一切准备好之后,可以开始配准了,创建ICP对象,设置它的参数//以需要匹配的两个点云作为输入,使用时,参数的设置需要根据自己的数据集进行调整。////举例说明我们自定义点的表示(以上定义)
MyPointRepresentation point_representation;//调整'curvature'尺寸权重以便使它和x, y, z平衡
float alpha[4] = {1.0, 1.0, 1.0, 1.0};point_representation.setRescaleValues (alpha);//// 配准
pcl::IterativeClosestPointNonLinear<PointNormalT, PointNormalT> reg;  //配准对象
reg.setTransformationEpsilon (1e-6); //设置收敛判断条件,越小精度越大,收敛也越慢//将两个对应关系之间的(src<->tgt)最大距离设置为10厘米//注意:根据你的数据集大小来调整
reg.setMaxCorrespondenceDistance (0.1);//设置点表示
reg.setPointRepresentation (boost::make_shared<const MyPointRepresentation> (point_representation));reg.setInputCloud (points_with_normals_src); //设置源点云
reg.setInputTarget (points_with_normals_tgt); //设置目标点云////在一个循环中运行相同的最优化并且使结果可视化
Eigen::Matrix4f Ti = Eigen::Matrix4f::Identity (), prev, targetToSource;PointCloudWithNormals::Ptr reg_result = points_with_normals_src;//由于这是一个demo,因而希望显示匹配过程的迭代过程,为达到该目的,ICP在内部进行计算时,限制其最大的迭代次数为2,即每迭代两次就认为收敛,停止内部迭代
reg.setMaximumIterations (2); //设置最大迭代次数//手动迭代,此处设置为30次,每手动迭代一次,在配准结果视口对迭代的最新的结果进行刷新显示
for (int i = 0; i < 30; ++i){PCL_INFO ("Iteration Nr. %d.n", i);//为了可视化的目的保存点云
points_with_normals_src = reg_result;reg.setInputCloud (points_with_normals_src);reg.align (*reg_result);//在每一个迭代之间累积转换
Ti = reg.getFinalTransformation () * Ti;//如果这次转换和之前转换之间的差异小于阈值//则通过减小最大对应距离来改善程序//也就是说,如果迭代N次找到的变换和迭代N-1次中找到的变换之间的差异小于传给ICP的变换收敛阈值,我们选择源和目标之间更靠近的对应点距离阈值来改善配准过程if (fabs ((reg.getLastIncrementalTransformation () - prev).sum ()) < reg.getTransformationEpsilon ())reg.setMaxCorrespondenceDistance (reg.getMaxCorrespondenceDistance () - 0.001);prev = reg.getLastIncrementalTransformation ();//可视化当前状态
showCloudsRight(points_with_normals_tgt, points_with_normals_src);}////一旦找到最优的变换,ICP返回的变换是从源点云到目标点云的变换矩阵,我们求逆变换得到从目标点云到源点云的变换矩阵,并应用到目标点云,变换后的目标点云然后添加到源点云中。// 得到目标点云到源点云的变换
targetToSource = Ti.inverse();////把目标点云转换回源框架
pcl::transformPointCloud (*cloud_tgt, *output, targetToSource);p->removePointCloud ("source");p->removePointCloud ("target");PointCloudColorHandlerCustom<PointT> cloud_tgt_h (output, 0, 255, 0);PointCloudColorHandlerCustom<PointT> cloud_src_h (cloud_src, 255, 0, 0);p->addPointCloud (output, cloud_tgt_h, "target", vp_2);p->addPointCloud (cloud_src, cloud_src_h, "source", vp_2);PCL_INFO ("Press q to continue the registration.n");p->spin ();p->removePointCloud ("source");p->removePointCloud ("target");//添加源点云到转换目标
*output += *cloud_src;final_transform = targetToSource;}
效果图如下,此为动态图,请耐心等待几秒钟,注意迭代过程。

思考:

对于小型或者中型数量的点云数据(点个数<100,000),我们选择ICP来进行迭代配准,可是利用对应点的特征计算和匹配较为耗时,如果对于两个大型点云(都超过100000)之间的刚体变换的确定,有什么好办法可以解决呢?

主要参考:郭浩主编的<点云库PCL从入门到精通>

上述内容,如有侵犯版权,请联系作者,会自行删文。

星球成员,可免费提问,并邀进讨论群

保存点云数据_3D点云配准(二多幅点云配准)相关推荐

  1. 3D点云配准(二多幅点云配准)

    原文首发于公众号「3D视觉工坊」:3D点云配准(二多幅点云配准) 在上一篇文章 点云配准(一 两两配准)中我们介绍了两两点云之间的配准原理.本篇文章,我们主要介绍一下PCL中对于多幅点云连续配准的实现 ...

  2. 背后的力量 | 推动政府数字化建设 华云数据为潜江市生态环境局搭建新一代企业级云平台

    潜江市生态环境局于2019年2月根据全市机构改革工作总体方案组建,同年3月1日正式挂牌.在市委市政府的领导下,贯彻落实党中央.省委关于生态环境保护工作的方针政策和决策部署.近年来,潜江市生态环境局通过 ...

  3. 点云数据转深度图像(二)

    文章目录 一.简介 二.图像生成代码 三.实现效果 参考资料 一.简介 之前使用过PCL中的生成深度图像功能,就想着使用MATLAB也实现一下类似的功能,整个过程是一个旋转水平和竖直角度的采样过程,如 ...

  4. 分割点云数据_3D点云深度学习综述:三维形状分类、目标检测与跟踪、点云分割等...

    3D点云学习( Point Clouds)作为近年来的研究热点之一,受到了广泛关注,每年在各大会议上都有大量的相关文章发表.当前,点云上的深度学习变得越来越流行,人们提出了许多方法来解决这一领域的不同 ...

  5. io获取 pcl_点云数据可视化之PCL滤波学习

    PCL滤波概述 在获取点云数据时 ,由于设备精度,操作者经验环境因素带来的影响,以及电磁波的衍射特性,被测物体表面性质变化和数据拼接配准操作过程的影响,点云数据中将不可避免的出现一些噪声.在点云处理流 ...

  6. 激光点云系列之一:详解激光雷达点云数据的处理过程

    交流群 | 进"传感器群/滑板底盘群"请加微信号:xsh041388 交流群 | 进"汽车基础软件群"请加微信号:Faye_chloe 备注信息:群名称 + 真 ...

  7. 从零实现一个3D目标检测算法(2):点云数据预处理

    在上一篇文章<从零实现一个3D目标检测算法(1):3D目标检测概述>对3D目标检测研究现状和PointPillars模型进行了介绍,在本文中我们开始写代码一步步实现PointPillars ...

  8. 云数据迁移(Cloud Data Migration,CDM)

    云数据迁移(Cloud Data Migration,CDM) 云数据迁移(Cloud Data Migration,CDM)提供同构/异构数据源之间批量数据迁移服务,帮助客户实现数据自由流动.支持客 ...

  9. 点云数据向图像数据转换(附源码)

    点云数据--The Point Cloud Data 点云数据应表示为具有N行和至少3列的numpy数组.每行对应于单个点,其在空间(x,y,z)中的位置使用至少3个值表示. 如果点云数据来自LIDA ...

最新文章

  1. 50篇经典珍藏 | Docker、Mesos、微服务、云原生技术干货
  2. linux常用命令(grep/cut/sed/which)
  3. SharePoint 2007图文开发教程(3)---实现简单的WebPart
  4. 【django轻量级框架】用Mysql的各种项目响应速度慢?一招解决!
  5. rsa php openssl,openssl rsa 使用简介
  6. 流批一体机器学习算法平台
  7. 陈彬 2019-1-17
  8. javac 与java_javac和参数用法java命令的-d -cp
  9. 音乐流媒体服务器Navidrome
  10. c语言反三角函数正切,arc(完整的反三角函数值表)
  11. OSPF区域类型及详解
  12. python读不出图片文件
  13. /Users/apple/.zshrc:2: command not found: i 终端报错解决方法
  14. Unity_回合制战斗系统_01
  15. java/php/net/python旅行社信息管理系统设计
  16. 召回率(Recall)
  17. Java实现简单的日历小程序之Java图形界面开发小日历
  18. 牛客每日练习----骑行川藏,Lucky Coins,不凡的夫夫
  19. janus videoroom之媒体录制
  20. 手持式尘埃粒子测试仪价格,尘埃粒子一般一个点测量几次

热门文章

  1. python基础笔记_python基础学习笔记
  2. sarscape 将dem文件转化成stl_SARscape与SARProz软件中的重要缩写
  3. centos7调节虚拟机字体_初次安装虚拟机中Ubuntu16.04系统设置的一些小问题(小白教程)...
  4. Python找出某元素的索引下标
  5. Android数据存储之sharedpreferences与Content Provider
  6. 大数据生态圈常用组件(二):概括介绍、功能特性、适用场景
  7. Learn X in Y minutes
  8. 电子音乐包括电声乐器和计算机音乐,浅谈电子音乐制作的发展方向
  9. 学计算机专业的自荐信,浙江大学(计算机类)自主招生自荐信范文
  10. python 动态调整控件大小_python GUI库图形界面开发之PyQt5动态(可拖动控件大小)布局控件QSplitter详细使用方法与实例...