# 在真实世界机器人上的模仿学习

本教程将讲解如何训练神经网络以自主控制真实机器人。

**你将学到：**

1. 如何记录和可视化您的数据集。
2. 如何使用您的数据训练策略并为其评估做好准备。
3. 如何评估您的策略并可视化结果。

按照这些步骤操作，您将能够以较高的成功率复现诸如拾取乐高积木并将其放入收纳箱中的任务，如下面的视频所示。

视频：拾取乐高积木任务

  
    <source
      src="https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/lerobot/lerobot_task.mp4"
      type="video/mp4"
    />
  

本教程不针对特定的机器人：我们将引导您了解可以适配到任何受支持平台的命令和 API 代码片段。

在数据收集过程中，您将使用“遥操作”设备（例如主臂或键盘）来远程操控机器人并记录其运动轨迹。

收集到足够的轨迹后，您将训练一个神经网络来模仿这些轨迹，并部署训练好的模型，使您的机器人能够自主执行任务。

如果在任何环节遇到问题，请加入我们的 [Discord 社区](https://discord.com/invite/s3KuuzsPFb) 获取支持。

想快速获取适用于您环境的正确命令？[快速入门笔记本](https://github.com/huggingface/lerobot/blob/main/examples/notebooks/quickstart.ipynb) [![Open in Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/huggingface/lerobot/blob/main/examples/notebooks/quickstart.ipynb) 允许您一次性配置机器人，并生成以下所有可直接粘贴的命令。

## 设置与校准

如果您尚未设置并校准您的机器人和遥操作设备，请按照您所用机器人的教程进行操作，例如 [SO-101](./so101)、[OpenArm](./openarm)、[Koch](./koch) 或 [LeKiwi](./lekiwi)。所有受支持的机器人均列在侧边栏的 **Robots** 下。

## 远程操作

在本示例中，我们将演示如何遥操作 SO101 机器人。对于每条指令，我们还提供了相应的 API 示例。

请注意，与机器人关联的 `id` 用于存储校准文件。在使用相同配置进行遥操作、录制和评估时，务必使用相同的 `id`。

```bash
lerobot-teleoperate \
    --robot.type=so101_follower \
    --robot.port=/dev/tty.usbmodem58760431541 \
    --robot.id=my_awesome_follower_arm \
    --teleop.type=so101_leader \
    --teleop.port=/dev/tty.usbmodem58760431551 \
    --teleop.id=my_awesome_leader_arm
```

```python
from lerobot.teleoperators.so_leader import SO101Leader, SO101LeaderConfig
from lerobot.robots.so_follower import SO101Follower, SO101FollowerConfig

robot_config = SO101FollowerConfig(
    port="/dev/tty.usbmodem5AB90687491",
    id="my_follower_arm",
)

teleop_config = SO101LeaderConfig(
    port="/dev/tty.usbmodem5AB90689011",
    id="my_leader_arm",
)

robot = SO101Follower(robot_config)
teleop_device = SO101Leader(teleop_config)
robot.connect()
teleop_device.connect()

while True:
    action = teleop_device.get_action()
    robot.send_action(action)
```

teleoperate 命令将自动执行以下操作：

1. 识别缺失的校准项并启动校准流程。
2. 连接机器人和遥操作设备，然后开始遥操作。

## 相机

要将摄像头添加到您的设置中，请遵循此[指南](./cameras)。

## 使用相机进行远程操作

借助 `rerun`，您可以在遥操作的同时可视化相机画面和关节位置。本示例使用两个相机，`wrist` 和 `top`；教程的其余部分将沿用相同的相机配置。

```bash
lerobot-teleoperate \
    --robot.type=so101_follower \
    --robot.port=/dev/tty.usbmodem5AB90687491 \
    --robot.id=my_follower_arm \
    --robot.cameras="{ wrist: {type: opencv, index_or_path: 0, width: 640, height: 480, fps: 30}, top: {type: opencv, index_or_path: 1, width: 640, height: 480, fps: 30}}" \
    --teleop.type=so101_leader \
    --teleop.port=/dev/tty.usbmodem5AB90689011 \
    --teleop.id=my_leader_arm \
    --display_data=true
```

```python
import time
from lerobot.teleoperators.so_leader import SO101Leader, SO101LeaderConfig
from lerobot.robots.so_follower import SO101Follower, SO101FollowerConfig
from lerobot.cameras.opencv import OpenCVCameraConfig
from lerobot.utils.visualization_utils import init_visualization, log_visualization_data, shutdown_visualization

robot_config = SO101FollowerConfig(
    port="/dev/tty.usbmodem5AB90687491",
    id="my_follower_arm",
    cameras={
        "wrist": OpenCVCameraConfig(index_or_path=0, width=640, height=480, fps=30),
        "top": OpenCVCameraConfig(index_or_path=1, width=640, height=480, fps=30)
    }
)

teleop_config = SO101LeaderConfig(
    port="/dev/tty.usbmodem5AB90689011",
    id="my_leader_arm",
)

init_visualization("rerun", session_name="teleoperation")  # pass "foxglove" to stream to Foxglove instead

robot = SO101Follower(robot_config)
teleop_device = SO101Leader(teleop_config)
robot.connect()
teleop_device.connect()

TARGET_HZ = 30
TIME_PER_FRAME = 1.0 / TARGET_HZ

while True:
    start_time = time.perf_counter()

    observation = robot.get_observation()
    action = teleop_device.get_action()
    robot.send_action(action)
    log_visualization_data("rerun", observation=observation, action=action)

    elapsed_time = time.perf_counter() - start_time
    sleep_time = TIME_PER_FRAME - elapsed_time
    if sleep_time > 0:
        time.sleep(sleep_time)
```

## 记录数据集

熟悉遥操作后，您就可以录制第一个数据集了。

我们使用 Hugging Face hub 功能来上传您的数据集。如果您之前未使用过 Hub，请确保能够通过 cli 使用具有写入权限的 token 登录，该 token 可以从 [Hugging Face 设置](https://huggingface.co/settings/tokens) 中生成。

运行以下命令将您的令牌添加到 CLI 中：

```bash
hf auth login --token ${HUGGINGFACE_TOKEN} --add-to-git-credential
```

然后将您的 Hugging Face 仓库名称存储在一个变量中：

```bash
HF_USER=$(NO_COLOR=1 hf auth whoami | awk -F': *' 'NR==1 {print $2}')
echo $HF_USER
```

现在您可以录制数据集。要录制 5 个 episode 并将数据集上传到 hub，请根据您的机器人调整以下代码，然后执行该命令或 API 示例。

```bash
lerobot-record \
    --robot.type=so101_follower \
    --robot.port=/dev/tty.usbmodem585A0076841 \
    --robot.id=my_awesome_follower_arm \
    --robot.cameras="{ front: {type: opencv, index_or_path: 0, width: 1920, height: 1080, fps: 30}}" \
    --robot.cameras="{ wrist: {type: opencv, index_or_path: 0, width: 640, height: 480, fps: 30}, top: {type: opencv, index_or_path: 1, width: 640, height: 480, fps: 30}}" \
    --teleop.type=so101_leader \
    --teleop.port=/dev/tty.usbmodem58760431551 \
    --teleop.id=my_awesome_leader_arm \
    --display_data=true \
    --dataset.repo_id=${HF_USER}/record-test \
    --dataset.num_episodes=5 \
    --dataset.single_task="Grab the black cube" \
    --dataset.streaming_encoding=true \
    --dataset.encoder_threads=2
# Optional: --dataset.rgb_encoder.vcodec=auto
```

```python
from lerobot.cameras.opencv import OpenCVCameraConfig
from lerobot.datasets.lerobot_dataset import LeRobotDataset
from lerobot.utils.feature_utils import hw_to_dataset_features
from lerobot.robots.so_follower import SO101Follower, SO101FollowerConfig
from lerobot.teleoperators.so_leader.config_so_leader import SO101LeaderConfig
from lerobot.teleoperators.so_leader.so_leader import SO101Leader
from lerobot.common.control_utils import init_keyboard_listener
from lerobot.utils.utils import log_say
from lerobot.utils.visualization_utils import init_visualization
from lerobot.scripts.lerobot_record import record_loop
from lerobot.processor import make_default_processors

NUM_EPISODES = 5
FPS = 30
EPISODE_TIME_SEC = 60
RESET_TIME_SEC = 10
TASK_DESCRIPTION = "My task description"

def main():
    # Create robot configuration
    robot_config = SO101FollowerConfig(
        port="/dev/tty.usbmodem5AB90687491",
        id="my_follower_arm",
        cameras={
            "wrist": OpenCVCameraConfig(index_or_path=0, width=640, height=480, fps=30),
            "top": OpenCVCameraConfig(index_or_path=1, width=640, height=480, fps=30)
        }
    )

    teleop_config = SO101LeaderConfig(
        port="/dev/tty.usbmodem5AB90689011",
        id="my_leader_arm",
    )

    # Initialize the robot and teleoperator
    robot = SO101Follower(robot_config)
    teleop = SO101Leader(teleop_config)

    # Configure the dataset features
    action_features = hw_to_dataset_features(robot.action_features, "action")
    obs_features = hw_to_dataset_features(robot.observation_features, "observation")
    dataset_features = {**action_features, **obs_features}

    # Create the dataset
    dataset = LeRobotDataset.create(
        repo_id="<hf_username>/<dataset_repo_id>",
        fps=FPS,
        features=dataset_features,
        robot_type=robot.name,
        use_videos=True,
        image_writer_threads=4,
    )

    # Initialize the keyboard listener and rerun visualization
    _, events = init_keyboard_listener()
    init_visualization("rerun", session_name="recording")

    # Connect the robot and teleoperator
    robot.connect()
    teleop.connect()

    # Create the required processors
    teleop_action_processor, robot_action_processor, robot_observation_processor = make_default_processors()

    episode_idx = 0
    while episode_idx < NUM_EPISODES and not events["stop_recording"]:
        log_say(f"Recording episode {episode_idx + 1} of {NUM_EPISODES}")

        record_loop(
            robot=robot,
            events=events,
            fps=FPS,
            teleop_action_processor=teleop_action_processor,
            robot_action_processor=robot_action_processor,
            robot_observation_processor=robot_observation_processor,
            teleop=teleop,
            dataset=dataset,
            control_time_s=EPISODE_TIME_SEC,
            single_task=TASK_DESCRIPTION,
            display_data=True,
        )

        # Reset the environment if not stopping or re-recording
        if not events["stop_recording"] and (episode_idx < NUM_EPISODES - 1 or events["rerecord_episode"]):
            log_say("Reset the environment")
            record_loop(
                robot=robot,
                events=events,
                fps=FPS,
                teleop_action_processor=teleop_action_processor,
                robot_action_processor=robot_action_processor,
                robot_observation_processor=robot_observation_processor,
                teleop=teleop,
                control_time_s=RESET_TIME_SEC,
                single_task=TASK_DESCRIPTION,
                display_data=True,
            )

        if events["rerecord_episode"]:
            log_say("Re-recording episode")
            events["rerecord_episode"] = False
            events["exit_early"] = False
            dataset.clear_episode_buffer()
            continue

        dataset.save_episode()
        episode_idx += 1

    # finalize dataset
    log_say("Finalizing dataset...")
    dataset.finalize()
    # Clean up
    log_say("Stop recording")
    robot.disconnect()
    teleop.disconnect()
    dataset.push_to_hub()

if __name__ == "__main__":
    main()
```

您选择的相机名称（此处为 `wrist` 和 `top`）将成为数据集键 `observation.images.wrist` 和 `observation.images.top`。在此数据集上训练的策略在部署时期望使用相同的相机名称和分辨率，因此请在本教程的每个步骤中复用相同的 `--robot.cameras` 值。如果名称必须不同，请参阅 [Rename Map](./rename_map)。

`lerobot-record` 会在 `--dataset.repo_id` 的末尾添加会话的日期和时间，因此每次录制都会获得一个唯一的名称：`${HF_USER}/record-test` 会被保存为 `${HF_USER}/record-test_YYYYMMDD_HHMMSS`（例如 `record-test_20260929_143012`）。您可以在 Hugging Face 个人资料页面或通过 `ls ~/.cache/huggingface/lerobot/${HF_USER}` 找到完整名称，并在下方命令中所有提到 `record-test_YYYYMMDD_HHMMSS` 的地方使用它。若要保留您传入的确切名称，请添加 `--dataset.no_stamp=true`。

#### 数据集上传

在本地，您的数据集存储在此文件夹中：`~/.cache/huggingface/lerobot/{repo-id}`。数据录制结束后，您的数据集将上传至您的 Hugging Face 页面（例如 `https://huggingface.co/datasets/${HF_USER}/record-test_YYYYMMDD_HHMMSS`），您可以通过运行以下命令获取该页面：

```bash
echo https://huggingface.co/datasets/${HF_USER}/record-test_YYYYMMDD_HHMMSS
```

您的数据集将自动添加 `LeRobot` 标签，以便社区轻松找到它，您还可以添加自定义标签，例如 `--dataset.tags='["tutorial"]'`。

您可以在 hub 上搜索 `LeRobot` [标签](https://huggingface.co/datasets?other=LeRobot)，以查找其他 LeRobot 数据集。

您也可以手动将本地数据集推送到 Hub，运行：

```bash
hf upload ${HF_USER}/record-test_YYYYMMDD_HHMMSS ~/.cache/huggingface/lerobot/${HF_USER}/record-test_YYYYMMDD_HHMMSS --repo-type dataset
```

#### 记录函数

`lerobot-record` 提供了一套用于在机器人运行期间捕获和管理数据的工具：

##### 1. 数据存储

- 数据使用 `LeRobotDataset` 格式存储，并在录制过程中保存到磁盘上。
- 默认情况下，录制完成后，数据集会推送到您的 Hugging Face 页面。
  - 要禁用上传，请使用 `--dataset.push_to_hub=False`。

##### 2. 检查点与恢复

- 在录制过程中会自动创建检查点。
- 如果出现问题，或者您想在同一数据集中记录更多集数，可以通过重新运行相同命令并添加 `--resume=true` 来恢复。请传入完整的数据集名称，包括其时间戳（`--dataset.repo_id=${HF_USER}/record-test_YYYYMMDD_HHMMSS`）。恢复录制时，`--dataset.num_episodes` 必须设置为 **要额外记录的集数**，而不是数据集中目标总集数！请确保同时设置 `--dataset.root="local_path"`，这是用于保存数据集新部分的本地路径，恢复录制时必需。
- 在不使用 `--resume=true` 的情况下重新运行该命令，将启动一个带有新时间戳的新数据集。如果使用 `--dataset.no_stamp=true`，请先删除现有的数据集目录。

##### 3. 记录参数

使用命令行参数设置数据记录的流程：

- `--dataset.episode_time_s=60` 每个数据录制片段的时长（默认：**60 秒**）。
- `--dataset.reset_time_s=60` 每个回合后重置环境的持续时间（默认：**60 秒**）。
- `--dataset.num_episodes=50` 要记录的总回合数（默认值：**50**）。

##### 4. 录制期间的键盘控制

使用键盘快捷键控制数据录制流程：

- 按 **右箭头键（`→`）** 或 **`n`**：提前结束当前回合，或重置时间并进入下一回合。
- 按 **左箭头键（`←`）** 或 **`r`**：取消当前回合并重新录制。
- 按下 **Escape (`ESC`)** 或 **`q`**：立即停止会话，对视频进行编码，并上传数据集。

这些控制流快捷方式适用于 **X11、Wayland 以及无头/SSH** 会话。当全局键盘后端不可用时（例如 Wayland、无头机器或未授予辅助功能权限的 macOS），`lerobot-record` 会自动从终端读取相同的按键——请从交互式终端启动该程序并保持其处于焦点状态。您也可以使用字母等效键 **`n`**（下一个，等同于 `→`）、**`r`**（重新录制，等同于 `←`）和 **`q`**（退出，等同于 `ESC`）。无需配置 `$DISPLAY`。

此说明仅适用于录制控制流程。键盘**遥操作**（通过键盘驱动机器人）仍然需要一个全局按键后端，因此它仅在 X11 会话、Windows 桌面，或已授予辅助功能/输入监控权限的 macOS 上可用——在 Wayland 或无头会话上不可用。

#### 收集数据的技巧

当您对数据录制感到熟练后，就可以创建更大的数据集用于训练。一个好的入门任务是抓取不同位置的物体并将其放入料箱中。我们建议至少录制 50 个片段，每个位置录制 10 个片段。请保持相机固定，并在整个录制过程中保持一致的抓取行为。同时，请确保您操作的物体在相机视野中可见。一个实用的经验法则是：您应该仅通过查看相机图像就能自己完成该任务。

在以下章节中，你将训练你的神经网络。在实现可靠的抓取性能后，你可以开始在数据收集中引入更多变化，例如额外的抓取位置、不同的抓取技术以及改变相机位置。

避免过快引入过多的变化，因为这可能会影响你的结果。

如果您想深入了解这一重要主题，可以阅读我们撰写的关于[如何构建优质数据集](https://huggingface.co/blog/lerobot-datasets#what-makes-a-good-dataset)的博客文章。

#### Cadence 报告

`lerobot-record` 以 `--dataset.fps` 的节奏控制循环，并报告实际达到的结果：每保留一个 episode 都会输出一行摘要，录制结束时（包括按 `Ctrl-C` 时）会输出整个会话的汇总块。

```
Cadence (episode 0): 29.88 Hz vs 30 Hz target · 600 ticks, 20.0 s measured · 8/599 ticks over the 33.3 ms budget (work mean 18.4 ms, worst 45.1 ms)
Cadence (episode 1): 29.88 Hz vs 30 Hz target · 600 ticks, 20.0 s measured · 8/599 ticks over the 33.3 ms budget (work mean 18.4 ms, worst 45.1 ms)
Cadence summary — whole run, 2 episodes · target 30 Hz (33.3 ms budget per tick): 1200 ticks, 1198 judged
  effective cadence: 29.88 Hz over 40.1 s measured
  ticks over the 33.3 ms work budget: 16/1198 (1.3%) — work mean 18.4 ms, worst 45.1 ms
  loop-body steps (share of measured work):
    observe      mean  13.65 ms · worst  40.20 ms ·  74.4% of work · 1200 calls
    process_obs  mean   0.30 ms · worst   0.30 ms ·   1.6% of work · 1200 calls
    teleop       mean   1.80 ms · worst   1.80 ms ·   9.8% of work · 1200 calls
    send         mean   0.90 ms · worst   0.90 ms ·   4.9% of work · 1200 calls
    record       mean   1.70 ms · worst   1.90 ms ·   9.3% of work · 1200 calls
  pacing headroom: 15.1 ms slept per tick on average (max 16.7 ms) — near zero means the loop is saturated
```

在训练数据集之前，建议先阅读此内容。帧的时间戳由其索引推导得出，因此以 25 Hz 运行的会话生成的数据集仍会_声称_为 30 Hz——记录的运动只是比实际情况更快。如上所述，`observe` 占循环工作量的 74%，其`worst` 为 40 毫秒，而预算为 33.3 毫秒：正是摄像头导致这 16 个 tick 超时。**Pacing headroom** 接近零意味着没有余量来吸收缓慢的 tick。

各回合之间的重置阶段采用相同的节奏控制，但有意不计入这些统计数据，因为它们不记录任何数据。`lerobot-teleoperate` 和 `lerobot-replay` 以相同方式报告，而 `lerobot-rollout` 则额外添加了 [节奏报告](./inference#cadence-reporting) 中描述的插值专用行。

#### 故障排除：

- 在 Linux 上，只要 `lerobot-record` 在交互式终端中运行，录制控制流按键（方向键、Escape）即可在 X11、Wayland 以及无头/SSH 会话中正常工作——无需配置 `$DISPLAY`。如果按键没有反应，请确保你处于交互式（TTY）终端中，而非管道/非 TTY 会话，并且该终端已获得焦点；字母等效键 `n` / `r` / `q` 同样有效。键盘 _遥操作_（区别于录制控制流）仍然需要一个全局按键后端——即 X11 会话、Windows 桌面，或已授予辅助功能/输入监控权限的 macOS——并且在 Wayland 或无头机器上不可用。请参阅 [pynput 限制](https://pynput.readthedocs.io/en/latest/limitations.html#linux)。

## 可视化数据集

默认情况下，您的数据集会上传到 Hub（除非您传入了 `--dataset.push_to_hub=False`），因此您可以立即在 [LeRobot Dataset Visualizer](https://huggingface.co/spaces/lerobot/visualize_dataset) 中打开它：在其主页上输入您的仓库 ID，或直接通过此命令打印的链接跳转到第一个 episode：

```bash
echo "https://huggingface.co/spaces/lerobot/visualize_dataset?path=/${HF_USER}/record-test_YYYYMMDD_HHMMSS/episode_0"
```

在可视化器中登录以打开私有数据集。有关操作指南，请参阅 [在浏览器中探索数据集](./using_dataset_tools#explore-a-dataset-in-the-browser)。

## 重放一个回合

一个实用的功能是 `replay` 函数，它允许您回放任何已记录的 episode 或来自任何数据集的 episode。该函数有助于测试机器人动作的可重复性，并评估同一型号机器人之间的可迁移性。

您可以使用以下命令或 API 示例在机器人上重放第一个 episode：

```bash
lerobot-replay \
    --robot.type=so101_follower \
    --robot.port=/dev/tty.usbmodem58760431541 \
    --robot.id=my_awesome_follower_arm \
    --dataset.repo_id=${HF_USER}/record-test_YYYYMMDD_HHMMSS \
    --dataset.episode=0 # choose the episode you want to replay
```

```python
import time

from lerobot.datasets import LeRobotDataset
from lerobot.robots.so_follower import SO101Follower, SO101FollowerConfig
from lerobot.utils.robot_utils import precise_sleep
from lerobot.utils.utils import log_say

episode_idx = 0

robot_config = SO101FollowerConfig(port="/dev/tty.usbmodem5AB90687491", id="my_follower_arm")

robot = SO101Follower(robot_config)
robot.connect()

dataset = LeRobotDataset("<hf_username>/<dataset_repo_id>", episodes=[episode_idx])
actions = dataset.select_columns("action")

log_say(f"Replaying episode {episode_idx}")
for idx in range(dataset.num_frames):
    t0 = time.perf_counter()

    action = {
        name: float(actions[idx]["action"][i]) for i, name in enumerate(dataset.features["action"]["names"])
    }
    robot.send_action(action)

    precise_sleep(max(1.0 / dataset.fps - (time.perf_counter() - t0), 0.0))

robot.disconnect()
```

您的机器人应复现与您录制动作相似的运动。例如，请查看[此视频](https://x.com/RemiCadene/status/1793654950905680090)，其中我们使用`replay`在来自[Trossen Robotics](https://www.trossenrobotics.com)的 ALOHA 机器人上进行了演示。

## 训练策略

要训练一个用于控制机器人的策略，请使用 [`lerobot-train`](https://github.com/huggingface/lerobot/blob/main/src/lerobot/scripts/lerobot_train.py) 脚本。需要传入几个参数。以下是一个示例命令：

```bash
lerobot-train \
  --dataset.repo_id=${HF_USER}/record-test_YYYYMMDD_HHMMSS \
  --policy.type=act \
  --output_dir=outputs/train/act_so101_test \
  --job_name=act_so101_test \
  --policy.device=cuda \
  --trackio.enable=true \
  --policy.repo_id=${HF_USER}/my_policy
```

让我们解释一下该命令：

1. 我们使用 `--dataset.repo_id=${HF_USER}/record-test_YYYYMMDD_HHMMSS` 作为参数，提供了之前记录的 dataset（请使用包含时间戳的 dataset 完整名称）。
2. 我们为策略提供了 `policy.type=act`。这会从 [`configuration_act.py`](https://github.com/huggingface/lerobot/blob/main/src/lerobot/policies/act/configuration_act.py) 加载配置。重要的是，该策略会自动适应您的机器人（例如 `wrist` 和 `top`）的电机状态数量、电机动作数量和相机数量，这些数量已保存在您的数据集中。
3. 由于我们在 Nvidia GPU 上进行训练，因此提供了 `policy.device=cuda`，但您也可以使用 `policy.device=mps` 在 Apple silicon 上进行训练。
4. 我们提供了 `trackio.enable=true` 以使用 [Trackio](https://huggingface.co/docs/trackio) 来可视化训练图表。这是可选的。运行 `trackio show --project lerobot` 以打开仪表板，或传入 `--trackio.space_id=<user>/<space>` 以将运行同步到 Hugging Face Space。
5. 我们未设置 `--steps`，因此训练将运行默认的 100,000 步。对于小型数据集而言，这通常超过了所需步数：模仿学习通常只需在数据上训练 5–10 个 epoch 即可收敛。请参阅 [计算与硬件指南](./hardware_guide#training-time)，以根据数据集大小和批大小将 epoch 数转换为 `--steps` 值。

训练过程可能需要数小时。您可以在 `outputs/train/act_so101_test/checkpoints` 中找到检查点。

要从检查点恢复训练，以下是一个从 `act_so101_test` 策略的 `last` 检查点恢复的示例命令：

```bash
lerobot-train \
  --config_path=outputs/train/act_so101_test/checkpoints/last/pretrained_model/train_config.json \
  --resume=true
```

`--config_path` 也接受 **Hub 仓库 ID**：如果某次运行已将检查点推送到 Hub（使用 `--save_checkpoint_to_hub=true`），您可以直接从该仓库恢复——系统会下载其最新的检查点并继续训练，同时恢复优化器、调度器、步数计数器和数据顺序：

```bash
lerobot-train --config_path=${HF_USER}/my_policy --resume=true
```

如果不想在训练后将模型推送到 hub，请使用 `--policy.push_to_hub=false`。

此外，你还可以通过添加以下内容来为模型提供额外的 `tags`、指定 `license` 或将模型仓库设为 `private`：`--policy.private=true --policy.tags=\[so101,tutorial\] --policy.license=mit`

#### 使用 Google Colab 进行训练

如果您的本地计算机没有高性能 GPU，可以按照 [ACT 训练笔记本](./notebooks#training-act) 使用 Google Colab 来训练您的模型。

#### 使用 Hugging Face Jobs 进行训练

Hugging Face Jobs 让您轻松选择硬件并在云端运行训练。因此，如果您没有高性能 GPU，或者需要更多 VRAM，又或者只是想更快地训练模型，请使用 HF Jobs！它采用按量付费模式，您只需为每一秒的使用时间付费，您可以在此处查看定价和附加信息 [here](https://huggingface.co/docs/hub/jobs)。

`lerobot-train` 默认在本地运行。要在 HuggingFace GPU 上运行，请传入带有硬件规格名称的 `--job.target`：

```bash
lerobot-train \
  --dataset.repo_id=${HF_USER}/record-test_YYYYMMDD_HHMMSS \
  --policy.type=act \
  --policy.repo_id=${HF_USER}/my_policy \
  --job.target=a10g-small
```

使用 `hf jobs hardware` 列出可用的硬件类型和价格。运行时会将日志流式传输到您的终端；按 Ctrl-C 可断开连接（任务将在云端继续运行）。重新连接或取消任务：

```bash
hf jobs logs <job-id>
hf jobs cancel <job-id>
```

如果您的数据集仅存在于本地（尚未上传到 Hub），它将被自动推送到一个**私有**的 Hub 仓库，以便任务能够通过`repo_id`下载该数据集（不会公开任何内容）。训练好的模型将在运行结束时推送到模型仓库。如果您希望在保存每个中间检查点时也将其推送到 Hub（以便在运行过程中监控进度），请添加`--save_checkpoint_to_hub=true`——这需要包含此功能的运行时镜像。

每个任务（以及运行期间推送的任何数据集）都会标记为 `lerobot`，以便在 Hub 上轻松查找。您可以使用 `--job.tags '["my-tag"]'` 添加自己的标签。

默认情况下，任务的墙钟时间上限为 `2d`（48 小时）。您可以使用 HF Jobs 的持续时间字符串来覆盖此设置，例如使用 `--job.timeout=4h` 以更快失败，或使用 `--job.timeout=7d` 以运行更长时间。

> **注意：**模型仓库会预先创建（其中保存了任务运行所使用的暂存训练配置）。如果模型推送前运行失败，该仓库将保留在 Hub 上以便您检查——它不会自动删除，因此反复失败可能会留下空仓库。使用 `hf repos delete <repo-id>` 删除其中一个。

**前提条件：**提交前请运行 `hf auth login`。对于 Trackio，请传入 `--trackio.space_id=<user>/<space>`——如果 Space 不存在则会自动创建，并且会自动转发该作业的 HF_TOKEN。

**在作业上恢复。** 在恢复命令中添加 `--job.target` 即可在云端执行恢复——同一命令既适用于本地也适用于远程。检查点仓库是事实来源，新的检查点将在同一仓库中延续其谱系：

```bash
# resume a Hub run on a job (its checkpoints are already on the Hub)
lerobot-train --config_path=${HF_USER}/my_policy --resume=true --job.target=a10g-small

# resume a LOCAL run on a job — the checkpoint is uploaded to a private Hub repo first,
# then the job resumes from it (a local-only dataset is uploaded the same way)
lerobot-train \
  --config_path=outputs/train/act_so101_test/checkpoints/last/pretrained_model/train_config.json \
  --resume=true \
  --job.target=a10g-small
```

作业设置来自当前命令，因此请根据需要覆盖 `--job.target`、`--job.timeout` 等参数；若希望恢复后的运行本身在之后仍可再次恢复，请保留 `--save_checkpoint_to_hub=true`。

#### 上传策略检查点

训练完成后，使用以下命令上传最新的检查点：

```bash
hf upload ${HF_USER}/act_so101_test \
  outputs/train/act_so101_test/checkpoints/last/pretrained_model
```

您还可以通过以下方式上传中间检查点：

```bash
CKPT=010000
hf upload ${HF_USER}/act_so101_test${CKPT} \
  outputs/train/act_so101_test/checkpoints/${CKPT}/pretrained_model
```

## 运行推理并评估您的策略

使用 `lerobot-rollout` 在您的机器人上部署已训练的策略。您可以根据需求选择不同的策略。

使用与录制数据集相同的 `--robot.id`（以便找到校准数据）、相同的 `--robot.cameras` 以及相同的任务描述。由 `lerobot-rollout` 录制的数据集，其名称必须以 `rollout_` 开头，并且与 `lerobot-record` 一样，它会在该名称后添加时间戳。

以下示例从 `--policy.path` 加载模型。若要固定某个已推送的版本——在 `--save_checkpoint_to_hub=true` 已提交多个检查点后尤为有用——请添加 `--policy.pretrained_revision` 并指定提交哈希、分支或标签。每个推送的检查点都会以其步数作为标签（例如 `--policy.pretrained_revision=010000`），因此您可以按步数恢复检查点，而无需查找其提交 sha。

```bash
lerobot-rollout \
  --strategy.type=base \
  --policy.path=${HF_USER}/my_policy \
  --robot.type=so101_follower \
  --robot.port=/dev/tty.usbmodem5AB90687491 \
  --robot.id=my_follower_arm \
  --robot.cameras="{ wrist: {type: opencv, index_or_path: 0, width: 640, height: 480, fps: 30}, top: {type: opencv, index_or_path: 1, width: 640, height: 480, fps: 30}}" \
  --task="Grab the black cube" \
  --duration=60
```

```bash
lerobot-rollout \
  --strategy.type=sentry \
  --strategy.upload_every_n_episodes=5 \
  --policy.path=${HF_USER}/my_policy \
  --robot.type=so101_follower \
  --robot.port=/dev/tty.usbmodem5AB90687491 \
  --robot.id=my_follower_arm \
  --robot.cameras="{ wrist: {type: opencv, index_or_path: 0, width: 640, height: 480, fps: 30}, top: {type: opencv, index_or_path: 1, width: 640, height: 480, fps: 30}}" \
  --dataset.repo_id=${HF_USER}/rollout_so101_test \
  --dataset.single_task="Grab the black cube" \
  --duration=600
```

`--strategy.type` 标志用于选择执行模式：

- `base`：自主 rollout，不记录数据（适用于快速评估）
- `sentry`：持续记录并自动上传（适用于大规模评估）
- `highlight`：环形缓冲区录音，支持按键保存（适用于捕获有趣的事件）
- `dagger`：人在回路的数据收集（参见 [HIL 数据收集](./hil_data_collection)）
- `episodic`：以回合为导向的策略记录，回合之间设有重置阶段

所有策略均支持 `--inference.type=rtc`，以便在运行较慢的 VLA 模型（Pi0、Pi0.5、SmolVLA）时实现流畅执行。有关每种策略及其标志的详细信息，请参阅 [策略部署](./inference)。

