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)。怎么做到的?

核心思路是两步走:

  1. SPFH 计算:只计算查询点与邻域内每个点的特征(α, φ, θ),不计算邻域内点与点之间的特征。这一步 O(k)。
  2. 加权融合:对每个邻域点,再计算它的 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 形状上下文扩展过来的。它的核心思想是:用球坐标系下的点分布直方图来描述局部形状。

具体做法:

  1. 以查询点为球心,建立球坐标系
  2. 沿径向(通常 3-5 层)、仰角(4-6 个)、方位角(8-12 个)划分球壳
  3. 统计每个球壳内的点数,归一化后作为特征

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 本章知识结构

下面这张图总结了四种特征描述子的核心逻辑和匹配流程:

点云特征提取与匹配知识结构 原始点云 PFH O(n·k²) 高维 FPFH O(n·k) 33维 SHOT LRF + 352维 3DSC 球壳统计 特征描述子集合(源点云 + 目标点云) 最近邻搜索 + 距离阈值 + 比率测试 + 互一致性 对应关系集合(匹配点对) 说明:特征匹配不是终点,而是粗配准的起点 后续还需要用 RANSAC 或 ICP 进一步精化对应关系

3.8 工程实践建议

最后,我总结几条实际项目中的经验:

  • 先降采样再提取特征:体素滤波降采样到 1-2cm 分辨率,特征计算量能降一个数量级
  • 搜索半径要匹配点云密度:半径太小特征不稳定,太大又包含太多无关点。我一般取 5-10 倍体素大小
  • 特征匹配后一定要过滤:别指望一次匹配就完美,RANSAC 是标配
  • 多尺度特征融合:在大场景中,我会同时计算小半径和大半径的 FPFH,然后拼接成更鲁棒的特征

小技巧:如果你用 PCL,记得在计算 FPFH 之前先计算法向量。法向量质量直接影响特征质量。我习惯用 pcl::NormalEstimationOMP 做并行计算,速度能快 3-5 倍。