3、结构光编码策略(上):时间多路复用编码与空间邻域编码
结构光编码,说白了就是给投影仪的光打上一套「暗号」。相机拍到了这些暗号,才能算出物体的三维坐标。我做了这么多年结构光,最深的体会就是:编码策略选对了,项目就成功了一半。
这一章我们先讲两大类最经典的编码方法:时间多路复用编码和空间邻域编码。它们各有各的脾气,也各有各的用武之地。
核心观点:时间多路复用编码追求「精度」,空间邻域编码追求「速度」。你需要在两者之间做取舍——没有完美的编码,只有最适合你场景的编码。
3.1 时间多路复用编码
这类编码的核心思路是:把编码信息分散到多帧图像里。每一帧只携带一部分信息,相机拍完所有帧后,再拼凑出完整的编码。这样做的好处很明显——每帧只需要区分黑白两种状态,抗干扰能力强,精度可以做到很高。
但代价也很直接:被测物体必须保持静止。我记得有一次在产线上做动态检测,用了格雷码+相移的方案,结果工件稍微一晃,重建出来的点云全是毛刺。嗯,从那以后我就记住了:时间多路复用只适合静态或准静态场景。
3.1.1 二进制编码
二进制编码是最朴素的方法。我们把投影区域划分成2N条条纹,然后用N帧图像来编码。每一帧对应一个二进制位,白色代表1,黑色代表0。
举个例子,假设我们要编码8条条纹(N=3):
- 第1帧:黑白黑白黑白黑白(最高位)
- 第2帧:白白黑黑白白黑黑
- 第3帧:白白白白黑黑黑黑(最低位)
相机拍完3帧后,每个像素就能得到一个3位的二进制数,范围0~7。这个数就对应了该像素在投影图案中的位置。
我曾经踩过的坑:二进制编码的相邻条纹之间,码值可能相差很大。比如从011(3)到100(4),所有位都翻转了。如果物体表面刚好落在边界上,稍微有点噪声就会导致解码错误。这就是所谓的「汉明距离」问题。
3.1.2 格雷码
格雷码就是为了解决上面那个问题而生的。它的特点是:相邻两个码值之间只有一位不同。你想想看,这样即使边界有噪声,最多也就错一个位,误差小得多。
二进制转格雷码的规则很简单:
// 二进制转格雷码
gray = binary ^ (binary >> 1);
// 格雷码转二进制(解码时用)
binary = gray;
while (mask >>= 1) {
binary ^= mask & gray;
}
我个人习惯在项目中直接用格雷码代替二进制编码。虽然解码时多了一步转换,但换来的是更鲁棒的边界检测。尤其是做高精度扫描时,这点提升很值得。
小技巧:格雷码配合亚像素级别的边界检测,可以把条纹定位精度做到1/10像素级别。我在做手机玻璃盖板检测时,就是用这个方案达到了0.02mm的重复精度。
3.1.3 相移法
相移法走的是另一条路。它不靠黑白条纹来编码,而是用正弦光栅。通过改变光栅的相位,相机拍到的灰度值会呈现正弦变化。利用多帧图像,我们可以反算出每个像素的精确相位值。
标准的N步相移公式(以三步相移为例):
I1 = A + B * cos(φ)
I2 = A + B * cos(φ + 2π/3)
I3 = A + B * cos(φ + 4π/3)
// 解相位
φ = atan2(√3 * (I1 - I3), 2*I2 - I1 - I3)
相移法的精度非常高,理论上可以达到千分之一条纹周期的水平。但有个问题——相位是周期性的,解出来的φ被包裹在[-π, π]之间。这就需要用到相位展开技术。
在实际项目中,我通常把格雷码和相移法结合起来用:格雷码提供粗定位(确定条纹序号),相移法提供精定位(亚条纹级相位)。这种组合方案在工业检测中非常常见。
关键参数对比:
| 编码方法 | 所需帧数 | 理论精度 | 抗噪能力 | 适用场景 |
|---|---|---|---|---|
| 二进制编码 | N | 1/2N 条纹宽度 | 中等 | 简单轮廓扫描 |
| 格雷码 | N | 1/2N 条纹宽度 | 较好 | 通用静态扫描 |
| 相移法 | 3~12 | 1/1000 条纹周期 | 优秀 | 高精度检测 |
| 格雷码+相移 | N+3~12 | 1/1000 条纹周期 | 优秀 | 工业级高精度 |
3.2 空间邻域编码
时间多路复用编码的致命伤是「需要多帧」。如果被测物体在动,或者相机和投影仪在动,那就没法用了。这时候就得请出空间邻域编码。
空间邻域编码的核心思想是:只用一帧图像,通过像素周围的邻居信息来确定它的位置。说白了,就是每个像素的编码值不仅取决于它自己,还取决于它旁边几个像素的图案。
3.2.1 De Bruijn序列
De Bruijn序列是一个循环序列,它包含了所有可能的长度为n的子序列。举个例子,一个2阶的De Bruijn序列(元素为0和1):
序列:0 0 1 1 0 1 0 0 1 1 ...
子序列:00, 01, 11, 10, 01, 10, 00, ...
在结构光中,我们把De Bruijn序列投影成彩色条纹。每个条纹的颜色由它自己和相邻几个条纹的颜色共同决定。解码时,相机只要看到一小段连续的条纹,就能唯一确定它在整个序列中的位置。
我记得在2018年做过一个手持式扫描仪的项目,用的就是De Bruijn编码。因为手持设备会晃动,没法拍多帧。当时选了4种颜色、3阶的De Bruijn序列,总共能编码43=64个位置。虽然精度不如格雷码+相移,但胜在单帧就能工作。
注意:De Bruijn编码对物体表面的颜色和纹理很敏感。如果被测物体本身就有彩色图案,很容易和投影的彩色条纹混淆。我建议在深色或中性色的物体上使用,或者用近红外波段的投影来避开可见光干扰。
3.2.2 M阵列
M阵列(伪随机阵列)是De Bruijn序列的二维扩展。它不再是一维的条纹,而是一个二维的网格图案。每个网格点或网格块的值由它在阵列中的位置决定,而且整个阵列具有伪随机性——任意一个局部窗口都是唯一的。
M阵列的生成通常基于线性反馈移位寄存器(LFSR)。一个m×n的M阵列,任意一个a×b的子窗口在整个阵列中只出现一次。这样,相机只要拍到足够大的局部区域,就能确定它在全局中的坐标。
我实际用过的一种M阵列是「点阵编码」:投影仪打出稀疏的圆点阵列,每个圆点周围的黑白模式构成一个局部编码。解码时先检测圆点中心,然后根据周围圆点的分布模式来匹配全局位置。
我的经验:M阵列的窗口大小选择很关键。窗口太小,容易误匹配;窗口太大,物体边缘区域可能拍不到完整的窗口。一般建议窗口大小取3×3或5×5,具体要看投影分辨率和物体尺寸。
3.3 两种编码策略的取舍
说了这么多,到底该选哪种?我个人的决策流程是这样的:
- 先问:物体动不动? 如果动,直接走空间邻域编码。如果不动,走时间多路复用。
- 再问:精度要求多高? 如果要求亚毫米级,必须用相移法或格雷码+相移。如果毫米级就够,空间邻域编码也能胜任。
- 最后问:环境光控制得住吗? 如果环境光复杂(比如室外),空间邻域编码的彩色条纹容易受干扰,这时候时间多路复用的黑白条纹更靠谱。
没有银弹,只有权衡。做工程师这么多年,我越来越觉得:理解每种编码的边界条件,比记住它们的公式更重要。公式可以查,但什么时候该用什么方案,这得靠经验积累。
一句话总结:时间多路复用编码用「时间换精度」,空间邻域编码用「空间换速度」。选哪个,取决于你的场景是静态还是动态,是追求极致精度还是追求实时响应。
公众号:蓝海资料掘金营,微信deep3321