PCL库源代码阅读—配准模块(Registration)—Registration类

Registration类是PCL库中实现点云配准的基类,本类定义了点云配准所需的基本变量和基本函数,为不同点云配准算法的实现提供了基本框架结构。Registration类通过registration.h和registration.hpp两个文件实现,registration.h为声明文件,registration.hpp为函数的具体实现文件。本文将解析源代码的逻辑实现,以便读者更好的理解PCL库中点云配准的具体实现流程。

在阅读Registration类前,需阅读PCLBase类,因为 Registration类继承于PCLBase类。PCLBase类通过pcl_base.h文件实现。

文中有任何错误或疑惑的地方可在评论区留言,看到均会回复!!! 

pcl_base.h文件

本类结构十分简单,是一个抽象类,主要实现了源点云的设置和初始化,需注意的是没有目标点云的相关变量和函数信息。

class PCLBase(基类、pcl_base.h - - pcl_base.hpp(具体实现))
public:						公有函数
setInputCloud (&cloud);		设置源点云
getInputCloud ();		    得到源点云		return (input_);
setIndices (&indices);		设置点云索引
getIndices (); 				得到点云索引		return (indices_);
protected:
PointCloudConstPtr input_;				定义源点云指针
IndicesPtr indices_;					定义源点云索引指针
initCompute ();				初始化,检查输入点云是否为空,且当没有索引提供时创建一个包含输入点云所有点的索引
class PCLBase<pcl::PCLPointCloud2>		一种点云数据结构,包含字段信息等,不做过多关注

Registration.h文件

Registration类继承于PCLBase类,直接引用了PCLBase类中的源点云指针input_和源点云索引指针indices_。

public:
  using PCLBase<PointSource>::input_;    引用PCLBase基类的源点云指针
  using PCLBase<PointSource>::indices_;  引用PCLBase基类的源点云索引指针
  
  using ...    引用了各种变量,方便本类中直接使用

构造函数及初始化列表,初始化列表中的变量在后续变量定义部分将详细介绍。

Registration():tree_(new KdTree),...    构造函数及初始化列表

下面给出关键函数的解释:

常见的变换估计的方法有SVD方法等,常见的对应关系估计方法有最近邻点等。具体的设置在具体配准算法有默认,实践中一般不进行单独设置。

本部分实现了源点云和目标点云的设置,并在其中检查了点云是否为空的异常情况,源点云的设置是通过设置父类源点云的方式实现的。

setTransformationEstimation(&);			设置变换估计的方法	
										请参考transformation_estimation模块
setCorrespondenceEstimation (&);		设置对应关系估计的方法
										请参考correspondence模块
setInputSource(& cloud);				设置源点云,通过设置父类源点云的方式
setInputTarget(& cloud);				设置目标点云
注:实现了源点云、目标点云是否为空的检查,input_,target_被赋值

 搜索方法采用的是KD-tree,无需专门设置,构造函数初始化列表时已经实例化了。

setSearchMethodTarget( ,);				设置目标点云中的搜索方法
setSearchMethodSource( ,);				设置源点云中的搜索方法,通常在相互对应关系寻找中使用
注:初始化列表已经设置,也可以用上述函数单独设置搜索方法

最终变换矩阵用来变换源点云坐标,将源点云变换到目标点云坐标系下。

getFinalTransformation();				得到最终的变换矩阵
getLastIncrementalTransformation();		得到最后一次迭代的变换矩阵

一些参数设置的函数,其默认值(default) 均在构造函数初始化列表时给出,具体的默认值见注释。

由于对应点对的最大距离阈值默认是无穷大,为了避免错误点对关系的建立,因此最大距离阈值必须人为设置具体值。为了提升了点云配准的精度,最大迭代次数也需要人为设置。

RANSAC的相关参数在配准框架的建立中没有用到的地方,其具体使用会在某些配准算法中用到,故在此无需特别关注此参数。

setMaximumIterations(int );				设置最大迭代次数,default:10
setRANSACIterations(int );				设置 RANSAC 算法的迭代次数,default:0
setRANSACOutlierRejectionThreshold(double );	设置 RANSAC 算法过滤离群点的距离阈值,default:0.05
setMaxCorrespondenceDistance(double );	设置搜索对应点对的最大距离阈值,default:无穷大

 连续两次变换矩阵的变化值的阈值可作为收敛条件之一,默认值为0,实践中也可不单独设置,此时主要以最大迭代次数为收敛的判断条件。

欧式距离误差:连续两次变换中对应点对间欧式距离的总和/对应点对数量的差值,用于ICP算法中。

配准后的得分:配准后对应点对之间的距离平方的平均值,衡量配准效果的参考指标, max_range:设置参与计算得分的配准后点对之间的最大距离阈值,默认值代表源点云所有点参与得分计算。该参数需人为设置。

setTransformationEpsilon(double );		设置平移矩阵变化的平方阈值,判断是否收敛,default:0
setTransformationRotationEpsilon(double );设置旋转矩阵变化的阈值,判断是否收敛,default:0
setEuclideanFitnessEpsilon(double);		设置点云间欧式距离误差的阈值,判断是否收敛,default:无穷大
getFitnessScore(double max_range);		得到配准后的得分

align()函数是点云配准的关键函数, 其具体实现在本文下一部分.hpp中详细讲解。

hasConverged();							获取收敛状态		return bool;
align(& output);						实现配准,将配准后的点云存储在output
align(& output,& guess);				output同上,guess:设置的变换矩阵初值
		output的大小无需指定,实现了将源点云拷贝到output中,然后计算T
