机器人遥操作数据采集实战:从硬件搭建到数据集构建全流程
2026/7/18 8:14:48 网站建设 项目流程

1. 项目概述:从遥操作到高质量数据集

最近在机器人学习社区里,Lerobot SO-101 这个项目挺火的,尤其是它那个通过遥操作(Teleoperation)来制作数据集的方法。我自己也花了不少时间研究,发现这确实是一条从零开始构建机器人技能学习数据的务实路径。简单来说,这个项目的核心就是:如何让一个普通人,通过一个直观的控制界面,引导一个名为SO-101的机械臂完成一系列精细的抓取、放置、堆叠等操作,并将整个过程完整地记录下来,最终形成一个结构清晰、可直接用于训练机器学习模型的数据集。

这听起来好像只是“录个视频”,但里面的门道可多了。为什么是遥操作?因为这是获取高质量、高成功率演示数据最直接的方式。想象一下,你要教机器人拧瓶盖,与其写一堆复杂的代码去描述“旋转”这个动作,不如直接用手柄或者VR设备控制机械臂做一遍。机器人会忠实地记录下每一时刻关节的角度、末端执行器的位置、相机的画面,甚至力传感器的读数。这些数据打包在一起,就是一个完美的“教学案例”。Lerobot SO-101项目正是提供了这样一套完整的工具链和流程,让研究者甚至爱好者都能相对轻松地启动自己的机器人技能数据采集工作。无论你是想研究模仿学习、强化学习,还是单纯的机器人行为克隆,一个干净、标注好的数据集都是成功的基石。

2. 核心思路与方案选型:为什么是遥操作?

在机器人领域,获取训练数据无外乎几种方式:仿真、程序化生成、以及真实世界采集。仿真数据量大、成本低,但存在“仿真到现实”的鸿沟;程序化生成的数据规则明确,但难以覆盖真实世界的复杂性和不确定性。而真实世界采集的数据最“保真”,但传统方法要么依赖昂贵的运动捕捉系统,要么就是让机器人自己瞎碰乱撞(通过强化学习探索),效率极低且危险。

遥操作恰恰在真实性和可控性之间找到了一个绝佳的平衡点。它的核心优势在于“人类在环”。操作员凭借其强大的感知、规划和应变能力,可以完成非常复杂、精细且成功率高的任务。机器人则扮演一个“乖学生”的角色,一丝不苟地记录下老师的每一个动作。对于Lerobot SO-101这样的项目,选择遥操作来构建数据集,背后有几点关键考量:

第一,数据质量与信噪比极高。由人类专家演示产生的轨迹,本身就是接近最优或次优的解决方案。这为后续的模仿学习算法提供了高质量的学习目标,避免了从大量随机、失败的尝试中艰难地提取有效模式。

第二,能覆盖长周期、多步骤的任务。像“打开抽屉、取出物品、再关上抽屉”这类任务,步骤间有很强的逻辑依赖。通过遥操作,人类可以自然地串联起这些子步骤,生成连贯的长序列数据,这是程序化脚本难以优雅实现的。

第三,快速启动,迭代灵活。相比调试复杂的自动化工站或编写数以千计的场景配置文件,搭建一个遥操作环境并开始采集数据要快得多。当你发现数据集缺少某种特定场景(比如物体被部分遮挡)时,操作员可以立即针对性地进行几次演示来补充,响应速度非常快。

在Lerobot的生态中,SO-101机械臂通常搭配多视角摄像头(比如手腕相机、固定于工作台上的第三方相机)和可能存在的触觉传感器。遥操作方案可以选择传统的游戏手柄映射、3D空间鼠标,或者更沉浸式的VR手柄。方案选型的关键在于“透明度”——即操作指令到机器人动作的映射要尽可能直观、自然,减少操作员的精神负担,让他能专注于任务本身,而不是纠结于如何控制机器。

3. 系统搭建与环境配置详解

要复现一个类似Lerobot SO-101的数据采集系统,我们需要从硬件和软件两方面着手。这不是一个即插即用的玩具,需要一些工程上的整合。

