COLLECT
COLLECT 把遥操作 episode 录制为标准机器人学习数据集,并在同一标签页内做 PASS/FAIL 审查。
概览
COLLECT 是控制台六个标签页(DEBUG·MANUAL·COLLECT·REPLAY·EVAL·RESULT)的第三个。它不跑策略:每段 episode 同时记录机械臂的观测状态和操作者下达的动作,得到的数据集可直接回放与训练。
标签页由配置门控。只有启动时的配置带有
collection 段,COLLECT 才会启用(见下文)。无独立进程。前置条件
eva在 PATH 中(见安装)。- 机器人上电,主从遥操作装置已运行。
- 数据源正在发布——ROS 下即驱动与相机节点已起(如
roscore加机械臂/相机节点)。 - 面向该机器人的采集配置(见下文)。
先用 MANUAL 确认机械臂运动与相机推流正常。
为什么 COLLECT 可能是灰的
- 无采集配置 → COLLECT 禁用(DEBUG、MANUAL、REPLAY 仍可用)。
- 带采集配置 → COLLECT 启用。
工作流
| # | 步骤 | 动作 |
|---|---|---|
| 1 | TASK | 选任务(列表来自 collection.tasks)。随 episode 一起保存,每个任务独立文件夹。 |
| 2 | START | 开始录制。状态 COLLECTING,帧数递增。 |
| 3 | STOP | episode 交给后台保存。状态 SAVING,写盘完成前保持。 |
| 4 | QC | 回放并标记 PASS/FAIL,可选备注。 |
录制内容。同时记录遥操作的两半:观测状态(关节角度 + EEF)与下达动作(关节角度 + EEF)。由配置决定,不在标签页里调。
录制期间
- START——立即开始录制。保存与图像编码在后台 worker 上运行,采集不阻塞。
- 积压——主机跟不上时队列增长并打印警告,录制继续。
- 预热帧丢弃——主臂尚未跟上时的开头几帧不带动作,会被跳过。
- STOP——episode 校验、打包、入队保存,可立即开始下一段。0 帧的空 episode 不保存。
- 取消——丢弃当前已采集的全部数据。
状态行:COLLECTING / SAVING / QUEUE_FULL / IDLE,附帧数、队列深度与 ETA。
录制为追加。重新启动后录入同一文件夹时,新 episode 追加在已有之后。磁盘布局见数据采集。
成功与常见故障
| 现象 | 原因 / 处理 |
|---|---|
| 帧数停在 0 | 无数据到达。核对驱动与配置话题名是否与发布端一致。 |
| 相机黑屏或缺失 | 未推流或相机名不匹配。先在 MANUAL 验证。 |
QUEUE_FULL | 保存跟不上。等回到 IDLE;若反复出现,降低 fps 或提高 save_queue_max。 |
| 审查时被标红 | 某项自动检查命中——见质量控制。 |
质量控制
STOP 后回放并标记 PASS/FAIL。同时每段 episode 还会运行自动检查;任一项不通过即标红,原因随数据一同持久化。
| 标记 | 触发条件 | 处理 |
|---|---|---|
episode_too_short | 帧数少于 min_episode_frames。 | 重录更长段,或降低阈值。 |
non_monotonic_timestamp | 某帧时间戳未越过前一帧。 | 重录。反复出现则检查发布端时钟。 |
missing_camera · invalid_image_shape | 配置相机缺失,或图像不是 H×W×3。 | 核对推流与配置中的相机名。 |
missing_configured_column · invalid_vector_dim | 配置字段缺失或长度不符。 | 核对话题是否发布、手臂数是否一致。 |
non_finite_value | 录值出现 NaN/Inf。 | 自动置零;频繁则重录。 |
frame_count_mismatch | 相机视频帧数与数据表行数不符。 | 重录;通常是相机掉帧或卡住。 |
坏字段补零,不丢弃。schema 保持一致;标记随 episode 持久化。
需要更细审查并为每段补写指令文本,使用 REPLAY。
编写采集配置
一份配置在声明要录哪些列的那一刻即成为采集配置——这一点正是 COLLECT 的启用条件。采集配置通过 _base_ 继承一份按机器人区分的部署文件,并叠加一个 collection 段(见配置)。
设置项
| 设置项 | 作用 |
|---|---|
storage.log_dir | 输出根目录,按任务分子目录。留空 → 以本配置命名的默认目录。 |
storage.fps | 目标帧率。 |
storage.save_queue_max | 同时排队保存的最大 episode 数。排满 → QUEUE_FULL。 |
schema.robot_type | 机器人 id,如 agilex_AGILEX。 |
schema.min_episode_frames | episode_too_short 的阈值。 |
schema.arms | 要录的手臂,每只一条。 |
schema.cameras | 要录的相机,至少一个。 |
schema.columns | 四个必填数据集字段:qpos、eef、action_qpos、action_eef。 |
transport.ros1.groups | 每只手臂的观测与下达动作源话题。ROS 2 用 ros2。 |
tasks | TASK 选择器里的任务字符串。 |
复制机器人自带的采集配置(如 configs/02_collection/dual_agilex_AGILEX.py)并改其中标出的值。标 ← 改成… 的行必须与你的机器人对应。
_base_ = ['<你机器人的部署 _base.py 路径>'] # ← 指向你机器人的部署配置。 collection = dict( storage=dict( log_dir='', # ← 存到哪里(留空=用以本配置命名的默认文件夹)。 fps=30, # 合理默认值,机器人不是这个帧率时再改。 save_queue_max=15, # 合理默认值。 ), schema=dict( robot_type='<你的机器人类型>', # ← 如 'agilex_AGILEX'。 min_episode_frames=10, # 合理默认值。 arms=dict(left_arm='left', right_arm='right'), # ← 每只手臂一条。 cameras=dict( cam_high='observation.images.cam_high', # ← 列出你的相机(名字 → 数据集字段)。 ), columns=dict( # 这四个键保留不动,值是标准数据集字段名。 qpos='observations.state.qpos', eef='observations.state.eef', action_qpos='action.qpos', action_eef='action.eef', ), ), transport=dict( ros1=dict(groups=dict( left_arm=dict( qpos_topic='<手臂关节话题>', # ← 改成你机器人的真实话题。 eef_topic='<手臂夹爪位姿话题>', action_qpos_topic='<下达的关节话题>', action_eef_topic='<下达的夹爪位姿话题>', ), # ……每只手臂重复一组。 )), ), tasks=['<你的任务指令>'], # ← 如 'pick up the apple'。 )
存到 configs/02_collection/ 下并启动。加载器强制校验:
columns四个键齐全——qpos、eef、action_qpos、action_eef。cameras至少一个相机。arms至少一只手臂。
字段长度受校验。关节字段必须与机器人关节数一致;EEF 字段每只手臂 8 维(x、y、z、姿态、夹爪)。不符会在审查时标记。
启动
eva --config configs/02_collection/my_robot.py
在浏览器打开 http://localhost:8080(可用 --web-port 覆盖,见命令行参考)。episode 可通过 dataset 传输回放,并在 REPLAY 中审查。