注:align()函数调用了computeTransformation()函数计算变换矩阵,后者是抽象函数,需在具体的配准方法实现

 初始化部分会检查点云是否为空,并为搜索方法设置点云源,如:tree_->setInputCloud(target_);

getClassName();							获取配准方法名
initCompute();							初始化
initComputeReciprocal();				点对的建立基于相互对应关系时的初始化

 对应关系的拒绝器是为了过滤掉不良的对应关系,使用场景有限,可不关注。

addCorrespondenceRejector();            增加对应关系的拒绝器
...                                     可不关注

本部分解释了protected的变量定义和函数定义。

protected:
string reg_name_;						定义配准方法名
KdTreePtr tree_;						定义目标点云搜索方法的指针变量
KdTreeReciprocalPtr tree_reciprocal_;	定义源点云搜索方法的指针变量
int nr_iterations_;						定义当前迭代次数
int max_iterations_;					定义最大迭代次数		default:10
int ransac_iterations_;					定义RANSAC算法迭代次数
PointCloudTargetConstPtr target_;		定义目标点云指针
Matrix4 final_transformation_;			定义最终的变换矩阵
Matrix4 transformation_;				定义当前的变换矩阵
Matrix4 previous_transformation_;		定义上一次的变换矩阵
注:上述三个变换矩阵的初值都是单位矩阵
double transformation_epsilon_;			定义平移矩阵变化平方的阈值
double transformation_rotation_epsilon_;定义旋转矩阵变化阈值
double euclidean_fitness_epsilon_;		定义点云间欧式距离误差判断收敛的阈值变量
double corr_dist_threshold_;			定义点云是否配对的距离阈值变量
double inlier_threshold_;				定义RANSAC算法判断内部点的距离阈值变量
										default:0.05
bool converged_;						定义收敛状态变量		
int min_number_correspondences_;		定义计算变换矩阵所需的最少配对点数量
										default:3
TransformationEstimationPtr transformation_estimation_;	定义变换矩阵估计方法变量,如:SVD,point to plane…
CorrespondenceEstimationPtr correspondence_estimation_;	定义点云间对应关系估计方法变量
bool target_cloud_updated_;
bool source_cloud_updated_;
bool force_no_recompute_;
bool force_no_recompute_reciprocal_;
上述变量避免了为新的点云建立kd-tree,即避免每次调用determineCorrespondences();
searchForNeighbors( , , , );		搜索给定点的最邻近点,内部仍然是tree_->nearestKSearch函数
virtual void computeTransformation( ,) = 0;	纯虚函数,子类具体实现计算变换矩阵
注:该类实现了配准参数的设定,变换矩阵的求解在子类中实现,该类没有具体实现迭代过程.

 setInputCloud()是私有函数,也是通过公有的setInputSource()函数实现的设置,无需特别关注。

private:
setInputCloud(& cloud) override{
			setInputSource(cloud);…}	设置源点云

Registration.hpp文件

本部分主要介绍该文件中的关键函数align(),它是点云配准算法都要使用到的配准函数,它实现了配准的框架,其通过内部computeTransformation()纯虚函数实现了不同配准算法的具体实现。

align(& output);该函数的实现也是通过align(& output,& guess)函数实现的,只是默认将guess矩阵设置为了单位矩阵。

align(& output);						实现配准,将配准后的点云存储在output
align(& output,& guess);				output同上,guess:设置的变换矩阵初值
template <typename PointSource, typename PointTarget, typename Scalar>
inline void
Registration<PointSource, PointTarget, Scalar>::align(PointCloudSource& output,
                                                      const Matrix4& guess)
{...}

 align(,);函数中代码具体如下。computeTransformation()函数实现了计算变换矩阵的功能,它由继承的子类具体实现。align函数只是实现了点云配准的框架,将源点云的信息复制给了output,并将output作为参数传递给了computeTransformation()函数。

align()函数中并没有实现迭代计算变换矩阵的功能。

if (!initCompute())        初始化,检查点云是否为空,并为搜索方法设置目标点云源
  return;

output.resize(indices_->size());    将output的大小重置为源点云索引的大小
output.header = input_->header;     将源点云的头文件复制给output
/ 检查output是对于所有点还是一部分点而言 /
if (indices_->size() != input_->size()) {
  output.width = indices_->size();
  output.height = 1;
}
else {
  output.width = static_cast<std::uint32_t>(input_->width);
  output.height = input_->height;
}
output.is_dense = input_->is_dense;

/ 将点复制到output中 /
for (std::size_t i = 0; i < indices_->size(); ++i)
  output[i] = (*input_)[(*indices_)[i]];

/ 设置内部点的表示形式,无需过多关注 /
if (point_representation_ && !force_no_recompute_)
  tree_->setPointRepresentation(point_representation_);

/ 进行变换矩阵的计算 /
converged_ = false;
final_transformation_ = transformation_ = previous_transformation_ =
    Matrix4::Identity();    初值给定为单位矩阵

/ 将output[i].data[3]的所有值设置为1.0,此时三维坐标成为了齐次坐标 /
for (std::size_t i = 0; i < indices_->size(); ++i)
  output[i].data[3] = 1.0;

/ 计算变换矩阵 /
computeTransformation(output, guess);

总结

Registration类是PCL库中用来配准的基类,它建立了配准的框架。两点云配准所需的源点云、目标点云源的设置,用来判断是否收敛的各种阈值参数的设置都是在该类中实现。而变换矩阵的迭代求解则通过computeTransformation()纯虚函数让子类进行重载,以实现具体的配准算法。

  • 24
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

想躺平的点云工程师

感谢各位看官!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值