ICP算法可以使用SVD或者非线性优化的方法进行求解。因为PCL源码中对ICP的求解就是使用SVD,所以这里对其进行一些探究。
1.公式推导
-
1.首先对第i个点构建误差项:
ei=pi−(Rpi′+t)
-
2.构建最小二乘问题:求得使得目标函数最小的未知量R,t
R,tminJ=21i=1∑n∥(pi−(Rpi′+t))∥22
接下来定义一些需要用的变量:
首先定义两组点云的质心:
p=n1i=1∑n(pi),p′=n1i=1∑n(pi′)
-
4.在误差函数中添加质心,进行配平
21i=1∑n∥pi−(Rpi′+t)∥2=21i=1∑n∥pi−Rpi′−t−p+Rp′+p−Rp′∥2=21i=1∑n∥(pi−p−R(pi′−p′))+(p−Rp′−t)∥2=21i=1∑n(∥pi−p−R(pi′−p′)∥2+∥p−Rp′−t∥2+2(pi−p−R(pi′−p′))T(p−Rp′−t))
注意到交叉项部分中,(pi−p−R(pi′−p′))在求和之后是为0的,因此目标优化函数可以进行化简,变成:
R,tminJ=21i=1∑n∥pi−p−R(pi′−p′)∥2+∥p−Rp′−t∥2
-
5.仔细观察左右两项,我们发现左边只和旋转矩阵 R 相关,而右边既有 $R 也有 $t,但 只和质心相关。只要我们获得了 R,令第二项为零就能得到 t。于是,ICP 可以分为以下 三个步骤求解:
-
5.1 计算两组点的质心位置 p,p′,然后计算每个点的去质心坐标:
qi=pi−p,qi′=pi′−p′
-
5.2 根据以下优化问题计算旋转矩阵:
R∗=argRmin21i=1∑n∥qi−Rqi′∥2
-
5.3 根据第二步的 R,计算$ t$:
t∗=p−Rp′
-
6.根据以上推导,我们重点需要求出旋转量R,求得之后代入自然可以获得t。对公式(6)进行展开:
21i=1∑n∥qi−Rqi′∥2=21i=1∑nqiTqi+qi′TRTRqi′−2qiTRqi′
注意到第一项和 R 无关,第二项由于 RTR=I,亦与 R 无关。因此,实际上优化 目标函数变为:
i=1∑n−qiTRqi′=i=1∑n−tr(Rqi′qiT)=−tr(Ri=1∑nqi′qiT)
其实就是3∗1∗1∗3的矩阵,自然只有对角线上的数据表示两个点之间的有效数据。
- 7.对这个优化函数的求解,则使用SVD来完成。定义矩阵:
W=i=1∑nqiqi′T
W 是一个 3 × 3 的矩阵,对 W 进行 SVD 分解,得:
W=UΣVT
其中,Σ 为奇异值组成的对角矩阵,对角线元素从大到小排列,而 U 和 V 为正交矩 阵。当 W 满秩时,R 为:
R=UVT
求得结果后,根据公式(7)即可获得t的结果。
2.PCL源码阅读

整个源码从align函数开始,调用关系如上图所示。
需要注意的是,PCL中根据算法的分类,对所有的代码进行归类。比如pcl属于Registration类,那么所有的匹配算法都统一放在Registration文件夹下。
其中h文件是定义好的函数接口,一些简单的函数直接在里面通过内联函数的形式进行了完善。
impl(implement)文件夹下的hpp文件则是对h文件中定义的复杂文件的实现。
src文件夹则是一个空的.cpp文件,只包含了对应的头文件。个人理解主要是为了编译生成可调用的库文件。