PCL学习笔记

内容分享4天前发布
2 0 0

这里写自定义目录标题

通用模块 (common)1.1 PCL 基础数据结构1.1.1 点类型 (Point Types) – PointT 的基石1.1.2 点云类 (pcl::PointCloud<PointT>) – 数据的容器
1.2 获取点云极值 (getMinMax3D)1.3 主成分分析 (PCA)1.4 计算点云质心 (compute3DCentroid)1.5 转换点类型和坐标变换 (copyPointCloud 和 transformPointCloud)
输入输出 (I/O)滤波处理 (filters)3.1 体素网格滤波 (Voxel Grid Filter)3.2 统计离群点去除 (Statistical Outlier Removal, SOR)3.3 半径离群点去除 (Radius Outlier Removal, ROR)3.4 直通滤波 (PassThrough Filter)3.5 移动最小二乘法 (Moving Least Squares, MLS)3.6 滤波流程建议
特征提取 (features)4.1
分割算法 (segmentation)5.1 基于采样一致性的分割 (RANSAC)5.2 欧几里得聚类提取 (Euclidean Cluster Extraction)5.3 区域生长分割 (Region Growing Segmentation)5.4 超体素聚类 (VCCS)5.5 实际分割流程建议
点云配准 (registration)6.1 精配准的基石:迭代最近点算法 (ICP) 及其变种6.2 ICP 的主要变体 (在PCL中均有实现)6.3 粗配准:采样一致性初始配准 (SAC-IA)6.4 其他重要配准算法6.5 实际配准流程建议
表面重建 (surface)7.1 泊松重建 (Poisson Surface Reconstruction)7.2 贪婪投影三角化 (Greedy Projection Triangulation, GP3)7.3 移动最小二乘 (Moving Least Squares, MLS)7.4 实际表面重建流程建议
关键点检测 (keypoints)8.1 使用关键点检测的一般步骤及代码片段
可视化工具 (visualization)其他工具

模块名称 核心功能 典型算法/组件
通用模块 (common) 提供基础数据结构与工具 点云数据类型(如 PointXYZ, PointXYZRGB, Normal)、向量计算、坐标转换、PCA
输入输出 (I/O) 点云数据的读取、写入和转换 支持格式:PCD, PLY, OBJ 等;与深度相机(如 Kinect, RealSense)交互
滤波处理 (filters) 对点云进行降噪、简化、平滑等预处理 体素网格降采样、统计离群点去除、半径离群点去除、直通滤波、卷积滤波
特征提取 (features) 从点云中提取几何特征 法线估计、曲率计算、特征描述符(如 FPFH, VFH, SHOT)
分割算法 (segmentation) 将点云分割成不同的区域或对象 基于采样一致性的分割(如平面模型、圆柱体模型)、区域生长分割、欧几里得聚类分割、最小切割分割
点云配准 (registration) 将多个点云数据对齐到同一坐标系中 ICP (Iterative Closest Point)、NDT (Normal Distributions Transform)、特征匹配
表面重建 (surface) 从点云数据重建三维网格模型或曲面 贪婪投影三角化(GP3)、移动最小二乘法(MLS)平滑、泊松重建、凸包重建
关键点检测 (keypoints) 检测点云中的特征点(关键点) ISS3D、SIFT3D、Harris3D
可视化工具 (visualization) 点云数据的可视化交互 PCLVisualizer、直方图可视化、多种渲染样式(点、网格、自定义)
其他工具 Kd树(pcl::search::KdTree)、八叉树(pcl::octree)用于高效空间搜索;范围图像(range_image)处理;等等。

通用模块 (common)

1.1 PCL 基础数据结构

1.1.1 点类型 (Point Types) – PointT 的基石

PointXYZ:最简单的 3D 点,包含 x, y, z 坐标。


struct PointXYZ
{
    float x;
    float y;
    float z;
};


cpp
运行1234567

PointXYZI:带强度的 3D 点,适用于激光雷达数据。


struct PointXYZI
{
    float x;
    float y;
    float z;
    float intensity;
};


cpp
运行12345678

PointXYZRGB:带 RGB 颜色信息的 3D 点。


struct PointXYZRGB
{
    float x;
    float y;
    float z;
    float rgb; // Packed as uint32_t (r|g|b)
};


cpp
运行12345678

PointNormal:结合坐标和法向量的点类型。


struct PointNormal
{
    float x;
    float y;
    float z;
    float normal_x;
    float normal_y;
    float normal_z;
    float curvature;//曲率
};


cpp
运行
PCL学习笔记1234567891011

1.1.2 点云类 (pcl::PointCloud) – 数据的容器

PCL学习笔记


