HIL-SERL 真机训练工作流指南

在本教程中,你将使用 LeRobot 体验完整的 Human-in-the-Loop Sample-Efficient Reinforcement Learning(HIL-SERL)工作流。你将掌握在几小时内即可在真实机器人上使用强化学习训练 policy 的技巧。

HIL-SERL 是一种样本高效的强化学习算法,它将人类 demonstration 与在线学习和人工干预相结合。该方法从少量人类 demonstration 开始,用它们训练一个奖励分类器,然后采用 actor-learner 架构,人类可以在 policy 执行过程中进行干预,以引导探索并纠正不安全的行为。在本教程中,你将使用游戏手柄来提供干预并在学习过程中控制机器人。

它结合了三个关键要素:

  1. 离线 demonstration 与奖励分类器: 少量人工 teleoperation episode 加上基于视觉的成功检测器,为 policy 提供一个塑形的起点。

  2. 真机上的 actor / learner 循环与人工干预: 分布式 Soft Actor Critic(SAC)学习器更新 policy,同时 actor 在实体机器人上探索;人类可以随时介入,纠正危险或无效的行为。

  3. 安全与效率工具: 关节/end-effector(EE)边界、裁剪感兴趣区域(ROI)预处理以及 WandB 监控,可保证数据有效且硬件安全。

这些要素共同使 HIL-SERL 能够达到近乎完美的任务成功率,并且比仅靠模仿学习的基线方法拥有更快的周期时间。

HIL-SERL workflow

HIL-SERL 工作流,Luo 等人,2024

本指南提供了使用 LeRobot 的 HilSerl 实现在真实机器人上训练机器人 policy 的分步说明。

