什么是深度相机?
3D 相机
为初学者介绍了深度相机的概念

内容概述
Community Wiki
深度图的每个有效像素都记录一个距离值:这个点离相机有多少毫米。多数深度相机还会同步输出 RGB 彩色图。
适合谁看:刚拿到第一台深度相机、需要读懂它输出的数据的人;给机器人、机械臂、AGV 加装视觉的开发者;正在为某个场景挑选型号的选型者。
读完能得到什么:
- 看得懂一张深度图,包括灰度版和彩色版分别该怎么读;
- 知道深度数据怎么变成三维坐标和点云;
- 判断手里的相机属于哪条技术路线,边界在哪里;
- 遇到深度图大面积发黑时,知道先检查什么。
建议阅读顺序:第一次接触深度相机,先读第 1 至 4 节;正在选型,再读第 5 至 8 节;已经拿到设备,可直接查阅第 9、10 节和附录 C、D。
全文内容概览
- 深度图保存的是距离,不是“看起来像灰度图”的普通照片。
- 算法计算应使用原始 16 位深度数据;伪彩深度图只用于观察和说明。
- 结构光、ToF 与双目各有边界,选型应先看环境光、工作距离和表面纹理。
- 黑洞、飞点和边缘毛刺通常是场景或成像原理造成的,并不等于相机损坏。
普通摄像头缺了什么
Community Wiki
一张 RGB 图片是一个二维数组,每个元素记录红绿蓝三个亮度值。三维世界被投影到这个平面上,深度信息在投影过程中丢失。
人在判断远近时,会结合双眼视差、遮挡关系、焦外模糊以及对物体大小的经验。这些依据足够日常使用,但它们不给出一份可计算的数值。深度相机补上的正是这个数值:每个像素对应一个到相机的距离,单位通常为毫米。
| 普通 RGB 相机 | 深度相机 | |
|---|---|---|
| 输出 | 颜色图(2D) | 深度图(2D + Z 值),多数同时输出彩色图 |
| 每个像素的含义 | 亮度 / 颜色 | 该点到相机的距离(mm) |
| 能回答 | 这是什么 | 这是什么,以及它在哪里、多大、多远 |
| 受光照影响 | 强,需要足够亮度 | 取决于技术路线:主动红外适合弱光,但可能受阳光干扰;被动双目依赖环境纹理和光照 |
| 典型用途 | 识别、检测、读码 | 避障、抓取、重建、体感交互 |
RGB 相机回答画面里有什么,深度相机在此基础上回答它们在哪里、多远、多大。
深度图怎么看
Community Wiki
深度图是一个二维数组,每个元素是距离值,不是颜色。同一份数据有两种常见呈现方式,初学者容易在这一步混淆。
- 灰度深度图:把距离映射成亮度,越近越白、越远越黑。这是原始数据的直接可视化,一个像素对应一个数值,没有插值,也没有色带。
- 彩色深度图,也称伪彩深度图或深度热度图:把同一个距离数值映射成一条彩色色带,常用 JET,从红到黄、绿、蓝。它不是另一份数据,只是灰度版换了一套配色。远近层次在灰阶里不容易分辨,换成色带之后差异更明显。
两者是同一份数据的两种画法,彩色版的颜色由灰度版的亮度换算而来。
2.1 一次实拍:同一画面的灰度与彩色

图 1:同一帧数据的两种呈现方式,Orbbec Astra 2 拍摄于实验室工位。左为 16 位灰度深度图,近处亮、远处暗;右为同一数据的伪彩版本,近处红、远处蓝。三处圆圈标出的是同一批没有数据的区域。
对比着看,有四点区别。
结构完全一致。 货架、桌子、保温杯、地面的轮廓在两张图里位置相同,因为就是同一份数据。彩色版看起来信息更多,来自配色对距离差异的放大,数据本身没有增加。
黑洞位置也一致。 左图的黑块对应右图的黑块,数量与位置都相同。这说明黑洞源于数据缺失,与渲染方式无关。
灰阶的分辨力集中在邻近范围。 这张图的深度范围是 516 mm 至 16616 mm,跨度 16 米,而人眼在灰阶上大约只能分辨几十级。8 米和 15 米的物体在灰度图里几乎是同一个深灰色,在彩色图里则落在不同的蓝调上。分析远近层次时用彩色版更合适。
灰度适合看形状,彩色适合看层次。 灰度图没有色相干扰,物体边缘和平面连续性更容易看清;彩色图的颜色跳变会把距离分层显示出来,前景、中景、背景可以直接区分。
本次采集的完整参数见附录 B。
2.2 彩色热度图的读数规则