// 创建一个存储PointXYZ点的点云
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);

// 设置点云属性
cloud->width = 100;    // 点云中有100个点
cloud->height = 1;     // 无序点云
cloud->is_dense = true;// 假设没有无效点
cloud->points.resize(cloud->width * cloud->height); // 分配内存

// 像操作数组一样操作点
for (auto& point : cloud->points) {
    point.x = 1.0f;
    point.y = 2.0f;
    point.z = 3.0f;
}

// 添加一个新点
pcl::PointXYZ p;
p.x = 4.0; p.y = 5.0; p.z = 6.0;
cloud->push_back(p); // 现在cloud->width变为101

cpp
运行
PCL学习笔记1234567891011121314151617181920

1.2 获取点云极值 (getMinMax3D)

PCL学习笔记


#include <pcl/common/common.h>
// ...
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);
// ... 假设cloud已经填充了数据

pcl::PointXYZ min_pt, max_pt; // 用于存储最小和最大点坐标
pcl::getMinMax3D(*cloud, min_pt, max_pt);

std::cout << "Min point: " << min_pt.x << ", " << min_pt.y << ", " << min_pt.z << std::endl;
std::cout << "Max point: " << max_pt.x << ", " << max_pt.y << ", " << max_pt.z << std::endl;

cpp
运行
PCL学习笔记12345678910

1.3 主成分分析 (PCA)

PCL学习笔记
PCL学习笔记
PCL学习笔记


#include <pcl/common/pca.h>
// ...
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);
// ... 假设cloud已经填充了数据

// 创建PCA对象,计算类型为PointXYZ,计算时使用协方差矩阵
pcl::PCA<pcl::PointXYZ> pca;
pca.setInputCloud(cloud);

// 计算并获取主成分
Eigen::Vector3f eigenvalues = pca.getEigenValues();      // 获取特征值 (λ1, λ2, λ3)
Eigen::Matrix3f eigenvectors = pca.getEigenVectors();    // 获取特征向量 (v1, v2, v3)
Eigen::Vector4f centroid = pca.getMean();                // 获取质心

// 例如,获取最小的主方向(常用于平面法线估计)
Eigen::Vector3f normal = eigenvectors.col(2); 
normal.normalize(); // 通常将其归一化为单位向量

// 将点云投影到主成分空间
pcl::PointCloud<pcl::PointXYZ>::Ptr projected_cloud(new pcl::PointCloud<pcl::PointXYZ>);
pca.project(*cloud, *projected_cloud); 

cpp
运行
PCL学习笔记123456789101112131415161718192021

1.4 计算点云质心 (compute3DCentroid)

PCL学习笔记


#include <pcl/common/centroid.h>
// ...
Eigen::Vector4f centroid; // 四维向量,第四位为1,用于齐次坐标变换
pcl::compute3DCentroid(*cloud, centroid);
std::cout << "Centroid is: " << centroid[0] << ", " << centroid[1] << ", " << centroid[2] << std::endl;

cpp
运行12345

1.5 转换点类型和坐标变换 (copyPointCloud 和 transformPointCloud)

PCL学习笔记


#include <pcl/common/transforms.h>
// ...
// 1. 复制点云(例如,从XYZ复制到XYZRGB,颜色信息为0)
pcl::PointCloud<pcl::PointXYZRGB>::Ptr copy_cloud(new pcl::PointCloud<pcl::PointXYZRGB>);
pcl::copyPointCloud(*original_cloud, *copy_cloud);

// 2. 变换点云
Eigen::Affine3f transform = Eigen::Affine3f::Identity();
transform.translation() << 2.5, 0.0, 0.0; // 沿X轴平移2.5米
float theta = M_PI / 4; // 旋转45度
transform.rotate(Eigen::AngleAxisf(theta, Eigen::Vector3f::UnitZ())); // 绕Z轴旋转

pcl::transformPointCloud(*cloud, *transformed_cloud, transform);

cpp
运行
PCL学习笔记12345678910111213

输入输出 (I/O)

滤波处理 (filters)

滤波类别 核心目的 典型算法
① 下采样 (Downsampling) 减少点云数据量,降低计算复杂度,同时保持整体形状。 体素网格滤波 (VoxelGrid)
② 离群点去除 (Outlier Removal) 剔除噪声点,例如测量误差产生的孤立点。 统计离群点去除 (StatisticalOutlierRemoval)、半径离群点去除 (RadiusOutlierRemoval)
③ 平滑滤波 (Smoothing) 减少点云表面的高频噪声,使其更平滑。 移动最小二乘法 (Moving Least Squares, MLS)
④ 基于条件的滤波 根据自定义规则过滤点云。 直通滤波 (PassThrough)
⑤ 其他滤波 如提取索引、投影等。 投影滤波 (ProjectInliers)