你需要什么?

  • 一个游戏手柄(推荐)或键盘来控制机器人
  • 一块 NVIDIA GPU
  • 一台带 follower arm 和 leader arm 的真实机器人(如果使用键盘或游戏手柄则为可选)
  • 用于运动学包的机器人 URDF 文件(参见 lerobot/model/kinematics.py

我可以训练哪些类型的任务?

你可以使用 HIL-SERL 训练各种各样的操作任务。一些建议:

  • 从一个简单的任务开始,以了解系统的工作方式。
    • 将方块推到目标区域
    • 用 gripper 抓取并提起方块
  • 避免过长时域的任务。专注于可以在 5-10 秒内完成的任务。
  • 一旦你很好地了解了系统的工作原理,就可以尝试更复杂的任务和更长的时域。
    • 抓取并放置方块
    • 用两只手臂抓取物体的双臂任务
    • 将物体从一只手臂递交给另一只手臂的交接任务
    • 尽情发挥吧!

安装 LeRobot(含 HIL-SERL)

要安装带 HIL-SERL 的 LeRobot,你需要安装 hilserl 额外扩展。

pip install -e ".[hilserl]"

真机训练工作流

理解配置

训练过程始于为 HIL-SERL 环境进行正确的配置。主要的配置类是 lerobot/rl/gym_manipulator.py 中的 GymManipulatorConfig,它包含嵌套的 HILSerlRobotEnvConfig(定义于 lerobot/envs/configs.py)和 DatasetConfig。配置被组织为聚焦的、嵌套的子配置:

class GymManipulatorConfig:
    env: HILSerlRobotEnvConfig    # Environment configuration (nested)
    dataset: DatasetConfig    # Dataset recording/replay configuration (nested)
    mode: str | None = None    # "record", "replay", or None (for training)
    device: str = "cpu"    # Compute device

class HILSerlRobotEnvConfig(EnvConfig):
    robot: RobotConfig | None = None    # Main robot agent (defined in `lerobot/robots`)
    teleop: TeleoperatorConfig | None = None    # Teleoperator agent, e.g., gamepad or leader arm
    processor: HILSerlProcessorConfig    # Processing pipeline configuration (nested)
    name: str = "real_robot"    # Environment name
    task: str | None = None    # Task identifier
    fps: int = 10    # Control frequency

# Nested processor configuration
class HILSerlProcessorConfig:
    control_mode: str = "gamepad"    # Control mode
    observation: ObservationConfig | None = None    # Observation processing settings
    image_preprocessing: ImagePreprocessingConfig | None = None    # Image crop/resize settings
    gripper: GripperConfig | None = None    # Gripper control and penalty settings
    reset: ResetConfig | None = None    # Environment reset and timing settings
    inverse_kinematics: InverseKinematicsConfig | None = None    # IK processing settings
    reward_classifier: RewardClassifierConfig | None = None    # Reward classifier settings
    max_gripper_pos: float | None = 100.0    # Maximum gripper position

# Sub-configuration classes
class ObservationConfig:
    add_joint_velocity_to_observation: bool = False    # Add joint velocities to state
    add_current_to_observation: bool = False    # Add motor currents to state
    add_ee_pose_to_observation: bool = False    # Add end-effector pose to state
    display_cameras: bool = False    # Display camera feeds during execution

class ImagePreprocessingConfig:
    crop_params_dict: dict[str, tuple[int, int, int, int]] | None = None    # Image cropping parameters
    resize_size: tuple[int, int] | None = None    # Target image size

class GripperConfig:
    use_gripper: bool = True    # Enable gripper control
    gripper_penalty: float = 0.0    # Penalty for inappropriate gripper usage

class ResetConfig:
    fixed_reset_joint_positions: list[float] | None = None    # Joint positions for reset
    reset_time_s: float = 5.0    # Time to wait during reset
    control_time_s: float = 20.0    # Maximum episode duration
    terminate_on_success: bool = True    # Whether to terminate episodes on success detection

class InverseKinematicsConfig:
    urdf_path: str | None = None    # Path to robot URDF file
    target_frame_name: str | None = None    # End-effector frame name
    end_effector_bounds: dict[str, list[float]] | None = None    # EE workspace bounds
    end_effector_step_sizes: dict[str, float] | None = None    # EE step sizes per axis

class RewardClassifierConfig:
    pretrained_path: str | None = None    # Path to pretrained reward classifier
    success_threshold: float = 0.5    # Success detection threshold
    success_reward: float = 1.0    # Reward value for successful episodes

# Dataset configuration
class DatasetConfig:
    repo_id: str    # LeRobot dataset repository ID
    task: str    # Task identifier
    root: str | None = None    # Local dataset root directory
    num_episodes_to_record: int = 5    # Number of episodes for recording
    replay_episode: int | None = None    # Episode index for replay
    push_to_hub: bool = False    # Whether to push datasets to Hub

处理器流水线架构

HIL-SERL 采用模块化的处理器流水线架构,通过一系列可组合的步骤处理机器人的 observation 和 action。流水线分为两个主要部分:

环境处理器流水线

环境处理器(env_processor)负责处理传入的 observation 和环境 state:

  1. VanillaObservationProcessorStep:将原始机器人 observation 转换为标准化格式
  2. JointVelocityProcessorStep(可选):向 observation 中添加关节速度信息
  3. MotorCurrentProcessorStep(可选):向 observation 中添加电机电流读数
  4. ForwardKinematicsJointsToEE(可选):根据关节位置计算 end-effector 位姿
  5. ImageCropResizeProcessorStep(可选):裁剪并调整相机图像大小
  6. TimeLimitProcessorStep(可选):强制 episode 时间限制
  7. GripperPenaltyProcessorStep(可选):对不恰当的 gripper 使用施加惩罚
  8. RewardClassifierProcessorStep(可选):使用视觉模型进行自动奖励检测
  9. AddBatchDimensionProcessorStep:将数据转换为批次格式以便神经网络处理
  10. DeviceProcessorStep:将数据移动到指定的计算设备(CPU/GPU)

action 处理器流水线

action 处理器(action_processor)负责处理输出的 action 和人工干预:

  1. AddTeleopActionAsComplimentaryDataStep:捕获 teleoperator 的 action 用于日志记录
  2. AddTeleopEventsAsInfoStep:记录干预事件和 episode 控制信号
  3. InterventionActionProcessorStep:处理人工干预和 episode 终止
  4. 逆运动学流水线(启用时):
    • MapDeltaActionToRobotActionStep:将增量 action 转换为机器人 action 格式
    • EEReferenceAndDelta:计算 end-effector 参考与增量运动
    • EEBoundsAndSafety:强制工作空间安全边界
    • InverseKinematicsEEToJoints:将 end-effector action 转换为关节目标
    • GripperVelocityToJoint:处理 gripper 控制命令

配置示例

基本 observation 处理

{
  "env": {
    "processor": {
      "observation": {
        "add_joint_velocity_to_observation": true,
        "add_current_to_observation": false,
        "display_cameras": false
      }
    }
  }
}

图像处理

{
  "env": {
    "processor": {
      "image_preprocessing": {
        "crop_params_dict": {
          "observation.images.front": [180, 250, 120, 150],
          "observation.images.side": [180, 207, 180, 200]
        },
        "resize_size": [128, 128]
      }
    }
  }
}

逆运动学设置

{
  "env": {
    "processor": {
      "inverse_kinematics": {
        "urdf_path": "path/to/robot.urdf",
        "target_frame_name": "end_effector",
        "end_effector_bounds": {
          "min": [0.16, -0.08, 0.03],
          "max": [0.24, 0.2, 0.1]
        },
        "end_effector_step_sizes": {
          "x": 0.02,
          "y": 0.02,
          "z": 0.02
        }
      }
    }
  }
}

高级 observation 处理

HIL-SERL 框架支持可以改进 policy learning 的附加 observation 处理功能:

关节速度处理

启用关节速度估计,为 policy 提供运动信息:

{
  "env": {
    "processor": {
      "observation": {
        "add_joint_velocity_to_observation": true
      }
    }
  }
}

该处理器:

  • 使用连续关节位置读数之间的有限差分来估计关节速度
  • 将速度信息添加到 observation state vector 中
  • 适用于需要对动态任务具有运动感知能力的 policy

电机电流处理

监测电机电流以检测接触力和负载状况:

{
  "env": {
    "processor": {
      "observation": {
        "add_current_to_observation": true
      }
    }
  }
}

该处理器:

  • 从机器人的控制系统中读取电机电流值
  • 将电流测量值添加到 observation state vector 中
  • 帮助检测接触事件、物体重量和机械阻力
  • 适用于接触密集的操作任务

组合 observation 处理

你可以同时启用多个 observation 处理功能:

{
  "env": {
    "processor": {
      "observation": {
        "add_joint_velocity_to_observation": true,
        "add_current_to_observation": true,
        "display_cameras": false
      }
    }
  }
}

注意:启用额外的 observation 功能会增加 state space 的维数,这可能需要调整你的 policy network 架构,并可能需要收集更多的训练数据。

查找机器人工作空间边界

在收集 demonstration 之前,你需要确定机器人的适当操作边界。

这通过两种方式简化了在真实机器人上学习的问题:1)将机器人的操作空间限制在能够解决任务并避免不必要或不安全探索的特定区域内;2)允许在 end-effector 空间而非关节空间中进行训练。经验表明,在操作任务中,使用强化学习在关节空间中学习通常是更困难的问题——有些任务在关节空间中几乎无法学习,但当 action space 转换为 end-effector 坐标后就变得可学习了。

