目录

1 基本概念

1.1 点云配准是什么?

1.1.1 ICP算法的一般计算方法

1.2 几页图示

1.3 PCL中ICP配准例子

1.4 两个重要信息

2 各种ICP变种

3 广义配准Registration

3.1 匹配过程中模块

3.1.1 Keypoints(关键点)

3.1.2 Feature descriptors(特征描述子)

3.1.3 Correspondences estimation(对应关系估计)

3.1.4 Correspondences rejection(剔除错误估计)

3.1.5 Transformation estimation(最后一步,计算变换)

3.2 匹配流程总结


1 基本概念

1.1 点云配准是什么?

维基百科上这样介绍:

Point cloud registration, is the process of finding a spatial transformation that aligns two point clouds. The purpose is to merge point clouds of multiple views into a globally consistent model.

在我的理解,有两个点集,source和target,target不变,source经过旋转(Rotation)和平移(Translation)甚至加上尺度(Scale)变换,使得变换后的source点集尽量和target点集重合,这个变换的过程就叫点集配准。   In the algorithm, target point cloud, is kept fixed, while the other one, the source, is transformed  to best match the reference. The algorithm iteratively revises the transformation (combination of  translation and rotation)  needed to minimize the distance from the source to the reference point cloud.

而ICP是最广泛应用的配准方法,也就是KinectFusion论文中所提到的ICP( Iterative Closest Point ), 最近邻迭代算法。icp利用迭代一步步地算出正确对应关系。

Iterative Closest Point (ICP) is an algorithm employed to minimize the difference between two clouds of points.

1.1.1 ICP算法的一般计算方法

  • 参考:cartographer详解:激光SLAM中的数据预处理 计算机视觉life / 北航博士 曹秀洁 2021.01.02

1.2 几页图示

下面几页PPT是ICP很好的解释。

1.3 PCL中ICP配准例子

先上例子, 下面是用PCL中的icp配准两个点云:

void PairwiseICP(const pcl::PointCloud<PointT>::Ptr &cloud_target, const pcl::PointCloud<PointT>::Ptr &cloud_source, pcl::PointCloud<PointT>::Ptr &output )
{PointCloud<PointT>::Ptr src(new PointCloud<PointT>);PointCloud<PointT>::Ptr tgt(new PointCloud<PointT>);tgt = cloud_target;src = cloud_source;pcl::IterativeClosestPoint<PointT, PointT> icp;icp.setMaxCorrespondenceDistance(0.1);icp.setTransformationEpsilon(1e-10);icp.setEuclideanFitnessEpsilon(0.01);icp.setMaximumIterations (100);icp.setInputSource (src);icp.setInputTarget (tgt);icp.align (*output);
//  std::cout << "has converged:" << icp.hasConverged() << " score: " <<icp.getFitnessScore() << std::endl;output->resize(tgt->size()+output->size());for (int i=0;i<tgt->size();i++){output->push_back(tgt->points[i]);}cout<<"After registration using ICP:"<<output->size()<<endl;
}
源码pcl.h里给出了Usage example, 源码从github上下载之后可以在这个目录找到 .\pcl-master\registration\include\pcl\registration。

1.4 两个重要信息