3.1 体素网格滤波 (Voxel Grid Filter)

PCL学习笔记


#include <pcl/filters/voxel_grid.h>
// ...
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_filtered(new pcl::PointCloud<pcl::PointXYZ>);

// 创建滤波对象
pcl::VoxelGrid<pcl::PointXYZ> sor;
sor.setInputCloud(cloud);           // 设置输入点云
sor.setLeafSize(0.01f, 0.01f, 0.01f); // 设置体素大小 (1cm的立方体)
sor.filter(*cloud_filtered);        // 执行滤波,结果存入cloud_filtered

cpp
运行
PCL学习笔记12345678910

3.2 统计离群点去除 (Statistical Outlier Removal, SOR)

PCL学习笔记


#include <pcl/filters/statistical_outlier_removal.h>
// ...
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_filtered(new pcl::PointCloud<pcl::PointXYZ>);

pcl::StatisticalOutlierRemoval<pcl::PointXYZ> sor;
sor.setInputCloud(cloud);
sor.setMeanK(50);           // 对每个点,分析50个近邻点
sor.setStddevMulThresh(1.0); // 阈值设为 μ + 1.0 * σ
sor.filter(*cloud_filtered);

cpp
运行123456789

3.3 半径离群点去除 (Radius Outlier Removal, ROR)

PCL学习笔记


#include <pcl/filters/radius_outlier_removal.h>
// ...
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_filtered(new pcl::PointCloud<pcl::PointXYZ>);

pcl::RadiusOutlierRemoval<pcl::PointXYZ> ror;
ror.setInputCloud(cloud);
ror.setRadiusSearch(0.05);    // 设置搜索半径为5cm
ror.setMinNeighborsInRadius(5); // 如果一个点周围5cm内少于5个点,则被剔除
ror.filter(*cloud_filtered);

cpp
运行123456789

3.4 直通滤波 (PassThrough Filter)

PCL学习笔记


#include <pcl/filters/passthrough.h>
// ...
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_filtered(new pcl::PointCloud<pcl::PointXYZ>);

pcl::PassThrough<pcl::PointXYZ> pass;
pass.setInputCloud(cloud);
pass.setFilterFieldName("z");        // 设置过滤字段为Z轴
pass.setFilterLimits(0.5, 2.0);      // 设置允许的范围
// pass.setFilterLimitsNegative(true); // 如果启用,则保留范围之外的点
pass.filter(*cloud_filtered);

cpp
运行
PCL学习笔记12345678910

3.5 移动最小二乘法 (Moving Least Squares, MLS)

PCL学习笔记


#include <pcl/surface/mls.h>
// ...
pcl::PointCloud<pcl::PointNormal>::Ptr mls_points(new pcl::PointCloud<pcl::PointNormal>); // 输出带法线的点

pcl::MovingLeastSquares<pcl::PointXYZ, pcl::PointNormal> mls;
mls.setInputCloud(cloud);
mls.setSearchRadius(0.03); // 设置搜索拟合曲面所用的半径
mls.setPolynomialOrder(2); // 设置拟合多项式的阶数,2次通常足够
mls.setComputeNormals(true); // 设置为true来计算法线

mls.process(*mls_points); // 执行重采样和平滑

cpp
运行
PCL学习笔记1234567891011

3.6 滤波流程建议

在实际应用中,通常组合使用多种滤波器,一个典型的处理流程可能是:

**直通滤波 (PassThrough):**粗略裁剪出感兴趣的大区域(如桌面以上、路面以上)。**离群点去除 (SOR/ROR):**去除明显的噪声点。**下采样 (VoxelGrid):**降低数据量,提高后续处理速度。**(可选) 平滑 (MLS):**如果需要高质量的表面和法线,进行平滑处理。

特征提取 (features)

PCL(Point Cloud Library)中的特征提取是点云处理的核心环节,用于从原始点云中捕获关键几何信息,为后续的识别、分割、配准等任务提供基础。由于搜索结果中的相关信息有限,我会结合自己的知识,用一个表格来汇总 PCL 中主要的特征提取方法,并阐述其核心思想、典型算法与适用场景:

