赞
踩
本文主要介如何用SVD分解法和最小二乘法拟合平面点云,包含原理推导和代码
已知若干三维点坐标
(
x
i
,
y
i
,
z
i
)
(x_{i},y_{i},z_{i})
(xi,yi,zi),拟合出平面方程
a
x
+
b
y
+
c
z
=
d
ax+by+cz=d
ax+by+cz=d (1)
约束条件为
a
2
+
b
2
+
c
2
=
1
a^{2}+b^{2}+c^{2}=1
a2+b2+c2=1 (2)
目标为使该平面到所有点的距离之和最小
所有点的平均坐标为 ( x ˉ , y ˉ , z ˉ ) (\bar{x},\bar{y},\bar{z}) (xˉ,yˉ,zˉ),则由先验知识(拟合平面一定经过离散点的质心),可以到以下方程 a x ˉ + b y ˉ + c z ˉ = d . a\bar{x}+b\bar{y}+c\bar{z}=d. axˉ+byˉ+czˉ=d.(3)
式(1)与式(3)相减,得 a ( x i − x ˉ ) + b ( y i − y ˉ ) + c ( z i − z ˉ ) = 0 a(x_{i}-\bar{x})+b(y_{i}-\bar{y})+c(z_{i}-\bar{z})=0 a(xi−xˉ)+b(yi−yˉ)+c(zi−zˉ)=0(4)
将所有点数据带入式4,整理可以得到矩阵
A
=
[
x
1
−
x
ˉ
,
y
1
−
y
ˉ
,
z
1
−
z
ˉ
x
2
−
x
ˉ
,
y
2
−
y
ˉ
,
z
2
−
z
ˉ
x
3
−
x
ˉ
,
y
3
−
y
ˉ
,
z
3
−
z
ˉ
.
.
.
x
n
−
x
ˉ
,
y
n
−
y
ˉ
,
z
n
−
z
ˉ
]
A=
理想情况下所有点都在平面上,式(5)成立;实际情况下有部分点在平面外,拟合的目的为平面距离所有点的距离之和尽量小,所以目标函数为 m i n ∥ A X ∥ min\left \| AX \right \| min∥AX∥ (6)
约束条件为 ∥ X ∥ = 1 \left \| X \right \|=1 ∥X∥=1 (7)
若A可做奇异值分解: A = U D V T A = UDV^{T} A=UDVT (8)
其中,D是对角矩阵,U和V均为酉矩阵。
则 ∥ A X ∥ = ∥ U D V T X ∥ = ∥ D V T X ∥ \left \| AX \right \|=\left \| UDV^{T}X \right \|=\left \| DV^{T}X \right \| ∥AX∥= UDVTX = DVTX (9)
其中为列矩阵,并且 ∥ V T X ∥ = ∥ X ∥ = 1 \left \| V^{T}X \right \|=\left \|X \right \|=1 VTX =∥X∥=1 (10)
因为D的对角元素为奇异值,假设最后一个对角元素为最小奇异值,则当且仅当
V
T
X
=
[
0
0
0
.
.
.
1
]
V^{T}X=
此时
X
=
V
[
0
0
0
.
.
.
1
]
=
[
v
1
v
2
v
3
.
.
.
v
n
]
[
0
0
0
.
.
.
1
]
=
v
n
X=V
所以,目标函数(6)在约束条件(7)下的最优解为 X = ( a , b , c ) = ( v n , 1 , v n , 2 , v n , 3 ) X=(a,b,c)=(v_{n,1},v_{n,2},v_{n,3}) X=(a,b,c)=(vn,1,vn,2,vn,3) (13)
综上:对矩阵A做奇异值分解,最小奇异值对应的特征向量就是拟合平面的系数向量。
1 读取点云数据
2 求解点云质心(x0,y0,z0)
3 将原始点云坐标减去点云质心,构建矩阵A
4 对矩阵A进行SVD分解,A = U * S * VT
5 V最后一列对应为(A,B,C); D=-(Ax0+By0+C*z0)
void fitPlane(cv::Mat &points,cv::Mat &plane)
//points输入点云
//plane输出平面方程系数
{
cv::Mat centor = cv::Mat::zeros(1,points.cols,CV_32FC1);
for(int i =0;i < points.cols;++i)
{
for(int j =0;j < points.rows;++j)
{
centor.at<float>(0,i) = centor.at<float>(0,i) + points.at<float>(j,i);
}
centor.at<float>(0.i) = centor.at<float>(0.i) / points.rows;
}
cv::Mat pointC = cv::Mat::ones(points.rows,points.cols,CV_32FC1);
for(int i =0;i < points.cols;++i)
{
for(int j =0;j < points.rows;++j)
{
pointC.at<float>(j,i) = points.at<float>(j,i) - centor.at<float>(0.i);
}
}
//SVD分解
cv::Mat A,W,U,V;
//构建奇异值矩阵(gemm矩阵相乘)
SVD::compute(pointC,W,U,V);
// 提取最小奇异值和相应的向量
double min_sing_val = W.at<double>(svd.w.rows-1); // 最后一个元素为最小奇异值
Mat min_sing_vec = V.row(V.rows-1); // 最后一行为最小奇异值对应的右奇异向量
plane[0]=min_sing_vec[0]
plane[1]=min_sing_vec[1]
plane[2]=min_sing_vec[2]
plane[4]= -(plane[0]*centor.at<float>(0,0)+plane[1]*centor.at<float>(0,1)+plane[2]*centor.at<float>(0,2)
}
void FitPlaneSVD::compute()
{
// 1、计算质心
Eigen::RowVector3d centroid = m_cloud.colwise().mean();
// 2、去质心
Eigen::MatrixXd demean = m_cloud;
demean.rowwise() -= centroid;
// 3、SVD分解求解协方差矩阵的特征值特征向量
Eigen::JacobiSVD<Eigen::MatrixXd> svd(demean, Eigen::ComputeThinU | Eigen::ComputeThinV);
Eigen::Matrix3d V = svd.matrixV();
Eigen::MatrixXd U = svd.matrixU();
Eigen::Matrix3d S = U.inverse() * demean * V.transpose().inverse();
// 5、平面的法向量a,b,c
Eigen::RowVector3d normal;
normal << V(0,2), V(1,2), V(2,2);
// 6、原点到平面的距离d
double d = -normal * centroid.transpose();
// 7、获取拟合平面的参数a,b,c,d和质心x,y,z。
m_planeparameters << normal, d, centroid
}
找到一个平面
Z=Ax+By+C
根据最小二乘法,使各个点到这个平面的距离最近:
S=∑(Axi + Byi + C - Zi) 2
void CaculateLaserPlane(std::vector<cv::Point3f> Points3ds,vector<double> &res)
{
//最小二乘法拟合平面
//获取cv::Mat的坐标系以纵向为x轴,横向为y轴,而cvPoint等则相反
//系数矩阵
cv::Mat A = cv::Mat::zeros(3, 3, CV_64FC1);
//
cv::Mat B = cv::Mat::zeros(3, 1, CV_64FC1);
//结果矩阵
cv::Mat X = cv::Mat::zeros(3, 1, CV_64FC1);
double x2 = 0, xiyi = 0, xi = 0, yi = 0, zixi = 0, ziyi = 0, zi = 0, y2 = 0;
for (int i = 0; i < Points3ds.size(); i++)
{
x2 += (double)Points3ds[i].x * (double)Points3ds[i].x;
y2 += (double)Points3ds[i].y * (double)Points3ds[i].y;
xiyi += (double)Points3ds[i].x * (double)Points3ds[i].y;
xi += (double)Points3ds[i].x;
yi += (double)Points3ds[i].y;
zixi += (double)Points3ds[i].z * (double)Points3ds[i].x;
ziyi += (double)Points3ds[i].z * (double)Points3ds[i].y;
zi += (double)Points3ds[i].z;
}
A.at<double>(0, 0) = x2;
A.at<double>(1, 0) = xiyi;
A.at<double>(2, 0) = xi;
A.at<double>(0, 1) = xiyi;
A.at<double>(1, 1) = y2;
A.at<double>(2, 1) = yi;
A.at<double>(0, 2) = xi;
A.at<double>(1, 2) = yi;
A.at<double>(2, 2) = Points3ds.size();
B.at<double>(0, 0) = zixi;
B.at<double>(1, 0) = ziyi;
B.at<double>(2, 0) = zi;
//计算平面系数
X = A.inv() * B;
//A
res.push_back(X.at<double>(0, 0));
//B
res.push_back(X.at<double>(1, 0));
//Z的系数为1
res.push_back(1.0);
//C
res.push_back(X.at<double>(2, 0));
return;
}
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。