问题描述
ICP(Iterative Closest Point)是点云精配准中最常用的方法之一。在上一篇博客中我复现了两两配准(pairwise registration)中的ICP算法。在实际应用中,经常需要同时配准多幅点云,这是就需要用到全局ICP算法在。其思路与两两配准的ICP算法相同,即通过寻找临近点构建误差的最小二乘公式,并计算出使得误差公式最小的旋转矩阵和平移矩阵。两者的不同在于误差公式中代求旋转矩阵和平移矩阵的个数。
由于我没有找到相关解析求解方法,故选用梯度下降法来求解最小二乘。
全局ICP思路
首先使用点对点(point-to-point)的方式寻找邻近点,误差公式为:
E
=
∑
m
=
1
K
∑
n
=
1
,
n
≠
m
K
∑
i
=
1
N
m
n
∣
∣
(
R
m
P
m
i
+
t
m
)
−
(
R
n
P
n
i
+
t
n
)
∣
∣
2
E=\sum_{m=1}^K\sum_{n=1,n\neq m}^{K}\sum_{i=1}^{N_{mn}}{||(R_mP_{m}^i+t_m)-(R_nP_{n}^i+t_n)||^2}
E=m=1∑Kn=1,n=m∑Ki=1∑Nmn∣∣(RmPmi+tm)−(RnPni+tn)∣∣2
其中
K
K
K是点云个数,
N
m
n
N_{mn}
Nmn是第
m
m
m片点云和第
n
n
n片点云邻近点的个数,
R
m
R_m
Rm、
R
n
R_n
Rn分别是第
m
m
m和
n
n
n片点云的旋转矩阵,
t
m
t_m
tm、
t
n
t_n
tn分别是第
m
m
m和
n
n
n片点云的平移矩阵。
全局ICP的目标——找到使误差 E E E最小的旋转矩阵{ R i R_i Ri}( i = 1 ⋯ K i=1\cdots K i=1⋯K)和平移矩阵{ t i t_i ti}( i = 1 ⋯ K i=1\cdots K i=1⋯K)
第一步:问题简化——将求解平移矩阵 t t t分离出来
类比两两ICP配准,我首先将每片点云中的点减去质心坐标,用以消除平移矩阵 t t t的影响。
每片点云的质心坐标:
P
m
ˉ
=
1
N
m
∑
i
=
1
N
m
P
m
i
\bar{P_m} = {1\over N_m}\sum_{i=1}^{N_m}{P_{mi}}
Pmˉ=Nm1i=1∑NmPmi
定义点云集{
Q
i
Q_i
Qi}(
i
=
1
⋯
K
i=1\cdots K
i=1⋯K)
Q
m
i
=
P
m
i
−
P
m
ˉ
Q_{m}^i = P_{m}^i-\bar{P_m}
Qmi=Pmi−Pmˉ
原公式可被简化为
E
=
∑
m
=
1
K
∑
n
=
1
,
n
≠
m
K
∑
i
=
1
N
m
n
∣
∣
R
m
Q
m
i
−
R
n
Q
n
i
∣
∣
2
E=\sum_{m=1}^K\sum_{n=1,n\neq m}^{K}\sum_{i=1}^{N_{mn}}{||R_mQ_{m}^i-R_nQ_{n}^i||^2}
E=m=1∑Kn=1,n=m∑Ki=1∑Nmn∣∣RmQmi−RnQni∣∣2
第二步:梯度下降法求解旋转矩阵
假设我们固定
i
=
1
i=1
i=1的点云,则上述公式右边有
K
−
1
K-1
K−1个待求变量{
R
2
,
R
3
⋯
,
R
k
R_2,R_3\cdots,R_k
R2,R3⋯,Rk}。每次迭代过程中,更新后的变量求解公式为
R
i
+
1
=
[
R
2
i
+
1
R
3
i
+
1
⋮
R
k
i
+
1
]
=
R
i
−
α
▽
R
=
[
∂
E
/
∂
R
2
i
∂
E
/
∂
R
3
i
⋮
∂
E
/
∂
R
k
i
]
R^{i+1}=\begin{bmatrix} R_2^{i+1}\\ R_3^{i+1}\\ \vdots\\ R_k^{i+1} \end{bmatrix}=R^{i}-\alpha \bigtriangledown_R =\begin{bmatrix} \partial E/\partial R_2^i\\ \partial E/\partial R_3^i\\ \vdots\\ \partial E/\partial R_k^i \end{bmatrix}
Ri+1=⎣⎢⎢⎢⎡R2i+1R3i+1⋮Rki+1⎦⎥⎥⎥⎤=Ri−α▽R=⎣⎢⎢⎢⎡∂E/∂R2i∂E/∂R3i⋮∂E/∂Rki⎦⎥⎥⎥⎤
其中,梯度求解公式为:
∂
E
/
∂
R
m
=
∑
n
=
1
,
n
≠
m
K
∑
i
=
1
N
m
n
2
R
m
Q
m
i
Q
m
i
T
−
2
R
n
Q
n
i
Q
m
i
T
\partial E/\partial R_m =\sum_{n=1,n\neq m}^{K}\sum_{i=1}^{N_{mn}}{2R_mQ_m^iQ_m^{iT}-2R_nQ_n^iQ_m^{iT}}
∂E/∂Rm=n=1,n=m∑Ki=1∑Nmn2RmQmiQmiT−2RnQniQmiT
梯度推导过程在文章后面
第三步:求解平移矩阵
平移矩阵计算公式为:
t
m
=
P
1
ˉ
−
R
m
P
m
ˉ
t_m = \bar{P_1}-R_m\bar{P_m}
tm=P1ˉ−RmPmˉ
注意:第二步和第三步在每一次迭代过程中都要执行一遍。并且将更新后的R、t应用到点云集之后需要重新计算邻近点。
实际应用中,程序重复执行第二和第三步,直到收敛。
核心代码
void GloballyICP::align(vector<pcl::PointCloud<pcl::PointXYZ>>& clouds_out) {
for (int indexIter = 0; indexIter < _iterNum; ++indexIter) {
cout << "iteration : " << indexIter << endl;
FindCentroid();
for (size_t indexCloud = 0; indexCloud < _cloudsNum; ++indexCloud) {
int centroidX = _Centroids[indexCloud].x;
int centroidY = _Centroids[indexCloud].y;
int centroidZ = _Centroids[indexCloud].z;
for (size_t indexPoint = 0; indexPoint < _clouds_ICP[indexCloud].points.size(); ++indexPoint) {
_Qclouds_ICP[indexCloud][indexPoint].x = _clouds_ICP[indexCloud][indexPoint].x - centroidX;
_Qclouds_ICP[indexCloud][indexPoint].y = _clouds_ICP[indexCloud][indexPoint].y - centroidY;
_Qclouds_ICP[indexCloud][indexPoint].z = _clouds_ICP[indexCloud][indexPoint].z - centroidZ;
}
}
/* step one : find correspondences */
FindCorrespondence();
cout << "MSE : " << _meanSquaredError << endl;
/* step two : 梯度法求解旋转矩阵R */
// 遍历计算梯度
for (int indexR = 0; indexR < _cloudsNum; ++indexR) {
//cout << indexR << endl;
if (indexR == _Ifix) {
_delta_Rotation_Matrices[indexR] = Eigen::Matrix3f::Zero();
continue;
}
Eigen::Matrix3f tempDeltaRotation = Eigen::Matrix3f::Zero();
int pointNum = 0;
for (int indexCloud = 0; indexCloud < _cloudsNum; ++indexCloud) {
if (indexCloud == indexR)
continue;
vector<size_t>correspondenceM = _correspondence_index[indexR][indexCloud];
vector<size_t>correspondenceN = _correspondence_index[indexCloud][indexR];
for (size_t indexPoint = 0; indexPoint < _correspondenceNum[indexR][indexCloud]; ++indexPoint) {
//cout << _Qclouds_ICP[indexR].points[correspondenceM[indexPoint]] << endl;
Eigen::Vector3f Mpoint(_Qclouds_ICP[indexR].points[correspondenceM[indexPoint]].x,
_Qclouds_ICP[indexR].points[correspondenceM[indexPoint]].y,
_Qclouds_ICP[indexR].points[correspondenceM[indexPoint]].z);
//cout << _Qclouds_ICP[indexCloud].points[correspondenceN[indexPoint]] << endl;
Eigen::Vector3f Npoint(_Qclouds_ICP[indexCloud].points[correspondenceN[indexPoint]].x,
_Qclouds_ICP[indexCloud].points[correspondenceN[indexPoint]].y,
_Qclouds_ICP[indexCloud].points[correspondenceN[indexPoint]].z);
//cout << "Mpoint:" << endl << Mpoint << endl;
//cout << "Npoint:" << endl << Npoint << endl;
tempDeltaRotation += 2 * (_Rotation_Matrices[indexR] * Mpoint * Mpoint.transpose() -
_Rotation_Matrices[indexCloud] * Npoint * Mpoint.transpose());
++pointNum;
}
}
if (pointNum == 0) {
_delta_Rotation_Matrices[indexR] = Eigen::Matrix3f::Zero();
}
else {
_delta_Rotation_Matrices[indexR] = tempDeltaRotation / pointNum;
}
//cout << "delta rotation Matrix:" << indexR << endl << _delta_Rotation_Matrices[indexR] << endl;
}
// 计算更新后的旋转矩阵 R
for (int indexR = 0; indexR < _cloudsNum; ++indexR) {
_Rotation_Matrices[indexR] = _Rotation_Matrices[indexR] - _alpha * _delta_Rotation_Matrices[indexR];
}
//cout << "RotationMatrix:" << endl << _Rotation_Matrices[1] << endl;
// 计算平移矩阵
Eigen::Vector3f centroidF(_Centroids[_Ifix].x, _Centroids[_Ifix].y, _Centroids[_Ifix].z);
for (int indexT = 0; indexT < _cloudsNum; ++indexT) {
if (indexT == _Ifix)
_Transformaton_Matrices[indexT] = Eigen::Vector3f::Zero();
else {
Eigen::Vector3f centroidM(_Centroids[indexT].x, _Centroids[indexT].y, _Centroids[indexT].z);
_Transformaton_Matrices[indexT] = centroidF - _Rotation_Matrices[indexT] * centroidM;
//cout << "Transformation matrix " << indexT << " : " << endl;
//cout << _Transformaton_Matrices[indexT] << endl;
}
}
/* step3 : 将R,T应用到 _clouds_ICP */
UpdateCloudsPosition();
}
clouds_out = _clouds_ICP;
}
实验效果
我对随机生成的点云进行人为平移和旋转,共使用四片点云(颜色不同)进行全局ICP配准,在步长为0.001时迭代300次的效果如下图:上图左边为待配准的原始点云,右边为配准完毕的点云。在右图中,PCL的可视化模块已经将四片点云中的对应点显示为一个点,这表明配准效果还是很好的。最后一次迭代的均方差为0.014。
梯度法求解推导
消除平移矩阵后的误差公式:
E
=
∑
m
=
1
K
∑
n
=
1
,
n
≠
m
K
∑
i
=
1
N
m
n
∣
∣
R
m
Q
m
i
−
R
n
Q
n
i
∣
∣
2
=
∑
m
=
1
K
∑
n
=
1
,
n
≠
m
K
∑
i
=
1
N
m
n
(
R
m
Q
m
i
−
R
n
Q
n
i
)
T
(
R
m
Q
m
i
−
R
n
Q
n
i
)
=
∑
m
=
1
K
∑
n
=
1
,
n
≠
m
K
∑
i
=
1
N
m
n
(
Q
m
i
T
R
m
T
−
Q
n
i
T
R
n
T
)
(
R
m
Q
m
i
−
R
n
Q
n
i
)
E=\sum_{m=1}^K\sum_{n=1,n\neq m}^{K}\sum_{i=1}^{N_{mn}}{||R_mQ_{m}^i-R_nQ_{n}^i||^2}\\ =\sum_{m=1}^K\sum_{n=1,n\neq m}^{K}\sum_{i=1}^{N_{mn}}{(R_mQ_m^i-R_nQ_n^i)^T(R_mQ_m^i-R_nQ_n^i)}\\ =\sum_{m=1}^K\sum_{n=1,n\neq m}^{K}\sum_{i=1}^{N_{mn}}{(Q_m^{iT}R_m^T-Q_n^{iT}R_n^T)(R_mQ_m^i-R_nQ_n^i)}
E=m=1∑Kn=1,n=m∑Ki=1∑Nmn∣∣RmQmi−RnQni∣∣2=m=1∑Kn=1,n=m∑Ki=1∑Nmn(RmQmi−RnQni)T(RmQmi−RnQni)=m=1∑Kn=1,n=m∑Ki=1∑Nmn(QmiTRmT−QniTRnT)(RmQmi−RnQni)
展开后,求和符号内部为
(
Q
m
i
T
R
m
T
−
Q
n
i
T
R
n
T
)
(
R
m
Q
m
i
−
R
n
Q
n
i
)
=
(Q_m^{iT}R_m^T-Q_n^{iT}R_n^T)(R_mQ_m^i-R_nQ_n^i)=
(QmiTRmT−QniTRnT)(RmQmi−RnQni)=
Q
m
i
T
R
m
T
R
m
Q
m
i
−
Q
m
i
T
R
m
T
R
n
Q
n
i
−
Q
n
i
T
R
n
T
R
m
Q
m
i
+
Q
n
i
T
R
n
T
R
n
Q
n
i
Q_m^{iT}R_m^TR_mQ_m^i-Q_m^{iT}R_m^TR_nQ_n^i-Q_n^{iT}R_n^TR_mQ_m^i+Q_n^{iT}R_n^TR_nQ_n^i
QmiTRmTRmQmi−QmiTRmTRnQni−QniTRnTRmQmi+QniTRnTRnQni
所以,偏导
∂
E
/
∂
R
m
\partial E /\partial R_m
∂E/∂Rm为
∂
E
/
∂
R
m
=
∑
n
=
1
,
n
≠
m
K
∑
i
=
1
N
m
n
∂
Q
m
i
T
R
m
T
R
m
Q
m
i
∂
R
m
−
∂
Q
m
i
T
R
m
T
R
n
Q
n
i
∂
R
m
−
∂
Q
n
i
T
R
n
T
R
m
Q
m
i
∂
R
m
\partial E /\partial R_m=\sum_{n=1,n\neq m}^{K}\sum_{i=1}^{N_{mn}}{{\partial Q_m^{iT}R_m^TR_mQ_m^i \over \partial R_m}-{\partial Q_m^{iT}R_m^TR_nQ_n^i \over \partial R_m}-{\partial Q_n^{iT}R_n^TR_mQ_m^i \over \partial R_m}}
∂E/∂Rm=n=1,n=m∑Ki=1∑Nmn∂Rm∂QmiTRmTRmQmi−∂Rm∂QmiTRmTRnQni−∂Rm∂QniTRnTRmQmi
于是,现在遇到的问题就是三个偏导数如何求解。
在矩阵对矩阵的求导中,有三个公式,分别为
∂
a
T
X
T
X
b
∂
X
=
X
b
a
T
+
X
a
b
T
{\partial a^TX^TXb \over \partial X }=Xba^T+Xab^T
∂X∂aTXTXb=XbaT+XabT
∂
a
T
X
T
b
∂
X
=
b
a
T
{\partial a^TX^Tb \over \partial X } =ba^T
∂X∂aTXTb=baT
∂
a
T
X
b
∂
X
=
a
b
T
{\partial a^TXb \over \partial X}=ab^T
∂X∂aTXb=abT
将这三个公式应用到偏导求解过程则有:
∂
Q
m
i
T
R
m
T
R
m
Q
m
i
∂
R
m
=
2
R
m
Q
m
i
Q
m
i
T
{{\partial Q_m^{iT}R_m^TR_mQ_m^i \over \partial R_m}=2R_mQ_m^iQ_m^{iT} }
∂Rm∂QmiTRmTRmQmi=2RmQmiQmiT
∂
Q
m
i
T
R
m
T
R
n
Q
n
i
∂
R
m
=
R
n
Q
n
i
Q
m
i
T
{\partial Q_m^{iT}R_m^TR_nQ_n^i \over \partial R_m}=R_nQ_n^iQ_m^{iT}
∂Rm∂QmiTRmTRnQni=RnQniQmiT
∂
Q
n
i
T
R
n
T
R
m
Q
m
i
∂
R
m
=
R
n
Q
n
i
Q
m
i
T
{\partial Q_n^{iT}R_n^TR_mQ_m^i \over \partial R_m}=R_nQ_n^iQ_m^{iT}
∂Rm∂QniTRnTRmQmi=RnQniQmiT
即
∂
E
/
∂
R
m
=
∑
n
=
1
,
n
≠
m
K
∑
i
=
1
N
m
n
2
R
m
Q
m
i
Q
m
i
T
−
2
R
n
Q
n
i
Q
m
i
T
\partial E/\partial R_m =\sum_{n=1,n\neq m}^{K}\sum_{i=1}^{N_{mn}}{2R_mQ_m^iQ_m^{iT}-2R_nQ_n^iQ_m^{iT}}
∂E/∂Rm=n=1,n=m∑Ki=1∑Nmn2RmQmiQmiT−2RnQniQmiT
推导完毕
完整代码下载地址