LeRobot documentation

策略部署(lerobot-rollout)

You are viewing main version, which requires installation from source. If you'd like regular pip install, checkout the latest stable version (v0.6.1).
Hugging Face's logo
Join the Hugging Face community

and get access to the augmented documentation experience

to get started

策略部署(lerobot-rollout)

lerobot-rollout 是用于在真实机器人上部署已训练策略的统一命令行工具。它支持多种执行策略和推理后端,涵盖从快速评估到持续录制以及人在回路数据采集的多种场景。

快速入门

除了机器人和策略的额外依赖项之外,无需其他额外依赖项。

lerobot-rollout \
    --strategy.type=base \
    --policy.path=lerobot/act_koch_real \
    --robot.type=koch_follower \
    --robot.port=/dev/ttyACM0 \
    --task="pick up cube" \
    --duration=30

这会在不录制任何内容的情况下运行策略 30 秒。

选择设备

在目标主机上,如果该设备可用,请传入 --device=cpu 或 --device=cuda 以覆盖检查点设备。 解析后的设备会在加载策略并构建其处理器之前,应用于策略配置。 例如,如果策略支持 CPU 执行,可以在上述命令中添加 --device=cpu,以便在仅配备 CPU 的主机上运行使用 CUDA 训练的检查点。 在部署到机器人之前,请确认 CPU 推理能够满足所需的控制频率。

如果省略 --device,rollout 会使用策略配置中指定的设备(如果已设置),否则会自动选择一个可用设备。 RolloutConfig(device=...) 与 build_rollout_context 的行为与此相同。


策略

使用 --strategy.type=<name> 选择一种策略。每种策略都定义了不同的控制循环,并拥有各自的录制和交互语义。

基础(--strategy.type=base)

自主策略执行,不记录数据。适用于快速评估、演示或仅需观察机器人的场景。

lerobot-rollout \
    --strategy.type=base \
    --policy.path=${HF_USER}/my_policy \
    --robot.type=so100_follower \
    --robot.port=/dev/ttyACM0 \
    --robot.cameras="{ front: {type: opencv, index_or_path: 0, width: 640, height: 480, fps: 30}}" \
    --task="Put lego brick into the box" \
    --duration=60
标志描述默认
--duration运行时间(秒)(0 = 无限)0
--task传递给策略的任务描述—
--display_data将观察/操作流式传输到 Rerun 以进行可视化false

Sentry ( --strategy.type=sentry )

持续自主录制,并定期上传至 Hugging Face Hub。剧集边界会根据相机分辨率和 FPS 自动计算,确保每个保存的剧集都能生成完整的视频文件,从而保持上传的高效性。

策略状态(隐藏状态、RTC 队列)在 episode 边界之间持续存在:机器人在 episode 之间不会重置。

lerobot-rollout \
    --strategy.type=sentry \
    --strategy.upload_every_n_episodes=5 \
    --policy.path=${HF_USER}/my_policy \
    --robot.type=so100_follower \
    --robot.port=/dev/ttyACM0 \
    --robot.cameras="{ front: {type: opencv, index_or_path: 0, width: 640, height: 480, fps: 30}}" \
    --dataset.repo_id=${HF_USER}/rollout_eval_data \
    --dataset.single_task="Put lego brick into the box" \
    --duration=3600
标志描述默认
--strategy.upload_every_n_episodes每 N 个回合推送到 Hub5
--strategy.target_video_file_size_mb用于剧集轮换的目标视频文件大小auto
--dataset.repo_id必填。 用于记录数据集的 Hub 仓库—
--dataset.push_to_hub是否在拆卸时推送到 Hubtrue

高亮(--strategy.type=highlight)

通过内存受限的环形缓冲区实现自主展开与按需录制。该缓冲区会捕获最近 N 秒的遥测数据。按下保存键以刷新缓冲区并开始实时录制;再次按下以保存该回合。在使用异步后端时,每次切换都会暂停推理,使已排队或插值的运动失效,并在阻塞式排空或保存之前请求受支持的本地保持。运行正常时,随后将从新的捕获中恢复。

lerobot-rollout \
    --strategy.type=highlight \
    --strategy.ring_buffer_seconds=30 \
    --strategy.save_key=s \
    --strategy.push_key=h \
    --policy.path=${HF_USER}/my_policy \
    --robot.type=koch_follower \
    --robot.port=/dev/ttyACM0 \
    --dataset.repo_id=${HF_USER}/rollout_highlight_data \
    --dataset.single_task="Pick up the red cube"

键盘控制:

密钥操作
s(可配置)开始录制(刷新缓冲区)/ 停止并保存剧集
h(可配置)将数据集推送到 Hub
ESC停止会话
标志描述
--strategy.ring_buffer_seconds缓冲遥测的持续时间(默认值:10)
--strategy.ring_buffer_max_memory_mb环形缓冲区的内存上限(默认值:1024)
--strategy.save_key切换录制的按键(默认:s)
--strategy.push_key用于推送到 Hub 的按键(默认:h)

