==================================================
3D 结构光编码原理
—— 二进制 / 格雷码 / 相位移 / 相位展开 / 三角测量
【内容结构】
一、结构光编码的本质
二、二进制编码(Binary Coding)— 基础
三、格雷码(Gray Code)— 优化
四、相位移(Phase Shift)— 高精度
五、格雷码 + 相位移的组合
六、2π 相位跳变(Phase Ambiguity)
七、相位展开(Phase Unwrapping)
八、完整系统 + 面试总结
九、三角测量(Triangulation)— 从对应点换算出高度
十、文档导航
==================================================
一、结构光编码的本质
结构光编码的目的是给投影仪的每一列(或每一条条纹)分配一个唯一 ID,
从而回答一个关键问题:
相机看到的这个像素,对应投影仪发出的哪一列光?
根据这个对应关系,再结合三角测量(Triangulation)即可恢复出高度。
主流程:
投影仪 → 投射条纹 → 物体发生高度起伏 → 条纹变形→ 相机拍摄 → 解码 → 三角测量 → 高度图
二、二进制编码(Binary Coding)— 基础
2.1 核心思想
用多张亮/暗二值条纹图,把投影仪的每一列编码成一个唯一的二进制编号。
然后再把读出的二进制转回十进制,得到该列号。
2.2 8 列例子(最简单情况)
投影仪横向 8 列:
列号: 0 1 2 3 4 5 6 7
二进制: 000 001 010 011 100 101 110 111
因为 2^3 = 8,所以只需要 3 位二进制编码。
三张投影图案(每一位对应一张):
Pattern 1 (最高位 MSB): 0 0 0 0 1 1 1 1 → 00001111
Pattern 2 (中间位) : 0 0 1 1 0 0 1 1 → 00110011
Pattern 3 (最低位) : 0 1 0 1 0 1 0 1 → 01010101
2.3 相机解码过程
假设物体表面某点 A,相机依次拍摄 3 张图:
第 1 张: 亮 → 1
第 2 张: 暗 → 0
第 3 张: 亮 → 1
得到二进制码: 101
转换为十进制:
1 × 2^2 + 0 × 2^1 + 1 × 2^0
= 4 + 0 + 1
= 5
于是知道:
该像素对应投影仪第 5 列。
2.4 16/32 列的扩展规律
假设投影仪有 2^N 列,则需要 N 张编码图。
+----------+----------+--------------------+--------------------------------+
| 列数 | 编码位数 | 编码图数 | 图案规律 |
+----------+----------+--------------------+--------------------------------+
| 8 (2^3) | 3 | 3 张 | 每 4/2/1 列翻转 |
| 16 (2^4) | 4 | 4 张 | 每 8/4/2/1 列翻转 |
| 32 (2^5) | 5 | 5 张 | 每 16/8/4/2/1 列翻转 |
| 1024 | 10 | 10 张 | 类推 |
+----------+----------+--------------------+--------------------------------+
规律总结:
第 k 张图案:每 2^(N-k) 列翻转一次亮/暗
2.5 工业实践:编码多少列?
编码列数由"需要的空间分辨率"决定,与投影仪像素数无必然关系。
假设投影仪 1024 × 768,常见情况:
- 每列都需唯一识别 → 编码 1024 列 → log₂(1024) = 10 张图
- 第 537 列 → 二进制 1000011001(10 位)→ 相机拍完后即可恢复 537
- 投影仪 1920 列但测量精度要求没那么高 → 可只划分 512 列
→ log₂(512) = 9 张图
2.6 为什么不能无限增加列数?
列数越多 → 条纹越细 → 越容易出问题:
- 镜头模糊
- 投影散焦
- 像素串扰
- 表面反射
导致"黑不够黑,白不够白",边界处容易误判。
结论:编码越细 → 测量越难。
2.7 二进制编码的局限
解码只能得到整数列号:
0, 1, 2, 3, 4, 5, 6, 7
不能知道:
5.1, 5.2, 5.3
因此精度受限。需要更高精度时,必须结合相位移。
三、格雷码(Gray Code)— 优化
3.1 普通二进制的边界跳变问题
以 8 列为例子观察相邻两列:
3 = 011
4 = 100
从 3 → 4:三位全部变化。
如果条纹边界附近受噪声影响:
本应读到 011 → 却误读为 100
直接从 列 3 跳到 列 4,甚至更大的误差。
3.2 格雷码定义
格雷码仍然是二进制编码,但满足:
相邻位置的编码只有 1 位发生变化。
8 列的二进制 / 格雷码对照:
列号 普通二进制 格雷码
────────────────────────────
0 000 000
1 001 001
2 010 011
3 011 010
4 100 110
5 101 111
6 110 101
7 111 100
相邻两列变化情况:
000 → 001 : 第 3 位
001 → 011 : 第 2 位
011 → 010 : 第 3 位
010 → 110 : 第 1 位
... 每次都只变 1 位
3.3 拆成投影图案
把格雷码竖向按位拆分,得到 3 张图:
Gray Pattern 1: 0 0 0 0 1 1 1 1 → 00001111
Gray Pattern 2: 0 0 1 1 1 1 0 0 → 00111100
Gray Pattern 3: 0 1 1 0 0 1 1 0 → 01100110
对比二进制编码的 3 张图:
二进制: 00001111 / 00110011 / 01010101
格雷码: 00001111 / 00111100 / 01100110
差异:格雷码的条纹变化更平滑,不会出现大量位同时翻转。
3.4 抗噪声原理
假设真实位置:第 3 列,格雷码 = 010
邻居:第 2 列,格雷码 = 011 (只差 1 位)
即使边界判断错了:010 ↔ 011,误差也只错 1 个位置。
而普通二进制:3 = 011、4 = 100,边界处可能出现多位同时出错,
误差远大于 1。
3.5 格雷码 → 二进制(解码)
转换规则(逐位异或):
B1 = G1
Bk = B(k-1) XOR Gk (k >= 2)
例:格雷码 111 → 二进制
B1 = G1 = 1
B2 = B1 XOR G2 = 1 XOR 1 = 0
B3 = B2 XOR G3 = 0 XOR 1 = 1
→ Binary = 101 → 即十进制 5
工业软件一般自动完成,无需手算。
3.6 格雷码的生成方法
【方法 1:直接观察法】手工逐个比较,适用于教学/小规模编码。
【方法 2:公式法】(工业实际最常用)
Gray = Binary XOR (Binary >> 1)
即:格雷码 = 当前二进制 ⊕ 右移一位后的二进制。
例:5 → 101,右移 → 010
XOR: 101
010
-----
111
→ 5 的格雷码 = 111
例:4 → 100,右移 → 010
XOR: 100
010
-----
110
→ 4 的格雷码 = 110
与 5 的格雷码 111 只差 1 位 ✓
3.7 为什么格雷码天然保证只变 1 位?
普通二进制产生多位同时变化的原因是进位。
而 Gray = B XOR (B >> 1) 这一公式巧妙消除了多位同时翻转,
因此天生保证:相邻数字只变 1 位。
3.8 为什么工业结构光喜欢格雷码?
✅ 绝对位置编码,不会出现 2π 歧义
✅ 相邻仅差 1 位,抗噪声
✅ 解码简单(异或)
缺点:依然只能定位到整数列(列 537 而非列 537.234),
因此工业系统通常:
格雷码 + 相位移
四、相位移(Phase Shift)— 高精度
4.1 二进制 vs 正弦条纹(空间连续 vs 不连续)
【这里"连续变化"指的是空间上的连续,不是时间上的连续】
二进制条纹:
255 ███████
███████
0 ███████
███████
沿横向看:白 → 黑 在边界处突变,不连续。
正弦条纹:
亮度沿横向平滑变化,类似正弦曲线:
/\ /\
/ \ / \
/ \ / \
0 ----/----\----/--→
从 0 缓慢上升到 255,再缓慢下降到 0,再上升到 255 ……
中间无数亮度级(0, 30, 80, 128, 180, 220, 255 ……),符合正弦规律。
重要:相位移计算的并非"黑白变化",
而是该点在条纹周期内的相位(Phase)位置。
4.2 为什么拍 4 张?— 四步相移
单张图内部亮度变化是空间连续的;
而相位移需要"在时间上依次投影多张发生平移的正弦条纹图"。
四步相移依次投影:
第 1 张 (0°): I = cos(x)
第 2 张 (90°): I = cos(x + 90°) → 整体右移 1/4 周期
第 3 张 (180°): I = cos(x + 180°) → 再右移 1/4 周期
第 4 张 (270°): I = cos(x + 270°) → 再右移 1/4 周期
4.3 实际例子(单点 A 的 4 次亮度)
假设物体表面有固定一点 A:
第 1 张:A 在亮区,I1 = 220
第 2 张:A 跑到较暗位置,I2 = 120
第 3 张:A 在最暗位置,I3 = 20
第 4 张:A 又开始变亮,I4 = 130
由这 4 个亮度值,通过 atan2 即可求 A 对应的相位 φ:
φ = atan2(I4 - I2, I1 - I3)
4.4 相位移的作用
求出相位 φ 后,可以得到亚像素位置。例如:
537.23
537.54
537.89
这是条纹周期内的精确位置,而非仅仅"第 537 条"。
五、格雷码 + 相位移的组合
5.1 各自负责什么
+----------+--------------------------------+--------------------------------+
| 编码 | 解决的问题 | 输出 |
+----------+--------------------------------+--------------------------------+
| 格雷码 | 你在第几条条纹?绝对定位 | 整数级位置(如第 537 列) |
| 相位移 | 该条纹周期内的具体位置 | 亚像素位置(如 0.326) |
+----------+--------------------------------+--------------------------------+
合并后:537 + 0.326 = 537.326
5.2 形象的尺子比喻
格雷码 → 尺子上的厘米刻度(537 cm),只能知道整数
相位移 → 尺子上的游标卡尺(537.32 cm),能看小数部分
两者结合 → 工业级高精度结构光定位
5.3 为什么必须组合?
如果只有相位移:
求出相位 = 180°
那么第 5 周期、第 15 周期、第 25 周期 …… 看起来都一样。
系统无法判断属于哪个周期。
如果只有格雷码:
能定位到"第 537 条",但不能获得 0.326 的小数精度。
因此工业结构光通常采用 "格雷码 + 相位移" 组合方案。
5.4 面试标准表述
格雷码通过黑白条纹为每个投影位置分配唯一编码,实现绝对定位,
但只能获得整数级位置;相位移通过投影正弦灰度条纹,利用同一点
在多个相移图像中的灰度变化计算相位,从而获得条纹周期内的亚像素位置。
格雷码解决"第几条"的问题,相位移解决"条纹内具体位置"的问题,
因此工业结构光通常采用"格雷码 + 相位移"组合方案。
六、2π 相位跳变(Phase Ambiguity / Phase Jump)
6.1 问题本质
四步相移计算出:
φ = atan2(I4 - I2, I1 - I3)
atan2 的结果范围只能是一个周期:
[-π, +π] 或 [0, 2π)
但真实条纹周期可能无限重复:
0, π, 2π, 3π, 4π, 5π, 6π …
例如真实相位为 0.5π, 2.5π, 4.5π,atan2 结果全部都是 0.5π。
这种"多个真实相位压缩到 [0, 2π) 区间"的现象,叫做:
Wrapped Phase(包裹相位)
也称为:2π Ambiguity 或 Phase Jump(相位跳变)
6.2 经典例子
假设条纹周期 = 10 像素:
点 A:第 5 个周期 + 90°,真实相位 = 5 × 2π + π/2
点 B:第 20 个周期 + 90°,真实相位 = 20 × 2π + π/2
atan2 计算结果:
A → π/2
B → π/2
系统根本无法分辨 A 与 B 属于哪个周期。
6.3 为什么会出现?
+----------------------+----------------------------+----------------------------+
| 方法 | 输出范围 | 真实物理世界 |
+----------------------+----------------------------+----------------------------+
| atan2 | [0, 2π) 单一周期 | 多个周期无限增长 |
+----------------------+----------------------------+----------------------------+
| 结果差异 | 单一包裹值 | 需要"周期编号 + 包裹值" |
+----------------------+----------------------------+----------------------------+
七、相位展开(Phase Unwrapping)— 解决 2π 歧义
7.1 思路本质
需要把"被压缩在 [0, 2π) 的单一周期相位"还原为连续的
真实相位(可跨多个周期)。这个过程就叫相位展开。
业界主要有两条技术路线。
7.2 空间相位展开(Spatial Phase Unwrapping)
【思路】利用邻近像素相位连续性
相邻点的高度通常是连续的,相位也是连续的。
【做法】
相邻像素相位本应连续:1.1π, 1.2π, 1.3π, 1.4π ……
如果突然从 1.4π → 0.1π,说明发生 2π 跳变。
把 0.1π 补回 +2π,变成 2.1π。
【缺点】
在台阶、孔洞、断层等突变处容易失败。
7.3 时间相位展开(Temporal Phase Unwrapping)
【思路】低频辅助高频(工业最常用)
【做法】
先投"宽条纹"(条纹周期大,只有 2 个周期)
→ 容易判断属于第几周期;
再投"窄条纹"(条纹周期小,有 20 个周期)
→ 提供高精度位置。
组合后:既知道"第几个周期",又知道"周期内精确位置"。
【本质联系】
格雷码能告诉你"第几条",解决的正是"周期编号问题";
多频相位移(低频 + 高频)能在不增加格雷码位数的情况下,
进一步提供高精度周期编号。
7.4 工业结构光通常的选择
工业结构光最常用 "时间相位展开(多频)" + "单频高精相位移"。
原因:
- 不依赖相邻像素 → 不受表面突变影响
- 精度更高、可控性好
八、完整的高精度结构光 3D 测量系统(对应关系部分)
±---------------------±-------------------------±--------------------------±---------------------------+
| 模块 | 解决的问题 | 输出 | 工业做法 |
±---------------------±-------------------------±--------------------------±---------------------------+
| 格雷码 | 第几条条纹(整数) | 537 | Black & White 二值条纹 |
| 相位移 | 条纹周期内亚像素 | 0.326 | 4 张正弦条纹 |
| 相位展开(2π 解决) | 到底是第几个周期 | 周期编号 + 亚像素位置 | Spatial / Temporal |
| 三角测量 | 视差 → 高度 Z | 高度图 Height Map | Z = f × B / d |
±---------------------±-------------------------±--------------------------±---------------------------+
【一句话理解】
格雷码解决"第几条"的问题,
相位移解决"条纹中的哪个位置"的问题,
相位展开解决"到底是第几个周期"的问题,
三角测量解决"对应点 → 高度"的问题。
这四步组合起来,才构成完整的高精度结构光 3D 测量系统。
九、面试总结(分主题速记)
9.1 什么是结构光编码?
结构光编码通过给投影仪的每一列分配唯一 ID,建立相机像素与投影
仪列号的对应关系,再结合三角测量恢复高度。常用组合方案为
"格雷码 + 相位移 + 相位展开"。
9.2 二进制编码的核心思想?
用 N 位二进制表示 2^N 个投影位置。每位对应一张投影图,相机按位
解码后转换为十进制即可恢复列号。需要 log₂(N) 张编码图。
9.3 为什么工业喜欢格雷码?
- 绝对位置编码,不出现 2π 歧义
- 相邻编码只差 1 位,抗噪声
- 编码 / 解码简单(生成:B XOR (B>>1);解码:逐位异或)
9.4 相位移为什么能提高精度?
相位移投影正弦灰度条纹,通过 4 张相移图像中的灰度变化计算出
相位 φ = atan2(...),从而得到条纹周期内的亚像素位置(小数部分)。
9.5 什么是 2π 相位跳变?
由于 atan2 计算得到的相位只有一个周期 [0, 2π),而真实条纹
存在多个重复周期,因此无法判断当前相位属于第几个周期,这种
现象称为 2π Phase Ambiguity(相位歧义)或 Phase Jump(相位跳变)。
9.6 如何解决 2π 歧义?
两种主流方法:
Spatial Phase Unwrapping(空间展开)
- 利用邻近像素的相位连续性
- 对边缘和突变表面效果较差
Temporal Phase Unwrapping(时间展开)
- 利用低频条纹辅助高频条纹
- 需要拍摄更多图像
- 工业结构光最常用
十、三角测量(Triangulation)— 从对应点换算出高度
【衔接上一章】
前八章已经解决:格雷码 + 相位移 → 相机像素 ↔ 投影仪像素对应关系。
本章解决:已知对应关系后 → 计算目标点的 3D 坐标(X, Y, Z)。
10.1 结构光 = 把投影仪当作"反向相机"
双目视觉:
左相机 右相机
\ /
\ /
● 目标
/ \
/ \
→ 两个相机视角不同 → 视差 → 深度
结构光完全一样,只是把右相机换成投影仪:
Camera
\
\
● Object Point
/
/
Projector
→ 相机拍摄 + 投影仪投射,等效两支不同视角的光线
→ 三角关系 → 三角测量(Triangulation)
10.2 系统标定后知道什么?
系统已经知道:
[1] 相机位姿 Camera Pose
包括相机位置和朝向
[2] 投影仪位姿 Projector Pose
包括投影仪位置和朝向
[3] 相机内参
fx, fy, cx, cy
[4] 投影仪内参(把投影仪看作反向相机)
fxp, fyp, cxp, cyp
10.3 测量时得到什么?
通过格雷码 + 相位移 解码后,得到:
Camera Pixel (u, v) 例:(800, 400)
Projector Pixel (up, vp) 例:(920, 400)
建立对应关系:
Camera Pixel <-> Projector Pixel
10.4 单视角为什么算不出 3D?
相机看到的一个像素 (800, 400),实际上只代表:
Camera
\
\
\ (一条从相机出发的空间光线)
\
\
系统只能知道:
"物点在这条光线上,但不知道在何处。"
单视角无法确定深度,必须再有一条光线与之相交。
10.5 两条光线相交 → 3D 坐标
把相机和投影仪分别反投影为空间中的两条光线:
Camera
\
\
● ← 两条光线的交点
/
/
Projector
交点坐标 = (X, Y, Z)
这就是目标点的 3D 坐标,也是"三角测量"得名的由来。
10.6 高度(Z)与条纹偏移的关系
假设参考平面 Z = 0,非常平坦:
投影条纹落在 x = 100 位置
如果中间出现一个凸起,条纹会发生偏移:
原来: x = 100
现在: x = 110
位移: Δx = 10
位移越大 → 高度越高
位移越小 → 高度越低
因此:
条纹偏移量 ⇔ 高度
10.7 最常见公式
如果采用简单三角模型:
Z = B × f / d
其中:
Z = 深度
B = Camera 与 Projector 之间的距离(Baseline)
f = 焦距
d = 视差(条纹偏移量)
与双目视觉深度公式完全一致:
Depth ∝ 1 / Disparity
10.8 为什么相位移能提高高度精度?
假设相邻列对应的高度差:
第 537 列 → 1.000 mm
第 538 列 → 1.020 mm
仅用格雷码,最小分辨率 = 0.02 mm。
加入相位移后:
537.50 列 → 1.010 mm
537.05 列 → 1.001 mm
精度提升链条:
相位精度 → 投影像素精度 → 三角测量精度 → 高度精度
(层层传递,这是为什么工业结构光必须配合相位移)
10.9 面试最简单回答
结构光系统首先通过格雷码和相位移建立相机像素与投影仪像素的对应关系,然后将相机和投影仪分别看作双目系统中的两个视角。
根据系统标定得到的内外参数,将对应像素反投影为空间中的两条光线,两条光线的交点即为目标点的三维坐标 (X, Y, Z)。
目标点相对于参考平面的 Z 值就是高度信息,这一过程称为三角测量(Triangulation)。
10.10 一句话本质
格雷码 + 相位移 → 负责"找到对应点"(Camera Pixel ↔ Projector Pixel)
三角测量 Triangulation → 负责"把对应点转换成高度"(3D 坐标)
两步组合,就是完整的结构光 3D 测量链路。
十一、文档导航
【完整链路速记】
格雷码 → 第几条(绝对位置)
相位移 → 条纹周期内位置(亚像素)
相位展开 → 到底是第几个周期(2π 解歧义)
三角测量 → 对应点 → 3D 坐标(高度)
这四步组合,才构成完整的高精度结构光 3D 测量系统。
498



被折叠的 条评论
为什么被折叠?