特征类型 核心思想与描述 典型算法与描述 主要应用场景
① 单点特征 (Single-point) 描述单个点自身的属性或其极小邻域的几何性质。 法线 (Normal) 和 曲率 (Curvature): 估计点云表面的朝向和弯曲程度。
② 局部特征 (Local Descriptors) 描述一个点与其一定邻域内其他点共同构成的局部表面的几何特征。强调细节区分和对应点匹配。 PFH (Point Feature Histogram): 精确描述局部几何,计算量较大。 物体识别、点云配准(精细)、点云分割
FPFH (Fast Point Feature Histogram): PFH的简化加速版本,效率高,仍保持较好判别性。 实时性要求较高的物体识别与配准
SHOT (Signature of Histograms of Orientations): 将邻域空间划分为网格,在每个网格内计算特征。 细节丰富的表面匹配、具有遮挡的场景
3D Shape Context: 使用在球坐标系中创建的直方图来描述点周围的形状上下文。 物体识别、形状匹配
③ 全局特征 (Global Descriptors) 描述整个点云模型的总体形状和特征。强调整体辨识和分类。 VFH (Viewpoint Feature Histogram): 结合扩展的FPFH和视点方向,对点云整体进行描述。 物体识别与分类
CVFH (Clustered Viewpoint Feature Histogram): VFH的增强版,对遮挡和缺失更鲁棒。 存在遮挡或点云不完整的物体识别
ESF (Ensemble of Shape Functions): 基于十种形状函数集合的全局描述子,对噪声和遮挡鲁棒性好。 物体分类

4.1

分割算法 (segmentation)

点云分割的目标是将点云数据划分为多个独立的、有意义的子集(区域或对象),例如将场景分割成地面、墙壁、家具、行人等。这是从原始数据中提取高级信息的关键步骤。

分割类别 核心思想 典型算法 优点 缺点
① 基于模型拟合的分割 寻找与特定几何模型(如平面、圆柱体、球体)匹配的点集。 采样一致性算法 (SAC),如 RANSAC 对噪声和离群点鲁棒性强;能直接提取模型参数。 只能提取预设的模型;场景复杂时可能失败。
② 基于聚类的分割 基于点之间的欧氏距离或特征相似性进行分组。 欧几里得聚类提取, K-Means, DBSCAN 简单高效;无需预设模型,能分割任意形状的物体。 对密度变化敏感;难以分割接触或重叠的物体。
③ 基于区域生长的分割 从“种子点”开始,根据法线、曲率等特征的相似性逐步合并邻近点。 区域生长分割 能产生连续、光滑的分割区域;结果更符合物体表面。 对初始种子点和参数敏感;计算量相对较大。
④ 基于图论的分割 将点云视为图结构,用最小割/最大流算法进行分割。 最小割分割 (Min-Cut) 能够将物体从背景中“切割”出来,即使它们相连。 需要用户交互(提供前景/背景点);计算复杂度高。
⑤ 超体素分割 将点云过分割成体素化的、形状规则的“超级像素”。 超体素聚类 (VCCS) 为后续处理提供了更高级的基元,大幅减少计算量。 是一种预处理,而非最终分割结果。

5.1 基于采样一致性的分割 (RANSAC)

PCL学习笔记


#include <pcl/ModelCoefficients.h>
#include <pcl/sample_consensus/method_types.h>
#include <pcl/sample_consensus/model_types.h>
#include <pcl/segmentation/sac_segmentation.h>
// ...
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);
// ... 假设cloud已经填充了数据

pcl::ModelCoefficients::Ptr coefficients(new pcl::ModelCoefficients);
pcl::PointIndices::Ptr inliers(new pcl::PointIndices);

// 创建分割对象
pcl::SACSegmentation<pcl::PointXYZ> seg;
seg.setOptimizeCoefficients(true);      // 设置对估计的模型系数进行优化
seg.setModelType(pcl::SACMODEL_PLANE);  // 设置分割模型为平面
seg.setMethodType(pcl::SAC_RANSAC);     // 使用RANSAC算法
seg.setMaxIterations(1000);             // 设置最大迭代次数
seg.setDistanceThreshold(0.01);         // 设置内点到模型的最大距离阈值 (1cm)

seg.setInputCloud(cloud);
seg.segment(*inliers, *coefficients);   // 执行分割

if (inliers->indices.size() == 0) {
    PCL_ERROR("Could not estimate a planar model for the given dataset.");
    return (-1);
}

// 提取平面内点
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_plane(new pcl::PointCloud<pcl::PointXYZ>);
pcl::ExtractIndices<pcl::PointXYZ> extract;
extract.setInputCloud(cloud);
extract.setIndices(inliers);
extract.setNegative(false); // false表示提取内点(平面),true表示提取外点(非平面)
extract.filter(*cloud_plane);

cpp
运行
PCL学习笔记12345678910111213141516171819202122232425262728293031323334

5.2 欧几里得聚类提取 (Euclidean Cluster Extraction)

PCL学习笔记


