C++使用最小二乘法计算点云法向量原理以及全部代码

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

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

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

m i n ∑ i = 1 n d i s t ( p i , ∏ ) min \sum_{i=1}^{n}dist(p_i, \prod) mini=1ndist(pi,) = m i n ∑ i = 1 n ( a 0 x i + a 1 y i + a 2 z i + 1 ) 2 min \sum_{i=1}^{n}(a_0x_i + a_1y_i+a_2z_i + 1 )^2 mini=1n(a0xi+a1yi+a2zi+1)2

S = m i n ∑ i = 1 n ( a 0 x + a 1 y + a 2 z + 1 ) 2 S = min \sum_{i=1}^{n}(a_0x + a_1y+a_2z + 1 )^2 S=mini=1n(a0x+a1y+a2z+1)2
要使得S最小,则 ∂ S ∂ a 0 = 0 , ∂ S ∂ b 0 = 0 , ∂ S ∂ c 0 = 0 \frac{\partial S}{\partial a_0} = 0, \frac{\partial S}{\partial b_0} = 0, \frac{\partial S}{\partial c_0} = 0 a0S=0,b0S=0,c0S=0
即:
∂ S ∂ a 0 = ∑ i = 1 n 2 ( a 0 x + a 1 y + a 2 z + 1 ) x i = 0 \frac{\partial S}{\partial a_0} = \sum_{i=1}^n2(a_0x + a_1y+a_2z + 1 )x_i = 0 a0S=i=1n2(a0x+a1y+a2z+1)xi=0
∂ S ∂ b 0 = ∑ i = 1 n 2 ( a 0 x + a 1 y + a 2 z + 1 ) y i = 0 \frac{\partial S}{\partial b_0} = \sum_{i=1}^n2(a_0x + a_1y+a_2z + 1 )y_i = 0 b0S=i=1n2(a0x+a1y+a2z+1)yi=0
∂ S ∂ c 0 = ∑ i = 1 n 2 ( a 0 x + a 1 y + a 2 z + 1 ) z i = 0 \frac{\partial S}{\partial c_0} = \sum_{i=1}^n2(a_0x + a_1y+a_2z + 1 )z_i = 0 c0S=i=1n2(a0x+a1y+a2z+1)zi=0
上式经过变形可得(请读者自行下去推导)
[ ∑ x i 2 ∑ x i y y ∑ x i z i ∑ x i y i ∑ y i 2 ∑ y i z i ∑ x i z i ∑ y i z i ∑ z i 2 ] ⋅ [ a 0 b 0 c 0 ] + [ ∑ x i ∑ y i ∑ z i ] = 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} = 0 xi2xiyixizixiyyyi2yizixiziyizizi2a0b0c0+xiyizi=0
A = [ x 1 y 1 z 1 x 2 y 2 z 2 ⋮ ⋮ ⋮ x n y n z n ] ; − b = [ 1 1 ⋮ 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
则有: A T A [ a 0 b 0 c 0 ] − A T b = 0 A^{T}A \begin{bmatrix} a_0\\ b_0 \\ c_0\\ \end{bmatrix} - A^Tb = 0 ATAa0b0c0ATb=0
由于 A T A A^TA ATA可逆
所以 [ a 0 b 0 c 0 ] = ( A T A ) − 1 A T b \begin{bmatrix} a_0\\ b_0 \\ c_0\\ \end{bmatrix} = (A^TA)^{-1}A^Tb a0b0c0=(ATA)1ATb
由高等数学等知识可知: [ a 0 b 0 c 0 ] 即 为 法 向 量 \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());
	}

}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值