DAgger(--strategy.type=dagger)

Human-in-the-loop 数据收集。在自主策略执行与通过遥操作器进行的人工干预之间交替进行。干预帧会被标记为 intervention=True。需要配置遥操作器(--teleop.type)。

有关详细操作指南,请参阅人机协同数据收集。

仅修正模式(默认):仅记录人工修正窗口。每次修正构成一个 episode。

lerobot-rollout \
    --strategy.type=dagger \
    --strategy.num_episodes=20 \
    --policy.path=outputs/pretrain/checkpoints/last/pretrained_model \
    --robot.type=bi_openarm_follower \
    --teleop.type=bi_openarm_mini \
    --dataset.repo_id=${HF_USER}/rollout_hil_data \
    --dataset.single_task="Fold the T-shirt"

连续录制模式(--strategy.record_autonomous=true):自主帧和校正帧均会进行录制,并采用基于时间的 episode 轮换(与 Sentry 相同)。

lerobot-rollout \
    --strategy.type=dagger \
    --strategy.record_autonomous=true \
    --strategy.num_episodes=50 \
    --policy.path=${HF_USER}/my_policy \
    --robot.type=so100_follower \
    --robot.port=/dev/ttyACM0 \
    --teleop.type=so101_leader \
    --teleop.port=/dev/ttyACM1 \
    --dataset.repo_id=${HF_USER}/rollout_dagger_data \
    --dataset.single_task="Grasp the block"

键盘控制(默认输入设备):

密钥操作
Space暂停 / 恢复策略执行
Tab开始 / 停止人工校正
Enter将数据集推送到 Hub(仅更正模式)
ESC停止会话

也支持通过 --strategy.input_device=pedal 使用脚踏板输入。使用 --strategy.pedal.* 标志配置踏板代码。

当机器人支持 rollout 位置保持时,暂停操作会刷新驱动程序实际应用的最后一个位置目标,包括其裁剪和夹爪目标。在第一条命令之前,经过验证的测量位姿将作为目标提供。这保留了命令位置误差,而不是将目标更改为测量位姿;在策略轨迹推进停止期间,机器人可以继续向该目标收敛。请在实际机器人上验证负载臂和夹爪的行为;此软件保持功能不保证保持抓握或物理静止。

标志描述
--strategy.num_episodes要记录的纠正回合数(默认值:--dataset.num_episodes)
--strategy.record_autonomous同时记录自主帧(默认:false)
--strategy.upload_every_n_episodes每 N 个回合推送到 Hub(默认值:5)
--strategy.input_device输入设备:keyboard 或 pedal(默认:keyboard)
--strategy.smooth_handover在暂停/校正开始时平滑地移交控制权(默认值:true)。对于在接合时以当前机器人位姿重新参考的离合器式遥操作,请禁用此功能
--teleop.type必填。 遥操作类型

情景式(--strategy.type=episodic)

采用以回合(episode)为导向的录制方式,其行为与 lerobot-record 保持一致。在每个回合中,策略驱动机器人;在回合之间的重置阶段,可选的遥操作者可以驱动机器人。

lerobot-rollout \
    --strategy.type=episodic \
    --policy.path=${HF_USER}/my_policy \
    --robot.type=so100_follower \
    --robot.port=/dev/ttyACM0 \
    --teleop.type=so100_leader \
    --teleop.port=/dev/ttyACM1 \
    --dataset.repo_id=${HF_USER}/rollout_eval_data \
    --dataset.num_episodes=20 \
    --dataset.episode_time_s=30 \
    --dataset.reset_time_s=10 \
    --dataset.single_task="Pick up the red cube"

Teleop 是可选的——如果省略,机器人在重置阶段将保持当前位置。

键盘控制:

密钥操作
→(右)提前结束当前回合
←(左)丢弃该回合并重新录制
ESC停止录制会话
标志描述
--dataset.num_episodes要记录的剧集数量
--dataset.episode_time_s每个录制片段的持续时间(以秒为单位)。如果设置了--duration且未设置此项,则默认使用--duration的值。
--dataset.reset_time_s剧集之间重置阶段的持续时间(以秒为单位)
--teleop.type可选。在重置期间由遥操作员控制机器人
--strategy.reset_to_initial_position是否在回合之间将机器人重置到其初始位置
--strategy.smooth_leader_to_follower_handover是否开启或关闭领导者 -> 跟随者的平滑交接行为。
--strategy.smooth_handover在重置开始时平滑地将控制权移交给遥操作(默认值:true)。对于在接合时以当前机器人位姿重新参考的离合器式遥操作,请禁用此功能

自带策略