使用 lerobot-find-joint-limits

此脚本帮助你找到机器人 end-effector 的安全操作边界。既然你拥有 follower arm 和 leader arm,你可以使用该脚本找出训练期间将应用到 follower arm 的边界。 限制 action space 将减少智能体的冗余探索,并保证安全性。

lerobot-find-joint-limits \
  --robot.type=so100_follower \
  --robot.port=/dev/tty.usbmodem58760431541 \
  --robot.id=black \
  --teleop.type=so100_leader \
  --teleop.port=/dev/tty.usbmodem58760431551 \
  --teleop.id=blue

工作流

  1. 运行脚本,让机器人穿过完成该任务所需的空间
  2. 脚本会记录 end-effector 的最小和最大位置以及关节角度,并将其打印到控制台,例如:
    Max ee position [0.2417 0.2012 0.1027]
    Min ee position [0.1663 -0.0823 0.0336]
    Max joint positions [-20.0, -20.0, -20.0, -20.0, -20.0, -20.0]
    Min joint positions [50.0, 50.0, 50.0, 50.0, 50.0, 50.0]
  3. 在环境配置中使用这些值,放在 env.processor.inverse_kinematics.end_effector_bounds 下(参见 lerobot/envs/configs.py 中的 InverseKinematicsConfig

示例配置

{
  "env": {
    "processor": {
      "inverse_kinematics": {
        "end_effector_bounds": {
          "max": [0.24, 0.2, 0.1],
          "min": [0.16, -0.08, 0.03]
        }
      }
    }
  }
}

收集 demonstration

定义好边界后,你就可以安全地收集用于训练的 demonstration。使用离 policy 算法训练强化学习,使我们能够利用离线收集的 dataset 来提高学习过程的效率。

设置录制模式

创建一个用于录制 demonstration 的配置文件(或编辑已有文件,如 env_config.json):

  1. 在根级别将 mode 设置为 "record"
  2. dataset 部分为你的 dataset 指定一个唯一的 repo_id(例如 “username/task_name”)
  3. dataset 部分将 num_episodes_to_record 设置为你要收集的 demonstration 数量
  4. 先将 env.processor.image_preprocessing.crop_params_dict 设置为 {}(我们稍后会确定裁剪参数)
  5. env 部分配置 env.robotenv.teleop 和其他硬件设置

示例配置部分:

{
  "env": {
    "type": "gym_manipulator",
    "name": "real_robot",
    "fps": 10,
    "processor": {
      "control_mode": "gamepad",
      "observation": {
        "display_cameras": false
      },
      "image_preprocessing": {
        "crop_params_dict": {},
        "resize_size": [128, 128]
      },
      "gripper": {
        "use_gripper": true,
        "gripper_penalty": 0.0
      },
      "reset": {
        "reset_time_s": 5.0,
        "control_time_s": 20.0
      }
    },
    "robot": {
      // ... robot configuration ...
    },
    "teleop": {
      // ... teleoperator configuration ...
    }
  },
  "dataset": {
    "repo_id": "username/pick_lift_cube",
    "root": null,
    "task": "pick_and_lift",
    "num_episodes_to_record": 15,
    "replay_episode": 0,
    "push_to_hub": true
  },
  "mode": "record",
  "device": "cpu"
}

使用 teleoperator

除了机器人之外,你还需要一个 teleoperator 来控制它,以便为你的任务收集 dataset,并在在线训练期间进行干预。 我们支持使用游戏手柄、键盘或机器人的 leader arm。

HIL-SERL 在机器人的 end-effector 空间中学习 action。因此,teleoperation 将控制 end-effector 的 x、y、z 位移。

end-effector 变换由配置在 env.processor.inverse_kinematicsInverseKinematicsConfig)以及 env.processor.gripper / env.processor.max_gripper_pos 下的处理器流水线(InverseKinematicsRLStepEEBoundsAndSafetyEEReferenceAndDeltaGripperVelocityToJoint)应用。与 end-effector 空间相关的默认值如下:

class InverseKinematicsConfig:
    """Configuration for inverse kinematics processing."""

    urdf_path: str | None = None
    target_frame_name: str | None = None
    # bounds for the end-effector in x,y,z direction
    end_effector_bounds: dict[str, list[float]] | None = None
    # maximum step size for the end-effector in x,y,z direction
    end_effector_step_sizes: dict[str, float] | None = None

class HILSerlProcessorConfig:
    ...
    # maximum gripper position that the gripper will be open at
    max_gripper_pos: float | None = 100.0

Teleoperator 定义了 teleoperator。你可以在 lerobot/teleoperators 中查看可用的 teleoperator 列表。

设置游戏手柄

游戏手柄提供了一种非常方便的方式来控制机器人和 episode state。

要设置游戏手柄,你需要在配置文件中将 control_mode 设置为 "gamepad",并定义 teleop 部分。

{
  "env": {
    "teleop": {
      "type": "gamepad",
      "use_gripper": true
    },
    "processor": {
      "control_mode": "gamepad",
      "gripper": {
        "use_gripper": true
      }
    }
  }
}

Figure shows the control mappings on a Logitech gamepad.

用于机器人控制和 episode 管理的游戏手柄按键映射

设置 SO101 leader arm

SO101 leader arm 采用减速齿轮,使其能够在探索期间移动并跟踪 follower arm。因此,接管过程比无齿轮的 SO100 顺畅得多。

