LeRobot documentation

处理器简介

Hugging Face's logo
Join the Hugging Face community

and get access to the augmented documentation experience

to get started

处理器简介

在机器人领域,机器人和人类产生的数据与机器学习模型所期望的数据之间存在根本性的不匹配。 机器人输出的是原始传感器数据,例如相机图像和关节位置,这些数据在模型处理之前需要进行归一化、批处理和设备放置。 来自人类的语言指令必须被分词为数值表示,并且不同的机器人使用不同的坐标系,需要进行标准化。

这一挑战同样延伸至模型输出。 模型可能输出末端执行器位置,而机器人需要关节空间指令;或者遥操作员产生相对运动,而机器人期望绝对指令。 模型预测值通常经过归一化处理,需要转换回真实世界的尺度。

跨领域翻译增加了另一层复杂性。 来自一种机器人配置的训练数据需要适配才能在不同的硬件上部署,使用特定相机配置训练的模型必须能够适应新的布局,而具有不同命名规范的各个数据集需要进行统一。

处理器正是为此而生的。它们充当通用翻译器,弥合这些差距,确保数据从传感器到模型再到执行器的无缝流动。 处理器负责所有预处理和后处理步骤,将原始环境数据转换为模型可用的输入,反之亦然。

这意味着您可以像这样使用您喜欢的策略:

import torch

from lerobot.datasets import LeRobotDataset
from lerobot.policies import make_pre_post_processors
from lerobot.policies.your_policy import YourPolicy
from lerobot.processor import RobotProcessorPipeline, PolicyProcessorPipeline
dataset = LeRobotDataset("hf_user/dataset", episodes=[0])
sample = dataset[10]

model = YourPolicy.from_pretrained(
    "hf_user/model",
)
model.eval()
model.to("cuda")
preprocessor, postprocessor = make_pre_post_processors(model.config, pretrained_path="hf_user/model", dataset_stats=dataset.meta.stats)

preprocessed_sample = preprocessor(sample)
action = model.select_action(preprocessed_sample)
postprocessed_action = postprocessor(action)

什么是处理器?

在机器人领域,数据有多种形式:来自摄像头的图像、来自传感器的关节位置、来自用户的文本指令等等。每种类型的数据在模型能够有效使用之前,都需要进行特定的转换。模型需要这些数据满足以下条件:

  • 归一化:缩放到适合神经网络处理的范围
  • 批处理:以适合批量处理的正确维度进行组织
  • 已分词:转换为数值表示的文本
  • 设备放置:已移至正确的硬件(CPU/GPU)
  • 类型转换:转换为适当的数据类型

处理器通过可组合、可复用的步骤来处理这些转换,这些步骤可以串联成流水线。可以将其想象为一条模块化装配线,每个工位都对您的数据执行特定的转换。

核心概念

EnvTransition:通用数据容器

EnvTransition 是流经所有处理器的基本数据结构。它是一个类型化字典,表示一次完整的机器人与环境的交互:

  • 观察:所有传感器数据(图像、状态、本体感觉)
  • ACTION:要执行或已执行的操作
  • REWARD:强化学习信号
  • DONE/TRUNCATED:回合边界指示器
  • INFO:任意元数据
  • COMPLEMENTARY_DATA:任务描述、索引、填充标志、步骤间数据

批量转换器会将 index、episode_index 和 frame_index 等索引元数据保留在 COMPLEMENTARY_DATA 中。 在 batch_to_transition / transition_to_batch 往返转换中,frame_index 会保留其原始的标量或张量值。 如果输入中省略了 frame_index,转换器不会添加它。

ProcessorStep:构建模块

ProcessorStep 是处理状态转换的单个转换单元。它是一个抽象基类,包含两个必需的方法:

from lerobot.processor import ProcessorStep, EnvTransition

class MyProcessorStep(ProcessorStep):
    """Example processor step - inherit and implement abstract methods."""

    def __call__(self, transition: EnvTransition) -> EnvTransition:
        """Transform the transition - REQUIRED abstract method."""
        # Your processing logic here
        return transition

    def transform_features(self, features):
        """Declare how this step transforms feature shapes/types - REQUIRED abstract method."""
        return features  # Most processors return features unchanged

__call__ 是处理器步骤的核心。它接收一个 EnvTransition 并返回一个修改后的 EnvTransition。