--strategy.type 接受环境中注册的任何策略,而不仅仅是上述内置策略。一个策略是 pip 可安装包中的两个类:一个注册在 RolloutStrategyConfig 上的配置,以及一个以其名称去掉 Config 后缀命名的 RolloutStrategy 子类——与 自定义硬件 相同的 SomethingConfig/Something 约定,从配置的模块或包中解析。

from dataclasses import dataclass
from typing import ClassVar

from lerobot.rollout import RolloutContext, RolloutStrategy, RolloutStrategyConfig


@RolloutStrategyConfig.register_subclass("patrol")
@dataclass
class PatrolStrategyConfig(RolloutStrategyConfig):
    dataset_mode: ClassVar[str] = "required"  # the engine creates the dataset for you
    legs: int = 2  # becomes --strategy.legs


class PatrolStrategy(RolloutStrategy):
    def run(self, ctx: RolloutContext) -> None:
        ...  # the paced control loop: see the contract on RolloutStrategy.run

    def teardown(self, ctx: RolloutContext) -> None:
        ctx.data.dataset.finalize()
        self._teardown_hardware(ctx.hardware, ctx.runtime.cfg.return_to_initial_position)

配置声明了引擎代表策略所安排的内容,因此 LeRobot 中的任何部分都无需了解其类型:

声明效果
dataset_mode"none"(默认值)会拒绝 --dataset.* 标志,"optional" 会在提供时记录它们,"required" 会使 --dataset.repo_id 成为必填项
requires_teleop使 --teleop.type 成为必填项
supports_interactive允许使用 --interactive=true;此时 run() 必须遵循 RolloutStrategy 中记录的可重启契约
requires_streaming_encoding()当帧从控制循环内部写入时返回 True:--dataset.streaming_encoding 将被强制启用
extra_dataset_features()策略拥有的数据集列(例如 DAgger 的intervention);每个记录的帧都必须包含这些列

将分发命名为 lerobot_strategy_<name>,lerobot-rollout 会在解析 CLI 之前导入该包——此时仅执行其 __init__.py,因此请在此处导入配置。任何其他名称的包(包括其子模块)都会通过 --strategy.discover_packages_path=<package> 加载。策略的数据类字段会成为 --strategy.* 标志:

lerobot-rollout \
    --strategy.type=patrol \
    --strategy.legs=3 \
    --policy.path=${HF_USER}/my_policy \
    --robot.type=so100_follower \
    --robot.port=/dev/ttyACM0 \
    --dataset.repo_id=${HF_USER}/rollout_patrol \
    --dataset.single_task="patrol the aisle"

SentryStrategy(支持录制,交互安全)和 BaseStrategy(不录制)是可供参考的实现;lerobot.rollout.strategies 中的 send_next_action、safe_push_to_hub 和 estimate_max_episode_seconds 是它们所依赖的辅助函数。

对于异步后端,分发契约是强制性的。使用 engine.notify_observation(obs_processed) 发布每个捕获的观测值,在 每个自主电机节拍(包括插值和保持节拍)上调用 send_next_action(...),然后调用 engine.pump_query(obs_processed)。仅调用 engine.get_action() 会绕过权限和保持握手:计划中的文本查询可能会无限期等待,且终端故障可能永远不会发出关闭信号。

第三方策略可以调用 self._tick(timer, new_cycle=interpolator.needs_new_action()) 来替代 timer.tick(...),以便在 _init_engine(ctx) 之后包含定期健康报告。

send_next_action 基于 lerobot.inference.dispatch_tick 及其组成部分(next_interpolated_action、commit_action、dispatch_permitted)构建,这些部分一次性编码了该顺序:在拉取动作之前和之后,以及在发送之前调用 dispatch_allowed();当被拒绝时,执行插值失效处理、支持的本地保持以及 acknowledge_hold();在发送之后调用 record_dispatch()。失败的保持操作永远不会被确认。请使用这些辅助函数,而不是重新实现该序列;send_next_action 仅添加了数据集帧、机器人端的动作处理器以及节奏核算。远程上下文中,ctx.policy.policy、preprocessor 和 postprocessor 均设置为 None;请使用 ctx.policy.inference 以及 ctx.processors 中的机器人端流水线。

将运行中已知的阻塞性录制工作包裹在 self._pause_for_recording(ctx) 中,包括环形缓冲区排空和回合结束处理。这会在录制停顿前使异步运动失效,并且仅在运行仍然健康且处于活动状态时才恢复。具有操作员控制阶段的策略必须将其当前的运动权限作为 resume_allowed 传入。下一个动作必须来自新的观测;已保存的工作不得使已暂停、已停止或已发生故障的运行恢复。


可视化策略预测

