# Pengenalan Prosesor

Dalam robotika, terdapat ketidakcocokan mendasar antara data yang dihasilkan oleh robot dan manusia dengan apa yang diharapkan oleh model machine learning. Robot mengeluarkan data sensor mentah seperti gambar kamera dan posisi sendi yang perlu dinormalisasi, di-batching, dan ditempatkan pada perangkat sebelum model dapat memprosesnya. Instruksi bahasa dari manusia harus ditokenisasi menjadi representasi numerik, dan robot yang berbeda menggunakan sistem koordinat yang berbeda yang perlu distandardisasi.

Tantangan ini juga meluas ke keluaran model. Model mungkin menghasilkan posisi ujung-efektor sementara robot membutuhkan perintah ruang sendi, atau teleoperator menghasilkan gerakan relatif sementara robot mengharapkan perintah absolut. Prediksi model sering kali dinormalisasi dan perlu dikonversi kembali ke skala dunia nyata.

Terjemahan lintas domain menambahkan lapisan kompleksitas lainnya. Data pelatihan dari satu konfigurasi robot memerlukan adaptasi untuk deployment pada perangkat keras yang berbeda, model yang dilatih dengan konfigurasi kamera tertentu harus berfungsi dengan susunan baru, dan dataset dengan konvensi penamaan yang berbeda perlu diharmonisasi.

**Di sinilah peran processor.** Mereka berfungsi sebagai penerjemah universal yang menjembatani celah-celah tersebut, memastikan aliran data yang mulus dari sensor ke model hingga ke aktuator. Processor menangani semua langkah pra-pemrosesan dan pasca-pemrosesan yang diperlukan untuk mengubah data lingkungan mentah menjadi input yang siap untuk model, dan sebaliknya.

Ini berarti bahwa kebijakan favorit Anda dapat digunakan seperti ini:

```python
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)
```

## Apa itu Prosesor?

Dalam robotika, data hadir dalam berbagai bentuk: gambar dari kamera, posisi sendi dari sensor, instruksi teks dari pengguna, dan lainnya. Setiap jenis data memerlukan transformasi spesifik sebelum model dapat menggunakannya secara efektif. Model membutuhkan data ini untuk:

- **Normalized**: Disesuaikan ke rentang yang sesuai untuk pemrosesan jaringan saraf
- **Batched**: Diorganisasi dengan dimensi yang tepat untuk pemrosesan batch
- **Tokenized**: Teks yang diubah menjadi representasi numerik
- **Device-placed**: Dipindahkan ke perangkat keras yang tepat (CPU/GPU)
- **Diubah tipe**: Dikast ke tipe data yang sesuai

Processor menangani transformasi ini melalui langkah-langkah yang dapat dikomposisikan dan digunakan kembali, yang dapat dirangkai menjadi pipeline. Anggaplah mereka sebagai lini perakitan modular di mana setiap stasiun melakukan transformasi spesifik pada data Anda.

## Konsep Inti

### EnvTransition: Wadah Data Universal

`EnvTransition` adalah struktur data fundamental yang mengalir melalui semua prosesor. Ini adalah kamus bertipe yang merepresentasikan interaksi robot-lingkungan yang lengkap:

- **OBSERVATION**: Semua data sensor (gambar, keadaan, propriosepsi)
- **ACTION**: Aksi yang akan dieksekusi atau yang telah dieksekusi
- **REWARD**: Sinyal pembelajaran penguatan
- **DONE/TRUNCATED**: Indikator batas episode
- **INFO**: Metadata arbitrer
- **COMPLEMENTARY_DATA**: Deskripsi tugas, indeks, bendera padding, data antar-langkah

Batch converters mempertahankan metadata indeks seperti `index`, `episode_index`, dan `frame_index` di `COMPLEMENTARY_DATA`. Dalam perjalanan bolak-balik `batch_to_transition` / `transition_to_batch`, `frame_index` mempertahankan nilai skalar atau tensor aslinya. Jika input tidak menyertakan `frame_index`, converter tidak akan menambahkannya.