3.1 硬件选型与连接

核心硬件包括机器人本体、感知系统、遥操作设备以及控制计算机。

  1. 机器人平台(SO-101机械臂):这是执行机构。你需要确保机械臂的控制器开放了足够的接口,通常是以ROS(机器人操作系统)驱动包的形式提供。这意味着你可以通过ROS话题(Topic)来订阅机器人的状态(如关节角度、力矩),并通过发布话题或服务(Service)来发送控制指令(位置、速度或力矩控制)。检查机器人的负载、工作范围是否满足你的任务需求。

  2. 感知系统:这是数据的眼睛。多视角摄像头是标配。

    • 固定视角相机:安装在操作台周围,提供全局的、稳定的第三人称视图。建议使用RGB-D相机(如Intel RealSense D435),既能获取彩色图像,也能得到深度信息,对于后续的三维空间理解至关重要。
    • 手眼相机:安装在机械臂末端执行器上,提供第一人称视角。这个视角对于需要精细对齐的操作(如插孔、拧螺丝)特别有用。通常选用更小巧的相机模组。
    • 其他传感器(可选):如果机械臂腕部有力/力矩传感器,务必启用。触觉数据对于区分“捏住”和“碰到”有巨大帮助。有些项目还会加入麦克风录制操作声音,作为额外的模态信息。
  3. 遥操作设备

    • 游戏手柄:最经济、入门最快的方案。通过ROS的joy包可以轻松映射手柄按键和摇杆到机器人的运动指令(如控制末端执行器在X/Y/Z方向移动,或绕轴旋转)。缺点是自由度有限,控制精度一般。
    • 3D空间鼠标/专业操纵杆:如3Dconnexion SpaceMouse或工业级的力反馈操纵杆。它们为六自由度控制进行了优化,操作更符合直觉,适合长时间作业。
    • VR手柄:如HTC Vive或Meta Quest的控制器。能提供最沉浸式的操作体验,操作员可以直接“用手”在虚拟空间中抓取、移动虚拟的机器人末端。但这需要额外的定位基站和更复杂的坐标映射与校准。
  4. 控制计算机:需要一台性能足够的电脑,运行ROS主节点,并处理多路相机数据流。确保有足够的USB端口和网络带宽。强烈建议使用有线网络连接所有设备,以保证控制的实时性和数据记录的稳定性。

注意:安全永远是第一位的。在首次进行遥操作前,务必设置好机器人的软件限位和物理急停开关。操作区域要清空,并确保有人员在旁监护。先从低速、低负载的模式开始测试。

3.2 软件框架与数据流设计

软件层面的核心是ROS,它充当了所有硬件组件和数据流的“中间件”。

  1. ROS驱动与桥接:为机械臂、相机、手柄分别安装并启动对应的ROS驱动包。例如,roslaunch realsense2_camera rs_camera.launch来启动RealSense相机。对于手柄,运行rosrun joy joy_node

  2. 遥操作控制节点:这是需要自己编写的核心程序。它的作用是:

    • 订阅/joy话题,获取手柄的原始输入。
    • 将手柄输入映射为机器人末端的位姿增量。例如,左摇杆前后推对应末端在Y轴移动,左右摇对应X轴移动;右摇杆上下推对应Z轴,左右摇对应末端绕Z轴旋转(偏航);肩键可能对应绕X轴(俯仰)和Y轴(横滚)旋转。这里需要一个比例系数来控制“按一下摇杆,末端移动多少毫米”,这个系数需要根据任务精度反复调整。
    • 发布目标位姿或关节角度到机器人控制话题。通常采用位置控制模式,计算流畅。更高级的可以采用阻抗控制,让操作有“手感”。
    • 实现模式切换:例如,一个按键用于在“位置控制模式”和“抓取器开合控制”之间切换。
  3. 数据记录节点:使用ROS提供的rosbag工具是最高效的方式。你需要精心选择要记录的话题。一个典型的数据记录命令可能如下:

    rosbag record -O so101_teleop_demo.bag \ /joint_states \ /camera_front/color/image_raw \ /camera_front/aligned_depth_to_color/image_raw \ /camera_front/color/camera_info \ /camera_wrist/color/image_raw \ /camera_wrist/aligned_depth_to_color/image_raw \ /camera_wrist/color/camera_info \ /ft_sensor/wrench \ /tf \ /tf_static
    • /joint_states:记录了所有关节的角度、速度、力矩,是动作的核心。
    • 相机话题:记录了RGB图像、深度图像和相机内参。
    • /ft_sensor/wrench:力/力矩传感器数据。
    • /tf/tf_static:记录了所有坐标系(如机器人基座、每个连杆、末端、相机)之间的变换关系。这是重中之重,没有正确的TF树,你就无法将图像中的像素点对应到三维空间中的具体位置。
  4. 同步与时间戳:确保所有传感器的数据时间戳是同步的。硬件同步是最佳方案(如使用相机和机械臂控制器的同步信号线)。如果做不到,则需要在软件层面确保ROS系统的时间是同步的(使用NTP),并且记录时使用rosbag--clock选项。