使用 --display_data 时,查看器还会显示策略除动作之外的预测内容:世界模型想象出的相机帧、预测的子任务,以及绘制在相机画面上的边界框。每个策略自行决定其预测内容,并通过各自的配置将其启用,例如对于 LingBot-VA,使用 --policy.return_predicted_video=true。预测项以其预测的对象命名:Rerun 将预测的 observation.images.top 显示为 prediction.images.top,将预测的子任务显示为 prediction.subtask,并将边界框显示在其所属相机下(<camera>/prediction);Foxglove 则通过 /prediction/... 主题以及 <camera topic>/prediction 图像注释来发布它们。在使用 RTC 时,预测内容每个 chunk 刷新一次。请参阅 Bring your own policy 以了解如何从策略返回预测结果。

推理后端

使用 --inference.type=<name> 选择后端。本地同步推理和 RTC 推理使用下方配置的策略。远程后端 从服务器获取其策略契约,并且不会在机器人计算机上加载权重。

同步(默认)

每个控制周期调用一次策略。主循环会阻塞,直到动作计算完成。

适用于所有策略。无需额外标志。

实时分块(--inference.type=rtc)

后台线程会异步生成动作块。主控制循环轮询下一个已就绪的动作,同时策略并行计算下一个块。

使用 RTC 与兼容的 VLA 模型(Pi0、Pi0.5、SmolVLA)以重叠推理和运动。持续执行仍需足够的可用动作播放来覆盖推理延迟及其波动。

lerobot-rollout \
    --strategy.type=base \
    --inference.type=rtc \
    --inference.rtc.execution_horizon=10 \
    --inference.rtc.max_guidance_weight=10.0 \
    --policy.path=${HF_USER}/pi0_policy \
    --robot.type=so100_follower \
    --robot.port=/dev/ttyACM0 \
    --robot.cameras="{ front: {type: opencv, index_or_path: 0, width: 640, height: 480, fps: 30}}" \
    --task="Pick up the cube" \
    --duration=60 \
    --device=cuda
标志描述
--inference.rtc.execution_horizon与上一个块进行混合的步骤(默认值:因策略而异)
--inference.rtc.modeguided(默认)或兼容 Pi05 检查点的训练前缀 trained
--inference.rtc.max_guidance_weight一致性强制强度(默认值:因策略而异)
--inference.rtc.prefix_attention_schedule混合计划:LINEAR、EXP、ONES、ZEROS
--inference.queue_threshold背压阈值;训练 RTC 至少需要其最大延迟
--inference.action_starvation_grace_s在支持的机器人上,等待饥饿状态后的新运动(默认值为 1.0;0 会立即触发故障)

有关调整 RTC 参数的详细信息,请参阅 Real-Time Chunking 指南。

本地 RTC 在 rollout 进程的后台线程中运行,无需传输层。同一台计算机上的服务器和客户端应改用 --inference.type=remote。有关块对齐/混合以及异步后端共有的等待行为,请参阅远程指南。

等待功能需要受支持的机器人。普通的本地 RTC 在没有该功能的情况下仍然可用,但计划中的文本/指令变更暂停以及可恢复的饥饿状态需要它。位置控制的 SO、OMX、Koch、HopeJr 和 Rebot 跟随器支持此功能;OpenArm、Reachy2 和双手包装器存在配置限制,详见机器人支持表。在受支持的情况下,空缓冲区可以保留最后应用的目标,持续时间为 action_starvation_grace_s(默认 1 秒),包括排空旧推理并从新捕获中进行预测所需的时间。在运动开始后,如果宽限期为零或机器人不受支持,耗尽时会立即发生故障;启动有其自身的截止时间。与以前的本地 RTC 路径不同,耗尽不再是一个无限期跳过的 tick。宽限期永远不会延长已过期的请求或新鲜度限制。

Local RTC 通过 --inference. 公开了这些额外的预算:

参数默认含义
max_observation_age_s5 s通过操作执行实现的最大源年龄
action_timeout_s10 s待处理的操作请求
startup_timeout_s120 s初始准备和新鲜操作启动
language_timeout_s120 s计划中的语言操作

这些是初始值,而非实测的硬件保证。RTC 必须保留足够的修剪后播放时间,以应对下一次推理及时间余量。如果推理通常会耗尽可用的预测时域,那么提高queue_threshold也无济于事;请降低延迟,或验证更长的时域/不同的动作频率。

终端故障绝不会自动恢复策略运动。在可用的观测获取和执行器控制下,关机操作会遵循 --return_to_initial_position(默认为 true)并断开连接。返回过程需要读取完整的观测,因此持续的相机故障可能会阻止该过程。命令应用失败会抑制所有后端(包括同步后端)的进一步关机运动。这不会改变同步预测,也不会引入针对相机的特定恢复机制。

Compilation and instruction changes

RTC 编译会预热初始调用和带前缀的调用(包括一个具有代表性的非零延迟),然后才执行运动。尽管 --compile_warmup_inferences 的默认值为 2,但启用 RTC 至少需要 3;启动日志会报告实际生效的数量。预热必须在 startup_timeout_s 内完成。后续更改张量形状的指令可能会触发重新编译,从而导致超出播放或操作截止时间。

