看到 Figure 全球悬赏人类“干活”这条消息,我的第一反应不是“机器人公司怎么开始做人力外包了”,而是:机器人行业的数据竞争,已经进入需要专门建一条人类数据生产线的阶段。
如果只看表面,这像是一则招聘新闻;往深一层看,它其实是具身智能训练体系的一次工程化升级。机器人要学会拿东西、开门、叠衣服,靠的不只是更好的执行器或更聪明的控制算法,更关键的是要让模型先看见“人是怎么做的”。无论是通过遥操作设备控制机器人,还是让人佩戴传感器完成一套任务,本质都是把人类行为转换成机器人模型可以学习的“燃料”。
这篇文章不打算停在新闻复述上,而是想从技术视角拆解三件事:机器人数据为什么这么贵、这么缺;Figure 这种“全球悬赏”模式在工程上到底在构建什么;以及普通机器人和 AI 开发者,能从中借鉴哪些关于数据采集、数据清洗、数据集组织和数据迭代的方法。即使你不做人形机器人,这套思路也可以迁移到机械臂、移动机器人甚至自动驾驶项目中。
1. 这篇文章真正要解决的问题
很多开发者第一次听到“机器人数据采集员”这个岗位时,会觉得这是蓝领人力外包的变体,跟算法没什么关系。但站在工程师的角度,这件事真正暴露的是具身智能当前最大的瓶颈:智能体并不缺模型结构,缺的是高质量、高覆盖、带时序的“操作数据”。
传统互联网 AI 的数据来自爬虫、网页、图片库,这些数据本质上已经被人类整理过了。机器人不一样。要让一个机械臂学会“把桌上的杯子拿起来放到托盘里”,模型需要同时知道:
- 杯子在视觉画面中的位置;
- 机械臂当前每个关节的角度和速度;
- 末端夹爪应该用什么力度闭合;
- 手臂从起点到终点需要经过哪些中间轨迹;
- 如果中途有障碍,应该怎么绕开;
- 如果杯子滑落,后续动作应该如何修正。
这些信息不可能从静态图片里直接得到,必须从“人类或机械臂实际操作过程的连续记录”中学习。而一次真实操作只能产生一条轨迹,无法像网页一样批量复制。这就导致机器人操作数据天然稀缺、天然昂贵。
这篇文章适合三类读者:正在做人形机器人或机械臂项目的工程师,他们需要建立自己的数据采集和清洗流程;研究视觉-语言-动作模型(VLA)的开发者,他们需要理解模型背后的数据工程是如何运作的;以及准备进入机器人行业的同学,他们需要知道行业真正的竞争壁垒已经从哪里开始。
2. 机器人数据到底在囤什么:类型与价值分层
如果把机器人训练数据按模态拆开,大概可以分成以下几类,它们的获取成本差别非常大。
| 数据类型 | 典型示例 | 主要用途 | 获取成本 |
|---|---|---|---|
| 轨迹与动作数据 | 关节角度、末端位姿、速度 | 模仿学习、强化学习动作策略 | 高 |
| 视觉数据 | RGB、深度图、相机内外参 | 目标识别、抓取检测、VLA 训练 | 中 |
| 本体感觉数据 | 关节力矩、电流、角速度 | 力控、柔顺操作、碰撞检测 | 高 |
| 语言指令数据 | 任务描述、自然语言定义 | 语言条件策略、任务规划 | 中 |
| 触觉数据 | 指尖压力、振动信号 | 精细装配、柔软物体操作 | 很高 |
很多人以为机器人数据就是“视频数据”,这其实是一个常见误区。摄像头画面只是其中一层,真正驱动机器人运动的往往是关节状态、力矩、力传感器这些内部数据。一个关节角度序列如果缺少视觉信息,模型不知道要抓什么;一段 RGB 视频如果缺少关节状态,模型也不知道怎么把画面上看到的“位置差”转化为电机指令。多模态对齐,才是操作数据最麻烦的地方。
相比互联网文本数据,机器人操作数据的稀缺性有两个原因。第一,它必须被“执行一次”才能产生一次。你可以用爬虫在一小时内抓取数百万篇文章,但一个熟练的采集员一小时也未必能完成几十次高质量抓取操作。第二,它对物理环境高度敏感。同一个“倒水”任务,换了杯子的位置、换了桌面高度、换了光照,数据就可能分布偏移。要覆盖真实世界的长尾,采集量必须呈指数级增长。
这也是为什么 Figure 选择“全球悬赏”而不是只在一两个实验室里做数据采集。它需要在不同国家、不同家庭布局、不同生活习惯下收集大量变体,让模型见过足够多的“桌子长什么样、碗放在哪里、人怎么弯腰拿东西”。从数据工程角度看,这是一场以“空间覆盖度”和“动作多样性”为目标的规模化采集行动。
3. “全球悬赏”背后的数据飞轮
仅靠一次采集就训练出可用模型,是不现实的。机器人数据项目真正厉害的地方在于它构建了一个数据飞轮:采集 -> 清洗 -> 训练 -> 部署 -> 发现失败 -> 补充采集 -> 再训练,循环持续运转。
Figure 的做法,从公开信息看,并不是简单把人招进来随便做动作,而是搭建了一套完整的数据生产体系。任务会被拆成标准动作,采集员需要按照脚本在不同环境中完成整理桌面、叠放衣物、开合柜门、分类物品等操作,同时记录多路传感器数据和任务描述。采集完成后,数据还要经过质量筛选、时间戳对齐、动作片段切分、标注复核,最后才能进入训练集。
这套体系的关键指标不是“采集了多少小时”,而是“有效数据密度”,也就是每小时采集到的、能被模型直接用于学习的完整动作片段数量。如果采集过程中大量动作是失败的、没有明确任务边界的、或者传感器时间戳对不齐的,那这些录像即使时长很长,训练价值也会大打折扣。
数据飞轮最难的不是第一圈,而是第二圈。第一轮采集的数据训练出的模型,在实际部署时会暴露出大量失败场景。这些失败场景就是补充采集的指令:机械臂夹不住湿滑的杯子,那就补充不同材质、不同表面摩擦力的抓取数据;机器人分不清桌上的遥控器和手机,那就补充更多相似物体的区分数据。越到后期,数据采集的方向越明确,模型迭代的速度越快。
从工程实现上看,飞轮的每个环节都需要自动化。任务脚本下发、采集数据自动上传、质量初筛、标注任务分配、训练集版本生成,这些都可以通过一套数据管线串联起来。很多团队容易低估的是“标注一致性”问题:同一个动作,A 标注员认为是“成功”,B 标注员认为是“部分成功”,最终会污染训练标签。相比多花一倍时间采集,更稳妥的做法是先把标注规范和质检标准做细。
4. 为什么仿真数据不能完全替代真实人类数据
在做机器人数据方案时,一定会有人问:为什么不用仿真环境生成数据?仿真可以批量生成场景、无限改变物体位置、自动输出完美的关节轨迹,成本看起来远低于人工采集。
仿真数据确实有不可替代的优势。它可以做大规模域随机化,让模型见过各种颜色、形状、光照条件的物体;可以轻松生成极端状态,比如物体从桌面掉落、机械臂碰撞后的恢复;还可以在训练初期提供海量样本,帮助模型学到基本运动模式。对于抓取常见的刚性物体,仿真数据的效率往往比真实数据高得多。
但仿真数据解决不了“真实世界的分布偏移”。布料、毛巾、数据线这类软体物体,在仿真中的物理参数很难贴近现实;透明玻璃杯、反光金属、液体流动,在图形学渲染里再怎么调参,也没有真实传感器噪声和光学反射来得自然。更关键的是,仿真环境里很难复现人类演示中的“意图感”:人拿杯子时,手指会在接触瞬间自然减速,这种细微信号来自真实触觉反馈,仿真很难生成。
所以更准确的判断是:仿真数据负责扩大覆盖范围,真实人类数据负责校正分布。两者不是替代关系,而是上下游关系。先用仿真的合成数据做预训练,让模型具备基本的操作先验;再用真实采集数据做微调,让模型真正适配真实环境的物理特性和传感器噪声。Figure 这类公司之所以愿意投入巨大成本去雇人采集,恰恰是因为到了部署阶段,真实数据微调这最后一步是绕不开的。
5. 数据采集任务的工程化拆解
5.1 任务分解与场景覆盖
数据采集不能靠采集员“自由发挥”,任务设计本身就是一门工程。以“叠毛巾”为例,完整操作可以拆成:
- 走到毛巾放置位置,或者让机械臂移动到毛巾上方;
- 抓取毛巾一角;
- 将毛巾铺平;
- 对折;
- 再次对折;
- 移动到指定放置点;
- 释放毛巾。
每个子步骤都要有明确的起止时间、成功判定和失败原因。如果只记录整段视频而不做步骤级切分,模型很难理解“哪段动作对应哪个子目标”,训练效率会很低。
场景覆盖也需要提前设计。同一个任务至少要考虑物体颜色与材质、桌面高度、光照强度、背景杂乱程度、相机视角这几类变量。较好的做法是先列一个场景矩阵,比如“白色毛巾/深色桌面/室内灯光”“彩色毛巾/木质桌面/弱光”,然后按矩阵组合采集,避免数据大量集中在同一类场景。
5.2 一条数据记录的最小结构
采集端的数据记录格式,建议从一开始就设计成结构化文件,而不是只存视频。下面是一个标注文件的示例,这段结构也可以应用到机械臂、移动操作等多种场景中。
{ "task_id": "fold_towel_001", "task_name": "fold_towel", "scene": { "location": "bedroom", "lighting": "indoor_normal", "surface": "bed", "camera_view": "right_wrist_rgbd" }, "episode": [ { "step": 1, "action": "pick_towel", "start_timestamp": 17.32, "end_timestamp": 19.05, "success": true, "note": "towel grasped at corner" }, { "step": 2, "action": "spread_towel", "start_timestamp": 19.06, "end_timestamp": 23.11, "success": false, "failure_reason": "towel corner slipped", "retry_needed": true } ], "sensor_streams": { "wrist_rgbd": "recordings/fold_towel_001/wrist_rgbd.mp4", "joint_states": "recordings/fold_towel_001/joint_states.csv", "force_torque": "recordings/fold_towel_001/ft.csv" } }这个文件的价值在于把非结构化的录像变成了可以查询、统计、筛选的结构化元数据。时间戳是这里最容易出错的地方。不同传感器可能运行在不同的时钟源上,如果视觉流和关节流的时间基准不一致,模型训练时就会“看到”画面和动作错位。
另一个容易忽略的点是失败标记。很多早期项目只保留成功的演示数据,觉得失败数据没有价值。但实践中,想让模型学会“从失败中恢复”,就必须保留失败片段,并标注失败原因。没有失败的样本,模型只会学习理想轨迹,一旦在真实环境中出现偏差,就很难自我修正。
6. 从原始记录到可训练的数据集:完整处理流水线
数据采集回来之后,必须经过一条清洗和处理流水线,才能进入模型训练。这个过程,本质上就是机器人领域的数据治理。
6.1 时间对齐、去重与重采样
下面这段 Python 代码演示了如何读取一段关节状态数据,处理时间戳问题,并统一重采样到 30Hz。这里刻意用了常见的数据处理库,方便你直接改造成自己的流程。
import pandas as pd # 文件路径:recordings/fold_towel_001/joint_states.csv df = pd.read_csv("recordings/fold_towel_001/joint_states.csv") df.columns = [ "timestamp", "joint_0", "joint_1", "joint_2", "joint_3", "joint_4", "joint_5" ] df["timestamp"] = pd.to_numeric(df["timestamp"], errors="coerce") df = df.dropna(subset=["timestamp"]) # 去除重复时间戳,保留第一个观测值 df = df.drop_duplicates(subset="timestamp", keep="first") # 将时间戳作为索引,统一重采样到 30Hz df = df.set_index("timestamp").resample("33.33ms").interpolate().reset_index() print(df.head()) print("samples after resample:", len(df))这段代码解决了三类常见问题:脏时间戳导致的数据缺失、采集端偶发的重复采样、以及不同传感器帧率不一致。重采样时选择线性插值只适用于角度、位置这类连续量,如果是触觉力度这类突变信号,需要谨慎处理,否则可能会抹掉关键的接触瞬间。
6.2 按标注切分有效动作片段
刚采集回来的视频往往很长,包含大量无效的“思考时间”和“等待时间”。如果直接把完整序列喂给模型,会引入大量噪声。更合理的做法是根据标注文件切分成有效动作片段。
import json # 文件路径:annotations/fold_towel_001.json with open("annotations/fold_towel_001.json", "r", encoding="utf-8") as f: ann = json.load(f) steps = ann["episode"] valid_segments = [ (step["start_timestamp"], step["end_timestamp"]) for step in steps if step.get("success", False) and step["end_timestamp"] > step["start_timestamp"] ] print(f"task={ann['task_name']}, valid_segments={len(valid_segments)}") for seg in valid_segments[:5]: print(f"start={seg[0]:.2f}s end={seg[1]:.2f}s duration={seg[1] - seg[0]:.2f}s")切分之后,最好再过滤掉时长过短(比如小于 0.3 秒)和时长过长(比如大于 20 秒)的异常片段。短片段往往是误触发,长片段往往跨越了多个子目标。
6.3 数据集划分与版本管理
模型训练前必须做数据集划分,这一点很容易被忽略。机器人数据不同于普通图像分类,同一段轨迹的相邻帧高度相似,如果按帧随机划分,会导致训练集和测试集之间出现数据泄漏。正确做法是按任务或按采集会话划分。
from pathlib import Path import random import json data_dir = Path("recordings") all_tasks = [str(p) for p in data_dir.glob("*/annotations/*.json")] random.seed(42) random.shuffle(all_tasks) n = len(all_tasks) train_ratio, val_ratio = 0.8, 0.1 train_files = all_tasks[: int(n * train_ratio)] val_files = all_tasks[int(n * train_ratio): int(n * (train_ratio + val_ratio))] test_files = all_tasks[int(n * (train_ratio + val_ratio)):] split = { "train": train_files, "val": val_files, "test": test_files } with open("dataset_split.json", "w", encoding="utf-8") as f: json.dump(split, f, indent=2, ensure_ascii=False) print("train:", len(train_files), "val:", len(val_files), "test:", len(test_files))这种按任务划分的方式,能更真实地反映模型在“没见过的任务实例”上的表现。数据集的版本管理也建议尽早做。数据集和代码一样,每天都在变:今天加了几条失败样本,明天删了一段坏数据。如果没有版本号,训练日志里的指标将无法复现。实践中可以用 Git LFS 管理大文件,也可以用 DVC 这类数据版本工具,核心是保证“哪个训练任务用了哪个数据版本”是可以追溯的。
7. 数据暗物质:失败样本与长尾场景怎么处理
很多团队在做机器人数据时,默认只要“成功的演示”。但如果目标是把模型部署到真实场景,失败样本就是不能忽略的“数据暗物质”。
原因很简单:模型在学习时如果只见过成功轨迹,它就不知道碰到异常后该怎么办。真实环境中,物体可能滑落、夹爪可能没对准、桌面可能比预期的湿滑。一个成熟的策略不应该只会“从头做一遍”,还要会在失败后尝试纠偏。要学到这种恢复策略,训练集里就必须包含失败轨迹和紧随其后的恢复轨迹。
前面标注文件里的retry_needed和failure_reason字段,就是为了筛选这类样本。建议从采集阶段开始,就单独维护一个失败样本清单,记录每个失败步骤的时间段和失败原因。下面的代码演示了如何从多个标注文件中聚合失败样本。
import json failed_samples = [] for ann_path in [ "annotations/fold_towel_001.json", "annotations/fold_towel_002.json" ]: with open(ann_path, "r", encoding="utf-8") as f: ann = json.load(f) for step in ann["episode"]: if not step.get("success", True): failed_samples.append({ "task": ann["task_name"], "action": step["action"], "start_timestamp": step["start_timestamp"], "end_timestamp": step["end_timestamp"], "failure_reason": step.get("failure_reason", "unknown") }) print("failed steps:", len(failed_samples)) for s in failed_samples[:3]: print(s)失败样本的用法不止一种。可以在训练时对失败样本重采样,放大它们在训练集里的权重;可以把失败片段截取出来,作为负样本输入给判别模型,让模型学会“预测失败”;还可以把失败条件整理成清单,回到仿真环境里去复现,从而生成更多同类变体。这个过程,本质上就是数据增强的另一种形式。
需要提醒的是,失败样本也不是越多越好。同一个原因反复出现的失败片段,信息量会迅速饱和。更有价值的失败样本,是“不同原因、不同场景、不同物体”带来的多样性失败。因此失败样本库同样需要去重和场景覆盖分析。
8. 机器人数据工程的最佳实践与工程建议
结合前面几章的流程,这里梳理几条可以直接落地的最佳实践,适合任何正在建机器人数据集或训练管线的团队。
第一,存储格式要区分“原始记录”和“训练样本”。原始录像和传感器文件体积大、不便于频繁读取,建议归档在对象存储或 NAS 中;而经过切分和清洗的训练样本,需要能被训练脚本高效读取,建议单独落盘并做格式规范。把两个环境混在一起,会很快失控。
第二,传感器时间同步要从采集端就开始解决。用 ROS2 这类框架做机器人开发时,rosbag会记录每一条消息的时间戳,但不同传感器之间的时钟偏差仍然存在。最稳妥的做法是采集前统一使用主时钟源做时间同步校准,并在标注文件中记录每条数据流的时间基准。如果等训练时才发现画面和动作错位,返工成本极高。
第三,数据集必须要有版本号,并且和模型 checkpoint 关联。训练脚本里除了保存模型权重,还应该保存当前数据集的 commit id 或 hash 值。这样当模型表现异常时,才能快速定位是数据变化、代码变化还是超参变化引起的。
第四,涉及人类采集的数据,必须做好合规和安全边界的把控。采集场景中可能包含人脸、家庭环境、语音等个人敏感信息。采集前要取得明确授权,存储时要去标识化,涉及云存储和远程传输时要按最小权限原则分配访问权限。任何情况下都不应该在未授权环境里采集数据,也不要为了追求训练多样性而绕过隐私保护。
第五,质量控制要前置。与其花大量时间在训练后分析“为什么模型学歪了”,不如在数据进入训练集之前就建立自动质检规则。常见规则包括:动作片段时长是否合理、时间戳是否单调递增、标注的 success 标签是否和传感器数值矛盾、是否存在长时间画面静止等。质检规则可以先用规则脚本实现,后期再引入模型辅助。
第六,不要把“数据量”当作唯一指标。同样是 100 小时数据,如果都来自同一个房间、同一个物体、同一个动作,模型泛化能力会很差。更值得跟踪的指标是场景覆盖数、动作类型数、失败样本占比、以及标注一致率。这些指标比单纯的总时长更能反映数据集的真实价值。
9. 常见问题与排查思路
在搭建机器人数据采集和处理流程时,下面几个问题出现的频率最高,这里给出排查方向。
| 问题现象 | 可能原因 | 排查方式 | 解决方案 |
|---|---|---|---|
| 多模态数据时间戳对不齐 | 相机和关节控制器使用不同时钟源 | 对比视觉流与关节流的首尾时间戳 | 采集前做时间同步,统一使用主时钟 |
| 模型训练时学不到有价值动作 | 成功样本太多,失败恢复样本缺失 | 统计标签分布和动作类型覆盖度 | 增加失败与恢复片段,或对失败样本重采样 |
| 仿真环境效果好、真机测试明显下降 | Sim-to-Real gap 未处理 | 在真机场景做小批量验证 | 加入真实数据微调,配合域随机化预训练 |
| 数据集体积增长过快 | 原始录像未按标注切分 | 查看录像总时长与有效片段占比 | 按标注切分有效片段,丢弃无效帧 |
| 同一任务在不同环境表现差异大 | 场景覆盖不足 | 按场景标签聚类分析 | 按照场景矩阵补充采集 |
| 标注标签不一致,训练指标波动 | 标注规范未对齐 | 抽检标注结果,计算标注一致性 | 制定详细标注规范,增加质检环节 |
上面表格里的问题,大多数都可以通过“先建小样本流程、再规模化扩展”的方式规避。不要在上千小时数据建完之后才发现流程有问题,先用数十条样本跑通从采集、标注到训练的完整链路,确认每个环节的输出都正确,再投入资源做大规模采集。
10. 总结:机器人数据竞争的下一个分水岭
Figure 的“全球悬赏”只是一个信号,它真正揭示的是:机器人行业的竞争重心,正在从单纯的硬件参数比拼,转向数据基础设施的比拼。谁能以更低成本采集到更多高质量操作数据,谁能把数据清洗成模型真正能用的训练集,谁能更快地在失败场景上完成迭代,谁就越有可能在下一阶段遥遥领先。
对普通开发者和中小团队来说,不必一开始就追求千人规模的采集体系。更务实的做法是,从现在开始统一自己的数据记录格式,给数据集加上版本号,把失败样本单独建库,清洗脚本和训练脚本放进同一个仓库。这些看起来不起眼的工程习惯,会慢慢积累成一个属于你自己的数据飞轮。
如果你正在做机械臂抓取、移动操作或 VLA 相关项目,建议把这套数据处理流程保存下来。下一次从零整理数据集时,对照着时间对齐、片段切分、集合划分、失败样本处理这几步来走,能省下大量返工调试的时间。机器人数据这条路没有捷径,但它一定可以被工程化。