要设置 SO101 leader arm,你需要在配置文件中将 control_mode 设置为 "leader",并定义 teleop 部分。

{
  "env": {
    "teleop": {
      "type": "so101_leader",
      "port": "/dev/tty.usbmodem585A0077921",
      "use_degrees": true
    },
    "processor": {
      "control_mode": "leader",
      "gripper": {
        "use_gripper": true
      }
    }
  }
}

为了标注 episode 的成功/失败,你需要使用键盘:按 s 表示成功,按 esc 表示失败。 在在线训练期间,按 space 接管 policy 控制,再次按 space 将控制权交还给 policy。

视频:SO101 leader arm teleoperation

SO101 leader arm teleoperation 示例:leader arm 跟踪 follower arm,按 `space` 进行干预

录制 demonstration

启动录制过程,配置文件示例可在此处找到:

python -m lerobot.rl.gym_manipulator --config_path src/lerobot/configs/env_config_so100.json

录制期间:

  1. 机器人将重置到配置文件中定义的初始位置 env.processor.reset.fixed_reset_joint_positions
  2. 成功完成任务
  3. 当你按下“成功”按钮时,episode 以奖励 1 结束
  4. 如果达到时间限制,或按下了“失败”按钮,episode 以奖励 0 结束
  5. 你可以通过按下“重新录制”按钮来重新录制 episode
  6. 过程会自动继续到下一个 episode
  7. 录制完所有 episode 后,dataset 会被推送到 Hugging Face Hub(可选)并保存在本地

处理 dataset

收集 demonstration 后,对其进行处理以确定最佳的相机裁剪区域。 强化学习对背景干扰很敏感,因此将图像裁剪到相关的工作空间区域非常重要。

视觉强化学习算法直接从像素输入中学习,因此容易受到无关视觉信息的影响。诸如变化的照明、阴影、移动的人或工作空间之外的物体等背景元素可能会干扰学习过程。好的 ROI 选择应该:

  • 只包含任务发生的必要工作空间
  • 捕捉机器人的 end-effector 和任务涉及的所有物体
  • 排除不必要的背景元素和干扰物

注意:如果你已经知道裁剪参数,可以跳过此步骤,只需在录制期间在配置文件中设置 crop_params_dict

确定裁剪参数

使用 crop_dataset_roi.py 脚本以交互方式选择相机图像中的感兴趣区域:

python -m lerobot.rl.crop_dataset_roi --repo-id username/pick_lift_cube
  1. 对于每个相机视角,脚本会显示第一帧
  2. 在相关的工作空间区域周围绘制一个矩形
  3. 按 ‘c’ 确认选择
  4. 对所有相机视角重复操作
  5. 脚本输出裁剪参数并创建新的裁剪后 dataset

示例输出:

Selected Rectangular Regions of Interest (top, left, height, width):
observation.images.side: [180, 207, 180, 200]
observation.images.front: [180, 250, 120, 150]

用于选择感兴趣区域的交互式裁剪工具

更新配置

将以下裁剪参数添加到你的训练配置中:

{
  "env": {
    "processor": {
      "image_preprocessing": {
        "crop_params_dict": {
          "observation.images.side": [180, 207, 180, 200],
          "observation.images.front": [180, 250, 120, 150]
        },
        "resize_size": [128, 128]
      }
    }
  }
}

推荐的图像分辨率

大多数基于视觉的 policy 都已在 128×128(默认)或 64×64 像素的方形输入上得到验证。因此,我们建议将 resize_size 参数设置为 [128, 128]——或者,如果你需要节省 GPU 显存和带宽,可设置为 [64, 64]。其他分辨率也是可行的,但尚未经过广泛测试。