transform_features 用于声明此步骤如何转换特征的形状/类型。

DataProcessorPipeline:通用编排器

DataProcessorPipeline[TInput, TOutput] 将多个 ProcessorStep 实例串联起来:

from lerobot.processor import RobotProcessorPipeline, PolicyProcessorPipeline

# For robot hardware (unbatched data)
robot_processor = RobotProcessorPipeline[RobotAction, RobotAction](
    steps=[step1, step2, step3],
    name="robot_pipeline"
)

# For model training/inference (batched data)
policy_processor = PolicyProcessorPipeline[dict[str, Any], dict[str, Any]](
    steps=[step1, step2, step3],
    name="policy_pipeline"
)

RobotProcessorPipeline 与 PolicyProcessorPipeline

关键区别在于它们处理的数据结构:

方面RobotProcessorPipelinePolicyProcessorPipeline
输入dict[str, Any] - 单个机器人的值dict[str, Any] - 批处理张量
输出dict[str, Any] - 单个机器人命令torch.Tensor - 策略预测
用例实时机器人控制模型训练/推理
数据格式未批处理,异构批处理,同质
示例{"joint_1": 0.5}{"observation.state": tensor([[0.5]])}

使用 RobotProcessorPipeline 处理机器人硬件接口:

# Robot data structures: dict[str, Any] for observations and actions
robot_obs: dict[str, Any] = {
    "joint_1": 0.5,           # Individual joint values
    "joint_2": -0.3,
    "camera_0": image_array   # Raw camera data
}

robot_action: dict[str, Any] = {
    "joint_1": 0.2,          # Target joint positions
    "joint_2": 0.1,
    "gripper": 0.8
}

使用 PolicyProcessorPipeline 进行模型训练和批处理:

# Policy data structures: batch dicts and tensors
policy_batch: dict[str, Any] = {
    "observation.state": torch.tensor([[0.5, -0.3]]),      # Batched states
    "observation.images.camera0": torch.tensor(...),        # Batched images
    "action": torch.tensor([[0.2, 0.1, 0.8]])              # Batched actions
}

policy_action: torch.Tensor = torch.tensor([[0.2, 0.1, 0.8]])  # Model output tensor

转换器函数

LeRobot 在 lerobot.processor.converters 中提供了转换函数,用于桥接不同的数据格式。这些函数负责处理机器人硬件数据结构、策略模型格式以及流经处理器管道的内部 EnvTransition 表示之间的关键转换。

类别函数描述
机器人硬件转换器robot_action_to_transitionRobot dict → EnvTransition
observation_to_transition机器人观测 → EnvTransition
transition_to_robot_actionEnvTransition → Robot 字典
策略/训练转换器batch_to_transition批量字典 → EnvTransition
transition_to_batchEnvTransition → 批次字典
policy_action_to_transition策略张量 → EnvTransition
transition_to_policy_actionEnvTransition → Policy 张量
实用工具create_transition使用默认值构建过渡
identity_transition直通转换器

关键洞察在于,机器人硬件转换器处理的是单个值和字典,而策略/训练转换器处理的是批量张量和模型输出。转换器函数会自动处理结构上的差异,因此您的处理器步骤可以专注于核心转换,而无需担心数据格式兼容性问题。

处理器示例

以下示例展示了策略训练和推理中实际使用的处理器配置。

以下是一个用于策略训练和推理的处理器示例:

# Training data preprocessing (optimized order for GPU performance)
training_preprocessor = PolicyProcessorPipeline[dict[str, Any], dict[str, Any]](
    steps=[
        RenameObservationsProcessorStep(rename_map={}),     # Standardize keys
        AddBatchDimensionProcessorStep(),                   # Add batch dims
        TokenizerProcessorStep(tokenizer_name="...", ...),  # Tokenize language
        DeviceProcessorStep(device="cuda"),                 # Move to GPU first
        NormalizerProcessorStep(features=..., stats=...),   # Normalize on GPU
    ]
)

# Model output postprocessing
training_postprocessor = PolicyProcessorPipeline[torch.Tensor, torch.Tensor](
    steps=[
        DeviceProcessorStep(device="cpu"),                  # Move to CPU
        UnnormalizerProcessorStep(features=..., stats=...), # Denormalize
    ],
    to_transition=policy_action_to_transition,
    to_output=transition_to_policy_action,
)

机器人与带有处理器的策略之间的交互

