最小二乘计算点云法向量原理

对于任意一点 p(x,y,z)p(x, y, z)p(x,y,z)查找其一定领域内的点{pi}\{{p_i}\}{pi}

求得一个平面 ∏:a0x+a1y+a2z+1=0\prod: a_0x + a_1y+a_2z + 1 = 0:a0x+a1y+a2z+1=0使得其到平面距离的平方和最小,即:

min∑i=1ndist(pi,∏)min \sum_{i=1}^{n}dist(p_i, \prod)mini=1ndist(pi,) = min∑i=1n(a0xi+a1yi+a2zi+1)2min \sum_{i=1}^{n}(a_0x_i + a_1y_i+a_2z_i + 1 )^2mini=1n(a0xi+a1yi+a2zi+1)2

S=min∑i=1n(a0x+a1y+a2z+1)2S = min \sum_{i=1}^{n}(a_0x + a_1y+a_2z + 1 )^2S=mini=1n(a0x+a1y+a2z+1)2
要使得S最小,则∂S∂a0=0,∂S∂b0=0,∂S∂c0=0\frac{\partial S}{\partial a_0} = 0, \frac{\partial S}{\partial b_0} = 0, \frac{\partial S}{\partial c_0} = 0a0S=0,b0S=0,c0S=0
即:
∂S∂a0=∑i=1n2(a0x+a1y+a2z+1)xi=0\frac{\partial S}{\partial a_0} = \sum_{i=1}^n2(a_0x + a_1y+a_2z + 1 )x_i = 0a0S=i=1n2(a0x+a1y+a2z+1)xi=0
∂S∂b0=∑i=1n2(a0x+a1y+a2z+1)yi=0\frac{\partial S}{\partial b_0} = \sum_{i=1}^n2(a_0x + a_1y+a_2z + 1 )y_i = 0b0S=i=1n2(a0x+a1y+a2z+1)yi=0
∂S∂c0=∑i=1n2(a0x+a1y+a2z+1)zi=0\frac{\partial S}{\partial c_0} = \sum_{i=1}^n2(a_0x + a_1y+a_2z + 1 )z_i = 0c0S=i=1n2(a0x+a1y+a2z+1)zi=0
上式经过变形可得(请读者自行下去推导)
[∑xi2∑xiyy∑xizi∑xiyi∑yi2∑yizi∑xizi∑yizi∑zi2]⋅[a0b0c0]+[∑xi∑yi∑zi]=0\begin{bmatrix} \sum x_i^2 &\sum x_iy_y &\sum x_iz_i \\ \sum x_iy_i &\sum y_i^2 &\sum y_iz_i \\ \sum x_iz_i&\sum y_iz_i &\sum z_i^2 \\ \end{bmatrix} \cdot \begin{bmatrix} a_0\\ b_0 \\ c_0 \\ \end{bmatrix} + \begin{bmatrix} \sum x_i\\ \sum y_i \\ \sum z_i \\ \end{bmatrix} = 0xi2xiyixizixiyyyi2yizixiziyizizi2a0b0c0+xiyizi=0
A=[x1y1z1x2y2z2⋮⋮⋮xnynzn];−b=[11⋮1] A = \begin{bmatrix} x_1 &y_1&z_1 \\ x_2 &y_2&z_2\\ \vdots &\vdots &\vdots \\ x_n &y_n&z_n\\ \end{bmatrix}; -b = \begin{bmatrix} 1 \\ 1\\ \vdots \\ 1\\ \end{bmatrix}A=x1x2xny1y2ynz1z2zn;b=111
则有:ATA[a0b0c0]−ATb=0 A^{T}A \begin{bmatrix} a_0\\ b_0 \\ c_0\\ \end{bmatrix} - A^Tb = 0ATAa0b0c0ATb=0
由于ATAA^TAATA可逆
所以[a0b0c0]=(ATA)−1ATb \begin{bmatrix} a_0\\ b_0 \\ c_0\\ \end{bmatrix} = (A^TA)^{-1}A^Tba0b0c0=(ATA)1ATb
由高等数学等知识可知:[a0b0c0]即为法向量\begin{bmatrix} a_0\\ b_0 \\ c_0\\ \end{bmatrix} 即为法向量a0b0c0

代码

void lsNormal(const PointCloud<PointXYZ>::Ptr& cloud, const int& k_neighbors, PointCloud<Normal>::Ptr& normals)
{
	//1. build kdtree index
	KdTreeFLANN<PointXYZ>::Ptr kdtree(new KdTreeFLANN<PointXYZ>);
	kdtree->setInputCloud(cloud);//set input cloud

	if (normals == NULL)
		normals = PointCloud<Normal>::Ptr(new PointCloud<Normal>);
	if (!normals->empty())
		normals->resize(0);

	for (const auto& point: * cloud)
	{
		// 3.1 set k nearest neighbors
		PointXYZ searchPoint = point;
		vector<int> KNNIndices(k_neighbors);//an int vector to store the k nearest neighbor of points
		vector<float> KNNSquareDistance(k_neighbors);//a float vector to store the distance of k nearest neighbor

		//3.2 least square for each point and calculate the normal vectors
		if (kdtree->nearestKSearch(searchPoint, k_neighbors, KNNIndices, KNNSquareDistance) > 0)
		{
			//if the search result > 0, search succeed
			//build a new point cloud, store the k nearest neighbor of current point
			PointCloud<PointXYZ>::Ptr neighborsPoints(new PointCloud<PointXYZ>);
			pcl::copyPointCloud(*cloud, KNNIndices, *neighborsPoints);

			size_t i = 0;
			MatrixXd A = MatrixXd::Random(k_neighbors, 3);
			MatrixXd b = MatrixXd::Random(k_neighbors, 1);
			MatrixXd X = MatrixXd::Random(3, 1);
			//A, b, X is correspond to A*X = b
			for (int idx = 0; idx < k_neighbors; idx++)
			{
				A(i, 0) = cloud->points[KNNIndices[idx]].x;
				A(i, 1) = cloud->points[KNNIndices[idx]].x;
				A(i, 2) = cloud->points[KNNIndices[idx]].x;
				b(i, 0) = -1;
				i = i + 1;
			}

			X = (A.transpose() * A).inverse() * A.transpose() * b;

			//Normalize
			double norm_xyz = sqrt(X(0) * X(0) + X(1) * X(1) + X(2) * X(2));
			double nx = X(0) / norm_xyz;
			double ny = X(1) / norm_xyz;
			double nz = X(2) / norm_xyz;
			normals->push_back(Normal(nx, ny, nz));
		}
		else
			normals->push_back(Normal());
	}

}
Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