3. 点云特征提取:PFH、FPFH、SHOT、3DSC 特征描述子,特征匹配与对应关系建立
点云配准的核心,说白了就是找对应点。你想想看,两片点云叠在一起,怎么知道哪个点跟哪个点是一对?靠坐标肯定不行——两片点云本身就有旋转平移。所以我们需要一种更聪明的办法:给每个点算一个“指纹”,这个指纹要足够独特,还要对旋转平移不敏感。这就是特征描述子干的事。
我刚开始做配准时,总觉得特征提取是锦上添花。后来在一个室外大场景项目里,纯靠 ICP 迭代,跑了半小时都没收敛。换成 FPFH 做粗配准,十几秒就搞定了。嗯,从那以后我再也不敢跳过这一步了。
3.1 从原始点到特征描述子
特征描述子的本质是什么?是对一个点周围局部几何信息的编码。一个好的描述子,应该满足三个条件:
- 唯一性:不同结构的点,描述子差异要大
- 鲁棒性:对噪声、采样密度变化不敏感
- 不变性:对刚体变换(旋转+平移)保持不变
目前主流的点云特征描述子,大致可以分为两类:基于直方图的(PFH、FPFH、SHOT)和基于形状上下文的(3DSC)。下面我一个一个说。
核心思路:特征描述子 = 局部邻域几何信息的统计编码。直方图是统计工具,不是特征本身。
3.2 PFH:点特征直方图
PFH(Point Feature Histograms)是最早被广泛使用的点云特征之一。它的思路很直接:对查询点邻域内所有点对,计算一组几何特征,然后统计成直方图。
具体来说,对邻域内的任意两点 Pi 和 Pj,我们建立一个局部坐标系:
u = n_i
v = (P_j - P_i) × u
w = u × v
然后计算四个特征值:
| 特征 | 含义 | 范围 |
|---|---|---|
| α | v 与 nj 的夹角 | [0, π] |
| φ | u 与 (Pj - Pi) 的夹角 | [0, 2π] |
| θ | w 与 nj 的夹角 | [0, π] |
| d | 两点之间的欧氏距离 | [0, +∞) |
每个特征分成 b 个区间(通常 b=5),四个特征组合起来就是 b4 = 625 维的直方图。维度很高,但信息量也大。
注意:PFH 的计算复杂度是 O(n·k²),其中 k 是邻域点数。如果 k=50,n=10万,那计算量就是 2.5 亿次点对运算。我当年在笔记本上跑一个中等场景,等了快十分钟。所以 PFH 虽然精度高,但实际工程中很少直接用。
3.3 FPFH:快速点特征直方图
FPFH(Fast Point Feature Histograms)是 PFH 的加速版。它把计算复杂度从 O(n·k²) 降到了 O(n·k)。怎么做到的?
核心思路是两步走:
- SPFH 计算:只计算查询点与邻域内每个点的特征(α, φ, θ),不计算邻域内点与点之间的特征。这一步 O(k)。
- 加权融合:对每个邻域点,再计算它的 SPFH,然后加权求和。这一步 O(k²) 但 k 很小(通常取邻域的子集)。
FPFH 的最终公式:
FPFH(P_q) = SPFH(P_q) + (1/k) · Σ [ (1/ω_k) · SPFH(P_k) ]
其中 ωk 是查询点与邻域点之间的距离权重。
实际使用中,FPFH 的维度通常是 33(11 个区间 × 3 个特征)。比 PFH 的 625 维小得多,但区分能力并没有明显下降。
我的经验:FPFH 是工程中最常用的特征描述子。PCL 里直接调用 pcl::FPFHEstimation,参数调起来也简单。我一般把搜索半径设成 0.05-0.1 倍的点云尺度,效果比较稳。
3.4 SHOT:方向直方图与纹理直方图的结合
SHOT(Signature of Histograms of OrienTations)是另一种思路。它不直接统计点对特征,而是先建立一个局部参考坐标系(LRF),然后在坐标系内划分空间区域,统计每个区域内的法向量分布。
SHOT 的构建分三步:
- LRF 建立:对邻域点做 PCA,得到三个主轴方向。这里有个坑——PCA 的方向有歧义性(正负方向不确定),需要用符号消歧。
- 空间划分:沿径向(2 层)、方位角(8 个)、俯仰角(2 个)划分,总共 32 个空间区域。
- 直方图统计:每个区域内统计法向量与 LRF 各轴的夹角余弦,分成 11 个区间。最终维度 = 32 × 11 = 352。
SHOT 的优点是鲁棒性好,对噪声和遮挡都不太敏感。缺点是计算 LRF 比较耗时。
对比:FPFH 快但区分度一般,SHOT 区分度好但慢。选哪个?看场景。如果是实时 SLAM,我选 FPFH;如果是离线精细配准,我选 SHOT。
3.5 3DSC:三维形状上下文
3DSC(3D Shape Context)是从 2D 形状上下文扩展过来的。它的核心思想是:用球坐标系下的点分布直方图来描述局部形状。
具体做法:
- 以查询点为球心,建立球坐标系
- 沿径向(通常 3-5 层)、仰角(4-6 个)、方位角(8-12 个)划分球壳
- 统计每个球壳内的点数,归一化后作为特征
3DSC 的维度一般在 200-500 之间。它的优点是能很好地描述形状的全局分布,缺点是对旋转敏感——需要先对齐参考方向。
| 描述子 | 维度 | 计算复杂度 | 旋转不变性 | 适用场景 |
|---|---|---|---|---|
| PFH | 625 | O(n·k²) | 是 | 小规模精细配准 |
| FPFH | 33 | O(n·k) | 是 | 大规模实时配准 |
| SHOT | 352 | O(n·k) | 是(LRF) | 高精度离线配准 |
| 3DSC | 200-500 | O(n·k) | 需对齐 | 形状识别、分类 |
3.6 特征匹配与对应关系建立
特征算完了,下一步就是匹配。匹配的核心是:在源点云的特征集合中,为目标点云的每个特征找到最相似的那个。
最常用的方法是最近邻搜索:
// 伪代码
for each feature_f in source_features:
for each feature_g in target_features:
dist = L2_distance(feature_f, feature_g)
if dist < min_dist:
min_dist = dist
match = g
但这样直接匹配会有很多误匹配。我常用的过滤策略有:
- 距离阈值:只保留距离小于某个阈值的匹配对
- 比率测试:最近邻距离 / 次近邻距离 < 0.7-0.8
- 互一致性:A 匹配到 B,B 也要匹配到 A
- 几何一致性:匹配点对之间的空间关系要一致(比如距离、角度)
我曾经踩过的坑:在一个室内场景中,直接用 FPFH 匹配,误匹配率高达 60%。后来加了比率测试和互一致性过滤,误匹配率降到了 15% 以下。记住:特征匹配不是一次性的,需要迭代过滤。
3.7 本章知识结构
下面这张图总结了四种特征描述子的核心逻辑和匹配流程:
3.8 工程实践建议
最后,我总结几条实际项目中的经验:
- 先降采样再提取特征:体素滤波降采样到 1-2cm 分辨率,特征计算量能降一个数量级
- 搜索半径要匹配点云密度:半径太小特征不稳定,太大又包含太多无关点。我一般取 5-10 倍体素大小
- 特征匹配后一定要过滤:别指望一次匹配就完美,RANSAC 是标配
- 多尺度特征融合:在大场景中,我会同时计算小半径和大半径的 FPFH,然后拼接成更鲁棒的特征
小技巧:如果你用 PCL,记得在计算 FPFH 之前先计算法向量。法向量质量直接影响特征质量。我习惯用 pcl::NormalEstimationOMP 做并行计算,速度能快 3-5 倍。