4. 结构光三维重建:相移法、格雷码与点云生成

结构光三维重建,说白了就是给被测物体“打光编码”,然后通过相机拍回来的变形条纹,反算出物体的三维形貌。我最早接触这个技术是在2016年做手机玻璃盖板检测的时候,当时被客户要求检测0.02mm的划痕深度,传统方法根本搞不定,最后就是靠这套方案解决的。

这一章我们重点讲四个核心模块:相移法(三步、四步)、格雷码编码、多频外差解包裹,以及最后的点云生成与滤波。嗯,内容不少,但都是实战中绕不开的硬骨头。

4.1 相移法:三步与四步

相移法的核心思想很简单:投影一组正弦条纹,每次移动一定的相位,然后通过多幅图像的灰度值变化,反算出每个像素点的相位值。你想想看,这本质上就是一个三角测量问题。

4.1.1 三步相移法

三步相移法需要投影三幅条纹图,每幅之间相位差120°。公式如下:

I1(x,y) = I'(x,y) + I''(x,y) * cos(φ(x,y) - 2π/3)
I2(x,y) = I'(x,y) + I''(x,y) * cos(φ(x,y))
I3(x,y) = I'(x,y) + I''(x,y) * cos(φ(x,y) + 2π/3)

其中I'是背景光强,I''是调制强度,φ就是我们要的相位。解出来的相位公式是:

φ(x,y) = atan2(√3 * (I1 - I3), 2*I2 - I1 - I3)
我的经验:三步相移法计算量小,适合高速场景。但我曾经在一个产线上遇到过问题——投影仪非线性响应导致相位误差偏大。后来我加了一个预校正表,把投影仪的gamma曲线拉直了,效果立竿见影。

4.1.2 四步相移法

四步相移法投影四幅图,相位差90°。公式更对称:

I1 = I' + I'' * cos(φ)
I2 = I' + I'' * cos(φ + π/2)
I3 = I' + I'' * cos(φ + π)
I4 = I' + I'' * cos(φ + 3π/2)

解相位公式:

φ = atan2(I4 - I2, I1 - I3)
方法 投影幅数 抗噪性 计算速度 适用场景
三步相移 3 一般 高速在线检测
四步相移 4 较好 稍慢 高精度离线测量
注意:四步相移虽然多拍一张图,但能消除二阶非线性误差。我建议在精度要求高的场合,别省这一张图的时间。

4.2 格雷码编码

相移法算出来的相位是包裹在[-π, π]之间的,说白了就是有歧义——你不知道这个点是第几个周期的相位。格雷码就是用来解决这个问题的。

格雷码的特点是相邻两个码字之间只有一位不同。这有什么好处?你想想看,如果投影仪或相机有噪声,传统二进制码可能因为一位误判导致整个解码出错,但格雷码最多只错一个条纹周期。

// 格雷码解码伪代码
for each pixel (x,y):
    for each bit k:
        if 投影的格雷码条纹在该像素为亮:
            gray_code[k] = 1
        else:
            gray_code[k] = 0
    // 格雷码转二进制
    binary[0] = gray_code[0]
    for k = 1 to N-1:
        binary[k] = binary[k-1] XOR gray_code[k]
    // 二进制转十进制,得到条纹序号
    stripe_index = binary_to_decimal(binary)

避坑指南:我曾经在项目里直接用8位格雷码,结果发现物体表面反光太强,高光区域解码全错。后来我改用“格雷码+相移”混合策略——格雷码只用来确定周期序号,相位值由相移法提供。这样即使格雷码偶尔错一位,相位连续性也能帮你纠回来。

4.3 多频外差解包裹

多频外差是另一种解包裹思路,不需要投影格雷码,而是投影多个不同频率的条纹。核心原理是:两个不同频率的相位差,可以等效为一个更低频率的相位,从而扩大无歧义范围。

假设有两个频率f1和f2,对应的包裹相位分别是φ1和φ2。外差后的等效频率为:

f_eq = |f1 - f2|

等效相位为:

φ_eq = φ1 - φ2 (如果φ_eq < 0, 则加2π)

然后利用这个低频相位去辅助高频相位的解包裹。实际操作中,我一般用三个频率:高频(精细)、中频(过渡)、低频(全局)。

我的习惯:多频外差的好处是不需要投影额外的格雷码图,适合对时间敏感的场景。但要注意,频率差不能太小,否则外差后的等效波长太大,精度反而下降。我一般取频率比为 1:8:64 左右。

4.4 点云生成与滤波

相位解包裹完成后,我们就得到了每个像素点的绝对相位值。接下来就是根据三角测量原理,把相位映射到三维坐标。

点云生成的公式(简化版):

Z = (L * f) / (d + f * tan(θ))
X = (u - cx) * Z / fx
Y = (v - cy) * Z / fy

其中L是基线距离,f是焦距,d是视差,θ是投影角度。这些参数需要通过标定获得。

点云生成后,滤波是必不可少的。我常用的滤波方法:

  • 统计滤波:去除离群点。每个点计算到邻域点的平均距离,超过阈值就剔除。
  • 体素滤波:下采样。把空间划分成小立方体,每个立方体内只保留一个重心点。
  • 双边滤波:保边去噪。在深度突变区域保留边缘,在平坦区域平滑。
// PCL 统计滤波示例
pcl::StatisticalOutlierRemoval<pcl::PointXYZ> sor;
sor.setInputCloud(cloud);
sor.setMeanK(50);          // 邻域点数
sor.setStddevMulThresh(1.0); // 标准差倍数阈值
sor.filter(*cloud_filtered);
注意:滤波参数不是固定的。我遇到过有人把标准差阈值设成0.5,结果把正常的边缘特征全滤掉了。建议先用可视化工具预览效果,再批量处理。

4.5 知识体系总览

下面这张图是我自己整理的,把这一章的核心逻辑串起来了。你跟着这个流程走,基本不会跑偏。

结构光三维重建核心流程 投影编码条纹 相移法(三步/四步) 格雷码编码 多频外差解包裹 相位解包裹 (绝对相位获取) 点云生成 (三角测量映射) 点云滤波 (统计/体素/双边) 三维点云输出 图例: 输入编码 相移法路径 格雷码路径 多频外差路径 点云处理

这张图把整个流程串起来了。你从投影开始,走相移法或者格雷码或者多频外差,最终都汇聚到相位解包裹,然后生成点云,滤波后输出。实际项目中,我经常把格雷码和相移法结合使用——格雷码定周期,相移法定精度,这样既快又准。


专注资料整理