4. 数据采集实操流程与规范

有了软硬件环境,接下来就是正式的采集工作。这个过程需要像实验室做实验一样严谨,否则采集回来的数据杂乱无章,后期处理会异常痛苦。

4.1 任务设计与场景定义

不要一上来就漫无目的地操作。首先明确你要教机器人什么技能。

  1. 任务拆解:将复杂任务分解为原子操作。例如,“泡一杯茶”可以分解为:移动到茶杯上方、抓取茶杯、移动到水壶旁、对准壶嘴、倾斜茶杯、移回原处、放下茶杯。为每个原子操作设计清晰的起始和终止状态。
  2. 场景标准化:保持背景环境一致。固定相机位置、光照条件。使用标记物(如AprilTag)贴在操作台和关键道具上,这能极大简化后续的标定和物体追踪。所有道具(积木、杯子、工具)的初始位置应该用模具或画线的方式严格固定,确保每次演示的开始条件相同。
  3. 设计数据表:创建一个表格,记录每一次演示(Episode)的元数据。包括:演示编号、任务名称、操作员ID、成功与否、特殊备注(如“第三次尝试时物体滑落”)。这个表格后期是数据清洗和筛选的重要依据。

4.2 采集过程操作要点

开始录制rosbag,然后进行遥操作演示。

  1. 起始与结束标记:在每次演示开始前和结束后,通过一个特定的、不常用的手柄按键(如“开始”键)发送一个信号。可以在代码里让这个按键发布一个特殊的消息到某个自定义话题(如/episode_marker)。这样,在后期处理时,你可以通过检测这个消息来自动切分数据流中的不同演示片段。
  2. 匀速与平稳操作:操作机器人时,动作尽量平滑、匀速。避免突然的加速和急停,这样产生的轨迹数据更干净,有利于模型学习。想象你在教一个新手,你的动作就是教科书。
  3. 多角度与异常情况:对于同一个任务,不仅要从最常见的角度演示,也要有意制造一些“困难模式”的演示。比如,物体初始位置稍微偏移、被另一个物体部分遮挡、或者以不同的姿态摆放。这些数据能极大地提升模型的鲁棒性。
  4. 即时标注:如果任务的成功与否有一个明确的、可程序化判断的标准(如物体是否被放入目标区域),可以在操作完成后,立刻通过另一个按键(如“成功”键)进行标注。将成功/失败的标签与演示数据关联起来。

4.3 数据采集后的初步检查

每次采集环节结束后,不要急着进行下一轮,先做快速检查。

  1. 回放验证:用rosbag play回放录制好的bag文件,并用RViz可视化机器人的运动轨迹和相机画面。检查动作是否流畅,TF变换是否正确(所有坐标系是否都出现在正确的位置),相机画面有无丢帧或卡顿。
  2. 数据完整性检查:编写一个小脚本,检查bag文件中所有预定话题的发布频率是否正常,有无长时间中断。检查图像话题的编码是否正确(是rgb8还是bgr8?)。
  3. 备份:将检查无误的bag文件连同元数据表格,立即备份到至少两个不同的存储设备中。原始bag文件是黄金母版,不要轻易修改。

