Loading [MathJax]/jax/output/CommonHTML/config.js
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >3D点云配准(二多幅点云配准)

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

作者头像
3D视觉工坊
发布于 2020-12-11 08:16:11
发布于 2020-12-11 08:16:11
2.1K00
举报
运行总次数:0

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

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

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

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/* ---[ */
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(),我们重点介绍如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/**匹配一对点云数据集并且返还结果
  *参数 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;
 }
效果图如下,此为动态图,请耐心等待几秒钟,注意迭代过程。
代码语言:javascript
代码运行次数:0
运行
复制

思考:

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

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

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

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-07-22,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 3D视觉工坊 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
【3D篇】点云拼接
computeSurfaceNormals() has finished in 0 s
threeQing
2021/09/29
1.9K0
【3D篇】点云拼接
PCL点云配准(1)
在逆向工程,计算机视觉,文物数字化等领域中,由于点云的不完整,旋转错位,平移错位等,使得要得到的完整的点云就需要对局部点云进行配准,为了得到被测物体的完整数据模型,需要确定一个合适的坐标系,将从各个视角得到的点集合并到统一的坐标系下形成一个完整的点云,然后就可以方便进行可视化的操作,这就是点云数据的配准。点云的配准有手动配准依赖仪器的配准,和自动配准,点云的自动配准技术是通过一定的算法或者统计学规律利用计算机计算两块点云之间错位,从而达到两块点云自动配准的效果,其实质就是把不同的坐标系中测得到的数据点云进行坐标系的变换,以得到整体的数据模型,问题的关键是如何让得到坐标变换的参数R(旋转矩阵)和T(平移向量),使得两视角下测得的三维数据经坐标变换后的距离最小,,目前配准算法按照过程可以分为整体配准和局部配准,。PCL中有单独的配准模块,实现了配准相关的基础数据结构,和经典的配准算法如ICP。
点云PCL博主
2019/07/31
2.7K0
PCL点云配准(1)
一分钟详解PCL中点云配准技术
本文是对前两篇文章:点云配准(一 两两配准)以及3D点云(二 多福点云配准)的补充,希望可以在一定程度上帮助大家对点云配准理解地更为深刻。
计算机视觉
2020/12/11
2.5K0
一分钟详解PCL中点云配准技术
【PCL】NDT点云配准(Registration)
由于LiDAR一次扫描只能得到局部点云信息,为了能获得全局点云信息(如一个房间、一个三维物体),就需要进行多次连续扫描,并进行点云配准。由于每次扫描得到的点云都有独立的坐标系,因此点云配准时要进行坐标变换(旋转、平移),将多帧不同坐标系下的点云整合到一个坐标系下。
DevFrank
2024/07/24
7500
点云NDT配准方法介绍
本文介绍的是另一种比较好的配准算法,NDT配准。所谓NDT就是正态分布变换,作用与ICP一样用来估计两个点云之间的刚体变换。用标准最优化技术来确定两个点云间的最优的匹配,因为其在配准过程中不利用对应点的特征计算和匹配,所以时间比其他方法快。
点云PCL博主
2019/08/05
5K0
点云NDT配准方法介绍
PCL点云配准(2)
(1)正态分布变换进行配准(normal Distributions Transform)
点云PCL博主
2019/07/31
1.9K0
PCL点云配准(2)
【PCL】VoxelGrid滤波器下采样
VoxelGrid滤波器是用体素化网格方法实现下采样的一种常用滤波方法,这里重点学习。
DevFrank
2024/07/24
4380
【PCL】VoxelGrid滤波器下采样
Open3d学习计划—高级篇 2(彩色点云配准)
Open3D是一个开源库,支持快速开发和处理3D数据。Open3D在c++和Python中公开了一组精心选择的数据结构和算法。后端是高度优化的,并且是为并行化而设置的。
点云PCL博主
2020/10/26
3.4K0
Open3d学习计划—高级篇 2(彩色点云配准)
PCL点云分割(1)
点云分割是根据空间,几何和纹理等特征对点云进行划分,使得同一划分内的点云拥有相似的特征,点云的有效分割往往是许多应用的前提,例如逆向工作,CAD领域对零件的不同扫描表面进行分割,然后才能更好的进行空洞修复曲面重建,特征描述和提取,进而进行基于3D内容的检索,组合重用等。
点云PCL博主
2019/07/31
4.3K0
PCL点云分割(1)
PCL点云特征描述与提取(1)
3D点云特征描述与提取是点云信息处理中最基础也是最关键的一部分,点云的识别。分割,重采样,配准曲面重建等处理大部分算法,都严重依赖特征描述与提取的结果。从尺度上来分,一般分为局部特征的描述和全局特征的描述,例如局部的法线等几何形状特征的描述,全局的拓朴特征的描述,都属于3D点云特征描述与提取的范畴,
点云PCL博主
2019/07/31
3.2K0
PCL点云特征描述与提取(1)
PCL点云曲面重建(1)
在测量较小的数据时会产生一些误差,这些误差所造成的不规则数据如果直接拿来曲面重建的话,会使得重建的曲面不光滑或者有漏洞,可以采用对数据重采样来解决这样问题,通过对周围的数据点进行高阶多项式插值来重建表面缺少的部分,
点云PCL博主
2019/07/31
2.2K0
PCL点云曲面重建(1)
点云配准资源汇总
点云配准的目标是根据原始点云和目标点云,通过配准求出变换矩阵,即旋转矩阵R和平移矩阵T,并计算误差,来比较匹配结果。主要有以下几种比较
点云PCL博主
2020/05/21
2.1K0
点云配准资源汇总
一起做激光SLAM:ICP匹配用于闭环检测
https://pan.baidu.com/s/1o-noUxgVCdFkaIH21zPq0A
3D视觉工坊
2022/03/11
1K0
一起做激光SLAM:ICP匹配用于闭环检测
[python][pcl]python-pcl案例之圆柱模型分割(Cylinder model segmentation)
PointCloud has: 307200 data points. PointCloud has: 139897 data points. PointCloud representing the planar component: 116300 data points.
云未归来
2025/07/20
1280
学习PCL库:PCL中的配准模块介绍
将多个数据集合并成一个全局一致的模型通常使用一种称为"配准"的技术来完成。其关键思想是识别数据集之间的对应点,并找到一个最小化对应点之间距离的变换关系。由于对应点的查找受到数据集的相对位置和方向的影响,因此需要重复这个过程。一旦最小化误差降到给定的阈值以下,就可以说完成了配准。pcl_registration库实现了众多点云配准算法,适用于有序和无序点云的数据集。
点云PCL博主
2023/11/07
3.3K0
学习PCL库:PCL中的配准模块介绍
Open3d 学习计划—9(ICP配准)
Open3D是一个开源库,支持快速开发和处理3D数据。Open3D在c++和Python中公开了一组精心选择的数据结构和算法。后端是高度优化的,并且是为并行化而设置的。
点云PCL博主
2020/08/17
4.2K0
Open3d 学习计划—9(ICP配准)
pcl_filters模块api代码解析
上周点云公众号开启了学习模式,由博主分配任务,半个月甚至一个月参与学习小伙伴的反馈给群主,并在微信交流群中进行学术交流,加强大家的阅读文献能力,并提高公众号的分享效果。在此期待更多的同学能参与进来!(目前已经有成员反馈,还有需要小伙伴没有发过来哦,下周开始会将分享整理出来,定期分享,并将文档上传至github组群,已经有部分分享上传至github组群中,供大家下载查看,并且有问题可以在github的issues中提问,大家可以相互提问并解答)
点云PCL博主
2019/07/30
2.1K0
pcl_filters模块api代码解析
Open3d学习计划—高级篇 4(多视角点云配准)
Open3D是一个开源库,支持快速开发和处理3D数据。Open3D在c++和Python中公开了一组精心选择的数据结构和算法。后端是高度优化的,并且是为并行化而设置的。
点云PCL博主
2020/11/19
5.4K0
Open3d学习计划—高级篇 4(多视角点云配准)
PCL
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
sofu456
2019/08/31
2.4K0
PCL中的区域生长分割(region growing segmentation)
首先注意一点,这里是region growing segmentation,不是color-based region growing segmentation.
点云PCL博主
2019/07/30
7.2K0
PCL中的区域生长分割(region growing segmentation)
相关推荐
【3D篇】点云拼接
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档