3. 点云特征提取

点云特征提取,说白了就是给激光雷达的数据做「降维打击」。你想想看,一帧点云动辄十几万个点,直接拿去匹配,计算量大到离谱。所以我们要提取出那些最能代表点云结构的特征——就像你描述一个人,不会去数他有多少根头发,而是看脸型、五官这些关键信息。

我个人习惯把特征提取分成两步走:关键点检测和特征描述。关键点就是那些「特别」的点,比如墙角、边缘;特征描述则是给这些关键点算一个「身份证号」,让匹配算法能认出它们。今天我们就来聊聊这四个核心工具:法向量、FPFH、ISS、SHOT。

核心思路:先算法向量(基础),再找关键点(ISS),最后算描述子(FPFH或SHOT)。这套流程我在多个项目中验证过,稳定可靠。

3.1 法向量估计:点云的「皮肤」

法向量是什么?就是点云表面每个点的朝向。比如地面点的法向量朝上,墙面点的法向量朝水平方向。没有法向量,后面的特征描述子根本没法算。

估计方法其实很直观——用PCA(主成分分析)。对每个点,找它周围的K个近邻点,然后算这些点的协方差矩阵。最小特征值对应的特征向量,就是法向量方向。

// PCL中的法向量估计
pcl::NormalEstimation<pcl::PointXYZ, pcl::Normal> ne;
ne.setInputCloud(cloud);
pcl::search::KdTree<pcl::PointXYZ>::Ptr tree(new pcl::search::KdTree<pcl::PointXYZ>());
ne.setSearchMethod(tree);
ne.setKSearch(20);  // K近邻搜索,我一般设15-30
ne.compute(*normals);

我的经验:K值的选择很关键。K太小,法向量对噪声敏感;K太大,会模糊掉边缘特征。在城区场景,我通常设20;在隧道或室内,会降到10-15。

嗯,这里要注意一点:法向量有方向歧义。同一个平面,法向量可能朝上也可能朝下。PCL默认会做方向一致性调整,但如果你发现法向量忽上忽下,记得检查一下视点方向设置。

3.2 FPFH特征描述子:快速又靠谱

FPFH全称是Fast Point Feature Histogram,快速点特征直方图。它是PFH的加速版,计算量从O(nk²)降到了O(nk)。

FPFH的核心思想是:对每个点,统计它和邻域点之间的角度关系。具体来说,它会算三个角度:

  • 法向量之间的夹角
  • 法向量与点对连线之间的夹角
  • 点对连线与法向量投影之间的夹角

然后把这些角度统计成33维的直方图。为什么是33维?因为每个角度分11个bin,3个角度就是33维。

// FPFH特征计算
pcl::FPFHEstimation<pcl::PointXYZ, pcl::Normal, pcl::FPFHSignature33> fpfh;
fpfh.setInputCloud(cloud);
fpfh.setInputNormals(normals);
fpfh.setSearchMethod(tree);
fpfh.setRadiusSearch(0.5);  // 搜索半径,单位米
fpfh.compute(*fpfh_features);

避坑指南:我曾经在高速场景用过FPFH,发现特征区分度不够。后来排查发现,是因为搜索半径设得太小(0.2米),导致邻域点太少。对于稀疏点云,建议把半径放大到0.5-1.0米。

3.3 ISS关键点提取:找「最特别」的点

ISS(Intrinsic Shape Signatures)是一种基于特征值分析的关键点检测方法。它的逻辑很简单:如果一个点周围的点云在各个方向上的分布差异很大,那它很可能是个关键点。

具体做法是:对每个点算协方差矩阵的三个特征值λ1≥λ2≥λ3。然后看两个比值:

  • λ2/λ1:越小说明点云越像一条线(边缘点)
  • λ3/λ2:越小说明点云越像一个面(平面点)

ISS会保留那些λ2/λ1和λ3/λ2都小于某个阈值的点——这些点就是所谓的「角点」或「显著点」。

// ISS关键点提取
pcl::ISSKeypoint3D<pcl::PointXYZ, pcl::PointXYZ> iss;
iss.setInputCloud(cloud);
iss.setSearchMethod(tree);
iss.setSalientRadius(0.6f);     // 显著半径
iss.setNonMaxRadius(0.4f);      // 非极大值抑制半径
iss.setThreshold21(0.975f);     // λ2/λ1阈值
iss.setThreshold32(0.975f);     // λ3/λ2阈值
iss.compute(*keypoints);

我的建议:ISS的参数调优是个体力活。我一般先设SalientRadius为0.6米,NonMaxRadius为0.4米,然后看提取的关键点数量。如果太多(超过点云总数的5%),就增大阈值;太少(低于1%),就减小阈值。

3.4 SHOT描述子:更鲁棒的选择

SHOT(Signature of Histograms of OrienTations)是另一种特征描述子,比FPFH更鲁棒,但计算量也更大。它把关键点周围的球体空间分成32个区域(8个方位角 × 2个仰角 × 2个半径),然后在每个区域内统计法向量方向的直方图。

SHOT的维度是352维(32个区域 × 11个bin)。为什么这么高?因为每个区域都要统计法向量与参考方向之间的夹角,而且还要考虑法向量与径向方向的夹角。

// SHOT特征计算
pcl::SHOTEstimation<pcl::PointXYZ, pcl::Normal, pcl::SHOT352> shot;
shot.setInputCloud(keypoints);  // 注意:输入是关键点
shot.setInputNormals(normals);
shot.setSearchMethod(tree);
shot.setRadiusSearch(0.8f);     // SHOT的搜索半径通常比FPFH大
shot.compute(*shot_features);

对比总结:

特征 维度 计算速度 鲁棒性 适用场景
FPFH 33 快 中等 实时性要求高的场景
SHOT 352 慢 高 精度要求高的场景

我个人在实际项目中,如果做实时匹配(比如10Hz的激光雷达),会用FPFH;如果是离线建图或者回环检测,会用SHOT。说白了就是速度和精度的trade-off。

3.5 本章知识体系

下面这张图展示了本章的核心逻辑:从原始点云到特征提取的完整流程。

点云特征提取流程 原始点云 法向量估计 ISS关键点提取 FPFH描述子 SHOT描述子 特征向量输出 图例:蓝色-输入 | 红色-基础处理 | 绿色-关键点 | 紫色/橙色-特征描述 | 黑色-输出

从图中可以看到,法向量估计是基础,它同时服务于ISS关键点提取和FPFH/SHOT特征描述。ISS负责筛选出「值得描述」的点,而FPFH和SHOT则负责给这些点生成特征向量。这两条路径最终汇合,输出可用于匹配的特征数据。

实战建议:如果你刚开始接触点云特征提取,我建议先跑通FPFH的流程,因为它快、参数少、容易调试。等对特征匹配有了直观感受,再尝试SHOT和ISS的组合。别一上来就搞最复杂的,容易劝退。

好了,关于点云特征提取的核心内容就这些。法向量是基础,ISS帮你找关键点,FPFH和SHOT帮你算描述子。记住:没有最好的特征,只有最合适的特征。根据你的场景和性能要求,灵活选择就好。