训练奖励分类器

奖励分类器在 HIL-SERL 工作流中扮演着重要角色,它自动化奖励分配并自动检测 episode 是否成功。奖励分类器无需手动定义奖励函数或依赖每个时间步的人工反馈,而是学习根据视觉 observation 预测成功/失败。这使得强化学习算法能够基于机器人的相机输入获得一致且自动化的奖励信号,从而高效地学习。

本指南说明了如何为 LeRobot 的人在环路强化学习实现训练奖励分类器。奖励分类器学习在给定 state 下预测奖励值,该奖励值可用于强化学习设置中以训练 policy。

注意:训练奖励分类器是可选的。你可以在第一轮强化学习实验中通过游戏手柄或键盘设备手动标注成功。

lerobot/rewards/classifier/modeling_classifier.py 中的奖励分类器实现使用预训练的视觉模型来处理图像。它可以输出单个值用于二元奖励以预测成功/失败情况,也可以输出多个值用于多类设置。

为奖励分类器收集 dataset

在训练之前,你需要收集一个带有标注示例的 dataset。在配置中设置 mode: "record" 并运行 gym_manipulator.py,即可启动收集 observation、action 和奖励 dataset 的过程。

要收集 dataset,你需要修改基于 HILSerlRobotEnvConfig 的环境配置中的一些参数。

python -m lerobot.rl.gym_manipulator --config_path src/lerobot/configs/reward_classifier_train_config.json

