机器人操作模型不仅需要大量数据,还要求动作、传感器观测和任务上下文能够准确对齐。Grabette 将重点放在一个明确的问题上:以开放系统记录机器人操作数据。由于现有材料只提供了项目标题,下面不假定其具体硬件接口或文件格式,而是分析这类系统需要解决的工程问题,并给出一套可以直接改造的最小采集方案。
记录的不只是机械臂轨迹
一条可用于训练或回放的操作数据,通常至少包含四类信息:
- 观测:相机图像、深度图、关节位置、末端执行器位姿和夹爪状态。
- 动作:关节目标、速度指令、末端位姿增量或控制器实际执行的命令。
- 任务上下文:任务名称、物体信息、操作者、场景配置和成功标记。
- 时间信息:单调时钟时间戳、传感器采样时间和控制周期序号。
其中最容易被低估的是时间同步。相机以 30 Hz 输出图像,机械臂控制器可能以 100 Hz 或更高频率运行。如果采集程序只在收到数据时写入文件,却没有保留原始时间戳,后续很难判断某个动作究竟对应哪一帧图像。
开放采集系统的价值也不只是“代码可见”。真正可复用的数据还需要公开或固定数据模式、坐标系定义、单位、设备参数和版本信息。否则,同名字段可能分别表示角度、弧度或归一化控制量,数据在跨实验室使用时会迅速失真。
用 episode 组织一次完整操作
机器人模仿学习数据通常适合按 episode 管理。一次抓取、放置或装配尝试构成一个 episode,其中包含连续步骤以及最终结果。推荐将大文件和结构化元数据分开:图像或视频单独保存,JSONL、Parquet 或数据库记录动作与索引。
一个便于检查和迁移的目录可以这样设计:
dataset/
├── manifest.yaml
└── episodes/
└── pick-cube-000001/
├── metadata.json
├── steps.jsonl
└── camera_front/
├── 000000.jpg
└── 000001.jpg
manifest.yaml 应明确声明模式版本,而不是依赖采集代码的 Git 提交记录来猜测字段含义:
schema_version: "1.0"
dataset_name: "tabletop-pick-demo"
robot:
model: "replace-with-your-robot"
control_mode: "joint_position"
joint_unit: "radian"
frames:
world: "table_origin"
end_effector: "tool0"
sensors:
camera_front:
type: "rgb"
rate_hz: 30
resolution: [640, 480]
policy_rate_hz: 10
运行前需要替换机器人型号、控制模式、坐标系名称和实际采样率。模式文件一旦用于正式数据集,就应通过增加版本号演进,避免直接改变已有字段语义。
一个可运行的最小记录器
下面是独立于具体机器人 SDK 的示例。它从标准输入接收每一步的 JSON,补充单调时钟和序号,然后写入 JSONL。这里明确假设上游适配器已经把机器人状态与动作转换成统一字段。
将代码保存为 record_episode.py,Python 3.9 及以上版本可直接运行:
#!/usr/bin/env python3
import argparse
import json
import time
from pathlib import Path
def main() -> None:
parser = argparse.ArgumentParser()
parser.add_argument("--episode", required=True)
parser.add_argument("--output", default="dataset/episodes")
args = parser.parse_args()
episode_dir = Path(args.output) / args.episode
episode_dir.mkdir(parents=True, exist_ok=False)
output_file = episode_dir / "steps.jsonl"
with output_file.open("w", encoding="utf-8") as stream:
for sequence, line in enumerate(iter(input, "")):
sample = json.loads(line)
record = {
"sequence": sequence,
"monotonic_ns": time.monotonic_ns(),
"observation": sample["observation"],
"action": sample["action"],
}
stream.write(json.dumps(record, separators=(",", ":")) + "\n")
stream.flush()
print(f"recorded {sequence + 1 if 'sequence' in locals() else 0} steps")
if __name__ == "__main__":
main()
可以用以下命令生成一段两步测试数据:
printf '%s\n' \
'{"observation":{"joint_position":[0.0,0.2],"gripper":0.8},"action":{"joint_target":[0.1,0.2]}}' \
'{"observation":{"joint_position":[0.1,0.2],"gripper":0.7},"action":{"joint_target":[0.2,0.2]}}' \
| python3 record_episode.py --episode pick-cube-000001
python3 -m json.tool < dataset/episodes/pick-cube-000001/steps.jsonl
接入真实设备时,可以让 ROS 2 节点、机器人 SDK 适配器或遥操作进程向该记录器输出 JSON。生产实现还应加入图像索引、数据队列、磁盘空间检测和异常终止标记,避免慢速磁盘阻塞控制线程。
开放数据仍然需要严格边界
采集系统开放,并不意味着所有记录都适合直接发布。相机可能拍到操作者、人脸、屏幕或实验室标识;遥操作日志也可能包含账户和设备信息。发布前应执行隐私检查,并在元数据中标明许可证、允许用途和删除流程。
采用 Grabette 或同类系统时,可以重点检查以下项目:
- 时间戳是否来自稳定时钟,跨设备时钟如何校准。
- 动作记录的是目标命令、控制器输出,还是机器人实际状态。
- 坐标系、单位和夹爪数值范围是否写入模式文件。
- episode 中断、失败和人工重置是否有明确标记。
- 数据能否由独立程序读取,而不依赖采集机器上的私有环境。
- 模式升级后,旧数据是否仍可解析和验证。
开放系统降低了接入和审计门槛,但数据质量最终取决于时间同步、模式治理和故障处理。先用少量 episode 完成采集、回放与校验闭环,再扩大到长时间、多设备录制,通常比一开始追求数据规模更稳妥。