在机器人技术中,机器人和人类产生的数据与机器学习模型期望的数据之间存在根本性的不匹配。 机器人输出原始的传感器数据,如相机图像和关节位置,这些数据在模型处理之前需要归一化、批处理和设备放置。 来自人类的语言指令必须被 token 化为数值表示,而不同的机器人使用不同的坐标系,需要标准化。
这一挑战同样延伸到模型输出。 模型可能输出 end-effector 位置,而机器人需要关节空间命令;或者 teleoperator 产生相对运动,而机器人期望绝对命令。 模型预测通常是归一化的,需要转换回真实世界的尺度。
跨域转换又增加了一层复杂性。 来自一种机器人设置的数据需要适应在不同硬件上部署,使用特定相机配置训练的模型必须能与新的布置配合,而具有不同命名约定的 dataset 需要统一。
这正是处理器发挥作用的地方。 它们充当通用翻译器,弥合这些差距,确保数据从传感器到模型再到执行器的无缝流动。 处理器处理所需的所有预处理和后处理步骤,将原始环境数据转换为模型可用的输入,反之亦然。
这意味着你最喜爱的 policy 可以像这样使用:
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)在机器人技术中,数据以多种形式出现:来自相机的图像、来自传感器的关节位置、来自用户的文本指令等。每种类型的数据在模型有效使用之前都需要特定的变换。模型需要这些数据:
处理器通过可组合、可复用的步骤处理这些变换,这些步骤可以链接成流水线。可以将它们视为模块化装配线,每个工位对你的数据执行特定的变换。
EnvTransition 是流经所有处理器的基本数据结构。
它是一个类型化字典,表示完整的机器人-环境交互:
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[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 |
|---|---|---|
| 输入 | dict[str, Any] - 单个机器人值 | dict[str, Any] - 批处理张量 |
| 输出 | dict[str, Any] - 单个机器人命令 | torch.Tensor - policy 预测 |
| 用例 | 实时机器人控制 | 模型训练/inference |
| 数据格式 | 非批处理、异构 | 批处理、同构 |
| 示例 | {"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 tensorLeRobot 提供转换器函数,用于在 lerobot.processor.converters 中桥接不同的数据格式。这些函数处理机器人硬件数据结构、policy 模型格式以及流经处理器流水线的内部 EnvTransition 表示之间的关键转换。
| 类别 | 函数 | 描述 |
|---|---|---|
| 机器人硬件转换器 | robot_action_to_transition | 机器人字典 → EnvTransition |
observation_to_transition | 机器人 observation → EnvTransition | |
transition_to_robot_action | EnvTransition → 机器人字典 | |
| policy/训练转换器 | batch_to_transition | 批次字典 → EnvTransition |
transition_to_batch | EnvTransition → 批次字典 | |
policy_action_to_transition | policy 张量 → EnvTransition | |
transition_to_policy_action | EnvTransition → policy 张量 | |
| 工具函数 | create_transition | 使用默认值构建转换 |
identity_transition | 直通转换器 |
关键洞察在于 机器人硬件转换器 处理单个值和字典,而 policy/训练转换器 处理批处理张量和模型输出。转换器函数自动处理结构差异,因此你的处理器步骤可以专注于核心变换,而无需担心数据格式兼容性。
以下示例演示了用于 policy 训练和 inference 的真实世界处理器配置。
以下是一个用于 policy 训练和 inference 的处理器示例:
# 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,
)最常见的真实世界场景结合了两种流水线类型:机器人硬件生成需要 policy 处理的 observation,而 policy 输出需要与机器人兼容的后处理:
# 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() 方法声明这些变化,这对于 dataset 录制和 policy 创建至关重要。
在构建 dataset 或 policy 时,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 featurescreate_initial_features() 和 aggregate_pipeline_dataset_features() 解决了一个关键的 dataset 创建问题:在任何数据处理之前确定确切的最终数据结构。
由于处理器流水线可以添加新特征(如速度字段)、改变张量形状(如图像裁剪)或重命名键,dataset 需要预先知道完整的输出规格,以分配适当的存储并定义模式。
这些函数协同工作:从机器人硬件规格(create_initial_features())开始,然后模拟整个流水线变换(aggregate_pipeline_dataset_features())以计算最终传递给 LeRobotDataset.create() 的特征字典,确保处理器输出与 dataset 期望存储的内容完美对齐。
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:使用 dataset 统计信息(均值/标准差或最小/最大值)归一化 observation/actiondevice_processor:将张量移动到 CPU/GPU,并可选择转换数据类型to_batch_processor:为转换添加批次维度以兼容模型rename_observations_processor:使用映射字典重命名 observation 键tokenizer_processor:将自然语言任务描述 token 化为 token 和注意力掩码处理器通过提供以下内容解决了机器人技术中的数据转换问题:
关键洞察:RobotProcessorPipeline 处理非批处理的机器人硬件数据,而 PolicyProcessorPipeline 处理批处理的模型数据。为你的数据结构选择正确的工具!