5. 从ROS Bag到结构化数据集

原始的ROS Bag文件只是一个按时间排列的消息序列,还不能直接丢给PyTorch或TensorFlow训练。我们需要将其转换成一个结构化的数据集,通常是像HDF5或TFRecord这样的格式,方便按需读取。

5.1 数据解析与同步对齐

这是数据处理中最关键、最繁琐的一步。由于不同传感器发布数据的频率不同(关节状态可能100Hz,图像只有30Hz),我们需要将所有数据对齐到统一的时间轴上。

  1. 提取数据:使用rosbagPython API (rosbag.bag) 读取bag文件。遍历每一个消息,根据其话题名称和时间戳,将其分类存储到不同的列表或字典中。
  2. 时间戳对齐:通常以某一个数据流为基准(比如相机图像,因为频率较低)。对于每一帧图像的时间戳t_image,在关节状态数据流中,寻找时间戳最接近t_image的那一条记录。更精确的做法是进行插值:找到t_image前后两个关节状态消息,然后按时间比例进行线性插值,得到图像拍摄瞬间的精确关节角度。
    # 伪代码示例:为每一帧图像找到对应的关节状态 for img_msg in image_messages: img_time = img_msg.header.stamp.to_sec() # 在joint_states列表中二分查找时间最接近的两个状态 idx = bisect_left(joint_times, img_time) if idx == 0: joints = joint_states[0] elif idx == len(joint_times): joints = joint_states[-1] else: # 线性插值 t_before, j_before = joint_times[idx-1], joint_states[idx-1] t_after, j_after = joint_times[idx], joint_states[idx] alpha = (img_time - t_before) / (t_after - t_before) joints = (1-alpha)*j_before + alpha*j_after aligned_data.append({'image': img_data, 'joints': joints})
  3. 坐标系变换计算:利用/tf数据,我们可以计算出在每一帧图像时刻,机器人末端执行器(tool0)相对于机器人基座(base_link)的变换矩阵,也就是末端位姿。这个位姿是7维的:3维位置 (x, y, z) 和4维四元数 (qx, qy, qz, qw) 表示的姿态。这是模仿学习中最常用的动作表示形式。

5.2 数据集格式设计与存储

对齐后的数据需要被组织成易于批量读取的格式。

  1. 选择存储格式

    • HDF5:非常适合存储大型、异构的科学数据。它可以像文件系统一样组织数据,支持压缩,读写速度快。你可以创建一个HDF5文件,里面包含多个组(Group),每个组代表一次演示(Episode)。组内则存放按时间顺序排列的图像数据集(Dataset)、关节状态数据集、末端位姿数据集等。
    • TFRecord:TensorFlow原生的二进制文件格式,对于使用TF训练管线非常友好,支持并行读取和预处理。
    • 自定义格式(如NPZ数组):对于小型项目,也可以直接使用NumPy的.npz文件存储多个数组。
  2. 数据结构设计:一个典型的数据集样本(在时间步t)应包含:

    • 观测(Observation):一个字典,键值对包括image_front(H, W, 3),image_wrist(H, W, 3),joint_positions(7,),joint_velocities(7,),end_effector_pose(7,),force_torque(6,) 等。
    • 动作(Action):下一个时间步机器人应该执行的动作。这通常可以是对末端位姿的变化量(delta pose),或者是对关节角度的变化量(delta joint)。这个动作信息可以从当前时刻的观测和下一时刻的观测中推导出来(例如,action_t = end_effector_pose_{t+1} - end_effector_pose_t)。
    • 奖励(Reward,可选):如果采集时标注了成功信号,可以设计一个稀疏奖励(如最终成功为+1,否则为0)。
    • 终止标志(Done):布尔值,表示当前片段是否结束。
  3. 数据增强与预处理:在存储前或加载时,可以进行一些预处理:

    • 图像:调整大小、归一化像素值到[0,1]或[-1,1]。
    • 状态:对关节角度、位置等进行归一化,减去均值除以标准差,有助于模型训练。
    • 数据增强:对于图像,可以在加载时进行随机裁剪、颜色抖动等在线增强,增加数据的多样性。