推理期间的任务变更会丢弃过时的结果。受支持的机器人会暂停并请求新的动作;如果机器人不具备这种等待能力,剩余的动作可能会耗尽。建议在这些机器人上于运行之间更改任务。


交互式会话

添加 --interactive=true 以从终端驱动 rollout,而不是立即开始。硬件连接和策略加载照常进行,但 在您输入 /start 之前,机器人保持静止 — 当您想先布置场景、在多次尝试之间重新指示策略,或在不重复支付加载时间的情况下运行多次拍摄时,这非常有用。

lerobot-rollout \
    --strategy.type=base \
    --policy.path=${HF_USER}/my_smolvla_policy \
    --robot.type=so100_follower \
    --robot.port=/dev/ttyACM0 \
    --robot.cameras="{ front: {type: opencv, index_or_path: 0, width: 640, height: 480, fps: 30}}" \
    --task="pick up the cube" \
    --interactive=true
命令操作
/start启动(或重启)策略控制循环
/subtask <text>更改当前运行中的指令;异步推理可能会进入计划中的暂停状态。不带参数时,将打印当前任务。仅影响基于语言进行条件判断的策略(SmolVLA、π0/π0.5 及类似策略)。当使用 外部规划器 时,/subtask 还会将控制权从 autosteer 交还
/vqa <text>询问机器人当前看到的内容。仅在运行进行中,且使用策略文本头或外部规划器时可用。
/autosteer <goal>提供一个高层目标,让策略的文本头部或外部规划器选择子任务。/autosteer off将控制权交还;不带参数则报告当前目标
/reset停止移动,将机器人返回到启动位置,并恢复--task指令
/stop结束会话并运行正常的关机例程
/help列出命令
> /start
Rollout running — task 'pick up the cube'. /subtask <text> to change it, ...
> /subtask put the cube in the box
Task: 'pick up the cube' → 'put the cube in the box' (applies from the next policy inference)
> /vqa is the cube inside the box?
Asked: 'is the cube inside the box?' — answering from the next observation...
Q: is the cube inside the box?
A: No — the cube is on the table to the left of the box.
> /reset
Task restored to 'pick up the cube'
Resetting — returning the robot to its initial position...
Robot reset — holding at initial position. /start to run.
> /stop

以 > 开头的行是您输入的内容;该会话本身不会显示任何提示符。

Ctrl-C 仍会按常规方式关闭,而关闭标准输入(Ctrl-D,或管道脚本的末尾)会结束会话——因此,管道脚本必须保持标准输入在预期时长内处于打开状态:

(printf '/start\n'; sleep 60; printf '/stop\n') | lerobot-rollout ... --interactive=true

/subtask 用于更新后续推理的任务。在使用 --inference.type=sync 时,预计算的块动作会被丢弃。在本地 RTC 或远程推理中,如果在动作请求期间发生更改,过时的结果将被丢弃。对于支持本地保持(local hold)的机器人,引擎会使队列中/插值中的运动失效,等待控制循环确认保持状态,然后在新任务下请求新的运动。在此过渡期间预计会出现停止;这不保证是连续的混合。请求之间的更改可以保留已接受的动作以保持连续性。在不支持保持的机器人上使用本地 RTC 时,仍然会丢弃过时的结果,但可能会耗尽剩余的动作并终止。如果该机器人无法适当保持,请在运行之间更改指令。

在本地使用 --use_torch_compile=true 时,改变处理后张量形状的指令可能会触发重新编译。填充(Padding)可能使某些任务变更保持相同的形状,但后续的编译并不保证能适配剩余的播放或指令变更截止时间:这可能导致终端故障。除非已验证这些转换,否则在探索指令时应保持编译关闭。在初始编译预热期间的变更不会请求运动保持,因为策略运动尚未开始。

/vqa 和 /autosteer 需要一个声明了文本生成支持并实现了 PreTrainedPolicy.generate_text() 的策略,例如经过适当训练的 EO1 检查点,或一个 外部规划器。在接受查询之前会检查该能力;特定于检查点的行为仍需验证。

/vqa 会基于下一帧观测进行回答,并在就绪时打印结果,因此您可以在此期间继续输入;同一时间只有一个问题在处理中(autosteer 自身的查询也共享该槽位)。若使用外部规划器,问题将在工作线程上回答,而机器人继续执行动作,以下描述均不适用。异步后端首先进入已确认的本地保持状态,并使队列中/插值后的运动失效,然后将新的观测提交给文本路径。健康的活跃运行会在回答后从新的动作块恢复;暂停或停止的运行保持非活动状态。同步文本生成在控制线程上内联执行,并丢弃策略预计算的动作,因此下一个 tick 将从新的推理恢复。查询时间会计入 --duration。仅在运行进行中时才会接受问题——空闲引擎没有当前视图。如果指令或执行上下文使异步回答失效,问题将收到一条取消通知,而不是过时的回答。片段结束时也会报告未回答的问题,包括仍在生成中的问题;报告取消不会中断正在运行的模型调用,也不会提前释放其查询槽位。