这里可以看到两个重要信息:

  • 一是PCL的icp里的transformation estimation是基于SVD的, SVD是奇异值分解,Singular Value Decomposition,后面我们会提到;
  • 二是使用之前要至少set三个参数:
    • setMaximumIterations, 最大迭代次数,icp是一个迭代的方法,最多迭代这些次;
    • setTransformationEpsilon, 前一个变换矩阵和当前变换矩阵的差异小于阈值时,就认为已经收敛了,是一条收敛条件;
    • setEuclideanFitnessEpsilon, 还有一条收敛条件是均方误差和小于阈值, 停止迭代。
  /** \brief @b IterativeClosestPoint provides a base implementation of the Iterative Closest Point algorithm. * The transformation is estimated based on Singular Value Decomposition (SVD).** The algorithm has several termination criteria:** <li>Number of iterations has reached the maximum user imposed number of iterations (via \ref setMaximumIterations)</li>* <li>The epsilon (difference) between the previous transformation and the current estimated transformation is smaller than an user imposed value (via \ref setTransformationEpsilon)</li>* <li>The sum of Euclidean squared errors is smaller than a user defined threshold (via \ref setEuclideanFitnessEpsilon)</li>* </ol>* Usage example:*  ...* \author Radu B. Rusu, Michael Dixon
**/
运行上面的代码就能得到两个点云配准的结果了,先不管结果好不好,看看他内部做了什么,实际配准算法都在aign里实现。icp.h里看到IterativeClosestPoint类是Registration的子类,icp.h给出了icp类的定义,而align的具体实现在上面的registration文件夹下的impl下的icp.hpp里,都说align with initial guess,如果可以从一个好的初始猜想变换矩阵开始迭代,那么算法将会在比较少的迭代之后就收敛,配准结果也较好,当像我们这里没有指定初始guess时,就默认使用单位阵Matrix4::Identity() ,迭代部分就像下面这样:
// Repeat until convergencedo{// Get blob data if neededPCLPointCloud2::Ptr input_transformed_blob;if (need_source_blob_){input_transformed_blob.reset (new PCLPointCloud2);toPCLPointCloud2 (*input_transformed, *input_transformed_blob);}// Save the previously estimated transformationprevious_transformation_ = transformation_;// Set the source each iteration, to ensure the dirty flag is updatedcorrespondence_estimation_->setInputSource (input_transformed);if (correspondence_estimation_->requiresSourceNormals ())correspondence_estimation_->setSourceNormals (input_transformed_blob);// Estimate correspondencesif (use_reciprocal_correspondence_)correspondence_estimation_->determineReciprocalCorrespondences (*correspondences_, corr_dist_threshold_);elsecorrespondence_estimation_->determineCorrespondences (*correspondences_, corr_dist_threshold_);//...size_t cnt = correspondences_->size ();// Check whether we have enough correspondencesif (cnt < min_number_correspondences_){PCL_ERROR ("[pcl::%s::computeTransformation] Not enough correspondences found. Relax your threshold parameters.\n", getClassName ().c_str ());convergence_criteria_->setConvergenceState(pcl::registration::DefaultConvergenceCriteria<Scalar>::CONVERGENCE_CRITERIA_NO_CORRESPONDENCES);converged_ = false;break;}// Estimate the transformtransformation_estimation_->estimateRigidTransformation (*input_transformed, *target_, *correspondences_, transformation_);// Tranform the datatransformCloud (*input_transformed, *input_transformed, transformation_);// Obtain the final transformation    final_transformation_ = transformation_ * final_transformation_;++nr_iterations_;converged_ = static_cast<bool> ((*convergence_criteria_));}while (!converged_);
这里的 transformation_estimation_->estimateRigidTransformation (*input_transformed, *target_, *correspondences_, transformation_);做了最重要的估计变换矩阵,

实际是用pcl::registration::TransformationEstimationSVD类来实现的,这里至少有两个信息量,

  • 第一,SVD奇异值分解在pcl里是直接调用Eigen内部的Umeyama实现的,我看了一眼,太过数学,暂时跳过,在另一篇博客中提过,Eigen是专门处理矩阵运算的库,pcl用的3rdParty之一,pcl用了很多第三方,这也是为什么当初配环境如此痛苦的原因之一,毕竟pcl这个库最开始只是一个德国博士的毕业论文顺手写出来的; 另外还可以看到,这里的实现除了用SVD还有另一种方法,else里提到的是先算两个点云的3D Centeroid, 再getTransformationFromCorrelation. 后面我们再来提这种思路。
  • 第二, final_transformation = current_transformation * final_transformation, 这和KinFu那篇论文提到的“变换矩阵不断叠加,最终得到唯一的全局摄像头位置(global camera pose)”是一致的。