图 2:(b) 伪彩映射规则;(c) 同一帧里三类黑洞的成因。
读彩色深度图时,有三项参数决定了图上的颜色代表什么。
色带方向。 常见的 JET 配色是红橙表示近、黄绿表示中、蓝色表示远,但方向取决于软件设置,有些工具用反向配色,有些用 VIRIDIS 之类不带色相的色带。拿到任何一台设备的图,先放一个已知距离的物体进去,例如把手置于 0.5 米处,确认色带方向后再做判断。
量程上限。 伪彩需要把一段距离区间映射到整条色带,这个区间称为量程。本文所有图使用固定量程 0 至 6000 mm。
如果改为按当前画面最大值自动归一化,会出现这样的问题:画面最远处达到 16 米时,6 米处的货架只占到色带的三分之一,2.5 米处的杯子被压到色带顶端附近,整片区域的颜色拉不开差距。固定量程可以让关心的距离区间分配到更多颜色层级。
纯黑与深灰的含义不同。 本文的处理约定如下:
| 图上颜色 | 数值含义 | 说明 |
|---|---|---|
| 纯黑 (0, 0, 0) | depth = 0 | 该像素没有测到有效深度 |
| 深灰 (40, 40, 40) | depth > 量程上限 | 测到了,但超出设定的分析范围 |
| 其他颜色 | 0 < depth ≤ 量程上限 | 正常数据,颜色即距离 |
把没测到和测到但太远这两种情况用不同方式标示,排查时可以直接区分二者。
2.3 三个典型特征
近处细节多,远处变糊。 深度精度通常会随距离增加而下降;双目方案的深度误差常随距离近似平方增长,其他路线也会受到信噪比和反射强度影响。这属于成像边界,不一定是产品缺陷。同一个平面在近处可能只有毫米级起伏,到了远处则可能扩大到厘米级,具体数值以型号规格和实测为准。
黑洞。 图 2(c) 标出三类原因:超出有效量程、被遮挡,光线去不了或回不来、镜面和圆柱面反光,光反射到其他方向去了。玻璃、镜面、黑色吸光材质同样会大面积失效。深度图有空洞是常态,没有空洞反而值得检查。
边缘毛刺。 物体轮廓处常有飞点或拖尾,原因是一个像素的视野里同时包含了前景和背景,算法只能给出折中值。中值滤波、双边滤波可以压掉一部分。
拿到任何一台新相机,先对着白墙、纸箱、自己的手、显示器屏、玻璃窗各拍一张深度图。能看出这五样东西的差别,基本就摸清了这台设备的工作边界。
灰度深度图与彩色深度图
Community Wiki
上一节说明如何读图,这一节说明两种图各自的用途。这是实际工作中最常见的选择。
| 灰度深度图 | 彩色深度图 | |
|---|---|---|
| 数据本质 | 原始测量值,本文为 16 位,单位 mm | 同一数据的着色版本,8 位三通道 |
| 位深 | 16 位,0 至 65535 mm 无损 | 8 位,颜色被量化成 256 级 |
| 精度损失 | 无 | 有,且不可逆 |
| 优点 | 保留全部数值,可直接参与计算 | 距离分层直观,色差敏感,适合汇报 |
| 缺点 | 灰阶分辨力低,远距层次看不出 | 颜色是映射结果,不能当作数据使用 |
| 适合场景 | 算法输入、测量、点云生成、存档 | 调试预览、写文档、汇报演示、快速判读 |
3.1 分工原则
机器计算用 16 位灰度数据,人眼判读用彩色图。
这个分工有具体的数值依据。16 位灰度图每个像素可以表达 0 至 65535 共 65536 个距离值,转成 8 位彩色后只剩 256 级,一个颜色台阶对应约 23 mm 的距离差。用彩色图做测量,精度只能到厘米级;用原始 16 位数据,理论上可以到毫米级。
因此彩色图只承担显示职责,不回写到算法流程中。
3.2 彩色深度图的典型用途
- 快速判断数据是否正常:检查视野里有没有大片黑洞、量程设置是否正确。
- 分层与分割:颜色跳变处往往就是物体边界,手动框选和调参时更直观。
- 向他人解释深度数据:一张彩色深度图比一堆数字更容易讲清,汇报和写文档都适用。
- 多帧比对:固定量程下,两帧之间的色差用眼睛就能捕捉到。
3.3 灰度深度图的典型用途
- 生成点云:需要真实毫米值,见第 4 节。
- 距离测量与阈值分割:
depth[(depth > 500) & (depth < 800)]这类操作只能在数值上做,不能在颜色上做。 - 图像存档与复现:存 16 位 PNG 是无损的,他人拿到可以算出相同结果。存成 JPG 或截图,数据就失效了。
用截图工具截下彩色深度图放进报告,然后注明某处深 2.5 米,这种写法有问题。截图里只有颜色,没有毫米值。要报数,就报从原始数据里读出来的数。
从深度图到三维坐标
Community Wiki
深度图配合相机内参,可以反算出三维坐标。点云、三维重建、位姿估计、抓取规划都从这里开始。

图 3:一个物点 P 投影到像素 (u, v);知道该像素的深度 Z 与内参,就能反算回 P 的三维坐标。
X = (u - cx) * Z / fxY = (v - cy) * Z / fyZ = depth(u, v)其中 fx fy cx cy 是内参,即焦距与主点,由出厂标定给出,SDK 提供读取接口。实际使用时注意三点:
- 光心指向场景的方向是 Z,图像平面上的偏移量需要按焦距换算回物理尺度;
- 内参不准会导致点云整体拉伸或偏移,使用前应读取并核对,不要假设为标准值;
- 对每个像素执行一遍,得到的结果就是点云。
点数估算:1600×1200 的深度图,即使有效像素只有 80%,单帧也有约 150 万个三维点。这对算力有要求,实际项目通常先降采样再处理。
三条主流技术路线
Community Wiki
三条路线回答的是同一个问题:如何测出光走过的距离。各自的物理原理不同,限制条件也不同。

