7.3.1 轨迹数据格式设计(时间戳、Topic 结构)
从本节开始,可以把自己想象成在给“未来的 Open X-Embodiment 数据集维护者”打地基:你设计好的格式,将直接决定后面所有模型训练、可视化与复现实验的上限。(arXiv)
7.3.1.1 数据格式
(1)轨迹与 episode 的层次结构
在机器人学习中,通常以“episode(任务执行回合)”为基本单位来组织数据。建议采用三层结构:
- 数据集级(dataset)元信息
dataset_name,version,coordinate_convention(例如世界坐标系定义)- 支持的机器人列表(如
robot_type: franka_panda / ur5 / mobile_base) - 全局时间基准(如统一采用 UNIX 时间戳,单位秒 + 纳秒)
- episode 级信息
episode_id:唯一标识一个任务执行task_id:对应 7.3.2 中的任务类型scene_config:环境配置(物体类型、初始位置、随机种子)robot_id/embodiment_id:哪台机器人、哪种形态start_time,end_time:绝对时间戳- 预留
success_flag、partial_success等标记(具体定义在 7.3.2)
- step 级数据(时间序列) 典型一个 step 可以包含:
{
"t": 1.23456789, // 相对 episode 起始的秒数
"timestamp": 1680000123.23456789, // 绝对时间戳,可选
"observation": {
"rgb_image_path": "episode_001/frame_000123.png",
"depth_image_path": "episode_001/depth_000123.png",
"joint_positions": [...],
"joint_velocities": [...],
"ee_pose": [...],
"gripper_state": 0
},
"action": {
"type": "delta_ee_pose",
"command": [...]
},
"extra": {
"reward": 0.0,
"done": false
}
}
大尺寸模态(图像、点云)一般不直接嵌在 JSON/Proto 中,而是单独存储为文件,在轨迹文件里只保存路径或索引(提升读写性能和灵活性)。
【图 7-3-1 占位:轨迹数据的层次结构示意图:dataset / episode / step 三层结构,step 中包含多模态观测与动作指针。】
(2)时间戳的设计
为了保证后续多模态对齐与 rosbag 回放一致,建议同时保留两类时间戳:
- 绝对时间戳:例如 UNIX 时间(秒 + 纳秒),直接使用 ROS 消息头
header.stamp。 - 相对时间戳:以 episode 起始时刻为 0 的相对时间(浮点秒),便于后续模型训练和可视化。
常见做法是:轨迹内部用相对时间用于“步长统一”与插值,跨日志同步时再回到绝对时间戳。
(3)Topic 级结构与逻辑步长
原始数据往往来自多个 ROS Topic(视觉、关节状态、控制命令等),频率各不相同。建议在“落盘到训练格式”时做一次结构化处理:
- 选择一个主时间轴(例如控制命令 Topic 或 ** 固定频率定时器**)作为 step 的基准;
- 其它传感器 Topic(相机、力矩传感器等)按时间就近插值 / 对齐 到该主时间轴上;
- 在 step 级别记录“来自哪些 Topic 的消息已经对齐良好”(例如缺帧时标记
observation.rgb_valid = false)。
这样可以在保持原始 rosbag 灵活性的同时,为模型提供统一粒度的“时间步”。
7.3.1.2 ROS bag
在 ROS 生态中,rosbag / ros2 bag 是事实标准的轨迹日志格式,用于记录任意 Topic 上的消息时间序列。(wiki.ros.org)
- ROS1 中使用
.bag文件,以自定义格式顺序存储:[\\((timestamp, topic_name, serialized_message)\\), ...]。(mathworks.com) - ROS2 中
ros2 bag将数据存入数据库(早期默认 SQLite.db3,后续发行版逐步转向 MCAP 作为默认存储后端)。(ROS)
(1)选择需要记录的 Topic
为具身智能任务设计 rosbag 时,通常会重点关注以下几类 Topic:
- 机器人状态类:
/joint_states、末端位姿、力矩/触觉传感器 - 视觉类:
/camera/rgb/image_raw、/camera/depth/image_raw、相机外参/内参 - 控制命令类:
/arm_controller/command、/cmd_vel等 - 任务相关信号:重置信号、任务 ID、成功标志、语言指令 Topic 等
原则是:能够在离线回放时还原完整任务上下文即可,不必记录实验中所有无关 Topic,以避免数据爆炸。
(2)bag 作为“原始事实”,轨迹文件作为“训练视图”
一个可行的工程实践:
- 在线实验时,用
rosbag record/ros2 bag record记录若干 Topic,作为原始事实日志; - 离线阶段,用解析脚本将 bag 转换为“episode + step”结构的训练轨迹文件(如 JSON Lines / Parquet / HDF5);
- 训练时仅加载结构化轨迹;调试或可视化时再用 bag 做细节回放。
【图 7-3-2 占位:左侧 ROS 节点与 Topic 流图,右侧 rosbag 文件与“加工后轨迹文件”的数据流示意。】
7.3.1.3 回放与解析
(1)回放(replay)
ROS 提供的 rosbag play / ros2 bag play 能将 bag 中的消息按原始时间戳重放到对应 Topic 上,仿佛“时光回溯”到实验时刻。(wiki.ros.org)
回放的典型用途:
- 在仿真或离线系统中,重复调试同一条轨迹;
- 用旧数据验证新算法(感知、控制、规划);
- 生成演示视频或用于教学展示。
需要注意的是,回放时通常会把 ROS 时间(/use_sim_time)绑定到 bag 时间轴,避免系统使用真实系统时间造成混乱。
(2)解析(parsing)与 episode 切分
将 rosbag 转换为训练轨迹,需要一个清晰的解析流程。一个常见方案:
- 扫描元信息
使用命令行
rosbag info/ros2 bag info获取 bag 中所有 Topic 名、消息类型、起止时间。(wiki.ros.org) - 按 Topic 读取消息流 用脚本(Python C++ 或 MATLAB 等)逐条读取消息,按时间排序(bag 内部本身已经按时间顺序,但跨多个文件或分段时需重新合并)。(mathworks.com)
- 确定 episode 边界
- 可以依据专门的“重置 Topic”(例如
/task_reset); - 或通过机器人状态检测 reset 事件(如末端回到初始 pose,物体恢复初始位置);
- 或根据外部标注文件给出的 episode 起止时间段。
- 可以依据专门的“重置 Topic”(例如
- 对齐多模态数据到统一 step
- 以控制命令 Topic 的时间戳为主轴;
- 对每个时间戳,在图像/传感器流中找到相邻两帧做最近邻或插值;
- 将结果写入标准化轨迹格式。
- 附加元数据与标签 留出接口与 7.3.2(任务标签)和 7.3.3(语言指令)对接。
(3)离线调试与可视化
为了帮助人类快速感知轨迹质量,推荐在解析脚本中顺便生成:
- 集成可视化:例如每个 episode 导出一个“关键帧 + 末端轨迹 2D 投影图”;
- 快速统计:轨迹长度分布、关节速度分布等,用于后续清洗(7.3.4)。
【图 7-3-3 占位:从 rosbag 时间线到统一 step 时间线的对齐示意图,展示多种 Topic 的时间戳如何投影到主时间轴上。】
7.3.2 任务标签与成功 / 失败标注
轨迹本身只是“机器人做了什么”的记录,要让 VLA 模型真正学习到“机器人为什么这样做、做得好不好”,就必须在轨迹之上叠加任务语义与质量标签 。近年来的大规模具身数据集(如 Open X-Embodiment、各种语言条件操作数据集)无一例外都重视这一层信息。(arXiv)
7.3.2.1 任务元数据
(1)任务级别信息
每个 episode 至少应具备:
task_id:简洁机器可读的标识,如"pick_and_place"、"open_drawer";task_family:更高层级的任务族,例如"manipulation"、"navigation";task_params:与本次任务实例相关的参数- 目标物体类别 / 颜色 / 尺寸
- 目标区域(容器类型、位置)
- 初始布局随机种子等
language_instruction_ids:与 7.3.3 中语言指令条目关联的 ID 列表。
这种设计既方便后续按照任务类型过滤数据,也为模板化语言生成(7.3.3.2)提供了参数来源。
(2)环境与机器人配置
建议把容易被忽略但对泛化十分关键的信息也纳入元数据:
- 机器人类型与工具末端(两指爪、三指手、吸盘等);
- 相机类型与安装位置(手眼 / 外参 ID 参考);
- 场景类型(桌面操作、地面导航、台面整理等)。
在跨机器人、多任务的大规模数据集中,这些字段是进行条件建模和跨平台迁移 的关键。(arXiv)
7.3.2.2 成功 / 失败记录
(1)“成功”的定义
理想情况下,需要在数据采集协议中提前定义每个任务的“成功条件”,例如:
- 抓取任务:目标物体被稳定抓起、离开桌面超过一定高度;
- 放置任务:目标物体中心落在目标区域内,且最终静止;
- 门 / 抽屉任务:开合角度超过一定阈值。
可以用规则 + 传感器检测 自动判断(如通过视觉检测物体位置),也可以在实验结束后由操作者通过 UI 一键标注“成功 / 失败”。
(2)标签结构
建议引入显式结构,而不是一个单布尔变量“一刀切”:
{
"episode_id": "...",
"success": true,
"failure_reason": null,
"metrics": {
"final_distance_to_goal": 0.012,
"time_to_success": 5.4
}
}
或者在失败时记录:
"success": false,
"failure_reason": "object_slipped",
"metrics": {
"max_gripper_force": 15.2
}
这些额外信息可以用于后续成功预测模型 或安全性分析,而不仅仅是过滤样本。
7.3.2.3 部分成功标记
现实任务往往是长序列、多阶段的,如“打开抽屉 → 拿出物体 → 放到盒子里”。只用一个最终成功标记,会导致大量“前半段做得很好但最后一步失败”的轨迹全部被当成失败样本丢弃,极度浪费。
(1)阶段划分
一种常见实践是为每个任务设计阶段化 schema,例如:
"subtasks": [
{
"name": "reach_object",
"success": true,
"t_start": 0.0,
"t_end": 1.2
},
{
"name": "grasp_object",
"success": true,
"t_start": 1.2,
"t_end": 2.0
},
{
"name": "place_object",
"success": false,
"t_start": 2.0,
"t_end": 5.0
}
]
阶段边界可以通过:
- 人工标注关键帧;
- 规则(如“末端距离物体小于阈值时认为进入 grasp 阶段”);
- 或基于任务分解模型自动推断。(CVF开放获取)
(2)部分成功的用法
- 在训练模仿策略或技能原语时,只使用对应阶段成功的轨迹片段;
- 在层级 RL 中,把每个子任务视作一个“option / skill”,利用阶段成功率进行单独评估;
- 在数据分析时识别瓶颈环节(例如普遍在 place 阶段失败),指导任务设计与硬件改进。
【图 7-3-4 占位:长任务时间轴上不同子任务阶段的标记示意图(reach / grasp / place 等),并显示各阶段 success / fail。】
7.3.3 语言指令的设计与采集(手工 / 自动生成)
在具身智能场景中,语言不再只是“标签”,而是直接参与决策的条件变量:同一视觉场景,在“把红色方块放进盒子”和“把蓝色圆柱放到桌子右侧”两条指令下,机器人应做出完全不同的行为。因此,本节关注如何为轨迹配备高质量语言指令,并扩展到大规模。(NeurIPS 会议记录)
7.3.3.1 人工编写指令
(1)指令风格与基本原则
面向 VLA 模型的指令,推荐采用自然、简洁、面向用户的任务描述,而不是低层控制命令。例如:
- “把红色方块放入右边的蓝色盒子里。”
- “从上面抓起杯子,然后放到桌子中央。”
为了兼顾可学性与可扩展性,编写时可遵循几点原则:
- 任务导向、结果导向:描述目标而非每一步动作细节,让模型有空间自发规划;
- 对视觉可对齐:指令中涉及的颜色、形状、位置等,应在图像中确实可见;
- 减少歧义:避免“它”“那里”等指代含糊的词,优先使用“红色杯子”“左边的盒子”等明确描述;
- 风格一致:同一数据集内,尽量保持时态、句式的一致性,降低模型学习负担。
(2)人工标注流程
典型工作流如下:
- 标注工具展示关键帧或者轨迹视频;
- 标注员观看后,为当前 episode 编写 1–3 条自然语言指令;
- 将每条指令分配唯一 ID,并与
episode_id建立关联; - 选取一部分样本做交叉审核,保证指令语义与轨迹行为一致。
【图 7-3-5 占位:标注界面示意图:左侧播放轨迹视频,右侧文本框输入多条语言指令,并显示任务元数据。】
人工指令的优势是自然、贴近真实用户语言;缺点是成本较高,因此通常与模板和大模型方法结合使用。
7.3.3.2 模板自动生成
为了在大型数据集中批量生成语言指令,可以基于任务元数据设计参数化模板 。
(1)模板结构
以抓取放置为例,可以设计一组中文模板:
- “把{color}{object}放到{target_location}的{target_object}里。”
- “将{target_location}的{target_object}中的{color}{object}拿出来。”
- “把桌子上{side}的{color}{object}移到{target_location}。”
其中 {color}、{object}、{target_location}、{side} 等字段,都来自 7.3.2.1 中定义的 task_params。
(2)模板系统要点
- 对每个任务族维护一套模板列表;
- 生成指令时,随机选择不同模板,增强语言多样性;
- 确保模板不会生成与环境不符的描述,例如在没有“蓝色物体”时不会使用
{color=蓝色}; - 可以为同一 episode 生成多条模板指令,供训练时随机采样。
很多语言–机器人数据集采用类似策略,将结构化任务参数转换为成千上万条指令,从而在有限的环境配置上构造出丰富语言分布。(NeurIPS 会议记录)
7.3.3.3 大模型辅助
大语言模型(LLM)与视觉语言模型(VLM)在具身数据标注中的角色,越来越从“锦上添花”变成“必备基础设施”。(arXiv)
(1)基于元数据的指令扩写与改写
在已有模板或人工指令的基础上,可以用 LLM 进行:
- 释义生成(paraphrasing):保持语义不变,改变表达方式,例如“把红色方块放进蓝色盒子里”→“请将红色的小方块移入蓝色的盒子中”;
- 难度分级:生成“口语化版本”“正式版本”或“儿童友好版本”,用于研究语言风格对模型行为的影响;
- 多语言扩展:在保持语义一致的前提下生成英文、日文等多语言指令。
这类生成通常只需提供结构化 task_params 与示例提示即可自动化运行。
(2)基于视觉的自动描述
对于没有现成元数据的旧轨迹,可以利用 VLM:
- 输入关键帧或短视频片段;
- 令模型生成动作摘要或任务说明;
- 再由人类抽检与修订,形成稳定风格的指令集。
(3)质量控制与防止“幻觉”
大模型可能会描述实际上并不存在的物体或动作。因此需要:
- 用脚本检查生成指令中的实体名是否都出现在环境元数据中;
- 对高风险任务(涉及安全)保留人工审核;
- 将自动生成的指令打上
source: llm,方便后续分析其对训练的影响。
【图 7-3-6 占位:利用大模型进行指令生成与扩写的流程图:输入轨迹 + 元数据 → 模型生成 → 自动检查 → 人工抽检。】
7.3.4 数据清洗与异常轨迹处理
在具身智能项目中,“垃圾进,垃圾出”尤为明显:带有错误传感器读数、时间错乱、标签不一致的轨迹,会直接使模型学到“违反物理常识”的策略。因此,构建系统性的数据清洗与异常处理流程,是整个数据工程链路的关键一环。(ceur-ws.org)
7.3.4.1 清洗目的
数据清洗的目标不只是“丢掉坏样本”,而是更精确地做到:
- 识别并修复结构性错误:例如时间戳乱序、缺失模态、轨迹被中途截断;
- 过滤物理不合理的轨迹:例如关节角度越界、速度跳变不符合机器人动力学;
- 纠正或标记错误标签:成功标为失败、任务 ID 错配、语言指令与行为不对应;
- 为后续分析提供“质量标签”:不是简单删除异常,而是标记其质量等级,使不同算法可以有选择地使用。
在大规模轨迹数据上,异常比例常常不会小(传感器故障、示教错误、软件崩溃等),但通过良好的清洗流程,可以在保证覆盖多样性的同时提高整体训练信噪比。(MDPI)
7.3.4.2 异常检测
异常检测可以分为三类:结构异常、物理异常、语义异常 。
(1)结构异常
关注轨迹“长什么样”是否合理:
- 时间戳是否单调递增;
- episode 长度是否在合理范围内(过短可能是记录中断,过长可能包含多个任务混在一起);
- 关键字段是否缺失(如动作为空、图像路径不存在)。
这类异常可以通过规则 + 脚本检查 快速发现,例如:
- 若
len(steps) < min_steps则标记为异常; - 若连续两步的
t差值超过上限(例如 > 1s),视为“中断”。
(2)物理异常
基于机器人动力学和控制常识判断:
- 关节角度突破硬件极限或安全范围;
- 在物理上不可能的加速度 / 速度(例如 1ms 内转动 90°);
- 碰撞检测 Topic 报警,但成功标记仍为 true。
可以利用简单统计与规则:
- 计算每个关节的速度与加速度直方图,将高于一定分位数(如 99.9%)的样本标记为 outlier;
- 检查末端与场景中障碍物的最小距离(若有几何信息)。
(3)语义异常
这是更“智能”的一层:
- 轨迹最终状态与任务成功条件矛盾(如物体未进入目标区域,却被标记为成功);
- 语言指令与真实行为不匹配(指令要求“放进盒子”,结果轨迹只有抓取没有放置);
- 中途出现明显“任务崩溃”(例如示教者抖手,把物体甩飞)。
对于语义异常,可以使用:
- 简单规则(基于 7.3.2 中的任务成功定义);
- 或训练一个**“正常轨迹”预测模型 / 自编码器**,通过重构误差或预测误差检测异常模式,这是当前机器人与轨迹异常检测文献中常用的思路。(arXiv)
7.3.4.3 清洗流程
为了让清洗过程可复用、可追溯,可以设计一个标准管线,类似机器学习训练 pipeline:
(1)步骤 0:版本化备份
- 原始 rosbag 与初始轨迹文件应只读保存;
- 每次清洗产生一个新的数据集版本,如
v1.0_raw→v1.1_cleaned_basic→v1.2_cleaned_semantic。
(2)步骤 1:批量结构检查
- 运行脚本对所有 episode 进行结构校验,统计:缺失字段数、异常时间间隔、长度分布;
- 给每个 episode 打一个结构级“质量分数”,例如
structure_score ∈ [0, 1]。
(3)步骤 2:物理约束过滤
- 使用机器人模型(关节上下界、最大速度等)定义一系列约束;
- 批量计算每条轨迹是否违反约束,并记录违反的类型(越界 / 高速 / 碰撞);
- 对严重违反物理约束的轨迹打上
quality: bad_physics标记,默认不用于训练。
(4)步骤 3:语义一致性检查
- 使用任务定义与成功条件,自动判断 episode 成功 / 失败,并与人工标注比对;
- 对冲突案例(如“系统判断失败但人工标成功”)输出列表,用于人工复查;
- 对语言–行为不一致的样本,选择修正语言或丢弃该指令。
(5)步骤 4:人工抽检与可视化
- 对边缘样本(例如自动评分在 0.4–0.6 之间)进行人工抽检;
- 借助可视化工具(轨迹动画、关键帧拼图)帮助快速决策。
(6)步骤 5:输出清洗报告与标签
最终,每条 episode 除了原始任务标签外,还应多出一组与清洗相关的字段:
"quality": {
"structure_score": 0.95,
"physics_score": 0.9,
"semantic_score": 0.85,
"flags": ["passed_basic_checks"]
}
这样,在后续训练中可以:
- 先只使用高质量子集训练基线模型;
- 再尝试加入部分噪声样本,研究模型对噪声的鲁棒性;
- 或在研究异常检测、恢复策略时专门用“异常集”做基准。(ceur-ws.org)
【图 7-3-7 占位:数据清洗流水线示意图:原始轨迹 → 结构检查 → 物理约束过滤 → 语义检查 → 人工抽检 → 生成带质量标签的清洗后数据集。】
通过本节内容,读者应当能从工程和建模两个角度,理解“一个高质量 VLA 训练数据集”在存储格式、任务标签、语言指令以及清洗流程上的设计要点。后续章节在介绍预训练与模仿学习训练范式(第 9 章)时,将反复依赖这里所建立的这些“数据基础设施”。