/autosteer 向策略(policy)提供一个目标,并每隔 --autosteer_interval_s 秒(默认 10 秒)请求下一个子任务,通过与 /subtask 相同的路径应用每个子任务,并在聊天中宣布。每次查询都是一次完整的文本生成,因此该间隔是一个成本调节旋钮:更低的值会更快重新规划,但会花费更多循环时间用于生成文本而非执行动作。计划进度保存在策略中,而非 rollout 中,因此计划会随 segment 结束而终止;sequencer 也会在 /reset、/subtask、/autosteer off 以及首次规划失败时停止。

会话运行期间,低于 ERROR 级别的日志会被静音,以避免常规输出与您正在输入的内容交错显示;错误和致命的推理失败仍会显示,会话结束后恢复正常日志记录。节奏摘要 会打印在聊天中,而不使用 --interactive 运行则会恢复实时日志。

会话通过 SSH 运行,也适用于无头机器——无需显示服务器。

交互过程中录制。 --strategy.type=sentry 还支持 --interactive=true,在您操控时持续录制:每个 /start…/reset 片段会保存完整的 episode 以及一个最终的 partial episode,并且数据集在关闭前保持打开状态。帧携带实际发送动作的任务来源信息,因此已在旧任务下排队或插值的动作仍保留旧标签。

运行中途的阻塞式 episode 保存会暂停异步推理,使已排队或插值的动作失效,并请求受支持的本地保持。仅当运行仍处于活跃且健康状态时,才会从新的捕获中恢复。相同的保护机制也适用于 DAgger 保存以及 Highlight 环形缓冲区的排空/保存。录制暂停和普通饥饿共享相同的物理等待行为,但它们的截止时间以及恢复原因保持独立。

lerobot-rollout \
    --strategy.type=sentry \
    --policy.path=${HF_USER}/my_smolvla_policy \
    --robot.type=so100_follower \
    --robot.port=/dev/ttyACM0 \
    --dataset.repo_id=${HF_USER}/rollout_cube_sessions \
    --task="pick up the cube" \
    --interactive=true

其他录制策略(episodic、DAgger、highlight)不受支持:它们会绑定自己的键盘控制,并在 run() 退出时完成数据集,因此无法按片段重启。

外部规划器

--planner.model_id=<name> 使 /vqa 和 /autosteer 通过注释管道的 VLM 客户端(annotations 附加组件)运行,而不是通过策略的文本头。调用在机器人持续行动期间于工作线程上执行,并且不会为它们请求语言保持,但有一个刻意的例外:当 /autosteer 启动时,推理后端会暂停,直到规划器给出第一个回复,因为目标是规划器的上下文,而不是策略的指令。异步后端(rtc、remote)会丢弃已排队或正在为上一条指令计算的动作;同步后端则会保持机器人。一旦第一个子任务被应用,后端将从新的观测中恢复。 /subtask 仍然直接应用您的指令并取消自动规划;规划器晚到的回复无法覆盖它。

规划器适用于所有推理后端。在本地推理中,如果已加载的处理器存在子任务配方,则复用该配方;否则,请求一条简短的指令;但这并不能证明检查点能够遵循该指令。--planner.instructions='["pick up the cup","open the drawer"]' 会限制规划器的输出:VLM 被要求从这些字符串中选择一个,而返回不在列表中的下一个子任务将被视为错误。大小写、多余的空格和末尾的句号会被忽略,而列表本身的措辞会发送给策略。如果不使用此标志,列表将从检查点训练数据集的任务标签中读取(通过其 train_config.json)。由于每个标签都会进入提示词,因此对于包含许多任务的数据集,请通过该标志传入更短的列表。如果无法读取数据集,则会记录一条警告,并且规划器的输出将保持自由格式。远程推理拥有策略,因此本地无法访问配方或训练词汇表:请显式传入 --planner.instructions,否则回答将保持自由格式。规划器仅在配备本地推理的硬件上经过测试。

外部规划器会接收最近 --planner.history 个观测/指令对(默认为 4 个),随后是当前图像。每个对包含用于请求指令的观测,以及引擎随后接受的指令;接受并不表示执行成功。重复的指令会保留在历史中,以便 VLM 评估是否缺乏进展。在手动接管、重置、停止、片段结束或新的 autosteer 目标时,历史会被清空。设置 --planner.history=0 可禁用此功能。/vqa 可以检查此历史,但不会添加条目。对于下一个子任务请求,VLM 必须先描述场景并说明上一条指令是已完成、进行中还是失败,然后才能给出下一条指令。done 的回复表示目标已完成:autosteer 停止,运行结束,机器人保持其姿态,直到执行 /reset 或 /start。在 /subtask、/reset 或新目标之前发出的迟到的 done 将被丢弃。每个应用的子任务都会打印出模型的场景评估和请求延迟。