template <typename PointSource, typename PointTarget, typename Scalar> inline void
pcl::registration::TransformationEstimationSVD<PointSource, PointTarget, Scalar>::estimateRigidTransformation (ConstCloudIterator<PointSource>& source_it,ConstCloudIterator<PointTarget>& target_it,Matrix4 &transformation_matrix) const
{// Convert to Eigen formatconst int npts = static_cast <int> (source_it.size ());if (use_umeyama_){Eigen::Matrix<Scalar, 3, Eigen::Dynamic> cloud_src (3, npts);Eigen::Matrix<Scalar, 3, Eigen::Dynamic> cloud_tgt (3, npts);for (int i = 0; i < npts; ++i){cloud_src (0, i) = source_it->x;cloud_src (1, i) = source_it->y;cloud_src (2, i) = source_it->z;++source_it;cloud_tgt (0, i) = target_it->x;cloud_tgt (1, i) = target_it->y;cloud_tgt (2, i) = target_it->z;++target_it;}// Call Umeyama directly from Eigen (PCL patched version until Eigen is released)transformation_matrix = pcl::umeyama (cloud_src, cloud_tgt, false);}else{source_it.reset (); target_it.reset ();// <cloud_src,cloud_src> is the source datasettransformation_matrix.setIdentity ();Eigen::Matrix<Scalar, 4, 1> centroid_src, centroid_tgt;// Estimate the centroids of source, targetcompute3DCentroid (source_it, centroid_src);compute3DCentroid (target_it, centroid_tgt);source_it.reset (); target_it.reset ();// Subtract the centroids from source, targetEigen::Matrix<Scalar, Eigen::Dynamic, Eigen::Dynamic> cloud_src_demean, cloud_tgt_demean;demeanPointCloud (source_it, centroid_src, cloud_src_demean);demeanPointCloud (target_it, centroid_tgt, cloud_tgt_demean);getTransformationFromCorrelation (cloud_src_demean, centroid_src, cloud_tgt_demean, centroid_tgt, transformation_matrix);}
}

到这里,pcl的算法基本上也捋清楚了,我们大概总结一下:首先icp是一步一步迭代得到较好的结果,解的过程其实是一个优化问题,并不能达到绝对正解。这个过程中求两个点云之间变换矩阵是最重要的,PCL里是用奇异值分解SVD实现的。

2 各种ICP变种

ICP有很多变种,有point-to-point的,也有point-to-plain的,一般后者的计算速度快一些,是基于法向量的,需要输入数据有较好的法向量,而法向量估计目前我对于自己的输入数据还没有很好的解决,具体使用时建议根据自己的需要及可用的输入数据选择具体方法。

PCL里有很多ICP可以用(TODO:区别与优缺点)

  • pcl::GeneralizedIterativeClosestPoint< PointSource, PointTarget >

    • is an ICP variant that implements the generalized iterative closest point algorithm as described by Alex Segal et al.
  • pcl::IterativeClosestPoint< PointSource, PointTarget, Scalar > 
    • provides a base implementation of the Iterative Closest Point algorithm.
  • pcl::IterativeClosestPointWithNormals< PointSource, PointTarget, Scalar > 
    • is a special case of  IterativeClosestPoint , that uses a transformation estimated based on Point to Plane distances by default.
  • pcl::IterativeClosestPointNonLinear< PointSource, PointTarget, Scalar > 
    • is an ICP variant that uses Levenberg-Marquardt optimization backend.
  • pcl::JointIterativeClosestPoint< PointSource, PointTarget, Scalar > 
    • extends ICP to multiple frames which share the same transform.
  • pcl::registration::IncrementalICP< PointT, Scalar > 
    • This class provides a way to register a stream of clouds where each cloud will be aligned to the previous cloud.

3 广义配准Registration

Pairwise registration

两个点集的对应,输出通常是一个4×4刚性变换矩阵:代表旋转和平移,它应用于源数据集,结果是完全与目标数据集匹配。下图是“双对应”算法中一次迭代的步骤:

