异步推理
通过我们的 SmolVLA,我们引入了一种在真实世界机器人上运行推理的新方法,将动作预测与动作执行解耦。 在本教程中,我们将展示如何使用异步推理(async inference)运行 SmolVLA 的微调版本,以及 LeRobot 支持的所有策略。 尝试对 LeRobot 支持的所有策略使用异步推理!
你将学到:
- 为什么异步推理很重要,以及它与更传统的顺序推理相比如何。
- 如何启动
PolicyServer并从同一台机器甚至通过网络连接RobotClient。 - 如何为你的机器人和策略调整关键参数(
actions_per_chunk、chunk_size_threshold)。
如果遇到困难,请加入我们的 Discord 社区!
简而言之:使用 异步推理,你的机器人在策略服务器已经忙于计算下一批动作时继续执行——消除了"等待推理"的延迟,实现更流畅、更具响应性的行为。 这与同步推理(sync)有本质区别,在同步推理中,机器人在策略计算下一批动作时保持空闲。
异步推理入门
你可以在我们的博客文章中阅读有关异步推理的更多信息。本指南旨在帮助你快速在环境中设置和运行异步推理。
首先,使用 async 标签安装 lerobot,以安装运行异步推理所需的额外依赖项。
pip install -e ".[async]"
然后,启动策略服务器(在一个终端中,或在单独的机器上),指定客户端连接的主机地址和端口。 你可以运行以下命令启动策略服务器:
python -m lerobot.async_inference.policy_server \
--host=127.0.0.1 \
--port=8080
这将启动一个监听 127.0.0.1:8080(localhost,端口 8080)的策略服务器。在此阶段,策略服务器是空的,因为所有与运行哪个策略以及使用哪些参数相关的信息都在客户端与服务器的首次握手期间指定。使用以下命令启动客户端:
python -m lerobot.async_inference.robot_client \
--server_address=127.0.0.1:8080 \ # SERVER: 策略服务器的主机地址和端口
--robot.type=so100_follower \ # ROBOT: 你的机器人类型
--robot.port=/dev/tty.usbmodem585A0076841 \ # ROBOT: 你的机器人端口
--robot.id=follower_so100 \ # ROBOT: 你的机器人 id,用于加载校准文件
--robot.cameras="{ laptop: {type: opencv, index_or_path: 0, width: 1920, height: 1080, fps: 30}, phone: {type: opencv, index_or_path: 0, width: 1920, height: 1080, fps: 30}}" \ # POLICY: 用于获取帧的相机,键与策略期望的键匹配
--task="dummy" \ # POLICY: 运行策略的任务(`Fold my t-shirt`)。并非所有策略都定义,例如 `act`
--policy_type=your_policy_type \ # POLICY: 要运行的策略类型(smolvla、act 等)
--pretrained_name_or_path=user/model \ # POLICY: 服务器上要运行的检查点的模型名称/路径(例如,lerobot/smolvla_base)
--policy_device=mps \ # POLICY: 在服务器上运行策略的设备(cuda、mps、xpu、cpu)
--actions_per_chunk=50 \ # POLICY: 一次输出的动作数量
--chunk_size_threshold=0.5 \ # CLIENT: 在向服务器发送新观测之前的块大小阈值
--aggregate_fn_name=weighted_average \ # CLIENT: 在重叠部分聚合动作的函数
--debug_visualize_queue_size=True # CLIENT: 是否在运行时可视化队列大小
总之,你需要指定以下内容的说明:
SERVER:策略服务器的地址和端口ROBOT:要连接的机器人类型、要连接的端口以及机器人的本地idPOLICY:要运行的策略类型,以及服务器上要运行的检查点的模型名称/路径。你还需要指定服务器应使用哪个设备,以及一次输出多少动作(上限为策略的最大动作值)。CLIENT:在向服务器发送新观测之前的块大小阈值,以及在重叠部分聚合动作的函数。可选地,你还可以在运行时可视化队列大小,以帮助你调整CLIENT参数。
重要的是,
actions_per_chunk和chunk_size_threshold是为你的设置调整的关键参数。aggregate_fn_name是在重叠部分聚合动作的函数。你可以向函数注册表添加新函数,或在robot_client.py中添加自己的函数(参见此处)debug_visualize_queue_size是调整CLIENT参数的有用工具。
完成!你现在应该看到你的机器人在移动了 😉
异步 vs. 同步推理
同步推理依赖于交错的动作块预测和动作执行。这本质上会导致 空闲帧,即机器人等待策略输出的帧:一个新的动作块。 反过来,推理受到明显的实时延迟的困扰,机器人由于缺乏可用动作而简单地停止执行。 随着机器人模型规模的增加,这个问题可能会变得更加严重。
同步推理使机器人在策略计算下一批动作时保持空闲。
为了克服这一点,我们设计了异步推理,这是一种动作规划和执行解耦的范式,导致 (1) 更高的适应性,最重要的是,(2) 没有空闲帧。 关键的是,使用异步推理,下一个动作块在当前动作块耗尽之前计算,导致没有空闲。 通过在重叠部分聚合不同的动作块,确保更高的适应性,获得最新的计划和更紧密的控制循环。
异步推理不会导致空闲,因为下一个块在当前块耗尽之前计算。
启动策略服务器
策略服务器是 PreTrainedPolicy 的包装器,将它们与来自机器人客户端的观测连接起来。
策略服务器初始化为空容器,在机器人客户端和策略服务器之间的初始握手中指定所请求的策略后填充。
因此,启动策略服务器就像指定主机地址和端口一样简单。如果你在与机器人客户端相同的机器上运行策略服务器,可以使用 localhost 作为主机地址。
python -m lerobot.async_inference.policy_server \
--host=127.0.0.1 \
--port=8080
from lerobot.async_inference.configs import PolicyServerConfig
from lerobot.async_inference.policy_server import serve
config = PolicyServerConfig(
host="localhost",
port=8080,
)
serve(config)
这会监听 localhost:8080 以接收来自关联的 RobotClient 的传入连接,该连接将在首次客户端-服务器握手期间传达要运行的策略。
启动机器人客户端
RobotClient 是 Robot 实例的包装器,RobotClient 将其连接到(可能是远程的)PolicyServer。
RobotClient 将观测流式传输到 PolicyServer,并接收在服务器上运行推理获得的动作块(我们假设服务器比机器人控制器具有更好的计算资源)。
python -m lerobot.async_inference.robot_client \
--server_address=127.0.0.1:8080 \ # SERVER: 策略服务器的主机地址和端口
--robot.type=so100_follower \ # ROBOT: 你的机器人类型
--robot.port=/dev/tty.usbmodem585A0076841 \ # ROBOT: 你的机器人端口
--robot.id=follower_so100 \ # ROBOT: 你的机器人 id,用于加载校准文件
--robot.cameras="{ laptop: {type: opencv, index_or_path: 0, width: 1920, height: 1080, fps: 30}, phone: {type: opencv, index_or_path: 0, width: 1920, height: 1080, fps: 30}}" \ # POLICY: 用于获取帧的相机,键与策略期望的键匹配
--task="dummy" \ # POLICY: 运行策略的任务(`Fold my t-shirt`)。并非所有策略都定义,例如 `act`
--policy_type=your_policy_type \ # POLICY: 要运行的策略类型(smolvla、act 等)
--pretrained_name_or_path=user/model \ # POLICY: 服务器上要运行的检查点的模型名称/路径(例如,lerobot/smolvla_base)
--policy_device=mps \ # POLICY: 在服务器上运行策略的设备
--actions_per_chunk=50 \ # POLICY: 一次输出的动作数量
--chunk_size_threshold=0.5 \ # CLIENT: 在向服务器发送新观测之前的块大小阈值
--aggregate_fn_name=weighted_average \ # CLIENT: 在重叠部分聚合动作的函数
--debug_visualize_queue_size=True # CLIENT: 是否在运行时可视化队列大小
import threading
from lerobot.robots.so_follower import SO100FollowerConfig
from lerobot.cameras.opencv import OpenCVCameraConfig
from lerobot.async_inference.configs import RobotClientConfig
from lerobot.async_inference.robot_client import RobotClient
from lerobot.async_inference.helpers import visualize_action_queue_size
# 1. 创建机器人实例
"""通过运行 `python lerobot/find_cameras.py` 检查你的设置中可用的相机"""
# 这些相机必须与策略期望的相机匹配
# 检查你正在使用的策略在 Hub 上的 config.json
camera_cfg = {
"top": OpenCVCameraConfig(index_or_path=0, width=640, height=480, fps=30),
"side": OpenCVCameraConfig(index_or_path=1, width=640, height=480, fps=30)
}
robot_cfg = SO100FollowerConfig(
port="/dev/tty.usbmodem585A0076841",
id="follower_so100",
cameras=camera_cfg
)
# 3. 创建客户端配置
client_cfg = RobotClientConfig(
robot=robot_cfg,
server_address="localhost:8080",
policy_device="mps",
client_device="cpu",
policy_type="smolvla",
pretrained_name_or_path="<user>/smolvla_async",
chunk_size_threshold=0.5,
actions_per_chunk=50, # 确保这小于策略的最大动作数
)
# 4. 创建并启动客户端
client = RobotClient(client_cfg)
# 5. 指定任务
task = "Don't do anything, stay still"
if client.start():
# 启动动作接收线程
action_receiver_thread = threading.Thread(target=client.receive_actions, daemon=True)
action_receiver_thread.start()
try:
# 运行控制循环
client.control_loop(task)
except KeyboardInterrupt:
client.stop()
action_receiver_thread.join()
# (可选)绘制动作队列大小
visualize_action_queue_size(client.action_queue_size)
以下两个参数在每个设置中都是关键的:
| 超参数 | 默认值 | 作用 |
|---|---|---|
actions_per_chunk
|
50 | 策略一次输出多少动作。典型值:10-50。 |
chunk_size_threshold
|
0.7 | 当队列 ≤ 50% 满时,客户端发送新的观测。 值在 [0, 1] 之间。 |
Tip
不同的 actions_per_chunk 和 chunk_size_threshold 值确实会导致不同的行为。
一方面,增加 actions_per_chunk 的值将减少最终没有动作可执行的可能性,因为在计算新块时将有更多动作可用。
然而,较大的 actions_per_chunk 值也可能导致动作不太精确,这是由于在更长时间跨度上预测动作导致的累积误差。
另一方面,增加 chunk_size_threshold 的值将导致更频繁地向 PolicyServer 发送观测以进行推理,从而导致更多的动作块更新,在重要部分重叠。这导致高适应性,在极限情况下为每个观测预测一个动作块,而该动作块在产生新动作块时仅被边际消耗。
这个选项也会给推理管道带来更大的压力,因为有许多请求。相反,接近 0.0 的 chunk_size_threshold 值会退化为同步边缘情况,即仅在当前块耗尽时才发送新观测。
我们发现 actions_per_chunk 和 chunk_size_threshold 的默认值在我们为 SmolVLA 论文开发的实验中效果良好,但建议尝试不同的值以找到最适合你的设置的值。
为你的设置调整异步推理
- 仔细选择你的计算资源。 PI0 在推理时占用 14GB 内存,而 SmolVLA 仅需要约 2GB。你应该根据你的用例确定最佳计算资源,记住较小的策略需要较少的计算资源。策略和使用的设备(CPU 密集型、使用 MPS 或给定 NVIDIA GPU 上的 CUDA 核心数量)的组合直接影响你应该期望的平均推理延迟。
- 根据推理延迟调整你的
fps。 当服务器生成新的动作块时,客户端不会空闲,而是在其当前动作队列中步进。如果这两个过程以根本不同的速度发生,客户端可能会最终队列为空。因此,如果你经常耗尽队列中的动作,应该降低你的 fps。 - 调整
chunk_size_threshold。 - 接近
0.0的值导致几乎顺序的行为。接近1.0的值 → 每步发送观测(更多带宽,依赖于良好的世界模型)。 - 我们发现 0.5-0.6 左右的值效果良好。如果你想调整这个,启动一个
RobotClient,将--debug_visualize_queue_size设置为True。这将在运行时绘制动作队列大小演变,你可以使用它来找到最适合你的设置的chunk_size_threshold值。
当传递 `--debug_visualize_queue_size` 标志时,在运行时绘制动作队列大小, 用于各种级别的 `chunk_size_threshold`(SmolVLA 论文中的 `g`)。
结论
异步推理代表了实时机器人控制的重大进步,解决了长期困扰机器人应用的推理延迟的基本挑战。通过本教程,你已经学会了如何实现完整的异步推理管道,消除空闲帧并实现更流畅、更具响应性的机器人行为。
关键要点:
- 范式转变:异步推理将动作预测与执行解耦,允许机器人在并行计算新动作块时继续执行
- 性能优势:消除同步方法固有的"等待推理"延迟,随着策略模型变得更大而变得越来越重要
- 灵活的架构:服务器-客户端设计支持分布式计算,推理可以在强大的远程硬件上运行,同时保持实时机器人控制
- 可调参数:成功取决于为你的特定硬件、策略和任务需求正确配置
actions_per_chunk和chunk_size_threshold - 通用兼容性:适用于所有 LeRobot 支持的策略,从轻量级 ACT 模型到像 SmolVLA 这样的视觉-语言模型
从默认参数开始实验,监控你的动作队列大小,并迭代优化你的设置以实现特定用例的最佳性能。 如果你想进一步讨论这个问题,请加入我们的 Discord 社区,或在我们的 GitHub 仓库上提出问题。