单独启动一个 OpenAI 兼容的 VLM 服务器,然后进行连接:

transformers serve Qwen/Qwen3-VL-8B-Instruct --port 8000
# Alternatively: vllm serve Qwen/Qwen3-VL-8B-Instruct --port 8000
lerobot-rollout ... --task="pick up the cup" --interactive=true \
    --planner.model_id=Qwen/Qwen3-VL-8B-Instruct \
    --planner.api_base=http://localhost:8000/v1
> /start
> /vqa What is the next short instruction to clear the table?
> /subtask put the cup in the sink
> /autosteer clear the table

托管模型的工作方式相同。此示例使用 Hugging Face Inference Providers,并使用具有“Make calls to Inference Providers”权限的令牌:

export HF_TOKEN=hf_...
lerobot-rollout ... --task="put all colored cubes in the green bin" --interactive=true \
    --planner.model_id=Qwen/Qwen3.8-27B:novita \
    --planner.api_base=https://huggingface.co/proxy/router.huggingface.co/v1 \
    --planner.api_key_env=HF_TOKEN

/vqa 仅报告答案;/autosteer 应用指令。使用 --planner.api_base 配置端点,使用 --planner.api_key_env=<variable>(推荐,密钥不会出现在命令和日志中)或 --planner.api_key 配置密钥。--planner.request_timeout_s(默认 600)和 --planner.request_max_retries(默认 2)限制每个请求;--planner.camera_key 选择一个摄像头;--planner.log_path 将每次交互追加为一行 JSON。模型服务依赖项应位于服务器环境中。--autosteer_interval_s 在应用回复后测量机器人运动时间,此外还包括请求延迟。多级转向和自动场景清单不在该适配器的范围内。

编程控制

RolloutController 暴露了与库 API 相同的操作——线程安全的 start() / reset() / stop() / set_task() 以及一个 RolloutEvent 回调,且不附带任何标准输入、打印或日志静默功能:

from threading import Event, Thread

from lerobot.rollout import (
    LinkedEvent,
    RolloutController,
    RolloutEvent,
    build_rollout_context,
    create_strategy,
)

parent = Event()  # your application's shutdown signal
ctx = build_rollout_context(cfg, LinkedEvent(parent))  # loads policy, connects robot
strategy = create_strategy(cfg.strategy)
strategy.setup(ctx)

controller = RolloutController(strategy, ctx, on_event=print)  # or your own observer
serve_thread = Thread(target=controller.serve)  # serve() blocks; run it where you like
serve_thread.start()

controller.start()                        # robot starts executing the policy
controller.set_task("grab the red cube")  # re-instruct mid-run
controller.reset()                        # stop movement, return home, stay warm
controller.stop()                         # end serve()

serve_thread.join()
strategy.teardown(ctx)  # teardown stays with the caller

RolloutController 要求上下文的关闭事件是一个 LinkedEvent,构建方式如上所述:控制器通过其本地标志结束运行段,而您的 parent 事件仍会强制完全关闭。除非您希望听到语音播报,否则请设置 play_sounds=False。


常用标志

标志描述默认
--policy.path必填。HF Hub 模型 ID 或本地检查点路径—
--robot.type必填。 机器人类型(例如 so100_follower、koch_follower)—
--robot.port机器人的串行端口—
--robot.cameras相机配置(JSON 字典)—
--fps策略推理和数据集记录频率30
--status_interval_s健康日志之间的秒数(0 表示禁用;仅限非交互模式)5
--duration运行时间(秒)(0 = 无限;交互模式下按段计算)0
--deviceTorch 设备(cpu、cuda、mps)auto
--task任务描述(在未提供数据集时使用)—
--display_data将遥测数据流式传输到 Rerun 可视化false
--display_ip / --display_port远程 Rerun 服务器地址—
--interpolation_multiplier每个策略动作对应的机器人指令(以 fps × N Hz 的频率进行控制)1
--interactive在 stdin 上进行聊天会话;机器人在收到 /start 之前保持空闲(base, sentry)false
--use_torch_compile为推理启用 torch.compilefalse
--resume恢复之前的录制会话false
--play_sounds活动语音合成true

Cadence 报告

非交互式 rollout 会每五秒记录一次其状态。使用 --status_interval_s=10 可更改该间隔,或使用 --status_interval_s=0 将其禁用。

Rollout: loop 29.7/30 Hz · over budget 2/149 · starved 0 · infer 18.7 ms (worst 40.1) · process 2.41 GiB · GPU 5.12/79.6 GiB