对两个数据源a,b匹配运算步骤如下:

  • 从其中一个数据源a出发,分析其最能代表两个数据源场景共同点的关键点k
  • 在每个关键点ki处,算出一个特征描述子fi
  • 从这组特征描述子{fi}和他们在a和b中的XYZ坐标位置,基于fi和xyz的相似度,找出一组对应
  • 由于实际数据源是有噪的,所以不是所有的对应都有效,这就需要一步一步排除对匹配起负作用的对应
  • 从剩下的较好对应中,估计出一个变换

3.1 匹配过程中模块

3.1.1 Keypoints(关键点)

关键点是场景中有特殊性质的部分,一本书的边角,书上印的字母P都可以称作关键点。PCL中提供的关键点算法如NARF,SIFT,FAST。你可以选用所有点或者它的子集作为关键点,但需要考虑的是按毎帧有300k点来算,就有300k^2种对应组合。

3.1.2 Feature descriptors(特征描述子)

根据选取的关键点生成特征描述。把有用信息集合在向量里,进行比较。方法有:NARF, FPFH, BRIEF 或SIFT.

3.1.3 Correspondences estimation(对应关系估计)

已知从两个不同的扫描图中抽取的特征向量,找出相关特征,进而找出数据中重叠的部分。根据特征的类型,可以选用不同的方法。

点匹配(point matching, 用xyz坐标作为特征),无论数据有无重组,都有如下方法:

  • brute force matching(强制匹配),
  • kd-tree nearest neighbor search (FLANN)(kd树最近邻搜索),
  • searching in the image space of organized data(在图像空间搜索有组织的数据),
  • searching in the index space of organized data(按索引搜索有组织的数据).

特征匹配(feature matching, 用特征做为特征),只有下面两种方法:

  • brute force matching (强制匹配)
  • kd-tree nearest neighbor search (FLANN)(kd树最近邻搜索).

除了搜索法,还有两种著名对应估计:

  • 直接估计对应关系(默认),对点云A中的每一点,搜索在B中的对应关系
  • “Reciprocal” 相互对应关系估计,只用A,B重叠部分,先从A到B找对应,再从B到A找对应。

3.1.4 Correspondences rejection(剔除错误估计)

剔除错误估计,可用 RANSAC 算法,或减少数量,只用一部分对应关系。有一种特殊的一到多对应,即模型中一个点对应源中的一堆点。这种情况可以用最短路径对应或检查附近的其他匹配过滤掉。

3.1.5 Transformation estimation(最后一步,计算变换)

  • 基于上述匹配评估错误测量值;
  • 评估相机不同pose之间所作的刚性变换(运动估计),使错误测量值最小化;
  • 优化点云结构;
  • E.g, - SVD 运动估计; - Levenberg-Marquardt用不同内核作运动估计;
  • 用刚性变换旋转/平移源数据到目标位置,可能需要对所有点/部分点/关键点内部运行ICP迭代循环;
  • 迭代,直到满足某些收敛标准。

3.2 匹配流程总结

