LeRobot documentation
Bộ xử lý Môi trường
Bộ xử lý Môi trường
Bộ xử lý môi trường là một lớp quan trọng trong kiến trúc xử lý dữ liệu của LeRobot, đảm nhận các phép biến đổi có tính đặc thù của môi trường, tách biệt khỏi xử lý đặc thù của chính sách. Việc tách biệt các mối quan tâm này cho phép mã nguồn sạch hơn, tính mô-đun hóa tốt hơn và dễ dàng thử nghiệm với các môi trường và chính sách khác nhau.
Tại sao cần Trình xử lý Môi trường?
Khi làm việc với các môi trường robot khác nhau (LIBERO, MetaWorld, Aloha, v.v.), mỗi môi trường thường có định dạng dữ liệu, hệ tọa độ và các quy ước riêng biệt cần được chuẩn hóa trước khi xử lý chính sách. Nếu không có bộ xử lý môi trường, các phép biến đổi này sẽ:
- Được hardcode trong mã môi trường - Gây khó khăn cho việc thử nghiệm với các biểu diễn trạng thái khác nhau
- Bị lặp lại giữa các chính sách - Mỗi chính sách sẽ cần xử lý các đặc thù riêng của môi trường
- Trộn lẫn với logic chính sách - Vi phạm nguyên tắc tách biệt mối quan tâm và khiến việc gỡ lỗi trở nên khó khăn hơn
Environment processors giải quyết vấn đề này bằng cách cung cấp một lớp xử lý chuyên dụng giữa các quan sát thô từ môi trường và đầu vào của policy.
Đường ống xử lý
Dưới đây là cách dữ liệu chảy qua toàn bộ quy trình xử lý trong quá trình đánh giá:
# In lerobot_eval.py rollout() function:
# 1. Raw environment observation (numpy arrays, various formats)
raw_observation = env.step(action)
# 2. Convert numpy to torch, normalize images [0,1]
observation = preprocess_observation(raw_observation)
# 3. Add the task description (for multi-task environments)
observation["task"] = list(env.call("task_description"))
# 4. ENVIRONMENT-SPECIFIC preprocessing (NEW!)
# - Flatten robot states
# - Rotate images to match dataset conventions
# - Handle environment-specific coordinate systems
observation = env_preprocessor(observation)
# 5. POLICY-SPECIFIC preprocessing
# - Normalize with dataset statistics
# - Add batch dimensions
# - Move to GPU
# - Tokenize language instructions
observation = preprocessor(observation)
# 6. Policy inference
action = policy.select_action(observation)
# 7. POLICY-SPECIFIC postprocessing
# - Unnormalize actions
# - Remove batch dimensions
action = postprocessor(action)
# 8. ENVIRONMENT-SPECIFIC postprocessing (NEW!)
# - Convert action formats if needed
# - Apply environment-specific constraints
action_transition = {"action": action}
action_transition = env_postprocessor(action_transition)
action = action_transition["action"]
# 9. Execute in environment
env.step(action)Lợi ích
1. Tách biệt mối quan tâm
Bộ xử lý môi trường xử lý các phép biến đổi đặc thù cho định dạng dữ liệu của môi trường, trong khi bộ xử lý chính sách xử lý các phép biến đổi đặc thù cho yêu cầu của mô hình.
# ❌ Before: Mixed concerns
class LiberoVLAPolicy:
def preprocess(self, obs):
# Environment-specific: Flatten robot state (shouldn't be in policy!)
state = self._flatten_robot_state(obs["robot_state"])
# Policy-specific: Normalize with dataset stats
state = self.normalizer(state)
return state
# ✅ After: Clear separation
# Environment processor: Handles LIBERO's nested robot state
env_preprocessor = LiberoProcessorStep() # Flattens robot_state
# Policy processor: Handles model requirements
policy_preprocessor = NormalizerProcessorStep(stats=dataset_stats)2. Tính linh hoạt và khả năng tái sử dụng
Cùng một chính sách có thể hoạt động với các bộ xử lý môi trường khác nhau, và cùng một bộ xử lý môi trường có thể hoạt động với các chính sách khác nhau:
# Use SmolVLA policy with LIBERO environment
libero_preprocessor, libero_postprocessor = make_env_pre_post_processors(
env_cfg=libero_cfg,
policy_cfg=smolvla_cfg,
)
smolvla_preprocessor, smolvla_postprocessor = make_pre_post_processors(smolvla_cfg)
# Or use ACT policy with the same LIBERO environment
libero_preprocessor, libero_postprocessor = make_env_pre_post_processors(
env_cfg=libero_cfg,
policy_cfg=act_cfg,
)
act_preprocessor, act_postprocessor = make_pre_post_processors(act_cfg)3. Thử nghiệm dễ dàng hơn
Muốn thử các biểu diễn trạng thái khác cho LIBERO? Chỉ cần tạo một bộ xử lý mới:
# Original: 8D state (pos + quat→axisangle + gripper)
@ProcessorStepRegistry.register("libero_processor")
class LiberoProcessorStep(ObservationProcessorStep):
def _process_observation(self, obs):
eef_pos = robot_state["eef"]["pos"] # 3D
eef_axisangle = quat2axisangle(quat) # 3D
gripper = robot_state["gripper"]["qpos"] # 2D
state = torch.cat([eef_pos, eef_axisangle, gripper], dim=-1) # 8D
return state
# Experiment: Add velocity for better control
@ProcessorStepRegistry.register("libero_velocity_processor")
class LiberoVelocityProcessorStep(ObservationProcessorStep):
def _process_observation(self, obs):
# Include velocities for 14D state
eef_pos = robot_state["eef"]["pos"] # 3D
eef_axisangle = quat2axisangle(quat) # 3D
eef_vel = robot_state["eef"]["vel"] # 3D (NEW)
gripper_pos = robot_state["gripper"]["qpos"] # 2D
gripper_vel = robot_state["gripper"]["qvel"] # 3D (NEW)
state = torch.cat([eef_pos, eef_axisangle, eef_vel,
gripper_pos, gripper_vel], dim=-1) # 14D
return state4. Mã Môi trường Sạch hơn
Environments cung cấp toàn bộ dữ liệu khả dụng mà không cần biết các mô hình hạ nguồn sẽ sử dụng những gì:
# LIBERO environment exposes full robot state
observation = {
"pixels": {"image": img, "image2": img2},
"robot_state": {
"eef": {"pos": ..., "quat": ..., "vel": ..., "mat": ..., "axisangle": ...},
"gripper": {"qpos": ..., "qvel": ...},
"joints": {"pos": ..., "vel": ...}
}
}
# Environment processor decides what to use
# Policy processor handles model-specific transformationsSử dụng Bộ xử lý Môi trường
Hàm Factory
Hàm make_env_pre_post_processors tuân theo cùng một mẫu như make_pre_post_processors cho các chính sách:
from lerobot.envs import make_env_pre_post_processors, PushtEnv
from lerobot.envs.configs import LiberoEnv
# policy_cfg is your policy's config (e.g. cfg.policy in lerobot-eval); a few policies,
# such as XVLA, provide their own environment processors
# For LIBERO: Returns LiberoProcessorStep in preprocessor
libero_cfg = LiberoEnv(task="libero_spatial")
env_preprocessor, env_postprocessor = make_env_pre_post_processors(libero_cfg, policy_cfg=policy_cfg)
# For other environments: Returns identity processors (no-op)
pusht_cfg = PushtEnv()
env_preprocessor, env_postprocessor = make_env_pre_post_processors(pusht_cfg, policy_cfg=policy_cfg)Implementation in envs/factory.py
def make_env_pre_post_processors(
env_cfg: EnvConfig,
) -> tuple[
PolicyProcessorPipeline[dict[str, Any], dict[str, Any]],
PolicyProcessorPipeline[dict[str, Any], dict[str, Any]],
]:
"""
Create preprocessor and postprocessor pipelines for environment observations.
Args:
env_cfg: The configuration of the environment.
Returns:
A tuple containing:
- preprocessor: Pipeline that processes environment observations
- postprocessor: Pipeline that processes environment outputs
"""
# For LIBERO environments, add the LiberoProcessorStep to preprocessor
if isinstance(env_cfg, LiberoEnv) or "libero" in env_cfg.type:
preprocessor = PolicyProcessorPipeline(steps=[LiberoProcessorStep()])
else:
# For all other environments, return an identity preprocessor
preprocessor = PolicyProcessorPipeline(steps=[])
# Postprocessor is currently identity for all environments
# Future: Could add environment-specific action transformations
postprocessor = PolicyProcessorPipeline(steps=[])
return preprocessor, postprocessorTích hợp trong Đánh giá
Trong lerobot_eval.py, các bộ xử lý môi trường được tạo một lần và được sử dụng xuyên suốt:
def eval_main(cfg: EvalPipelineConfig):
# Create environment
envs = make_env(cfg.env, n_envs=cfg.eval.batch_size)
# Create policy
policy = make_policy(cfg=cfg.policy, env_cfg=cfg.env)
# Create policy processors
preprocessor, postprocessor = make_pre_post_processors(
policy_cfg=cfg.policy,
pretrained_path=cfg.policy.pretrained_path,
)
# Create environment processors (NEW!)
env_preprocessor, env_postprocessor = make_env_pre_post_processors(env_cfg=cfg.env, policy_cfg=cfg.policy)
# Run evaluation with both processor types
eval_policy_all(
envs=envs,
policy=policy,
env_preprocessor=env_preprocessor, # Environment-specific
env_postprocessor=env_postprocessor, # Environment-specific
preprocessor=preprocessor, # Policy-specific
postprocessor=postprocessor, # Policy-specific
n_episodes=cfg.eval.n_episodes,
)Ví dụ: Bộ xử lý môi trường LIBERO
LiberoProcessorStep minh họa một bộ xử lý môi trường thực tế:
from lerobot.processor import ObservationProcessorStep
@dataclass
@ProcessorStepRegistry.register(name="libero_processor")
class LiberoProcessorStep(ObservationProcessorStep):
"""
Processes LIBERO observations into the LeRobot format.
**State Processing:**
- Extracts end-effector position (3D)
- Converts quaternion to axis-angle representation (3D)
- Extracts gripper joint positions (2D)
- Concatenates into 8D state vector
**Image Processing:**
- Rotates images 180° to match HuggingFaceVLA/libero convention
"""
def _process_observation(self, observation):
processed_obs = observation.copy()
# Process images: Flip 180° for camera convention
for key in list(processed_obs.keys()):
if key.startswith("observation.images."):
img = processed_obs[key]
img = torch.flip(img, dims=[2, 3]) # Flip H and W
processed_obs[key] = img
# Process robot_state: Flatten to 8D vector
if "observation.robot_state" in processed_obs:
robot_state = processed_obs.pop("observation.robot_state")
eef_pos = robot_state["eef"]["pos"] # (B, 3)
eef_quat = robot_state["eef"]["quat"] # (B, 4)
gripper_qpos = robot_state["gripper"]["qpos"] # (B, 2)
# Convert quaternion to axis-angle
eef_axisangle = self._quat2axisangle(eef_quat) # (B, 3)
# Concatenate into single state vector
state = torch.cat((eef_pos, eef_axisangle, gripper_qpos), dim=-1)
state = state.float()
processed_obs["observation.state"] = state
return processed_obsTại Sao Lại Có Những Biến Đổi Này?
Xoay ảnh: Bộ dữ liệu HuggingFaceVLA/libero có các ảnh bị xoay 180° so với bộ mô phỏng LIBERO gốc. Trình xử lý (processor) xử lý sự khác biệt về quy ước này để các chính sách (policy) được huấn luyện trên bộ dữ liệu hoạt động liền mạch.
Phẳng hóa trạng thái: Môi trường LIBERO thô hiển thị các từ điển lồng nhau chứa tất cả thông tin trạng thái khả dụng (vị trí, quaternion, vận tốc, biểu diễn ma trận, v.v.). Bộ xử lý:
- Chọn các thành phần liên quan (pos, quat, gripper)
- Chuyển đổi quaternion sang dạng trục-góc (phù hợp hơn cho việc học)
- Dập phẳng thành một vector 8 chiều duy nhất mà các chính sách mong đợi
Tính linh hoạt: Môi trường vẫn tiếp tục phơi bày toàn bộ dữ liệu thô. Nếu bạn muốn thử các biểu diễn trạng thái khác nhau (ví dụ: bao gồm vận tốc, sử dụng biểu diễn ma trận thay vì trục-góc), bạn có thể tạo một bộ xử lý mới mà không cần sửa đổi mã của môi trường.
Thêm Bộ Xử Lý Môi Trường Cho Các Môi Trường Mới
Để thêm bộ xử lý môi trường cho một môi trường mới:
1. Tạo bước Processor
# In src/lerobot/processor/env_processor.py
@dataclass
@ProcessorStepRegistry.register(name="myenv_processor")
class MyEnvProcessorStep(ObservationProcessorStep):
"""Process observations from MyEnv."""
def _process_observation(self, observation):
processed = observation.copy()
# Your environment-specific transformations
if "myenv.specific.state" in processed:
state = processed.pop("myenv.specific.state")
# Transform to standard format
processed["observation.state"] = self._transform_state(state)
return processed2. Cập nhật lớp con EnvConfig của bạn
# In src/lerobot/envs/factory.py
def make_env_pre_post_processors(env_cfg: EnvConfig):
if isinstance(env_cfg, LiberoEnv) or "libero" in env_cfg.type:
preprocessor = PolicyProcessorPipeline(steps=[LiberoProcessorStep()])
elif isinstance(env_cfg, MyEnvConfig) or "myenv" in env_cfg.type:
preprocessor = PolicyProcessorPipeline(steps=[MyEnvProcessorStep()])
else:
preprocessor = PolicyProcessorPipeline(steps=[])
postprocessor = PolicyProcessorPipeline(steps=[])
return preprocessor, postprocessor3. Sử dụng trong Đánh giá
Không cần thay đổi gì! Script đánh giá tự động sử dụng bộ xử lý phù hợp:
lerobot-eval \
--policy.path=lerobot/my_policy \
--env.type=myenv \ # Automatically uses MyEnvProcessorStep
--eval.n_episodes=10Tương lai: Bộ xử lý hậu kỳ Môi trường
Hiện tại, các bộ xử lý hậu xử lý (postprocessor) là đồng nhất (không thực hiện thao tác nào) cho tất cả các môi trường. Các trường hợp sử dụng trong tương lai bao gồm:
Biến đổi không gian hành động
@dataclass
class MyEnvActionPostprocessor(ProcessorStep):
"""Convert policy actions to environment-specific format."""
def __call__(self, transition: EnvTransition) -> EnvTransition:
action = transition["action"]
# Example: Convert from Cartesian to joint space
if self.action_space == "joint":
action = self.ik_solver(action)
# Example: Apply environment-specific safety limits
action = torch.clamp(action, self.min_action, self.max_action)
transition["action"] = action
return transitionChuyển đổi Hệ tọa độ
@dataclass
class CoordinateTransformPostprocessor(ProcessorStep):
"""Transform actions between coordinate systems."""
def __call__(self, transition: EnvTransition) -> EnvTransition:
action = transition["action"]
# Example: Policy outputs in world frame, env expects base frame
action = self.world_to_base_transform(action)
transition["action"] = action
return transitionThực hành tốt nhất
Giữ cho bộ xử lý môi trường đơn giản: Chúng chỉ nên xử lý các vấn đề về định dạng dữ liệu cụ thể của môi trường, không phải các phép biến đổi phức tạp liên quan đến học tập.
Sử dụng policy processors cho các yêu cầu của model: Chuẩn hóa, batching, đặt thiết bị và tokenization thuộc về policy processors.
Phơi bày tất cả dữ liệu từ môi trường: Để bộ xử lý quyết định sử dụng gì thay vì mã hóa cứng các lựa chọn trong môi trường.
Quy ước tài liệu: Tài liệu hóa rõ ràng các quy ước hệ tọa độ, hướng camera hoặc định dạng dữ liệu mà bộ xử lý của bạn xử lý.
Kiểm thử độc lập: Bộ xử lý môi trường nên có thể kiểm thử mà không cần tải các chính sách hoặc môi trường đầy đủ.
Tóm tắt
Bộ xử lý môi trường cung cấp sự tách biệt rõ ràng giữa các phép biến đổi dữ liệu cụ thể cho môi trường và các yêu cầu mô hình cụ thể cho chính sách. Kiến trúc này:
- ✅ Cho phép thử nghiệm dễ dàng với các biểu diễn trạng thái khác nhau
- ✅ Cho phép các chính sách hoạt động liền mạch trên các môi trường khác nhau
- ✅ Giữ cho mã môi trường tập trung vào giao diện mô phỏng/phần cứng
- ✅ Giúp các pipeline của processor dễ bảo trì và gỡ lỗi hơn
- ✅ Tuân thủ nguyên tắc trách nhiệm đơn
Insight chính: Môi trường định nghĩa định dạng dữ liệu, bộ xử lý chuẩn hóa chúng, chính sách tiêu thụ dữ liệu đã được chuẩn hóa. Mỗi lớp có một trách nhiệm rõ ràng, tập trung.
Update on GitHub