AcmeX's Blog

AuraVLA RGB-D 相机桥:序列元数据、原子快照与 stale 防护

从 AuraVLA 相机桥源码出发,说明单调时钟、RGB-D 原子快照、sequence 元数据和帧龄门控如何阻止旧帧与半写入进入 GraspNet。

TABLE OF CONTENTS01 / 07

问题不是采样率

相机桥的危险状态不是“没有新图像”,而是 RGB、深度和元数据来自不同采样时刻。若读端在写文件的中间阶段触发推理,可能得到上一帧 RGB、新一帧深度或截断的 JSON;GraspNet 随后会把不一致的观测误认为真实几何。

AuraVLA 将相机输出定义为一个带版本的快照,而不是三个可以独立读取的文件。实现位于 aura_hardware/aura_camera_bridge/camera_bridge.py 和 Isaac 运行时的 start_camera_bridge.py

单调时钟控制刷新

相机桥用 time.monotonic() 比较刷新间隔,不依赖 Isaac Sim 的渲染帧数,也不受系统时间回拨影响。当前源码默认 update_interval_sec=12.0,非正数在构造时直接抛出异常:

now = time.monotonic()
if now - last_capture >= self.update_interval_sec:
    self.capture_once()
    last_capture = now

12 秒是当前任务链路的配置值,不是相机硬件帧率或性能指标;改变它需要同时重新评估帧龄门控和 GraspNet 三帧采样窗口。

原子快照协议

RGB、深度和 metadata.json 先写入带 .tmp 后缀的临时文件,再通过 os.replace() 替换正式路径。读端因此只会看到旧的完整文件或新的完整文件,不会读到半个 PNG、深度文件或 JSON。

每次成功采样递增 sequence,元数据同时记录:

  • captured_at_unix:用于判断帧龄;
  • sequence:用于识别同一批快照;
  • 相机 Prim、分辨率和深度范围;
  • update_interval_sec:记录采样配置。

感知端必须把 RGB、深度和 metadata 作为一个版本组读取。JSON 解析失败、文件缺失、序列不一致或帧龄过大时,应返回 stale,而不是沿用上一批数据。

就绪与帧龄是两道门

aura_bringup/config/config.yaml 当前将 max_frame_age_sec 设为 3refresh_timeout_sec 设为 15。相机桥的 ready 只表示桥进程能够工作,不能证明当前 RGB-D 已经新鲜;感知端仍需读取 captured_at_unix 并计算实际 age。

启动顺序也分为两步:IsaacRuntimeLauncher 先等待 Isaac task bridge ready,再调用相机桥恢复逻辑。若在超时内没有产生有效 metadata,应报告相机 stale 或刷新超时,而不是继续调用 VLM。

错误恢复

相机桥保留最近一次错误,成功写入一组完整快照后才清除。这样可以区分 Isaac 未启动、相机 Prim 不存在、深度读取失败、目录不可写和读端发现旧帧等问题。

恢复后,sequence 必须继续递增,captured_at_unix 必须重新接近当前时间;只检查文件存在会把旧文件误判为恢复成功。

可执行验证

现有 aura_hardware/aura_isaac_bridge/test/test_telemetry.py 已验证遥测 JSON 的原子写入和 sequence 递增。相机桥回归还应覆盖以下边界:

  1. 在 PNG/深度/JSON 写入之间并发读取,确认不会出现截断文件;
  2. 人为延迟深度文件,确认版本组被拒绝;
  3. 停止相机桥,确认超过 3 s 后进入 stale;
  4. 修改系统时间,确认刷新周期仍由 monotonic clock 控制;
  5. 恢复桥后确认 sequence 递增且旧错误被清除。

这些测试验证的是数据完整性和拒绝边界,不应被写成相机帧率提升或端到端成功率。

小结

RGB-D 桥的工程价值是给感知输入增加了可验证的版本语义:单调时钟控制节奏,原子替换保证文件完整,sequence 和帧龄保证读到的是同一批新数据。只有通过这层门控的快照,才有资格进入 GraspNet 和后续抓取规划。