图 4:结构光投射已知图案,观察图案变形;ToF 发射调制光,测量往返时间;双目用两台相机观察同一物体,由视差计算距离。
5.1 结构光(Structured Light)
原理:相机旁边有一个红外投射器,把一副已知图案打到物体表面,图案形式包括散斑、条纹、网格。图案遇到凹凸会变形,另一颗红外相机拍下变形后的图案,比对理论图案与实际图案的差异,反推每个点的深度。
类比:在一张布满格子的透明纸上,格子弯了,就知道对应位置有起伏。
优势:近距离精度高,分辨率高,成本低,模组体积可以做到很小。
软肋:怕强光。太阳光里的红外成分会淹没投射图案,户外基本失效。怕镜面与透明物。测量距离通常限制在数米以内,工作距离越远,投射图案在物体表面越稀疏,精度下降越快。
代表:Orbbec Astra 2、Astra Mini Pro 系列。
5.2 飞行时间(ToF, Time of Flight)
原理:向场景发射调制过的红外光,测量光从发出到反射回来所需时间或相位差,乘以光速再除以二,得到距离。整幅画面同时曝光,一次得到全场景深度。
类比:与蝙蝠回声定位同原理,把声波换成光。
优势:测距快,抗纹理干扰,白墙、纯色表面、弱光环境都能稳定出图。作用距离比结构光远,帧率高,适合动态场景。
软肋:空间分辨率偏低,因为每个像素都要独立计时,像素尺寸难以做小。存在多路径干扰,光在角落和凹槽里多次弹射后才回到相机,会算出偏远的结果。镜面反射直接失效。工作距离与视场角互相牵制,要覆盖大范围就得牺牲测量距离,见第 7.1 节。
代表:Orbbec Femto 系列,包括 Femto Bolt、Femto Mega、Femto Mega I,基于 Microsoft iToF 技术。
5.3 双目立体视觉(Stereo Vision)
原理:用两颗相机间隔一段固定距离同时拍摄同一场景,这段距离称为基线。同一个物体在两幅图中的位置有偏移,偏移量称为视差,距离越近视差越大。根据三角测量原理,用视差和基线即可算出距离。
Z = f * B / d其中 f 为焦距,B 为基线,d 为视差。
类比:人用两只眼睛判断远近,依据的就是视差。
优势:被动方案不主动发射红外光,多台设备同时工作互不干扰。基线越长,远距离精度越好。既可以在室外阳光下被动工作,也可以在弱光下开启红外投射,转为主动双目。
软肋:极度依赖纹理。白墙、玻璃、光滑表面这类无纹理区域,两幅图里找不到可匹配的特征点,深度会缺失。对两个镜头的标定精度要求极高,轻微形变就会导致深度整体漂移。
代表:Orbbec Gemini 330 系列,包括 Gemini 335、335L、335Le,基于 MX6800 ASIC,主动与被动结合,IP65,室内外可用;Gemini 2 系列为主动双目,适用于室内与半室外。
5.4 三条路线对比
| 维度 | 结构光 | ToF | 双目立体视觉 |
|---|---|---|---|
| 是否主动发光 | 是,红外图案 | 是,调制红外 | 可被动,也可主动加投射 |
| 典型工作距离(按具体型号) | 0.6 至 8 m(Astra 2) | 0.25 至 5.46 m,随模式变化 | 0.15 至 10 m(Gemini 2)/ 0.17 至 20 m+(Gemini 335L) |
| 近距离精度 | ★★★ | ★★ | ★★ |
| 远距离表现 | ★ | ★★★ | ★★,长基线更佳 |
| 空间分辨率 | ★★★ | ★★ | ★★★ |
| 室外强光 | ★,失效 | ★★ | ★★★,被动模式可用 |
| 弱光与无光 | ★★★ | ★★★ | ★,需主动模式 |
| 无纹理表面 | ★★★ | ★★★ | ★,失效 |
| 镜面与透明物 | ★ | ★ | ★ |
| 多机同时使用 | 需分时或调频 | 需分时或调频 | ★★★,被动模式互不干扰 |
| 成本 | 低 | 中 | 中 |
星级仅表示定性趋势,用于快速建立直觉。具体型号的数值以官方数据手册为准。
场景反推选型
Community Wiki
选型的起点是场景,型号放在后面。