数据收集的关键参数

  • mode:将其设置为 "record" 以收集 dataset(在根级别)
  • dataset.repo_id"hf_username/dataset_name",即 Hub 上 dataset 和仓库的名称
  • dataset.num_episodes_to_record:要录制的 episode 数
  • env.processor.reset.terminate_on_success:检测到成功时是否自动终止 episode(默认值:true
  • env.fps:录制时每秒的帧数
  • dataset.push_to_hub:是否将 dataset 推送到 Hub

env.processor.reset.terminate_on_success 参数允许你控制 episode 终止行为。将其设置为 false 时,即使检测到成功,episode 也会继续,从而允许你收集更多带有 reward=1 标签的正例。这对于训练奖励分类器至关重要,因为它会在你的 dataset 中提供更多的成功 state 示例。将其设置为 true(默认值)时,episode 在检测到成功时立即终止。

重要:对于奖励分类器训练,设置 terminate_on_success: false 以收集足够的正例。对于常规的 HIL-SERL 训练,请保持为 true 以在任务成功完成时自动终止 episode。

用于数据收集的示例配置部分:

{
  "env": {
    "type": "gym_manipulator",
    "name": "real_robot",
    "fps": 10,
    "processor": {
      "reset": {
        "reset_time_s": 5.0,
        "control_time_s": 20.0,
        "terminate_on_success": false
      },
      "gripper": {
        "use_gripper": true
      }
    },
    "robot": {
      // ... robot configuration ...
    },
    "teleop": {
      // ... teleoperator configuration ...
    }
  },
  "dataset": {
    "repo_id": "hf_username/dataset_name",
    "root": "data/your_dataset",
    "task": "reward_classifier_task",
    "num_episodes_to_record": 20,
    "replay_episode": null,
    "push_to_hub": true
  },
  "mode": "record",
  "device": "cpu"
}

奖励分类器配置

奖励分类器使用 lerobot/rewards/classifier/configuration_classifier.py 进行配置。以下是关键参数:

  • model_name:基础模型架构(例如,我们主要使用 "helper2424/resnet10"
  • model_type"cnn""transformer"
  • num_cameras:相机输入的数量
  • num_classes:输出类别的数量(对于二元成功/失败通常为 2)
  • hidden_dim:隐藏表示的维度
  • dropout_rate:正则化参数
  • learning_rate:优化器的学习率

训练奖励分类器的示例配置:

{
  "dataset": {
    "repo_id": "hf_username/dataset_name",
    "root": null
  },
  "reward_model": {
    "type": "reward_classifier",
    "model_name": "helper2424/resnet10",
    "model_type": "cnn",
    "num_cameras": 2,
    "num_classes": 2,
    "hidden_dim": 256,
    "dropout_rate": 0.1,
    "learning_rate": 1e-4,
    "device": "cuda",
    "input_features": {
      "observation.images.front": {
        "type": "VISUAL",
        "shape": [3, 128, 128]
      },
      "observation.images.side": {
        "type": "VISUAL",
        "shape": [3, 128, 128]
      }
    },
    "push_to_hub": true,
    "repo_id": "hf_username/model_repo"
  },
  "batch_size": 16,
  "num_workers": 4,
  "steps": 5000,
  "log_freq": 10,
  "env_eval_freq": 1000,
  "save_freq": 1000,
  "save_checkpoint": true,
  "seed": 2,
  "resume": false,
  "optimizer": {
    "grad_clip_norm": 10.0
  },
  "wandb": {
    "enable": true,
    "project": "reward-classifier",
    "disable_artifact": false
  },
  "job_name": "reward-classifier"
}

训练分类器

要训练分类器,请使用 train.py 脚本并携带你的配置:

lerobot-train --config_path path/to/reward_classifier_train_config.json

部署和测试模型

要使用你训练好的奖励分类器,请配置 HILSerlRobotEnvConfig 以使用你的模型:

config = GymManipulatorConfig(
    env=HILSerlRobotEnvConfig(
        processor=HILSerlProcessorConfig(
            reward_classifier=RewardClassifierConfig(
                pretrained_path="path_to_your_pretrained_trained_model"
            )
        ),
        # Other environment parameters
    ),
    dataset=DatasetConfig(...),
    mode=None  # For training
)

或者在 json 配置文件中设置该参数。

{
  "env": {
    "processor": {
      "reward_classifier": {
        "pretrained_path": "path_to_your_pretrained_model",
        "success_threshold": 0.7,
        "success_reward": 1.0
      },
      "reset": {
        "terminate_on_success": true
      }
    }
  }
}

运行 gym_manipulator.py 来测试模型。

python -m lerobot.rl.gym_manipulator --config_path path/to/env_config.json

奖励分类器将根据机器人相机的视觉输入自动提供奖励。

训练奖励分类器的示例工作流

  1. 创建配置文件: 为奖励分类器和环境创建必要的 json 配置文件。查看此处的示例。

  2. 收集 dataset

    python -m lerobot.rl.gym_manipulator --config_path src/lerobot/configs/env_config.json
  3. 训练分类器

    lerobot-train --config_path src/lerobot/configs/reward_classifier_train_config.json
  4. 测试分类器

    python -m lerobot.rl.gym_manipulator --config_path src/lerobot/configs/env_config.json

使用 Actor-Learner 进行训练

LeRobot 系统采用分布式的 actor-learner 架构进行训练。该架构将机器人的交互与学习过程解耦,使它们能够并发运行而不互相阻塞。actor 服务器处理机器人的 observation 和 action,并将交互数据发送给 learner 服务器。learner 服务器执行梯度下降并定期更新 actor 的 policy 权重。你需要启动两个进程:一个 learner 和一个 actor。

配置设置

创建一个训练配置文件(示例在此处)。训练配置基于 lerobot/rl/train_rl.py 中的主类 TrainRLServerPipelineConfig

  1. 配置 policy 设置(type="gaussian_actor"device 等)
  2. 在顶层 algorithm 块下配置算法设置(type="sac"、学习率、折扣等,定义于 lerobot/rl/algorithms/sac/configuration_sac.py 中)。
  3. dataset 设置为你的裁剪后 dataset
  4. 使用裁剪参数配置环境设置
  5. configuration_gaussian_actor.py 中查看与 Gaussian Actor 相关的其他参数。
  6. 验证 policy 配置在针对你的任务的 input_featuresoutput_features 方面是否正确。

启动 Learner

首先,启动 learner 服务器进程:

python -m lerobot.rl.learner --config_path src/lerobot/configs/train_config_hilserl_so100.json

learner 会:

  • 初始化 policy network
  • 准备回放缓冲区
  • 打开一个 gRPC 服务器与 actor 通信
  • 处理转移并更新 policy

启动 Actor

在另一个终端中,使用相同的配置启动 actor 进程:

python -m lerobot.rl.actor --config_path src/lerobot/configs/train_config_hilserl_so100.json

actor 会:

  • 通过 gRPC 连接到 learner
  • 初始化环境
  • 执行 policy 的 rollout 以收集经验
  • 将转移发送给 learner
  • 接收更新后的 policy 参数

训练流程

训练会自动进行:

  1. actor 在环境中执行 policy
  2. 收集转移并将其发送给 learner
  3. learner 根据这些转移更新 policy
  4. 更新后的 policy 参数被发送回 actor
  5. 过程持续进行,直到达到指定的步数限制

人在环路

  • 高效学习的关键在于进行人工干预以提供纠正性反馈并完成任务,从而帮助 policy learning 和探索。
  • 要进行人工干预,你可以按下游戏手柄右上方扳机按钮(或键盘上的 space 键)。这将暂停 policy action,并允许你接管控制。
  • 成功的实验是:人类在开始时需要干预,但随着 policy 的改进而减少干预的次数。你可以在 wandb 仪表盘中监控干预率。

Figure shows the control mappings on a Logitech gamepad.

展示人工干预如何随着时间推移帮助引导 policy learning 的示例

  • 该图显示了 episode 奖励随交互步数的变化曲线,展示了人工干预对 policy learning 的影响。
  • 橙色曲线是没有人工干预的实验,而粉色和蓝色曲线是有人工干预的实验。
  • 我们可以观察到,当存在人工干预时,policy 开始获得最大奖励所需的步数减少了四分之一。

监控与调试

如果你在配置中将 wandb.enable 设置为 true,就可以通过 Weights & Biases 仪表盘实时监控训练进度。

人工干预指南

学习过程对干预 policy 非常敏感。需要运行几次才能了解如何有效干预。以下是一些提示和建议:

  • 在训练开始时让 policy 探索几个 episode。
  • 避免长时间干预。尽量在机器人偏离轨道时介入以纠正其行为。
  • 一旦 policy 开始完成任务,即使并不完美,你也可以将干预限制为简单的快速 action,例如简单的抓取命令。

理想的行为是,你的干预率应在训练过程中逐渐下降,如下图所示。

Intervention rate

在抓取并提起方块任务的一次训练运行中, 干预率的变化曲线

需要调整的关键超参数

某些配置值对训练稳定性和速度有着不成比例的影响:

  • temperature_initalgorithm.temperature_init)——SAC 中的初始熵温度。较高的值鼓励更多的探索;较低的值使 policy 在早期更加确定。一个不错的起点是 1e-2。我们观察到,将其设置过高会使人干预失效并减慢学习速度。
  • policy_parameters_push_frequencypolicy.actor_learner_config.policy_parameters_push_frequency)——learner 向 actor 推两次权重之间的时间间隔(以为单位)。默认值为 4 s。将其减小到 1-2 秒 可以提供更新的权重(代价是更多的网络流量);只有在连接较慢时才增大它,因为这会降低样本效率。
  • storage_devicepolicy.storage_device)——learner 保存 policy 参数所用的设备。如果你有多余的 GPU 显存,可将其设置为 "cuda"(而不是默认的 "cpu")。将权重保存在 GPU 上可以消除 CPU→GPU 的传输开销,并显著增加 learner 每秒的更新次数。

恭喜 🎉,你已经完成了本教程!

如果你有任何问题或需要帮助,请在 Discord 上联系我们。

论文引用:

@article{luo2024precise,
  title={Precise and Dexterous Robotic Manipulation via Human-in-the-Loop Reinforcement Learning},
  author={Luo, Jianlan and Xu, Charles and Wu, Jeffrey and Levine, Sergey},
  journal={arXiv preprint arXiv:2410.21845},
  year={2024}
}
在 GitHub 上更新