最常见的实际场景同时结合了这两种流水线类型:机器人硬件生成需要策略处理的观测数据,而策略输出则需要与机器人兼容的后处理:

# Real deployment: Robot sensors → Model → Robot commands
with torch.no_grad():
    while not done:
        raw_obs = robot.get_observation()  # dict[str, Any]

        # Add your robot observation to policy observation processor

        policy_input = policy_preprocessor(raw_obs)  # Batched dict

        policy_output = policy.select_action(policy_input)  # Policy tensor

        policy_action = policy_postprocessor(policy_output)

        # Add your robot action to policy action processor

        robot.send_action(policy_action)

功能合约:形状与类型转换

处理器不仅会转换数据,还可以更改数据本身的结构。transform_features() 方法用于声明这些更改,这对于数据集记录和策略创建至关重要。

为什么特征契约很重要

在构建数据集或策略时,LeRobot 需要知道:

  • 处理后将存在哪些数据字段
  • 每个字段的形状和类型
  • 如何为预期的数据结构配置模型
# Example: A processor that adds velocity to observations
class VelocityProcessor(ObservationProcessorStep):
    def observation(self, obs):
        new_obs = obs.copy()
        if "observation.state" in obs:
            # concatenate computed velocity field to the state
            new_obs["observation.state"] = self._compute_velocity(obs["observation.state"])
        return new_obs

    def transform_features(self, features):
        """Declare the new velocity field we're adding."""
        state_feature = features[PipelineFeatureType.OBSERVATION].get("observation.state")
        if state_feature:
            double_shape = (state_feature.shape[0] * 2,) if state_feature.shape else (2,)
            features[PipelineFeatureType.OBSERVATION]["observation.state"] = PolicyFeature(
                type=FeatureType.STATE, shape=double_shape
            )
        return features

特征规范函数

create_initial_features() 和 aggregate_pipeline_dataset_features() 解决了数据集创建中的一个关键问题:在数据被处理之前确定最终的确切数据结构。 由于处理器流水线可能会添加新特征(例如速度场)、更改张量形状(例如裁剪图像)或重命名键,数据集需要预先了解完整的输出规范,以便分配适当的存储并定义模式。 这些函数协同工作:首先从机器人硬件规格开始(create_initial_features()),然后模拟整个流水线的转换过程(aggregate_pipeline_dataset_features()),以计算最终的特征字典,并将其传递给 LeRobotDataset.create(),从而确保处理器输出的内容与数据集预期存储的内容完美对齐。

from lerobot.datasets import aggregate_pipeline_dataset_features

# Start with robot's raw features
initial_features = create_initial_features(
    observation=robot.observation_features,  # {"joint_1.pos": float, "camera_0": (480,640,3)}
    action=robot.action_features            # {"joint_1.pos": float, "gripper.pos": float}
)

# Apply processor pipeline to compute final features
final_features = aggregate_pipeline_dataset_features(
    pipeline=my_processor_pipeline,
    initial_features=initial_features,
    use_videos=True
)

# Use for dataset creation
dataset = LeRobotDataset.create(
    repo_id="my_dataset",
    features=final_features,  # Knows exactly what data to expect
    ...
)

通用处理器步骤

LeRobot 提供了许多已注册的处理器步骤。以下是最常用的核心处理器:

核心处理器

  • normalizer_processor:使用数据集统计量(均值/标准差或最小值/最大值)对观测/动作进行归一化
  • device_processor:将张量移动到 CPU/GPU,并可选择进行 dtype 转换
  • to_batch_processor:为 transitions 添加批次维度,以兼容模型
  • rename_observations_processor:使用映射字典重命名观测键
  • tokenizer_processor:将自然语言任务描述分词为 token 和注意力掩码

后续步骤

摘要

处理器通过提供以下内容,解决了机器人领域中的数据转换问题:

  • 模块化转换:可组合、可复用的处理步骤
  • 类型安全:具有编译时检查的通用流水线
  • 性能优化:GPU 加速操作
  • Robot/Policy 区分:为不同的数据结构设置独立的流水线
  • 全面的生态系统:为常见任务提供 30 多个已注册的处理器

关键洞察:RobotProcessorPipeline 处理未批处理的机器人硬件数据,而 PolicyProcessorPipeline 处理批处理后的模型数据。请根据数据结构选择合适的工具!

Update on GitHub