马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
×
原理
法线的界说:法线是指垂直于一个平面的法向量。
对于点云(非固定平面)数据,怎样盘算法线?告急有两种方法:
外貌网格技能:从获取的数据中提取一个根本的外貌,然后从网格中盘算外貌的法线。
近似值推断法:直接从点数据会合推断外貌法线。其核心头脑是:为每个点盘算其外貌的切平面,这通常通过最小二乘平面拟合来估计。
那什么是最小二乘平面拟合呢?
给定一组三维数据点,找到最佳拟合平面 Z = AX + BY + C,使得全部数据点到该平面的垂直隔断(残差)的平方和最小。
在点云处置处罚中,通常利用主因素分析(PCA) 来实现平面拟合。PCA的核心目标是通过降维和去干系,找到数据变革的告急方向。
PCA的实现流程先对点云去中央化,然后再盘算斜方差矩阵,末了计盘算协方差矩阵的特性向量,此中最小特性值对应的特性向量就是法向量。
算法实现
协方差矩阵盘算:- #include <pcl/common/centroid.h> // 协方差矩阵计算的头文件
- Eigen::Vector4f centroid; // 存储均值 (x, y, z, 1)
- Eigen::Matrix3f covariance; // 3x3协方差矩阵
- // 核心一行:计算均值和协方差矩阵
- int points_used = pcl::computeMeanAndCovarianceMatrix(*cloud, covariance, centroid);
复制代码 特性向量求解:- Eigen::Vector3f eigenvalues = solver.eigenvalues(); //特征值
- Eigen::Matrix3f eigenvectors = solver.eigenvectors(); // 列向量对应特征值
- Eigen::Vector3f normal = solver.eigenvectors().col(0); // 法向量赋值
复制代码 全部点盘算法向量:- for each point p in cloud:
- // 1. 找到 p 的邻域点集(半径内或 K 个最近邻)
- neighbors = tree->radiusSearch(p, radius);
- // 2. 如果邻域点数不足(如 < 3),法向量设为 NaN 或跳过
- if (neighbors.size() < 3) continue;
- // 3. 计算邻域重心
- Eigen::Vector4f centroid;
- computeCentroid(neighbors, centroid);
- // 4. 构建协方差矩阵
- Eigen::Matrix3f cov = Eigen::Matrix3f::Zero();
- for each neighbor in neighbors:
- Eigen::Vector3f centered = neighbor - centroid;
- cov += centered * centered.transpose();
- cov /= neighbors.size(); // 或除以 (n-1)
- // 5. 特征分解
- Eigen::SelfAdjointEigenSolver<Matrix3f> solver(cov);
- Eigen::Vector3f normal = solver.eigenvectors().col(0); // 最小特征值对应特征向量
- // 6. 法向量定向(使指向传感器方向)
- if (normal.dot(p - viewpoint) < 0) normal = -normal;
- // 7. 存储法向量
- cloud_normals[i] = normal;
复制代码 但在PCL中,有封装好了完备的实现函数pcl::NormalEstimation,因此只必要几行代码就能实现法向量盘算- #include <pcl/features/normal_3d.h> // 法向量估计头文件
- // 创建法向量估计对象
- pcl::NormalEstimation<pcl::PointXYZ, pcl::Normal> ne;
- ne.setInputCloud(cloud); // 设置输入点云
- ne.setSearchMethod(tree); // 设置邻域搜索方式
- //设置搜索半径(0.03 米)
- ne.setRadiusSearch(0.03);
- //创建存储法向量的点云对象,并执行计算
- pcl::PointCloud<pcl::Normal>::Ptr cloud_normals(new pcl::PointCloud<pcl::Normal>);
- ne.compute(*cloud_normals); // 核心计算:遍历每个点,估计法向量
复制代码 完备代码
[code]#include #include #include #include int main() { // 1. 加载点云 pcl: ointCloud: tr cloud(new pcl: ointCloud); if (pcl::io::loadPCDFile("02.pcd", *cloud) == -1) { std::cerr |