延迟为区间内的中位数/最差值(RTC/远程为turnaround)。不可用的字段将被省略;GPU 内存为设备级,远程内存为本地。当循环阻塞时,报告将暂停。

每种策略都会报告其控制循环实际达成的结果,lerobot-record、lerobot-teleoperate 和 lerobot-replay 也是如此——它们都按照同一个计时器推进。每个保存的 episode 都会生成一行摘要,当循环结束时——包括在按下 Ctrl-C 时——会输出一个涵盖整个运行过程的代码块:

Cadence (episode 1): 29.84 Hz policy vs 30 Hz target · 1200 ticks, 20.1 s measured · 6/598 cycles over the 33.3 ms budget (work mean 18.7 ms, worst 48.1 ms)
Cadence (episode 2): 29.84 Hz policy vs 30 Hz target · 1200 ticks, 20.1 s measured · 6/599 cycles over the 33.3 ms budget (work mean 18.7 ms, worst 48.1 ms)
Cadence summary — whole run, 2 episodes · target 30 Hz × 2 (16.7 ms tick slot, 33.3 ms cycle budget): 2400 ticks, 1197 cycles judged
  effective cadence: 29.84 Hz policy / 59.67 Hz commands over 40.2 s measured
  cycles over the 33.3 ms work budget: 12/1197 (1.0%) — work mean 18.7 ms, worst 48.1 ms
  ticks over their 16.7 ms slot: 40/2400 (costs interpolation smoothness only)
  loop-body steps (share of measured work):
    observe      mean   3.13 ms · worst   9.00 ms ·  31.7% of work · 2400 calls
    process_obs  mean   0.50 ms · worst   1.00 ms ·   5.1% of work · 2400 calls
    infer        mean   5.18 ms · worst  40.00 ms ·  52.4% of work · 2400 calls
    send         mean   0.40 ms · worst   0.40 ms ·   4.1% of work · 2400 calls
    record       mean   0.67 ms · worst 620.00 ms ·   6.8% of work · 2402 calls
  pacing headroom: 7.4 ms slept per tick on average (max 13.0 ms) — near zero means the loop is saturated

自上而下阅读:有效节奏 是相对于 --fps 的实际达成值,超出预算的周期数 表示循环未能维持该节奏的频率,步骤分解 说明了时间消耗在哪里,而接近零的 节奏余量 意味着没有剩余空间来吸收缓慢的节拍。较高的 infer 占比指向策略或设备;较高的 observe 占比指向相机。某一步骤的 worst 远高于其 mean 属于一次性事件——在上文中,record 为每帧 0.67 毫秒,而 620 毫秒的异常值是关闭剧集时阻塞的 save_episode。

此处的cycle指一个策略动作所对应的 tick 数量(即 --interpolation_multiplier 个)。只有当完整的 cycle 超出 1/fps 的预算时,才会导致掉帧;单个 tick 超出其时间槽只会使插值后的运动不够平滑,因此这两者被分开统计。在没有插值的情况下,一个 cycle 就是一个 tick,因此报告会取消这种区分,并始终按 tick 进行统计。

在 交互式会话中,每个 /start 片段都拥有自己的计时器,因此报告会在每个片段(/reset、/stop 或 --duration)结束时发送到聊天中,而不是在每个进程结束时发送。


编程式用法

对于扩展功能,lerobot.inference 是用于后端配置、本地引擎和共享调度契约的公共包。lerobot.remote_inference 负责远程引擎、协议、部署和会话的实现;lerobot.rollout 负责机器人循环、策略、插值和记录。请从各自的包中导入公共符号,而不是从实现文件中导入。仅在使用远程后端或服务器时,才需要可选的远程依赖项。

对于自定义部署(例如使用运动学处理器),请直接使用 rollout 模块 API:

from lerobot.rollout import BaseStrategyConfig, RolloutConfig, build_rollout_context
from lerobot.inference import SyncInferenceConfig
from lerobot.rollout.strategies import BaseStrategy
from lerobot.utils.process import ProcessSignalHandler

cfg = RolloutConfig(
    robot=my_robot_config,
    policy=my_policy_config,
    strategy=BaseStrategyConfig(),
    inference=SyncInferenceConfig(),
    fps=30,
    duration=60,
    task="my task",
)

signal_handler = ProcessSignalHandler(use_threads=True)
ctx = build_rollout_context(
    cfg,
    signal_handler.shutdown_event,
    robot_action_processor=my_custom_action_processor,       # optional
    robot_observation_processor=my_custom_obs_processor,     # optional
)

strategy = BaseStrategy(cfg.strategy)
try:
    strategy.setup(ctx)
    strategy.run(ctx)
finally:
    strategy.teardown(ctx)

请参阅 examples/so100_to_so100_EE/rollout.py 和 examples/phone_to_so100/rollout.py 以获取包含运动学处理器的完整示例。

Update on GitHub