LeRobot documentation

Bộ xử lý Môi trường

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

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ẽ:

  1. Đượ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
  2. 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
  3. 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 state

4. 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 transformations

Sử 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, postprocessor

Tí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_obs

Tại Sao Lại Có Những Biến Đổi Này?

  1. 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.

  2. 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
  3. 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 processed

2. 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, postprocessor

3. 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=10

Tươ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 transition

Chuyể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 transition

Thực hành tốt nhất

  1. 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.

  2. 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.

  3. 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.

  4. 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ý.

  5. 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