#include <pcl/kdtree/kd tree.h>
#include <pcl/segmentation/extract_clusters.h>
// ...
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);
// ... 假设cloud已经经过滤波和下采样

// 创建KdTree对象进行搜索
pcl::search::KdTree<pcl::PointXYZ>::Ptr tree(new pcl::search::KdTree<pcl::PointXYZ>);
tree->setInputCloud(cloud);

// 创建聚类提取对象
std::vector<pcl::PointIndices> cluster_indices;
pcl::EuclideanClusterExtraction<pcl::PointXYZ> ec;
ec.setClusterTolerance(0.02); // 设置近邻搜索半径 (2cm)
ec.setMinClusterSize(100);    // 设置一个cluster最少需要100个点
ec.setMaxClusterSize(25000);  // 设置一个cluster最多25000个点
ec.setSearchMethod(tree);
ec.setInputCloud(cloud);
ec.extract(cluster_indices);  // 执行聚类,结果保存在cluster_indices中

// 遍历所有聚类,并提取出来
int j = 0;
for (const auto& cluster : cluster_indices) {
    pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_cluster(new pcl::PointCloud<pcl::PointXYZ>);
    for (const auto& idx : cluster.indices) {
        cloud_cluster->push_back((*cloud)[idx]);
    }
    cloud_cluster->width = cloud_cluster->size();
    cloud_cluster->height = 1;
    cloud_cluster->is_dense = true;
    // 现在cloud_cluster就是其中一个物体的点云
    j++;
}

cpp
运行
PCL学习笔记123456789101112131415161718192021222324252627282930313233

5.3 区域生长分割 (Region Growing Segmentation)

PCL学习笔记


#include <pcl/segmentation/region_growing.h>
// ...
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);
pcl::PointCloud<pcl::Normal>::Ptr normals(new pcl::PointCloud<pcl::Normal>); // 需要先计算法线
// ... 假设cloud和normals已经准备好

// 创建区域生长分割对象
pcl::RegionGrowing<pcl::PointXYZ, pcl::Normal> reg;
reg.setMinClusterSize(50);    // 设置最小cluster点数
reg.setMaxClusterSize(1000000);
reg.setSearchMethod(tree);    // 设置搜索方法
reg.setNumberOfNeighbours(30); // 设置判断用的近邻点数量
reg.setInputCloud(cloud);
reg.setInputNormals(normals); // 输入法线
reg.setSmoothnessThreshold(3.0 / 180.0 * M_PI); // 设置法线角度差阈值 (3度)
reg.setCurvatureThreshold(1.0);                 // 设置曲率阈值

std::vector<pcl::PointIndices> clusters;
reg.extract(clusters);        // 执行分割

cpp
运行
PCL学习笔记12345678910111213141516171819

5.4 超体素聚类 (VCCS)

PCL学习笔记


#include <pcl/segmentation/supervoxel_clustering.h>
// ...
pcl::SupervoxelClustering<pcl::PointXYZRGBA> super(voxel_resolution, seed_resolution);
super.setInputCloud(cloud);
super.setColorImportance(0.2f);   // 设置颜色特征的权重
super.setSpatialImportance(0.4f); // 设置空间距离特征的权重
super.setNormalImportance(1.0f);  // 设置法线特征的权重

std::map<std::uint32_t, pcl::Supervoxel<pcl::PointXYZRGBA>::Ptr> supervoxel_clusters;
super.extract(supervoxel_clusters); // 提取超体素

cpp
运行
PCL学习笔记12345678910

5.5 实际分割流程建议

PCL学习笔记

点云配准 (registration)

点云配准是点云处理中的核心问题,其核心目标是找到将一个点云(源点云)精确对齐到另一个点云(目标点云)的空间变换矩阵(旋转和平移)。这在三维重建、SLAM(同步定位与地图构建)、物体识别等领域至关重要。

配准类别 核心思想 典型算法 优点 缺点 依赖
① 粗配准 (Coarse Registration) 在不依赖初始位姿估计的情况下,找到两个点云之间的大致变换。 采样一致性初始配准 (SAC-IA) 无需初始猜测,自动化程度高。 计算量大;依赖特征描述子的判别性。 特征提取
② 精配准 (Fine Registration) 在粗配准提供的初始估计基础上,进行高精度的迭代优化。 迭代最近点 (ICP) 及其众多变体 精度高,概念简单,实现成熟。 严重依赖初始估计,容易陷入局部最优解。 初始估计

6.1 精配准的基石:迭代最近点算法 (ICP) 及其变种

PCL学习笔记


#include <pcl/point_types.h>
#include <pcl/registration/icp.h>
// ...
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_src(new pcl::PointCloud<pcl::PointXYZ>);
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_tgt(new pcl::PointCloud<pcl::PointXYZ>);
// ... 假设两个点云已经加载