5.3 创建PyTorch Dataset类

最后,我们需要一个PyTorch的Dataset类来封装这个数据集,使其能够无缝接入训练循环。

import h5py import torch from torch.utils.data import Dataset, DataLoader import numpy as np class LerobotSO101Dataset(Dataset): def __init__(self, hdf5_path, episode_list=None, transform=None): """ 初始化数据集。 Args: hdf5_path: HDF5文件路径。 episode_list: 指定使用哪些演示片段,如果为None则使用全部。 transform: 应用于图像的变换(如数据增强)。 """ self.hdf5_path = hdf5_path self.transform = transform with h5py.File(hdf5_path, 'r') as f: if episode_list is None: self.episode_list = list(f.keys()) else: self.episode_list = episode_list # 预计算每个episode的长度和全局索引 self.index_map = [] # 存放(episode_name, step_idx)的列表 for ep in self.episode_list: ep_length = f[ep]['observations']['joint_positions'].shape[0] # 通常我们取从0到ep_length-2的步作为样本,因为需要t和t+1来计算动作 for step in range(ep_length - 1): self.index_map.append((ep, step)) def __len__(self): return len(self.index_map) def __getitem__(self, idx): ep_name, step_idx = self.index_map[idx] with h5py.File(self.hdf5_path, 'r') as f: ep_group = f[ep_name] obs_group = ep_group['observations'] # 获取当前时刻观测 image_front = obs_group['image_front'][step_idx] # 假设形状为(H, W, C) joint_pos = obs_group['joint_positions'][step_idx] ee_pose = obs_group['end_effector_pose'][step_idx] # 获取下一时刻观测,用于计算动作 next_ee_pose = obs_group['end_effector_pose'][step_idx + 1] # 计算动作:末端位姿的变化量 (delta pose) # 注意:对于四元数部分,不能直接相减,需要使用四元数差值。 # 这里简化处理,假设姿态变化很小,用轴角近似或直接使用位置差和欧拉角差。 # 更严谨的做法是使用专门的库计算四元数差值。 delta_position = next_ee_pose[:3] - ee_pose[:3] # 简化:直接使用下一时刻的姿态作为目标(在位置控制中常见) # 或者将四元数转换为欧拉角后再计算差值。 target_pose = next_ee_pose # 这里简单地将下一时刻的完整位姿作为动作 # 应用图像变换 if self.transform: image_front = self.transform(image_front) # 转换为Tensor image_front = torch.from_numpy(image_front).float().permute(2, 0, 1) # 转为(C, H, W) joint_pos = torch.from_numpy(joint_pos).float() # 动作可以是target_pose,也可以是delta_position等 action = torch.from_numpy(target_pose).float() # 返回观测和动作 # 观测可以是一个字典,方便模型按需取用 observation = { 'image': image_front, 'joint_positions': joint_pos, 'end_effector_pose': torch.from_numpy(ee_pose).float() } return observation, action

这个Dataset类提供了基本的数据加载功能。在实际使用中,你可能需要根据模型的需求,调整观测和动作的具体内容与格式。

6. 常见问题、避坑指南与心得

在实际操作中,你会遇到各种各样的问题。下面是我踩过的一些坑和总结的经验。