### ProcessorStep: Blok Bangun

`ProcessorStep` adalah unit transformasi tunggal yang memproses transisi. Ini adalah kelas dasar abstrak dengan dua metode wajib:

```python
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__` adalah inti dari langkah pemroses Anda. Metode ini menerima `EnvTransition` dan mengembalikan `EnvTransition` yang telah dimodifikasi.

`transform_features` digunakan untuk menyatakan bagaimana langkah ini mengubah bentuk/tipe fitur.

### DataProcessorPipeline: Orkestrator Generik

`DataProcessorPipeline[TInput, TOutput]` merangkai beberapa instans `ProcessorStep` menjadi satu:

```python
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 vs PolicyProcessorPipeline

Perbedaan utamanya terletak pada struktur data yang mereka tangani:

| Aspek          | RobotProcessorPipeline                       | PolicyProcessorPipeline                  |
| --------------- | -------------------------------------------- | ---------------------------------------- |
| **Input**       | `dict[str, Any]` - Nilai robot individual   | `dict[str, Any]` - Tensor yang di-batch       |
| **Output**      | `dict[str, Any]` - Perintah robot individual | `torch.Tensor` - Prediksi kebijakan      |
| **Kasus Penggunaan**    | Kontrol robot real-time                      | Pelatihan/inferensi model                 |
| **Format Data** | Tidak di-batch, heterogen                     | Batched, homogen                     |
| **Contoh**    | `{"joint_1": 0.5}`                           | `{"observation.state": tensor([[0.5]])}` |

**Gunakan `RobotProcessorPipeline`** untuk antarmuka perangkat keras robot:

```python
# 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
}
```

**Gunakan `PolicyProcessorPipeline`** untuk pelatihan model dan pemrosesan batch:

```python
# 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
```

## Fungsi Konverter

LeRobot menyediakan fungsi konverter untuk menjembatani berbagai format data di `lerobot.processor.converters`. Fungsi-fungsi ini menangani terjemahan penting antara struktur data perangkat keras robot, format model kebijakan, dan representasi internal `EnvTransition` yang mengalir melalui pipeline prosesor.

| Kategori                       | Fungsi                      | Deskripsi                     |
| ------------------------------ | ----------------------------- | ------------------------------- |
| **Konverter Perangkat Keras Robot**  | `robot_action_to_transition`  | Robot dict → EnvTransition      |
|                                | `observation_to_transition`   | Robot obs → EnvTransition       |
|                                | `transition_to_robot_action`  | EnvTransition → dict Robot      |
| **Konverter Kebijakan/Pelatihan** | `batch_to_transition`         | Batch dict → EnvTransition      |
|                                | `transition_to_batch`         | EnvTransition → Batch dict      |
|                                | `policy_action_to_transition` | Tensor kebijakan → EnvTransition   |
|                                | `transition_to_policy_action` | EnvTransition → tensor Policy   |
| **Utilitas**                  | `create_transition`           | Membangun transisi dengan nilai default |
|                                | `identity_transition`         | Konverter pass-through          |

Insight utamanya adalah bahwa **konverter perangkat keras robot** bekerja dengan nilai individual dan kamus, sedangkan **konverter kebijakan/pelatihan** bekerja dengan tensor batched dan output model. Fungsi konverter secara otomatis menangani perbedaan struktural, sehingga langkah pemrosesan Anda dapat berfokus pada transformasi inti tanpa perlu khawatir tentang kompatibilitas format data.

## Contoh Processor

Contoh berikut menunjukkan konfigurasi prosesor dunia nyata untuk pelatihan dan inferensi kebijakan.

Berikut adalah contoh prosesor untuk pelatihan dan inferensi kebijakan:

```python
# 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,
)
```

### Interaksi antara robot dan kebijakan dengan prosesor

Skenario dunia nyata yang paling umum menggabungkan kedua jenis pipeline: perangkat keras robot menghasilkan observasi yang memerlukan pemrosesan kebijakan, dan keluaran kebijakan memerlukan pascapemrosesan yang kompatibel dengan robot:

```python
# 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)
```

## Kontrak Fitur: Transformasi Bentuk dan Tipe

Processor tidak hanya mentransformasi data - mereka juga dapat **mengubah struktur data itu sendiri**. Metode `transform_features()` mendeklarasikan perubahan-perubahan ini, yang sangat penting untuk pencatatan dataset dan pembuatan kebijakan.

### Mengapa Kontrak Fitur Penting

Saat membangun dataset atau kebijakan, LeRobot perlu mengetahui:

- **Field data apa yang akan ada** setelah pemrosesan
- **Bentuk dan tipe** yang akan dimiliki setiap field
- **Cara mengonfigurasi model** untuk struktur data yang diharapkan

```python
# 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
```

### Fungsi Spesifikasi Fitur

`create_initial_features()` dan `aggregate_pipeline_dataset_features()` menyelesaikan masalah kritis dalam pembuatan dataset: menentukan struktur data akhir yang tepat sebelum data diproses. Karena pipeline prosesor dapat menambahkan fitur baru (seperti medan kecepatan), mengubah bentuk tensor (seperti memotong gambar), atau mengganti nama kunci, dataset perlu mengetahui spesifikasi output lengkap di awal untuk mengalokasikan penyimpanan yang tepat dan mendefinisikan skema. Fungsi-fungsi ini bekerja sama dengan memulai dari spesifikasi perangkat keras robot (`create_initial_features()`) kemudian mensimulasikan transformasi pipeline secara keseluruhan (`aggregate_pipeline_dataset_features()`) untuk menghitung kamus fitur akhir yang diteruskan ke `LeRobotDataset.create()`, memastikan keselarasan sempurna antara apa yang dioutput oleh prosesor dan apa yang diharapkan dataset untuk disimpan.

```python
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
    ...
)
```

## Langkah-langkah Pemroses Umum

LeRobot menyediakan banyak langkah prosesor yang terdaftar. Berikut adalah prosesor inti yang paling sering digunakan:

### Prosesor Esensial

- **`normalizer_processor`**: Menormalisasi observasi/aksi menggunakan statistik dataset (mean/std atau min/max)
- **`device_processor`**: Pindahkan tensor ke CPU/GPU dengan konversi dtype opsional
- **`to_batch_processor`**: Menambahkan dimensi batch ke transisi untuk kompatibilitas model
- **`rename_observations_processor`**: Mengubah nama kunci observasi menggunakan kamus pemetaan
- **`tokenizer_processor`**: Menokenisasi deskripsi tugas bahasa alami menjadi token dan masker perhatian

### Langkah Selanjutnya

- **[Implementasi Processor Anda Sendiri](./implement_your_own_processor)** - Buat langkah processor kustom
- **[Debug Pipeline Anda](./debug_processor_pipeline)** - Pemecahan masalah dan optimasi pipeline
- **[Prosesor untuk Robot dan Teleoperator](./processors_robots_teleop)** - Pola integrasi dunia nyata

## Ringkasan

Processors menyelesaikan masalah terjemahan data dalam robotika dengan menyediakan:

- **Transformasi modular**: Langkah pemrosesan yang dapat dikomposisikan dan digunakan ulang
- **Keamanan tipe**: Pipeline generik dengan pemeriksaan saat kompilasi
- **Optimasi kinerja**: operasi yang dipercepat GPU
- **Perbedaan Robot/Kebijakan**: Pipeline terpisah untuk struktur data yang berbeda
- **Ekosistem komprehensif**: 30+ prosesor terdaftar untuk tugas-tugas umum

Insight utama: `RobotProcessorPipeline` menangani data perangkat keras robot yang tidak di-batch, sedangkan `PolicyProcessorPipeline` menangani data model yang di-batch. Pilihlah alat yang tepat untuk struktur data Anda!

