在 Orbbec Viewer 里,把 Gemini 338Le 的彩色和深度都设为 1280 × 800、30 FPS,现场没能启用这个组合。排查前还缺两个数:彩色流选的是 YUYV 还是 MJPEG,相机到主机之间的链路实际协商到了多少 Mbps?同样的分辨率和帧率,这两个条件足以改变判断。
系列第一篇用 SDK 2.10.6 回放了固件 1.8.17 录制的 bag,其中一份同时包含 424 × 266 @ 30 FPS 的 Y16 深度和 1280 × 720 @ 30 FPS 的 MJPG 彩色。那份录像说明两路可以使用不同分辨率。这篇转到实时采集,讨论如何为每条流做带宽预算,以及怎样判断瓶颈落在网络、主机处理还是写盘。
先分清数据从哪里来
338Le 通过 M12 千兆以太网口传输数据。双目基线为 95 mm,理想工作距离为 0.25–6 m。官方列出的深度档位包括 1280 × 800 @ 30 FPS 和 848 × 530 @ 60 FPS。彩色在 1280 × 800 下能否选到 60 FPS 取决于格式:千兆档位表列出的 MJPEG 最高为 60 FPS,同分辨率 YUYV 最高为 30 FPS。每张表描述的是单路能力。以太网规格书,第 4.4 节 · 338Le 产品规格
| 通道 | 帧里有什么 | 开流与处理时关注什么 |
|---|---|---|
| Depth | Y16 深度,每像素 2 字节 | 保留原始值和该帧的深度比例。分辨率决定空间采样密度,帧率决定采样间隔。 |
| Color | YUYV 或 MJPEG 彩色图像 | YUYV 按每像素 2 字节估算。MJPEG 帧长随场景和压缩设置变化。若算法需要 RGB8,要另算解码、转换和内存开销。 |
| IR、左 IR、右 IR | 红外灰度图,原生 Y8 每像素 1 字节 | 按设备实际启用的红外路数计算带宽。使用深度图并不要求同时向主机输出左右 IR。 |
| Accel、Gyro | 三轴加速度、三轴角速度,分别带采样时间 | 按各自的采样频率和每样本字节数估算。保留时间戳,按时间窗关联视频,避免每帧图像只留一个 IMU 样本。 |
| Confidence(若可用) | 置信度数据 | 先枚举传感器和 profile。SDK 有对应类型,本次材料未确认 338Le 能提供独立置信度流。 |
| D2C、XYZ / RGB 点云 | 将深度对齐到彩色坐标系,或根据深度与标定生成点云 | 在主机用 SDK 生成时,计入主机处理和内存预算。只有再传给其他机器时,才另计这段网络流量。 |
读相机规格时,先看四个参数
基线与视差。338Le 的双目基线是 95 mm,指左右红外相机光学中心之间的距离。同一个箱角在左右图像里会落在不同的位置。把两幅图校正到对应点位于同一行后,这两个位置的水平像素差就叫视差。用理想双目模型估算,光轴方向的深度 Z ≈ fx × B / d,其中 fx 是以像素计的焦距,B 是基线,d 是以像素计的视差。目标越远,视差越小,同样大小的匹配误差会造成更大的深度误差。
视场角(FOV)。规格表按水平 × 垂直给出角度。338Le 在 2 m 处的标称深度视场为 90° × 65°(±3°),彩色视场为 94° × 68°(±3°)。用水平 90° 估算,距离相机 2 m、正对相机的平面上,画面覆盖的宽度约为 2 × 2 m × tan(45°) = 4 m。这是几何覆盖范围,画面边缘是否有有效深度,还取决于左右相机能否同时看到并匹配该点。
工作距离与空间精度。厂商给出的理想工作距离为 0.25–6 m。规格表另列出厂内标定条件下的空间精度(Spatial Precision):2 m 处 ≤0.8%,4 m 处 ≤1.6%。做项目验收时,可以把平板放在已知距离,分别记录平均读数与已知距离的偏差,以及平板上各点深度的离散程度。这样能区分整块平板测偏了,还是同一平面上的点起伏太大。
全局快门与曝光时间。338Le 的深度和彩色均采用全局快门,同一幅图的各行在同一曝光时段采光。拍移动物体时,即使帧率维持在 30 FPS,曝光时间太长也会让边缘模糊。FPS 决定相邻帧约隔多久,曝光时间决定每帧积累多久的光。Gemini 338Le 官方规格与测量条件
在彩色图上点一下,该读哪个深度像素
假设要读取彩色图里一个箱角的深度。即使彩色图和深度图都是 1280 × 800,直接把彩色图上的坐标拿到原始深度图里查,也可能读到箱子后面的墙。两枚镜头安装在不同位置,各自覆盖的角度也不同。
内参描述一枚相机怎样把三维点投到自己的图像上。fx、fy 是以像素为单位的焦距,cx、cy 是主点的像素坐标。畸变参数描述实际镜头相对理想投影的偏离。SDK 里可分别从当前深度和彩色档位调用 getIntrinsic() 和 getDistortion()。切换分辨率或裁剪方式后,应按新档位取值。
外参描述两个相机坐标系之间的旋转和平移。对深度档位调用 getExtrinsicTo(colorProfile),得到从深度到彩色的变换,平移单位是毫米。D2C(Depth to Color)用这些参数把深度点投到彩色图上。完成对齐后,才能按箱角的彩色坐标查询对应深度。箱子边缘仍可能出现空洞,例如彩色镜头看得到墙面上的一点,深度镜头的视线却被箱子挡住。SDK 档位与标定参数接口 · 内参、畸变与外参的字段定义
D2C 解决两幅图上位置怎样对应的问题。拍移动物体还要核对两帧的采集时间,即使空间标定准确,箱子在两次曝光之间移动,也会造成轮廓错位。若只发送原始深度和彩色两路、在主机做对齐,网口预算仍按这两路输入计算。
读懂格式名:一个像素存了什么
在 Viewer 或 SDK 里选一路视频时,会看到类似 Depth · Y16 · 1280 × 800 · 30 FPS 的档位(stream profile)。Depth 指这路数据来自深度通道,Y16 说明像素怎么存,1280 × 800 是一帧的像素数,30 FPS 表示目标是每秒 30 帧。看懂这四项,才能决定怎样读取像素、该为这一路预留多少带宽。
Y16 深度。每个像素用 16 位,也就是 2 字节,保存一个无符号原始值。要知道物体有多远,还要读这一帧的 getValueScale()。例如,像素值为 2400,若比例是 0.5 毫米/单位,该点的深度就是 1200 毫米。值为 0 表示该点没有有效深度。16 位描述的是存储宽度;相机在某个距离能测多准,要看该距离下的测量误差。SDK 读取深度值的示例
Y14 与打包。Y14 用 14 位表示一个单通道样本。紧密打包时,4 个像素正好占 56 位,也就是 7 字节。Orbbec SDK 2.10.6 默认会把 Y14 解包成 Y16,同样的 4 个像素在应用里就占 8 字节。解包方便程序按 uint16_t 读取,原始样本仍然只有 14 位信息。按 1280 × 800、30 FPS 举例,若线上确实传紧密打包的 Y14,图像载荷是 430.08 Mbps;解包后的帧数据量则是 61.44 MB/s。338Le 以太网规格表列出的深度格式是 Y16,本文按该档位做预算。Y14 用来说明打包差别,能否在自己的设备上选它,要看设备实际列出的档位。SDK 格式定义与默认解包行为
Y8 红外。每个像素用 1 字节记录一个灰度值,范围为 0–255。在 IR 图上,亮点表示该处记录到的红外信号较强。要读同一位置的距离,应查与该位置对应的深度像素。左右 IR 各是一幅图,同时输出两路就要算两份载荷。
YUYV、MJPEG 和 RGB8。它们都能表示彩色图像,算字节的方式却不同。YUYV 每两个相邻像素按 Y0 U Y1 V 排列:各有一个亮度值,共用一组色度值,所以两个像素共 4 字节。MJPEG(也写作 MJPG)逐帧做 JPEG 压缩,帧长随画面变化。解码成 RGB8 后,红、绿、蓝各占 1 字节,每像素合计 3 字节。做带宽统计时要注明取数位置:压缩帧长用来算彩色载荷,解码后的帧长用来算主机缓冲区。Linux 内核文档中的 YUYV 字节布局
算清 1280 × 800 @ 30 的载荷
对紧密排列、未压缩的图像,MB/s = 宽 × 高 × 每像素字节数 × FPS ÷ 1,000,000。再乘 8 得到 Mbps。这里采用十进制单位。一帧 1280 × 800 的 Y16 占 2,048,000 字节,30 帧每秒就是 61.44 MB/s,即 491.52 Mbps。下表只算图像,不含协议头、元数据和包间开销。
| 通道 | 宽 × 高 @ FPS | 字节/像素 | MB/s | Mbps |
|---|---|---|---|---|
| 深度 Y16 | 1280 × 800 @ 30 | 2 | 61.44 | 491.52 |
| 彩色 YUYV | 1280 × 800 @ 30 | 2 | 61.44 | 491.52 |
| 单路 IR Y8 | 1280 × 800 @ 30 | 1 | 30.72 | 245.76 |
| 深度 Y16 | 1280 × 800 @ 15 | 2 | 30.72 | 245.76 |
| 深度 Y16 | 848 × 530 @ 60 | 2 | 53.93 | 431.46 |
| 深度 Y16 | 640 × 400 @ 30 | 2 | 15.36 | 122.88 |
| 深度 Y16 | 424 × 266 @ 30 | 2 | 6.77 | 54.14 |
深度 Y16 加彩色 YUYV,同为 1280 × 800 @ 30 时,合计 983.04 Mbps。与千兆线速相比,只剩 16.96 Mbps 给全部传输开销,常见的标准 MTU 以太网传输很难承载这个组合。全双工也不会把相机发往主机的单向带宽翻倍。如果链路只协商到百兆,单路 491.52 Mbps 的深度载荷就已经超出上限。厂家的百兆表在 1280 × 800 下只列出了 5 FPS 的 Y16 深度。百兆档位与多流限制
如果还要把两路 1280 × 800 @ 30 的 Y8 红外图传到主机,两路 IR 合计又是 491.52 Mbps。还有一种容易漏算的情况:848 × 530 @ 60 的 Y16 仍需 431.46 Mbps,虽然像素少了,帧率却翻了一倍。降低分辨率时要把 FPS 一起代回公式。
换成 MJPEG 后,量帧长比猜压缩率可靠
保留 1280 × 800 @ 30 的深度,把彩色改为 MJPEG。深度仍占 491.52 Mbps,彩色则按压缩后的字节数计。举个算例,若彩色每帧平均 200,000 字节,30 FPS 就占 48 Mbps,两路图像载荷合计 539.52 Mbps。这里的 200,000 字节是假设值,不能当作 338Le 的典型码率。
实际采集时,在彩色帧解码前累加 frame->getDataSize(),用 8 × 总字节数 ÷ 采样秒数 ÷ 1,000,000 算接收载荷。同时保留一秒窗口的峰值和最大单帧长度,运动画面与静止画面分别采一段。若 SDK 已将彩色帧转为 RGB8,统计到的是主机内存数据。1280 × 800 @ 30 的 RGB8 单次写出就是 92.16 MB/s,额外拷贝还会继续消耗内存带宽。
官方以太网规格书的功耗测试分别用过 1280 × 800 的 Y16 深度与 MJPEG 彩色双流,典型功耗条件为 15 FPS,最大功耗条件为 30 FPS,IMU 均为 1000 Hz。它给了我们一个值得复现的组合,但这些页测的是功耗,不能据此推算现场丢帧率、CPU 占用或持续采集时长。官方功耗测试条件,第 45–46 页
Viewer 开不起来时,按什么顺序试
- 查实际链路。确认相机端和主机端各自连接的交换机端口速率。Linux 可用
ethtool 网卡名看协商速率和双工模式,用ip -s link show dev 网卡名对比采集前后的计数。macOS 可先用networksetup -listallhardwareports找到网卡,再用ifconfig 网卡名查看 media。主机端显示千兆,只能确认该段链路。 - 从单流到双流。从实际枚举的 profile 中选 1280 × 800 @ 15,分别试 Depth Y16 和 Color MJPEG,再同时启用。关闭额外 IR 输出、D2C、点云和写盘。每轮记录能否启动、多久收到首帧、每路实际 FPS。
- 一次只改一个条件。15 FPS 双流稳定后,将目标提高到 30 FPS。若失败,再试设备确实提供的低分辨率双流组合。若 Viewer 连目标格式都没有列出,先查工作模式、固件与 profile。如果已经收到帧但达不到目标 FPS,则继续查网卡计数、回调间隔和接收队列。
- 逐个加回主机负载。基础双流稳定后,依次打开 MJPEG 解码、RGB-D 对齐、点云和落盘。在哪一步开始出现队列增长或帧号跳变,就从那一步查。设备时间戳用于比较帧间隔,测主机处理耗时应使用主机单调时钟。
让采集封装报出实际跑到的结果
枚举到 profile 后,仍要用完整组合启动 Pipeline 并逐路确认收帧。建议会话同时记录请求档位和最终选中的档位。若允许降级,把 30 FPS 改为 15 FPS、或将深度改为较低分辨率,都应体现在录制清单里。IMU 有自己的采样节奏,官方多流示例将视频与 IMU 分开采集。Orbbec SDK v2.10.6 多流示例
性能记录至少留三组数字。网卡侧记录接收字节与错误计数的增量,SDK 回调侧按通道记录帧数、帧号间断和载荷,写盘侧记录队列峰值、主动丢弃数和实际保存数。比如 30 FPS 对应约 33.3 ms 的帧间隔;若某个串行写入环节平均每帧都耗时 40 ms,它每秒最多处理约 25 帧,队列迟早会满。此时就算网口还有余量,保存的录像也会掉帧。
回到这次 Viewer 的问题,已知的是目标双流组合没有成功启用。还缺少当时的彩色格式、链路协商结果和逐路收帧记录。下一次最值得留下的,就是这三项,加上同一组参数在 15 FPS 和 30 FPS 下的对照结果。
Orbbec SDK 使用笔记(二):Gemini 338Le 的多流带宽怎么算
本文采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。
评论交流
欢迎留下你的想法