Sparse Point Registration (SPR)是一篇2017年的点云配准算法,该算法的主要目的是对稀疏点云进行配准,并且取得了不错的成果和突破。本文一方面是对SPR配准算法模型进行了简单的原理解析以及附加代码实现,另一方面是对之前工作的总结,也算水篇博文,接下来的工作主要就是分割和光流预测方面的学习了。
所谓稀疏点云就是点数稀少的点云模型,有时我们需要用到一些物体上的关键点来和目标模型进行配准,计算一些关键指标。而传统的点云配准算法要求待配准的两片点云数量级相当,并且还包括粗配准和精配准两个阶段。经实验可得,传统点云配准算法在稀疏点云配准上表现较差,因此稀疏点云配准十分关键。
SPR算法不需要进行粗配准就可以实现效果较好的稀疏点云配准效果,该算法的核心思想主要包括扰动、迭代、细化三个部分。SPR算法模型包括以下步骤:
- 初始化目标模型点云数据A,稀疏点云数据B,最大迭代次数MaxIterations,配准误差阈值Threshold、扰动次数P
- 根据点云Size和高斯分布计算当前迭代的扰动量(扰动量随着迭代次数不断减小,最后减小到零)获取P个扰动扰动变换矩阵,然后对当前B点云进行扰动变换
- 分别计算P个扰动变换后的稀疏点云B和目标模型点云A的 Cost (这里可以使用K-D Tree 计算最近点偏差和作为Cost),选择Cost最小的作为局部最优扰动
- 基于当前的最优扰动变换后的稀疏点云B,进行 ICP or DQF 算法精配准细化(本文选择ICP),获得精配准矩阵Tk,并计算此时的误差
- 若计算的误差小于最小误差e,则更新全局最优边换矩阵T和最小误差e为当前迭代结果。
- 重复迭代以上过程,直到达到最大迭代次数MaxIterations或计算误差e<配准误差阈值Threshold
除此之外,论文中还阐释了一些比较细节的注意事项和参数设置,比如最大迭代次数MaxIterations、配准误差阈值Threshold、扰动次数P设置为多少,如何进行扰动计算,以及如何在试验中取点等等,当然稀疏点云中越具有特征性的关键点配准效果越好。
本文的SPR算法实现使用Matlab编程进行,采用ICP的精配准版本,在实现过程中对原算法进行了改进:在迭代过程中,对每一个出现的扰动变换都进行ICP精配准计算,然后基于这个误差进行比较,而不只是原算法所言的选出最优扰动后再计算ICP,这样可以最大程度的提高算法精确度,但是牺牲了时间复杂度。下面给出部分代码。
(1)SPR.m
% SPR Main logic function:transform A sparse point cloud into B model point cloud coordinate system for registration
% - Params:
% - A:sparse point cloud [n,3]
% - B:model point cloud [m,3]
% - Return:
% - R:optimal rotation matrix [3,3]
% - T:optimal translation matrix [1,3]
% - trans_res:transform point cloud [n,3]
function [ R,T,trans_res ] = SPR(A,B)
%1.kdtree model for B(euclidean distance) and size of the model
Mdl = KDTreeSearcher(B);
Size = max(range(B));
%2.Initialize R0,T0,e0
R = eye(3);
%Align the centroids of two point clouds A->B
cen_A = sum(A)/length(A);
cen_B = sum(B)/length(B);
T = cen_B - cen_A;
trans_res = transform(A,R,T);
e = calculateCost(trans_res,B,Mdl);
%3.Initialize the number of disturbed pose P, the maximum number of iterations I, and the minimum error threshold RMS
P = 10;
I = 30;
RMS = 1e-6;
%4.Initialize loop variable
k = 1;
%record data
x_loop = 1:I;
y_error = zeros(1,I);
%5.SPR Loop
while e > RMS && k <= I
cost_best = inf;
R_k = R;
T_k = T;
%(1). Perturbations for Sparse point cloud,and then the perturbation transformation matrix is obtained
% - R_p: [3,3,P+1]
% - T_p: [p+1,3]
[R_p, T_p] = perturb(Size,P,k,I);
%(2). Calculate the optimal pose
for j = 1:P+1
%Obtain the transformation matrix parameters rp_j(3,3), tp_j(1,3) of the current p-th disturbance pose
rp_j = R_p(:,:,j)*R;
tp_j = (R_p(:,:,j)*T')'+T_p(j,:);
%Calculate current optimal cost(from ICP)
[R_j,T_j,cost_j] = solveicp(A,B,Mdl,rp_j,tp_j);
%judge optimal cost
if cost_j < cost_best
cost_best = cost_j;
R_k = R_j;
T_k = T_j;
end
end
%(3). Error calculation and judge for update
trans_k = transform(A,R_k,T_k);
e_k = calculateCost(trans_k,B,Mdl);
if e_k < e
e = e_k;
R = R_k;
T = T_k;
end
y_error(k) = e;
k = k+1;
end
trans_res = transform(A,R,T);
figure
plot(x_loop,y_error,marker='o', MarkerFaceColor='r');
xlabel('loop');
ylabel('ems error');
end
(2)Perturb.m
%Disturbance function: Generates random perturbations based on iteration within loop and specified
% - Params:
% - size:Size of the model
% - P:Number of disturbances
% - k:Number of iterations
% - I:Iteration upper bound
% - Return:
% - R_p:Perturbation rotation matrix [3,3,P+1]
% - T_p:Perturbation translation matrix [P+1,3]
function [ R_p, T_p ] = perturb( size,P,k,I )
%1.Initial translation disturbance:10% of the model size
T_p = normrnd(0,(0.1*size*((I+1-k)/I)),[P,3]); %[P,3]
%2.Initial perturbation number of rotation:10 ° normal distribution(Right hand coordinate system)
%Around x
a = normrnd(0,(10*((I+1-k)/I)),[1,P]); %[1,P]
%Around y
b = normrnd(0,(10*((I+1-k)/I)),[1,P]);
%Around z
c = normrnd(0,(10*((I+1-k)/I)),[1,P]);
%3.convert to radians
a = a.*(pi()/180);
b = b.*(pi()/180);
c = c.*(pi()/180);
%4.initialize R_p
R_p = zeros(3,3,P);
%5.Generate P disturbances respectively (matlab subscript starts from 1)
for j = 1:P
%rotation matrix about x
r1 = [1,0,0; 0, cos(a(j)), -sin(a(j)); 0, sin(a(j)), cos(a(j))];
%rotation matrix about y
r2 = [cos(b(j)), 0, sin(b(j)); 0,1,0; -sin(b(j)), 0, cos(b(j))];
%rotation matrix about z
r3 = [cos(c(j)), -sin(c(j)), 0; sin(c(j)), cos(c(j)), 0; 0,0,1];
%Perturbation Rotation
R_p(:,:,j) = r1*r2*r3;
end
%P+1:Transformation without disturbance
R_p(:,:,P+1) = eye(3);
T_p(P+1,:) = [0,0,0];
end
(3)solveicp.m
%Function takes in A and B clusters, registers A to B using ICP, returns resultant transformation matrix and error calculation
% - Params:
% - A:initial point cloud [n,3]
% - B:target point cloud [m,3]
% - Return:
% - TR:solve rotation matrix [1,3]
% - RO:solve translation matrix [3,3]
% - e:registers error
function [ R,T,e ] = solveicp( A,B,Mdl,R_0,T_0 )
%1.initialize variable parameters
k = 1;
error = 0.0001;
Iterator = 50;
R = R_0;
T = T_0;
%2.initial:kdtree algorithm to find the nearest point pair of two point clouds
trans = transform(A,R,T);
e = calculateCost(trans,B,Mdl);
%3.ICP loop
while e > error && k <= Iterator
%find the nearest point
idx = knnsearch(Mdl,trans);
NN = B(idx,:);
%svd solve: optimal matching matrix of two point clouds in current state
[T_k, R_k] = solvesvd(trans,NN);
%Cumulative transformation T=[1,3] R=[3,3]
T = (R_k*T')'+T_k;
R = R_k*R;
trans = transform(A,R,T);
%Update error E
e = sum(sqrt(sum((NN-trans).^2,2)))/length(trans);
k = k+1;
end
end
(4)solvesvd.m
%function: svd solve yo find T and R between point set B and it nearest neighbour set
% - Params:
% - B:initial point clouds [n,3]
% - NN: target nearest neighbour point clouds [n,3]
% - Return:
% - T:solve optimal translation matrix [1,3]
% - R:solve optimal rotation matrix [3,3]
function [ T, R ] = solvesvd( B, NN )
num = length(B);
%1.Seeking centroid
cen_B = sum(B)/num;
cen_NN = sum(NN)/num;
%2.Decentralization:Set centroids as reference origin for both clusters
B2 = B-repmat(cen_B,num,1);
NN2 = NN-repmat(cen_NN,num,1);
%3.svd decompose:[U,S,V] = svd(A) ==> A = U*S*V'
H = B2'*NN2;
[U,~,V] = svd(H);
R = V*diag([1 1 sign(det(V*U'))])*U';
T = cen_NN' - R*cen_B';
T = T';
end
注意:上图中蓝色为初始稀疏点云,黑色为配准后的稀疏点云结果