6.1 硬件与同步问题

  • 问题:控制延迟高,机器人动作卡顿。

    • 排查:首先检查控制计算机的性能,使用htop查看CPU占用。关闭不必要的图形界面和程序。其次,检查网络,确保机器人与主机在同一局域网,且使用有线连接。最后,检查ROS节点间的通信是否顺畅,使用rqt_graph查看节点图。
    • 解决:优化代码,避免在控制回调函数中进行繁重计算。对于图像数据,使用压缩格式传输(如compressedImage话题)。考虑使用实时内核(如Linux的PREEMPT_RT补丁)提升系统响应能力。
  • 问题:多相机画面不同步,TF变换出错。

    • 排查:使用rqt_tf_tree检查TF树是否完整,是否存在断链。使用rosbag info your_bag.bag查看各话题的时间戳范围,检查是否有话题的起始时间相差巨大。
    • 解决:确保所有设备使用NTP同步时间。在启动相机驱动时,尝试使用硬件触发同步模式。如果无法硬件同步,在后期处理时,必须进行严格的时间戳插值对齐,而不是简单取最接近的消息。

6.2 数据质量与标注问题

  • 问题:演示数据成功率不高,包含很多失败或纠正的轨迹。

    • 心得:这是遥操作数据集的通病。人类操作员不是完美的。不要试图追求100%完美的演示。相反,可以保留这些“不完美”的数据,但要做好标注。在数据表中详细记录每次演示中出错的时间点和原因。后期清洗时,你可以选择:1) 直接剔除整条失败轨迹;2) 只截取成功的前半部分;3) 利用这些失败数据训练一个“错误检测器”或用于逆向强化学习。
  • 问题:动作分布不平衡,某些方向(如绕Z轴旋转)的操作很少。

    • 解决:这是手柄映射不直观导致的。在设计控制映射时,要反复测试,确保六个自由度的控制都同样方便。有时需要为不常用的自由度(如绕X/Y轴旋转)设置一个“慢速/精细”模式(如按住某个修饰键时,摇杆灵敏度降低)。

6.3 数据处理与模型训练问题

  • 问题:数据集太大,加载和训练非常慢。

    • 解决
      1. 下采样:如果原始数据是100Hz,对于视觉模仿学习来说,30Hz可能就足够了。
      2. 图像压缩:将原始高分辨率图像(如1280x720)在存储前就下采样到模型输入的尺寸(如224x224)。
      3. 使用高效的数据加载:确保你的Dataset类不是每次__getitem__都重新打开HDF5文件。可以使用DataLoadernum_workers参数进行多进程预加载。考虑使用更快的存储(如NVMe SSD)。
  • 问题:模型训练时损失下降,但机器人实际执行时效果很差(分布偏移)。

    • 排查:这是模仿学习的经典问题。首先检查你的动作表示(Action Representation)是否合理。使用“末端位姿”作为动作,要求你的机器人控制器能够高精度地跟踪这个位姿。如果控制器性能有偏差,就会累积误差。其次,检查训练数据和测试环境(真实机器人)的观测是否有差异(光照、相机颜色偏差、背景物体)。
    • 解决
      1. 使用相对动作:让模型预测的是相对于当前位姿的变化量(delta),而不是绝对位姿,对控制误差有一定容忍度。
      2. 加入噪声:在训练时,给观测数据(如图像)加入随机噪声,或者对动作加入微小扰动,可以提升模型的鲁棒性。
      3. 领域随机化:在仿真中训练时,随机化纹理、光照、物体质量等参数。对于真实数据,可以在图像预处理时进行较强的颜色抖动、随机裁剪等。
      4. 考虑使用操作空间(Operational Space)控制或关节空间控制,而不是直接输出末端位姿。

最后一点个人体会:通过遥操作制作数据集,是一个“体力活”和“细心活”的结合。前期在硬件集成、坐标标定、控制映射上多花一天时间,可能后期在数据处理上就能节省一周的时间。每次采集前,务必做好检查清单。数据质量永远比数据数量更重要。一个由100条干净、成功的演示组成的数据集,其价值远高于1000条杂乱、包含大量失败尝试的数据集。当你看到自己采集的数据集成功训练出一个能复现你操作的机器人模型时,那种成就感是无与伦比的。这个从“人”到“机器”的知识传递过程,正是机器人学习最迷人的地方之一。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询