pcl::IterativeClosestPoint<pcl::PointXYZ, pcl::PointXYZ> icp;
icp.setInputSource(cloud_src);   // 设置源点云
icp.setInputTarget(cloud_tgt);   // 设置目标点云
icp.setMaxCorrespondenceDistance(0.05); // 设置最大对应点距离(超出此距离的点对将被忽略)
icp.setMaximumIterations(50);    // 设置最大迭代次数
icp.setTransformationEpsilon(1e-8); // 设置变换增量收敛阈值
icp.setEuclideanFitnessEpsilon(1);   // 设置均方误差收敛阈值

pcl::PointCloud<pcl::PointXYZ> Final;
icp.align(Final);                // 执行配准

std::cout << "Has converged: " << icp.hasConverged() << " score: " << icp.getFitnessScore() << std::endl;
std::cout << "Transformation matrix:
" << icp.getFinalTransformation() << std::endl;

cpp
运行
PCL学习笔记1234567891011121314151617181920

6.2 ICP 的主要变体 (在PCL中均有实现)

变体名称 核心改进 适用场景
Point-to-Plane ICP 误差度量改为点到目标点切平面的距离,而不再是点对点。收敛更快,对部分重叠点云效果更好。 曲面匹配,有法线信息的点云。
Point-to-Line ICP 误差度量改为点到目标点对应线的距离。 主要用于2D激光扫描数据配准。
Generalized-ICP (GICP) 将Point-to-Plane推广,同时考虑局部表面结构和协方差,是最强大和最常用的变体之一。 对各种复杂场景都有很好的鲁棒性。
Non-linear ICP (NICP) 将点云局部特征(法线、曲率) 融入误差函数,进行非线性优化。 要求高精度匹配,且点云质量较好。
Multi-scale ICP 在多分辨率(由粗到精) 下进行ICP,加快速度并避免局部极小值。 大数据集,初始位姿估计较差时。

#include <pcl/registration/gicp.h>
// ...
pcl::GeneralizedIterativeClosestPoint<pcl::PointXYZ, pcl::PointXYZ> gicp;
gicp.setInputSource(cloud_src);
gicp.setInputTarget(cloud_tgt);
// ... 设置其他参数
gicp.align(Final);

cpp
运行1234567

6.3 粗配准:采样一致性初始配准 (SAC-IA)

PCL学习笔记


#include <pcl/features/fpfh.h>
#include <pcl/registration/ia_ransac.h>
// ...
// 1. 计算源点云和目标点云的FPFH特征
pcl::PointCloud<pcl::FPFHSignature33>::Ptr fpfh_src(new ...);
pcl::PointCloud<pcl::FPFHSignature33>::Ptr fpfh_tgt(new ...);
// ... (计算FPFH特征的代码,需先计算法线)

// 2. 执行SAC-IA粗配准
pcl::SampleConsensusInitialAlignment<pcl::PointXYZ, pcl::PointXYZ, pcl::FPFHSignature33> sac_ia;
sac_ia.setInputSource(cloud_src);
sac_ia.setSourceFeatures(fpfh_src); // 设置源点云的特征
sac_ia.setInputTarget(cloud_tgt);
sac_ia.setTargetFeatures(fpfh_tgt); // 设置目标点云的特征
sac_ia.setMinSampleDistance(0.05f);
sac_ia.setNumberOfSamples(50);      // 每次迭代采样的点数
sac_ia.setCorrespondenceRandomness(10); // 增加此值可考虑更多候选对应,提升鲁棒性但减慢速度

pcl::PointCloud<pcl::PointXYZ> cloud_src_aligned;
sac_ia.align(cloud_src_aligned);

Eigen::Matrix4f initial_guess = sac_ia.getFinalTransformation();

cpp
运行
PCL学习笔记12345678910111213141516171819202122

6.4 其他重要配准算法

算法名称 核心思想 特点
正态分布变换 (NDT) 将目标点云划分为网格 cell,并用多维正态分布来建模每个 cell 中点云的分布。配准即寻找一个变换,使得源点云的点最可能出现在目标点云的这些分布中。 对初始估计要求比ICP低;处理大规模点云时速度更快;PCL中实现为 pcl::NormalDistributionsTransform。
交互式配准 允许用户手动在源和目标点云上选取至少3对对应点,然后根据这些对应点直接计算变换矩阵。 非常简单直接,用于为自动化算法提供初始估计或快速验证。

6.5 实际配准流程建议

PCL学习笔记