图 5:三条问句先缩小到一条技术路线,最后再回到具体型号的模式表。
| 你的场景特征 | 优先考虑 | 原因 |
|---|---|---|
| 室内、近距离(小于 2 m)、需要看清细节 | 结构光 | 近距精度与分辨率最优,成本最低 |
| 需要识别手势、人体骨架、动作 | ToF | 抗纹理干扰,帧率高,动态捕捉稳 |
| 户外或阳光直射环境 | 双目被动模式 | 结构光与 ToF 的红外会被日光淹没 |
| 全黑环境或夜间作业 | 结构光或 ToF | 自带红外照明,不依赖环境光 |
| 以白墙、纯色地面为主的空间 | 结构光或 ToF | 双目在无纹理区域会大面积缺少深度 |
| 需要多台相机同时覆盖同一区域 | 双目被动模式 | 无主动光源,多机之间不会互相干扰 |
| 远距离(大于 5 m)避障 | 双目,长基线 | 精度随基线增加,远距衰减更慢 |
| 机械臂精细抓取(小于 1 m) | 结构光或 ToF | 近距离精度是首要指标 |
| 玻璃、镜面、抛光金属占比高 | 无理想方案 | 三者都受限,需改用其他传感手段或增加辅助措施 |
| 亚毫米级尺寸测量 | 不属于本类产品 | 需工业级 3D 扫描或激光轮廓仪 |
两条特征冲突时的优先级:环境光、距离、纹理条件、分辨率、成本。环境光决定能否出图,优先级最高;分辨率与成本可以权衡。
Orbbec 产品线速查
Community Wiki
本节回答一个问题:三条技术路线各自对应 Orbbec 的哪些型号。因此只列路线、型号与关键规格,完整规格表、包装清单、SDK 文档和购买链接跳转官方页面。选方向看这里,确认细节看官方页。
下表数值全部取自 Orbbec 官网产品页规格栏,并附原文出处。
| 型号 | 技术路线 | 关键规格(官网原文口径) | 定位 | 官方出处 |
|---|---|---|---|---|
| Astra 2 | 结构光 | 0.6 至 8 m;深度 1600×1200@30fps;FOV H58.2° V45.2°;RGB 1920×1080@30fps;室内,0 至 35°C;精度 ≤0.16% @1m | 室内近距、高分辨率,性价比首选 | 产品页 |
| Femto Bolt | ToF(iToF) | 0.25 至 5.46 m,随模式变化;深度 1024×1024@15fps(WFOV)或 640×576@30fps(NFOV);FOV 120°×120°(WFOV)或 75°×65°(NFOV);RGB 3840×2160@30fps | 与 Azure Kinect DK 模式兼容,存量项目迁移友好 | 产品页 |
| Femto Mega | ToF(iToF) | 同 Femto Bolt 的深度规格;内置 NVIDIA Jetson Nano;RGB 4K 最高 25fps;支持 PoE 与以太网 | 自带算力与网口,适合远端部署与多机同步 | 产品页 |
| Gemini 335 / 335L | 双目(主动加被动) | 0.17 至 20 m+,最佳 0.25 至 6 m;深度 1280×800@30fps;FOV H90° V65°;IP65,−10 至 50°C,室内外通用 | AMR、机械臂、人形机器人的主流选择 | 产品页 |
| Gemini 2 | 主动双目(Active Stereo IR) | 0.15 至 10 m;深度 1280×800@30fps;FOV H91° V66°;RGB 1920×1080@30fps;室内与半室外 | 上一代紧凑双目,存量项目与教程较多 | 产品页 |
7.1 深度模式对照表
ToF 的工作距离与视场角是绑定关系,不能拆开看。以 Femto 系列为例:
| 深度模式 | 分辨率 | 视场角 | 工作距离 |
|---|---|---|---|
| NFOV 非装箱 | 640×576 | 75°×65° | 0.5 至 3.86 m |
| NFOV 2×2 装箱 | 320×288 | 75°×65° | 0.5 至 5.46 m |
| WFOV 2×2 装箱 | 512×512 | 120°×120° | 0.25 至 2.88 m |
| WFOV 非装箱 | 1024×1024 | 120°×120° | 0.25 至 2.21 m |
宣传语里的 5.46 m 只在 320×288 的低分辨率窄视场模式下成立。需要 120° 大视场时,最远只能到 2.21 至 2.88 m。选型时应查实际要跑的那个模式所在行的数字,不要采用产品标题上的最大值。
数据来源:Orbbec 官方文档《Femto Mega Hardware Specifications》,更新于 2025-09-15。
一个估算方法:尚未确定模式时,先按 NFOV 加 2×2 装箱,也就是 5.46 m 这一档估算距离,这是 ToF 方案里距离与视场比较均衡的配置;确实需要大范围覆盖时再切到 WFOV,并把距离预期减半。
7.2 软件与生态
- Orbbec SDK v2:官方跨平台 SDK,支持 Windows、Linux、Android,提供深度、彩色、IR、IMU 等数据流,以及对齐、点云等后处理能力。
- ROS2 驱动:官方提供
OrbbecSDK_ROS2驱动包,可发布深度、彩色、点云、相机内参等话题。 - 算力平台:Jetson 系列(Orin Nano、Orin NX 等)与 x86 笔记本均已验证。
- 安装与接入步骤:见本系列后续文章《如何安装并使用奥比中光官方软件》与《在我的机器人中如何接入深度相机(ROS2)》。
典型应用场景
Community Wiki
深度相机开启的是空间交互类应用,与普通摄像头的用途有明显区分。业界已经验证的成熟方向大致分四类。
感知与导航:AGV 与 AMR 的障碍物检测、地形分析,机器人自主导航建图配合 SLAM,无人机避障。
操作与抓取:机械臂的目标定位与 6D 位姿估计,无序抓取,协作机器人的安全区监控。这类应用对近距离精度要求较高。
重建与测量:房间与物体的三维重建,体积测量用于物流计费,人体尺寸测量,AR 与 VR 场景锚定。
人机交互:手势识别、体感游戏、人体骨架跟踪、人脸活体检测用于防照片和屏幕攻击、动作捕捉。
想了解这类设备能做什么,可以直接看实际运行的项目。Orbbec 官方与 SeedStudio、AIFITLAB 等生态伙伴的 wiki 上有大量可复现教程,覆盖房间重建、手语识别等方向。
常见失效原因
Community Wiki
90% 的相机故障报告,实际原因是场景超出了设备的物理边界。下表按现象列出常见原因与处理办法。
| 现象 | 常见原因 | 应对 |
|---|---|---|
| 整幅深度图全黑或全是 0 | 曝光被日光压制;驱动未正常初始化;深度流未使能 | 遮光重测;检查设备枚举;确认 SDK 中深度流配置 |
| 存下来的图几乎全黑 | 深度流刚启动时前几帧数据不完整,或读到的是压缩帧 | 等有效像素占比稳定后再保存,见附录 D 第 3 条 |
| 大片区域无深度 | 玻璃、镜面、黑色吸光材质、超出量程、被遮挡 | 换角度避开反射;缩短距离;贴漫反射胶带 |
| 彩色深度图近处一片黑或一片红 | 伪彩量程设得太小,或按全图最大值归一化 | 固定量程,超量程单独涂色,见第 2.2 节 |
| 户外完全不可用 | 结构光被太阳光淹没 | 改用双目方案,或加装遮光罩并降低工作距离 |
| 同样的表面,深度时好时坏 | 环境光变化;自动曝光在调整 | 固定曝光时间,关闭自动曝光;稳定光源 |
| 深度整体偏移几厘米 | 标定漂移;温度变化;镜头有污渍 | 清洁镜头;预热 5 至 10 分钟后重测;必要时重新标定 |
| 物体边缘出现飞点或拖尾 | 前景与背景混在同一像素视场内 | 属正常现象,用中值滤波、双边滤波或时域滤波处理 |
| 多台相机互相干扰 | 主动光源频率冲突 | 错开调制频率;分时曝光;或改用被动双目 |
| 点云与彩色图错位 | D2C 对齐未开启,或该型号不支持 | 在 SDK 中开启对齐;核对两图分辨率;详见附录 E |
| Jetson 上帧率上不去 | USB 带宽不足;未使用 USB 3.0 接口 | 换 USB 3.0 口;降低分辨率或帧率;避免经过 hub |
| 深度图分辨率看着很方 | 深度传感器原生分辨率低,被上采样插值 | 正常现象,看原生分辨率而非输出尺寸 |
排查顺序固定为:场景、光线、距离、接线、驱动、硬件。先检查环境,最后再怀疑设备。
上手路径
Community Wiki
搞清楚是什么之后,接下来只有两步,按顺序走即可。
第一步 · 让相机出图
装好 Orbbec SDK 与官方查看器,先在桌面环境里看到正常的深度图与彩色图。这一步的重点是确认能稳定取到数值,效果好坏可以后面再调。附录 B 的脚本可以直接使用。 → 《安装并使用奥比中光官方软件》
第二步 · 接进机器人
用鱼香 ROS 安装 Desktop 版 ROS2,编译官方 OrbbecSDK_ROS2 驱动包,在 RViz2 里看到深度点云,并确认话题名称与坐标系。
→ 《在机器人中接入奥比中光深度相机(ROS 2)》
两步跑通之后,再看具体项目的教程,包括重建、抓取、体感、检测,会发现它们都只是在这两步之上叠加算法。
术语表
Community Wiki
| 术语 | 英文 | 含义 |
|---|---|---|
| 深度图 | Depth Map | 逐像素记录到相机距离的二维数组,单位通常为 mm |
| 灰度深度图 | Grayscale Depth | 深度数据的直接可视化,越近越亮,保留完整数值 |
| 彩色深度图 | Colored / Pseudo-color Depth | 同一份数据按色带着色,便于人眼判读 |
| 色带 | Colormap | 把数值映射成颜色的规则,常见 JET、VIRIDIS |
| 量程 | Range | 伪彩映射覆盖的距离区间,如 0 至 6000 mm |
| 点云 | Point Cloud | 由深度图与内参反算出的三维点集合 |
| 内参 | Intrinsics | 焦距、主点、畸变系数等描述相机成像几何的参数 |
| 外参 | Extrinsics | 描述两个传感器之间的相对位姿 |
| 基线 | Baseline | 双目方案中两颗相机光心之间的距离 |
| 视差 | Disparity | 同一物点在左右图中像素位置的偏移量 |
| D2C | Depth to Color | 把深度图对齐到彩色图坐标系 |
| iToF | indirect ToF | 通过测量调制光的相位差间接计算飞行时间 |
| FOV | Field of View | 视场角,分水平、垂直、对角 |
| 飞点 | Flying Pixels | 物体边缘处错误的三维点 |
| 多路径干扰 | Multi-path Interference | 光经多次反射后返回,导致测得距离偏大 |
| 主动与被动 | Active / Passive | 是否主动发射光信号参与测量 |
参考资料与复核记录
Community Wiki
主要依据
| 来源 | 链接 | 用于本文哪些内容 |
|---|---|---|
| Orbbec 官网 · Astra 2 产品页 | orbbec.com/products/structured-light-camera/astra-2 | Astra 2 的距离、分辨率、FOV、精度、工作环境 |
| Orbbec 官网 · Femto Bolt 产品页 | orbbec.com/products/tof-camera/femto-bolt | Femto Bolt 规格与误差定义 |
| Orbbec 官网 · Femto Mega 产品页 | orbbec.com/products/tof-camera/femto-mega | Femto Mega 规格与 Jetson 内置 |
| Orbbec 官方文档 · Femto Mega Hardware Specifications | doc.orbbec.com,更新于 2025-09-15 | 第 7.1 节四种深度模式的对照表 |
| Orbbec 官网 · Gemini 335L 产品页 | orbbec.com/products/stereo-vision-camera/gemini-335l | Gemini 335 与 335L 规格、IP65、MX6800 |
| Orbbec 官网 · Gemini 2 产品页 | orbbec.com/products/stereo-vision-camera/gemini-2 | Gemini 2 规格 |
| Orbbec 技术文章 · The Performance Metrics of 3D Vision Technologies | orbbec.com/?p=37264 | 精度、重复性、帧率、延迟、多机干扰等指标定义 |
| Orbbec 技术文章 · Integrated 3D Vision Systems | orbbec.com/blog/integrated-3d-vision-systems | 三条技术路线的原理与适用场景划分 |
| AIFITLAB Wiki | wiki.aifitlab.com | 栏目结构与品牌、产品、Quick start 分级参考 |
| SeedStudio Wiki | wiki.seedstudio.com | 产品 wiki 页的章节组织方式参考 |
文中数值的使用说明
- 所有具体参数均取自官方来源,未使用第三方博客、电商页或媒体转载的数值。
- 数值用于说明技术路线的量级差异与选型方向,不构成选型承诺或性能保证。
- 参数随固件版本与工作模式变化,实际选型以官方最新数据手册和手上设备的实测结果为准。
- 本文实拍图见第 2、3 节,由作者用 Orbbec Astra 2 在实验室采集,参数见附录 B。
- 若本文数值与官方页面出现出入,以官方页面为准,并请通过下方反馈渠道告知修正。
附录 A:图片索引
Community Wiki
| 图号 | 所在章节 | 内容 | 原始文件 |
|---|---|---|---|
| 图 1 | 2.1 一次实拍 | 同一帧的灰度深度图与彩色深度图并排,三处无数据区域用圆圈标出 | fig_gray_vs_color.png |
| 图 2 | 2.2 彩色热度图的读数规则 | 左为 JET 色带与量程标尺,右为同一帧中三类黑洞的成因标注 | fig_depth_reading.png |
| 图 3 | 4. 从深度图到三维坐标 | 针孔相机模型,展示物点、像素与反投影公式的关系 | fig_pinhole.png |
| 图 4 | 5. 三条主流技术路线 | 结构光、ToF、双目三者的原理示意 | fig_three_routes.png |
| 图 5 | 6. 场景反推选型 | 由场景特征逐步缩小到技术路线的判断流程 | fig_select_flow.png |
| 图 6 | 附录 B 原始采集图 | 彩色图,1280 × 720 | color.png |
| 图 7 | 附录 B 原始采集图 | 伪彩深度图,1600 × 1200 | depth_color.png |
| 图 8 | 附录 B 原始采集图 | 16 位灰度深度图,1600 × 1200 | depth_gray.png |
图 1 与图 2 中的实拍画面来自同一帧深度数据,原始输出见图 6 至图 8,采集条件见附录 B。图 3 至图 5 为示意图,由程序绘制。
附录 B:实拍样张与采集参数
Community Wiki
第 2、3 节的图来自以下这次采集,参数可复现。
| 项目 | 数值 |
|---|---|
| 设备 | Orbbec Astra 2 |
| 深度分辨率 | 1600 × 1200 |
| 彩色分辨率 | 1280 × 720 |
| 深度格式 | Y16,16 位,单位 mm |
| 伪彩量程 | 0 至 6000 mm,JET |
| 有效深度像素占比 | 80.7% |
| 6 m 以内像素占比 | 61.4% |
| 深度范围 | 516 mm 至 16616 mm |
| 深度中位数 | 2386 mm |
| D2C 对齐 | 未做,见附录 E |
| 场景 | 实验室工位:前台面与保温杯为前景,右侧白色货架为中景,后方实验室纵深为远景 |
原始采集图
以下三张为本次采集输出。图 1 由深度数据的灰度显示版与伪彩显示版合成;浏览器预览 16 位 depth_gray.png 时可能显得很暗,这是显示方式造成的,原始毫米值仍保存在文件中。
彩色图,1280 × 720,对应 Astra 2 的 RGB 输出:

伪彩深度图,1600 × 1200,量程 0 至 6000 mm:

16 位灰度深度图,1600 × 1200,像素值即毫米数,此处为便于观看做了亮度拉伸:

原始数据请保留 16 位灰度版本。彩色图和截图都无法还原毫米值,原始文件一旦丢失,这组数据只能看不能用。上图的亮度经过拉伸,不能用于读取距离。
附录 C:采集脚本
Community Wiki
两个版本。精简版适合第一次跑通,完整版是本文所有实拍图的生成脚本。
依赖:pip install pyorbbecsdk2 opencv-python numpy
C.1 精简版
import cv2import numpy as npimport pyorbbecsdk as ob
ctx = ob.Context()dev = ctx.query_devices().get_device_by_index(0)pipe = ob.Pipeline(dev)cfg = ob.Config()cfg.enable_stream(ob.OBStreamType.DEPTH_STREAM)cfg.enable_stream(ob.OBStreamType.COLOR_STREAM)pipe.start(cfg)
for _ in range(200): frames = pipe.wait_for_frames(1000) if frames is None: continue depth_frame = frames.get_depth_frame() color_frame = frames.get_color_frame() if depth_frame is None or color_frame is None: continue
# Astra 2 启动前几帧是 RLE 压缩,必须等到 Y16 才有完整深度 if depth_frame.get_format() != ob.OBFormat.Y16: continue
w, h = depth_frame.get_width(), depth_frame.get_height() raw = np.frombuffer(depth_frame.get_data(), dtype=np.uint16) if raw.size != w * h: continue depth_mm = raw.reshape(h, w) # 单位:毫米
# 伪彩:固定 6 m 量程,近处暖、远处冷,无效涂黑、超量程涂深灰 MAX_MM = 6000 norm = 255 - (np.clip(depth_mm, 0, MAX_MM) / MAX_MM * 255).astype(np.uint8) depth_vis = cv2.applyColorMap(norm, cv2.COLORMAP_JET) depth_vis[depth_mm == 0] = (0, 0, 0) depth_vis[depth_mm > MAX_MM] = (40, 40, 40)
# 彩色图,Astra 2 默认输出 MJPG 或 YUYV cw, ch = color_frame.get_width(), color_frame.get_height() buf = np.frombuffer(color_frame.get_data(), dtype=np.uint8) if color_frame.get_format() == ob.OBFormat.MJPG: color = cv2.imdecode(buf, cv2.IMREAD_COLOR) else: color = cv2.cvtColor(buf.reshape(ch, cw, 2), cv2.COLOR_YUV2BGR_YUYV)
cv2.imwrite("color.png", color) cv2.imwrite("depth_color.png", depth_vis) cv2.imwrite("depth_gray.png", depth_mm) # 16 位,保留原始毫米值 break
pipe.stop()C.2 完整版
相比精简版,增加了异常处理、启动等待、参数快照和交互保存。
"""capture_depth_color.py读取 Orbbec 相机,保存灰度深度图与彩色图。依赖:pip install pyorbbecsdk2 opencv-python numpy输出:depth_gray.png(16位) / depth_color.png(伪彩) / color.png / meta.txt"""
import os, sys, datetimeimport cv2import numpy as npimport pyorbbecsdk as ob
SAVE_DIR = "output"
def depth_to_color(depth_mm, max_mm=6000): """毫米深度 -> 伪彩图。近处暖色、超出量程深灰、无效深度纯黑。""" clipped = np.clip(depth_mm.astype(np.float32), 0, max_mm) norm = 255 - (clipped / max_mm * 255).astype(np.uint8) color = cv2.applyColorMap(norm, cv2.COLORMAP_JET) color[depth_mm == 0] = (0, 0, 0) # 测不到 color[depth_mm > max_mm] = (40, 40, 40) # 超出量程 return color
def frame_to_bgr(frame): """彩色帧 -> BGR,兼容 MJPG / YUYV / UYVY / RGB / BGR / RGBA / BGRA。""" w, h = frame.get_width(), frame.get_height() fmt = frame.get_format() data = np.frombuffer(frame.get_data(), dtype=np.uint8) if fmt == ob.OBFormat.RGB: return cv2.cvtColor(data.reshape(h, w, 3), cv2.COLOR_RGB2BGR) if fmt == ob.OBFormat.BGR: return data.reshape(h, w, 3).copy() if fmt == ob.OBFormat.RGBA: return cv2.cvtColor(data.reshape(h, w, 4), cv2.COLOR_RGBA2BGR) if fmt == ob.OBFormat.BGRA: return cv2.cvtColor(data.reshape(h, w, 4), cv2.COLOR_BGRA2BGR) if fmt == ob.OBFormat.YUYV: return cv2.cvtColor(data.reshape(h, w, 2), cv2.COLOR_YUV2BGR_YUYV) if fmt == ob.OBFormat.UYVY: return cv2.cvtColor(data.reshape(h, w, 2), cv2.COLOR_YUV2BGR_UYVY) if fmt == ob.OBFormat.MJPG: return cv2.imdecode(data, cv2.IMREAD_COLOR) raise RuntimeError(f"不支持的彩色格式:{fmt}")
def depth_to_mm(frame): """深度帧 -> uint16 毫米数组。非 Y16 帧返回 None(跳过)。""" if frame.get_format() != ob.OBFormat.Y16: return None w, h = frame.get_width(), frame.get_height() raw = np.frombuffer(frame.get_data(), dtype=np.uint16) if raw.size != w * h: return None depth = raw.reshape(h, w) scale = frame.get_depth_scale() if scale and abs(scale - 1.0) > 1e-6: depth = (depth.astype(np.float32) * scale).astype(np.uint16) return depth
def save_all(color, depth, dvis, info): os.makedirs(SAVE_DIR, exist_ok=True) cv2.imwrite(f"{SAVE_DIR}/color.png", color) cv2.imwrite(f"{SAVE_DIR}/depth_gray.png", depth) # 16 位原始值 cv2.imwrite(f"{SAVE_DIR}/depth_color.png", dvis)
valid = depth[depth > 0] with open(f"{SAVE_DIR}/meta.txt", "w", encoding="utf-8") as f: f.write(f"采集时间: {datetime.datetime.now():%Y-%m-%d %H:%M:%S}n") f.write(f"设备: {info.get_name()}n") f.write(f"序列号: {info.get_serial_number()}n") f.write(f"固件: {info.get_firmware_version()}n") f.write(f"深度尺寸: {depth.shape[1]}x{depth.shape[0]}n") f.write(f"彩色尺寸: {color.shape[1]}x{color.shape[0]}n") f.write(f"有效深度占比: {valid.size / depth.size * 100:.1f}%n") if valid.size: f.write(f"深度范围: {valid.min()} ~ {valid.max()} mmn") print(f"[已保存] 4 个文件 -> {os.path.abspath(SAVE_DIR)}")
def main(): ctx = ob.Context() devs = ctx.query_devices() if devs.get_count() < 1: print("未检测到设备") return 1
dev = devs.get_device_by_index(0) info = dev.get_device_info() print(f"已连接:{info.get_name()} SN={info.get_serial_number()}")
pipe = ob.Pipeline(dev) cfg = ob.Config() cfg.enable_stream(ob.OBStreamType.DEPTH_STREAM) cfg.enable_stream(ob.OBStreamType.COLOR_STREAM) pipe.start(cfg)
stable = 0 try: while True: fs = pipe.wait_for_frames(1000) if fs is None: continue df, cf = fs.get_depth_frame(), fs.get_color_frame() if df is None or cf is None: continue
depth = depth_to_mm(df) if depth is None: continue # RLE 帧,跳过
ratio = (depth > 0).sum() / depth.size * 100 if ratio > 30: stable += 1
color = frame_to_bgr(cf) dvis = depth_to_color(depth)
cv2.imshow(f"color | depth (valid {ratio:.1f}%)", np.hstack([ cv2.resize(color, (640, 360)), cv2.resize(dvis, (640, 480))[:360] ]))
# 等稳定 10 帧再存,避开启动时的不完整帧 if stable == 10: save_all(color, depth, dvis, info) print("按 s 再存,按 q 退出")
k = cv2.waitKey(1) & 0xFF if k == ord("q"): break if k == ord("s") and ratio > 10: save_all(color, depth, dvis, info) finally: pipe.stop() cv2.destroyAllWindows() return 0
if __name__ == "__main__": sys.exit(main())C.3 自己生成灰度与彩色对比图
需要做成图 1 那样的并排对比,在拿到 depth_mm 后加几行即可:
MAX_MM = 6000
# 灰度版:近处亮、远处暗grey = 255 - (np.clip(depth_mm, 0, MAX_MM) / MAX_MM * 255).astype(np.uint8)grey[depth_mm == 0] = 0grey[depth_mm > MAX_MM] = 40
# 彩色版:同一份 grey 套色带color_depth = cv2.applyColorMap(grey, cv2.COLORMAP_JET)
cv2.imwrite("depth_gray_view.png", grey) # 8 位,仅用于显示cv2.imwrite("depth_color.png", color_depth) # 8 位三通道这里有两个名字相近的灰度文件:depth_gray.png 为 16 位,存毫米值,用来计算;depth_gray_view.png 为 8 位,存拉伸后的亮度,用来看。
附录 D:实机踩坑记录
Community Wiki
以下 6 条均为实测结果,设备为 Orbbec Astra 2,固件 2.8.20,pyorbbecsdk 2.9.3。
| 现象 | 真实原因 | 解法 |
|---|---|---|
cannot reshape array of size 552422 | 启动前几帧是 RLE 压缩视差图,不是 Y16 | 检测 get_format() != Y16 就跳过,等它自行切换 |
用 FormatConverter 转换后拿不到毫米值 | 该过滤器把深度渲染成 3 通道彩色图像,convertType 取 0 至 21 结果都相同 | 不使用转换器,直接等待 Y16 帧 |
| 存下来的深度图几乎全黑 | Y16 刚切换后的十几帧数据不完整,有效率仅 1% 至 2% | 等有效率超过 30% 且连续稳定 10 帧再保存 |
| 彩色深度图近处物体一片黑 | 伪彩按全图最大值归一化,近景被压到色带底端 | 固定量程,例如 6 m;超量程涂深灰,无效涂黑 |
HResult 0xc00d3704 硬件 MFT 启动流式传输失败 | 相机被其他进程占用,例如预览窗口未关闭 | 关闭占用进程后重试 |
| 预览里彩色图被拉伸变形 | 彩色 1280×720 为 16:9,深度 1600×1200 为 4:3,宽高比不同 | 按高度等比缩放后拼接,不要强行拉满 |
第一条影响最大。Astra 2 的深度流在启动后前 2 帧格式为 OBFormat.RLE,是压缩视差数据,只有几十 KB;第 3 帧起自动切换为 OBFormat.Y16,尺寸 1600×1200×2 即 3,840,000 字节。本文记录的所有 reshape 报错和全黑图,根源都在这里。
附录 E:关于 D2C 对齐
Community Wiki
本文的实拍图未做深度到彩色(D2C)的对齐,两图视野不同,不能像素级对应。在 Astra 2 上的实测结果如下:
config.set_align_mode(OBAlignMode.SW_MODE)不生效,深度输出仍为 1600×1200,彩色为 1280×720- 改用独立的
Align过滤器可以把深度转到 1280×720,但会报Unsupported format for C2D conversion,或导致有效像素骤降至 4.4% get_camera_param()返回的内参为全 0,无法手工配准
如果需要彩色与深度精确叠加,例如做彩色点云或物体分色渲染,需换用支持的型号,或在应用层自行完成配准。
不过,说明数据本身长什么样并不需要对这两颗镜头做对齐。本文对比灰度与彩色两种呈现方式,用的是同一份深度数据,两种画法逐像素对应。
讨论、纠错与持续维护
Forum / GitHub / Community