3、点云特征提取:法向量估计、FPFH特征、SHOT特征、点云关键点检测(ISS/Harris3D)

好,咱们进入第三章。这一章讲的是点云特征提取,说白了就是给点云“画像”。你想想看,一堆散乱的点,怎么让算法认出它们是桌子、椅子还是墙角?靠的就是特征。

我个人习惯把特征提取比作“给点云做体检”。法向量是测“朝向”,FPFH和SHOT是测“局部纹理”,关键点检测则是找“骨架”。这三板斧砍下去,点云就不再是冷冰冰的数字了。

3.1 法向量估计:点云的“脸”朝哪边

法向量,就是点云表面每个点的朝向。比如地面上的点,法向量基本都朝上;墙上的点,法向量朝水平方向。这玩意儿是后续所有特征的基础。

怎么算? 最常用的方法是PCA(主成分分析)。对每个点,找它周围最近的K个邻居,然后算这堆点的协方差矩阵。最小特征值对应的特征向量,就是法向量方向。

嗯,这里要注意:算出来的法向量方向是不确定的,可能朝里也可能朝外。我刚开始做项目时就被这个坑过——明明是一面墙,法向量却一半朝里一半朝外,聚类结果乱七八糟。

避坑指南: 我曾经在室内点云重建时,因为没做法向量定向,导致后续分割全崩了。解决办法是:用视点方向做参考,让所有法向量都指向视点。PCL里用 flipNormalTowardsViewpoint() 搞定。
// 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.setRadiusSearch(0.03);  // 半径3cm内找邻居
ne.compute(*normals);

半径选多大?我建议根据点云密度来。激光雷达点云一般选0.02-0.05米,太大会把不同平面的点混在一起,太小则噪声大。

3.2 FPFH特征:快速点特征直方图

FPFH全称是Fast Point Feature Histogram。名字挺长,但核心思想很简单:用直方图描述一个点周围邻居的几何关系。

它比法向量强在哪? 法向量只描述“点”本身,FPFH描述的是“点+邻居”的局部结构。比如一个点在平面上,和在棱角上,FPFH的分布完全不同。

我记得有一次做点云配准,用FPFH做粗匹配,效果出奇的好。两个不同角度扫描的椅子,FPFH特征一匹配,直接就把对应点找出来了。

特征 维度 计算速度 鲁棒性
PFH 125
FPFH 33 中高
SHOT 352

FPFH只有33维,比PFH的125维小得多,但效果差不太多。说白了就是“用更少的钱办差不多的事”。

// FPFH特征计算
pcl::FPFHEstimation<pcl::PointXYZ, pcl::Normal, pcl::FPFHSignature33> fpfh;
fpfh.setInputCloud(cloud);
fpfh.setInputNormals(normals);
fpfh.setSearchMethod(tree);
fpfh.setRadiusSearch(0.05);
fpfh.compute(*fpfh_features);
实战技巧: 如果你做点云配准,建议先用FPFH做粗匹配,再用ICP做精匹配。我试过直接上ICP,十次有八次掉进局部最优。FPFH相当于给了ICP一个“好起点”。

3.3 SHOT特征:更精细的局部描述子

SHOT(Signature of Histograms of OrienTations)是另一种局部特征。它比FPFH更精细,但计算量也更大。

SHOT怎么工作的? 它把点周围的球空间分成多个小格子,每个格子内统计法向量方向的直方图。说白了就是“把局部空间切成小块,每块里统计朝向”。

我个人觉得SHOT在物体识别场景特别好用。比如识别桌子上的杯子,SHOT特征能区分出杯口和杯身的几何差异。FPFH在这类场景下就有点“脸盲”。

// SHOT特征计算
pcl::SHOTEstimation<pcl::PointXYZ, pcl::Normal, pcl::SHOT352> shot;
shot.setInputCloud(cloud);
shot.setInputNormals(normals);
shot.setSearchMethod(tree);
shot.setRadiusSearch(0.04);
shot.compute(*shot_features);

SHOT输出352维特征,比FPFH大了一个数量级。所以如果你的点云有几十万个点,算SHOT会有点慢。我建议只在关键点上算SHOT,别在全图上跑。

3.4 关键点检测:ISS与Harris3D

关键点,就是点云里“特别”的点。比如墙角、桌角、物体尖点。这些点信息量大,适合做后续的匹配和识别。

ISS(Intrinsic Shape Signatures) 是我比较喜欢用的方法。它通过分析每个点邻域的协方差矩阵特征值,来判断这个点是不是“突出”的。

具体来说,ISS算三个特征值λ1≥λ2≥λ3。如果λ2/λ1和λ3/λ2都小于某个阈值,说明这个点周围几何变化剧烈——这就是关键点。

ISS关键点检测参数:
  • salient_radius:搜索半径,一般0.02-0.05米
  • non_max_radius:非极大值抑制半径
  • threshold21:λ2/λ1阈值,默认0.975
  • threshold32:λ3/λ2阈值,默认0.975

Harris3D 是2D Harris角点检测的3D版本。它用点云的协方差矩阵构造“响应函数”,响应值大的点就是角点。

我记得有一次做建筑物点云分割,用Harris3D检测墙角,效果特别好。但要注意,Harris3D对噪声比较敏感,点云质量差的时候会检测出很多假角点。

// ISS关键点检测
pcl::ISSKeypoint3D<pcl::PointXYZ, pcl::PointXYZ> iss;
iss.setInputCloud(cloud);
iss.setSearchMethod(tree);
iss.setSalientRadius(0.05f);
iss.setNonMaxRadius(0.03f);
iss.setThreshold21(0.975f);
iss.setThreshold32(0.975f);
iss.compute(*keypoints);
注意: 关键点检测前一定要做降采样和去噪。我曾经拿原始点云直接跑ISS,结果检测出几千个“关键点”,大部分都是噪声点。先做个体素滤波,效果会好很多。

3.5 实战建议:怎么选特征?

说了这么多,到底用哪个?我根据经验给个建议:

  • 做配准:FPFH做粗匹配,ICP做精匹配。又快又稳。
  • 做物体识别:SHOT特征,虽然慢但准确率高。
  • 做分割:法向量+ISS关键点,够用了。
  • 做实时系统:只用法向量和FPFH,别碰SHOT。

你想想看,特征提取就像选工具——锤子螺丝刀各有各的用处。没有最好的特征,只有最合适的特征。

好,这一章就到这里。下一章咱们讲点云分割,到时候这些特征全都会用上。