// 1. 读取和预处理点云
pcl::PointCloud<pcl::PointXYZ>::Ptr src = ...;
pcl::PointCloud<pcl::PointXYZ>::Ptr tgt = ...;
preprocess(src); // VoxelGrid + SOR
preprocess(tgt);

// 2. 粗配准 (如果初始位姿未知)
Eigen::Matrix4f init_guess = Eigen::Matrix4f::Identity();
if (need_coarse_registration) {
    init_guess = sac_ia_align(src, tgt); // 执行SAC-IA
}

// 3. 精配准
pcl::GeneralizedIterativeClosestPoint<pcl::PointXYZ, pcl::PointXYZ> gicp;
gicp.setInputSource(src);
gicp.setInputTarget(tgt);
gicp.setMaxCorrespondenceDistance(0.05);
// ... 设置其他参数

pcl::PointCloud<pcl::PointXYZ> aligned;
gicp.align(aligned, init_guess); // 将粗配准结果作为初始估计传入

// 4. 输出结果
std::cout << "Fine Transform:
" << gicp.getFinalTransformation() << std::endl;

cpp
运行
PCL学习笔记123456789101112131415161718192021222324

表面重建 (surface)

表面重建是点云处理流程的最终阶段之一,其目标是将离散的、无组织的三维点云数据转换为连续的、可用于可视化、分析和制造的表面模型(通常是网格模型,如三角形网格)。

重建类别 核心思想 典型算法 优点 缺点 / 挑战
① 基于隐式函数的方法 定义一个函数 F(x,y,z),其零等值面 F=0 即为重建的表面。通过插值或拟合点云数据来求解该函数。 泊松重建 (Poisson) 能生成水密(Watertight) 的平滑表面;对噪声和缺失数据鲁棒性好。 计算量较大;输出网格非常稠密,常需后处理;难以重建尖锐特征。
② 基于显式三角化的方法 直接对点云进行逐点连接,生成三角形网格。通常基于局部二维投影。 贪婪投影三角化 (GP3) 速度快;内存效率高;适用于有序和无序点云。 对噪声和 outliers 敏感;在遮挡和边界区域容易产生错误;难以处理复杂拓扑。
③ 基于移动最小二乘 (MLS) 的方法 严格来说是重采样和平滑方法,但能产生非常平滑、连续的表面,是重建的优质预处理。 移动最小二乘平滑与重采样 能有效去除噪声并填补小孔洞;产生均匀采样的点云。 会平滑掉尖锐的棱角特征;不是严格的网格生成。
④ 其他方法 包括参数化曲面拟合、细分表面等。 凸包重建, 简单多边形重建 对特定形状(如凸形物体)非常有效且快速。 应用范围狭窄(只适用于凸形体或简单形状)。

7.1 泊松重建 (Poisson Surface Reconstruction)

PCL学习笔记


#include <pcl/surface/poisson.h>
// ...
pcl::PointCloud<pcl::PointNormal>::Ptr cloud_with_normals(new pcl::PointCloud<pcl::PointNormal>);
// !!! 注意:泊松输入需要是带法线的点云 (PointNormal或Separate Cloud+Normals)
// ... 假设已经通过NormalEstimation和concatenateFields计算好法线并合并

// 创建泊松重建对象
pcl::Poisson<pcl::PointNormal> poisson;
poisson.setInputCloud(cloud_with_normals); // 输入带法线的点云
poisson.setDepth(9); // 设置重建深度,这是最重要的参数

// 执行重建
pcl::PolygonMesh mesh; // 用于存储输出的三角形网格
poisson.reconstruct(mesh);

// 保存网格
pcl::io::savePLYFile("output_mesh.ply", mesh);

cpp
运行
PCL学习笔记1234567891011121314151617

7.2 贪婪投影三角化 (Greedy Projection Triangulation, GP3)

PCL学习笔记


#include <pcl/surface/gp3.h>
// ...
pcl::PointCloud<pcl::PointNormal>::Ptr cloud_with_normals(new ...);
// ... 假设已准备好带法线的点云

// 创建GP3对象
pcl::GreedyProjectionTriangulation<pcl::PointNormal> gp3;
gp3.setInputCloud(cloud_with_normals);
gp3.setSearchRadius(0.025); // 设置搜索半径 (2.5cm)

// 设置参数
gp3.setMu(2.5);             // 典型值 2.5-3.0
gp3.setMaximumNearestNeighbors(100);
gp3.setMaximumSurfaceAngle(M_PI / 4); // 45 degrees
gp3.setMinimumAngle(M_PI / 18);       // 10 degrees
gp3.setMaximumAngle(2 * M_PI / 3);    // 120 degrees
gp3.setNormalConsistency(false);      // 如果法线方向一致,可设为true

