全局ICP梯度解法推导及代码

问题描述

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=1Kn=1,n=mKi=1Nmn(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=1K)和平移矩阵{ t i t_i ti}( i = 1 ⋯ K i=1\cdots K i=1K)

第一步:问题简化——将求解平移矩阵 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=1NmPmi

定义点云集{ Q i Q_i Qi}( i = 1 ⋯ K i=1\cdots K i=1K)
Q m i = P m i − P m ˉ Q_{m}^i = P_{m}^i-\bar{P_m} Qmi=PmiPmˉ
原公式可被简化为
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=1Kn=1,n=mKi=1NmnRmQmiRnQni2

第二步:梯度下降法求解旋转矩阵

假设我们固定 i = 1 i=1 i=1的点云,则上述公式右边有 K − 1 K-1 K1个待求变量{ 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+1Rki+1=RiαR=E/R2iE/R3iE/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=mKi=1Nmn2RmQmiQmiT2RnQniQmiT
梯度推导过程在文章后面

第三步:求解平移矩阵

平移矩阵计算公式为:
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=1Kn=1,n=mKi=1NmnRmQmiRnQni2=m=1Kn=1,n=mKi=1Nmn(RmQmiRnQni)T(RmQmiRnQni)=m=1Kn=1,n=mKi=1Nmn(QmiTRmTQniTRnT)(RmQmiRnQni)
展开后,求和符号内部为
( 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)= (QmiTRmTQniTRnT)(RmQmiRnQni)=
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 QmiTRmTRmQmiQmiTRmTRnQniQniTRnTRmQmi+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=mKi=1NmnRmQmiTRmTRmQmiRmQmiTRmTRnQniRmQniTRnTRmQmi
于是,现在遇到的问题就是三个偏导数如何求解。
在矩阵对矩阵的求导中,有三个公式,分别为
∂ 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 XaTXTXb=XbaT+XabT
∂ a T X T b ∂ X = b a T {\partial a^TX^Tb \over \partial X } =ba^T XaTXTb=baT
∂ a T X b ∂ X = a b T {\partial a^TXb \over \partial X}=ab^T XaTXb=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} } RmQmiTRmTRmQmi=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} RmQmiTRmTRnQni=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} RmQniTRnTRmQmi=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=mKi=1Nmn2RmQmiQmiT2RnQniQmiT
推导完毕
完整代码下载地址

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
高斯牛顿法(Gauss-Newton method)是一种优化算法,主要用于解决非线性最小二乘问题。该方法通过将非线性最小二乘问题转化为一个线性最小二乘问题,来求解模型参数。 假设我们有一个非线性模型 $f(x;\theta)$,其中 $x$ 是输入变量,$\theta$ 是模型参数。我们希望找到最佳的参数 $\theta$,使得模型输出 $y=f(x;\theta)$ 与观测数据 $y_{obs}$ 最接近。 我们可以定义损失函数 $L(\theta)$ 来衡量模型输出与观测数据之间的差异,即: $$ L(\theta) = \frac{1}{2}\sum_{i=1}^{n}(y_i - y_{obs,i})^2 $$ 其中 $n$ 是观测数据的数量。 为了最小化损失函数 $L(\theta)$,我们可以使用梯度下降法或者牛顿法等数值优化算法。但是,对于非线性模型,梯度下降法的收敛速度可能会很慢,而牛顿法需要计算二阶导数,计算复杂度较高。 高斯牛顿法是一种介于梯度下降法和牛顿法之间的方法,它利用了二阶导数的信息,但避免了计算二阶导数的复杂度。 具体来说,高斯牛顿法通过将非线性最小二乘问题转化为一个线性最小二乘问题,来求解模型参数。假设我们在参数 $\theta_k$ 处进行一次迭代,我们可以将模型在点 $\theta_k$ 处的一阶导数和二阶导数展开为: $$ \nabla L(\theta_k) \approx J_k^T(y_k - y_{obs}) \\ \nabla^2 L(\theta_k) \approx J_k^T J_k $$ 其中 $J_k$ 是 Jacobian 矩阵,定义为: $$ J_k = \frac{\partial f(x_i;\theta_k)}{\partial \theta_k} $$ 接下来,我们可以用线性最小二乘法来求解参数的更新量 $\Delta \theta$: $$ \Delta \theta = -(J_k^T J_k)^{-1} J_k^T(y_k - y_{obs}) $$ 然后,我们可以使用更新量来更新参数: $$ \theta_{k+1} = \theta_k + \Delta \theta $$ 这样,我们就完成了一次迭代。重复执行以上步骤,直到损失函数收敛或达到最大迭代次数为止。 需要注意的是,高斯牛顿法有时可能会因为 Jacobian 矩阵不可逆而出现问题。此外,当模型存在局部最优解时,高斯牛顿法可能会陷入局部最优解而无法收敛到全局最优解。因此,在实际应用中,我们需要结合其他方法来提高算法的鲁棒性和收敛速度。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值