点云配准_各类ICP介及特征配准流程介绍相关推荐

  1. 根据经纬度坐标配准_扫描地图投影的识别及其配准

    注:本文是我为某历史地图项目组撰写的扫描地图配准方案,现分享出来,希望能为大家提供帮助.阅读本文需要一定的地图学基础. 在学习和工作中,由于无法获取矢量数据,经常需要以图片格式地图为工作底图,并且需要 ...

  2. 雅思阅读真经总纲_雅思天团集训课流程介绍

    欢迎宝宝们来到我们为期7天的雅思天团集训营,下面来给大家介绍一下这7天你将如何度过~ 课程简介及课前准备 本次课程时间为7天,第一天需要同学们提早到场进行签到,最后一天还给同学们安排了有关留学的讲座, ...

  3. python点云快速配准_分享一个V-SLAM中点云配准算法改进的方法

    近年来,随着自主导航的广泛应用,视觉同时定位与地图构建(Visual simultaneous localization and mapping, V-SLAM)通过自身携带的视觉传感器对周围环境进行 ...

  4. python 点云配准_点云的全局配准

    点云配准就是将当前点云匹配进行变换使其和目标点云匹配上. 先推荐一个python的点云处理库: opend3d. 什么时候需要全局配准?我说我知道的场景相机的角度变化太大了. 极少角度的物体重建. 3 ...

  5. 点云配准2:icp算法在PCL1.10.0上的实现+源码解析

    目录 本文最后实现的配准实例 点云配准系列 准备 程序结构 主程序 1.为什么要降采样 2.体素降采样原理 3.点云更新 icp 配准前的参数设置 icp配准算法内部 对应点对确定(determine ...

  6. 斯坦福的著名小兔子模型的点云数据_传统方法的点云分割以及PCL中分割模块

    之前在微信公众号中更新了以下几个章节 1,如何学习PCL以及一些基础的知识 2,PCL中IO口以及common模块的介绍 3,  PCL中常用的两种数据结构KDtree以及Octree树的介绍 有兴趣 ...

  7. 点集配准技术(ICP、RPM、KC、CPD)

    在计算机视觉和模式识别中,点集配准技术是查找将两个点集对齐的空间变换过程.寻找这种变换的目的主要包括:1.将多个数据集合并为一个全局统一的模型:2.将未知的数据集映射到已知的数据集上以识别其特征或估计 ...

  8. 容联云通讯_提供网络通话、视频通话、视频会议、云呼叫中心、IM等融合通讯能力开放平台。...

    容联云通讯_提供网络通话.视频通话.视频会议.云呼叫中心.IM等融合通讯能力开放平台. 容联云通讯_提供网络通话.视频通话.视频会议.云呼叫中心.IM等融合通讯能力开放平台. undefined po ...

  9. html页面文字随机效果,教你用javascript实现随机标签云效果_附代码

    标签云是一套相关的标签以及与此相应的权重.典型的标签云有30至150个标签.权重影响使用的字体大小或其他视觉效果.同时,直方图或饼图表是最常用的代表约12种不同的权数.因此,标签云彩能代表更多的权,尽 ...

  10. 云PBX 中小企业各类难题逐一攻破

    本文讲的是 : 云PBX 中小企业各类难题逐一攻破   ,   云PBX,一款由北京鸿博信通科技有限公司自主研发,基于云端的语音交换机.作用于企业级的办公电话系统,致力为中小企业的发展奉献一份自己的力 ...

最新文章

  1. ISME:南土所梁玉婷组-不同气候条件下微生物代谢及残体介导施肥对土壤有机碳的影响...
  2. 【Python初级】009-错误与异常
  3. grep及正则表达式
  4. Re:从 0 开始的微服务架构--(三)微服务架构 API 的开发与治理--转
  5. 初学__Python——Python数据类型之数字
  6. dylib java_将dylib库嵌入macOS应用的方法
  7. flink shell的local模式(benv与senv的使用+处理报错的解决方案)
  8. Facebook 中国程序员之死
  9. Python模块——HashLib(摘要算法)与base64
  10. 解决Admob Banner首次展示不显示的问题
  11. Terminate Instance 操作详解 - 每天5分钟玩转 OpenStack(33)
  12. mysql客户端navicat连接数据库
  13. puppet的使用:ERB模板
  14. Field eFaultPriorityService in com.ect.emes.admin.restapi.AlarmRestApi required a bean of type 解决方法
  15. Android简易Flash播放器
  16. Python_美多商城(商品)_6
  17. 【Web技术】1348- 聊聊水印实现的几种方式
  18. python pandas操作excel表
  19. librosa.effects.split
  20. java研发网页数据采集

热门文章

  1. PXE+kickstart——实现网络批量装机
  2. 第二篇:ssh.invoke_shell() 切换root出现的新问题
  3. Djangon 基础总结 汇总 从请求到返回页面的过程,
  4. 【BZOJ1901】Zju2112 Dynamic Rankings 主席树+树状数组
  5. Hibdernate入门
  6. Begin using git (Part1) - Git的安装与配置
  7. 类数组变量定义与初始化
  8. 借助Sigar API获取网络信息
  9. C++ coredump原因总结(转载)
  10. linux 自动清理var log,Linux 系统 /var/log/journal/ 垃圾日志清理-Fun言