// 执行重建
pcl::PolygonMesh mesh;
gp3.reconstruct(mesh);

cpp
运行
PCL学习笔记123456789101112131415161718192021

7.3 移动最小二乘 (Moving Least Squares, MLS)

PCL学习笔记


#include <pcl/surface/mls.h>
// ...
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);
pcl::PointCloud<pcl::PointNormal>::Ptr mls_points(new pcl::PointCloud<pcl::PointNormal>);

// 创建MLS对象
pcl::MovingLeastSquares<pcl::PointXYZ, pcl::PointNormal> mls;
mls.setInputCloud(cloud);
mls.setSearchRadius(0.03);    // 设置搜索拟合曲面所用的半径
mls.setPolynomialOrder(2);    // 设置拟合多项式的阶数
mls.setComputeNormals(true);  // 计算法线

// 执行重采样和平滑
mls.process(*mls_points);

// 现在可以将 mls_points 用于泊松或GP3重建

cpp
运行
PCL学习笔记12345678910111213141516

7.4 实际表面重建流程建议

PCL学习笔记

关键点检测 (keypoints)

PCL(Point Cloud Library)中的关键点检测是点云处理中的重要步骤,用于识别点云中具有稳定性、区别性的点集。这些关键点可以加快后续识别、追踪等数据处理速度。

算法名称 核心原理 特点与适用场景 PCL中的类
ISS (Intrinsic Shape Signatures) 通过计算每个点邻域的特征值,并利用特征值之间的关系(如阈值比)来识别角点等特征点。 基于局部几何特征,对物体内部特征点检测有效。 pcl::ISSKeypoint3D<PointInT, PointOutT>
SIFT (Scale-Invariant Feature Transform) 将2D图像中的SIFT算子Adapt到3D空间,通过在不同尺度空间检测极值点来提取关键点。 尺度不变性,对旋转、噪声有一定鲁棒性,适用于不同尺度的特征检测。 pcl::SIFTKeypoint<PointInT, PointOutT>
Harris 基于点云强度信息(Harris 2D)或3D空间信息及表面法线(Harris 3D/6D),通过计算Harris矩阵和特征值来检测角点。 Harris 2D基于强度,Harris 3D基于空间信息和法线,Harris 6D可结合XYZ和强度信息。对图像旋转变换保持较好的检测重复率,但不适合尺度变化的检测。 pcl::HarrisKeypoint2D<PointInT, PointOutT, IntensityT>, pcl::HarrisKeypoint3D, pcl::HarrisKeypoint6D
NARF (Normal Aligned Radial Feature) 从深度图像中识别物体,特别考虑边缘信息,通过表面变化系数、主方向计算兴趣值,并经过平滑和非极大值抑制得到关键点。 适用于深度图像,对物体边界敏感,关键点位置稳定且具有支持区域以便计算描述子和法线。 pcl::NarfKeypoint
Trajkovic 通过比较点与其邻域点的法线方向差异来检测角点,通常针对有序点云。 计算速度快,但对噪声敏感。

8.1 使用关键点检测的一般步骤及代码片段

PCL学习笔记


#include <pcl/io/pcd_io.h>
#include <pcl/point_types.h>
#include <pcl/keypoints/iss_3d.h>
// ... 其他必要的头文件

// 读取点云
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);
pcl::io::loadPCDFile("your_pointcloud.pcd", *cloud);

// 创建ISS关键点检测对象
pcl::ISSKeypoint3D<pcl::PointXYZ, pcl::PointXYZ> iss_detector;
pcl::search::KdTree<pcl::PointXYZ>::Ptr tree(new pcl::search::KdTree<pcl::PointXYZ>());

// 设置参数
iss_detector.setSearchMethod(tree);
iss_detector.setSalientRadius(0.01);  // 设置显著半径
iss_detector.setNonMaxRadius(0.005);  // 设置非极大值抑制半径
iss_detector.setThreshold21(0.975);   // 设置特征值阈值λ2/λ1
iss_detector.setThreshold32(0.975);   // 设置特征值阈值λ3/λ2
iss_detector.setMinNeighbors(5);      // 设置最小近邻数
iss_detector.setNumberOfThreads(4);   // 设置线程数
iss_detector.setInputCloud(cloud);    // 设置输入点云

// 计算关键点
pcl::PointCloud<pcl::PointXYZ>::Ptr keypoints(new pcl::PointCloud<pcl::PointXYZ>);
iss_detector.compute(*keypoints);

// 后续可保存或可视化关键点

cpp
运行
PCL学习笔记12345678910111213141516171819202122232425262728

可视化工具 (visualization)

其他工具

© 版权声明

相关文章

暂无评论

none
暂无评论...