LeRobot documentation
Pipeline Anotasi
Pipeline Anotasi
lerobot-annotate memantau video setiap episode dengan model bahasa-visi (VLM) dan menulis anotasi bahasa alami kembali ke dataset Anda. Alat ini mengisi dua kolom bahasa dari halaman Language Columns and Recipes — language_persistent dan language_events — langsung ke data/chunk-*/file-*.parquet.
Singkatnya: arahkan ke dataset LeRobot, dan ia menambahkan subtask, rencana, memori, interupsi, ucapan, serta visual Q&A yang dapat digunakan untuk melatih kebijakan.
Bagaimana semuanya saling terhubung
your dataset lerobot-annotate
(LeRobotDataset v3.0)
│
▼
┌─────────────────────────────────────────────────────┐
│ read episodes │
└──────────────────────────┬──────────────────────────┘
│
┌────────────────────┼────────────────────┐
▼ ▼ ▼
┌──────────┐ ┌───────────────┐ ┌──────────┐ one shared Qwen-VL
│ plan │ │ interjections │ │ vqa │ ◀── server (vLLM, OpenAI
└────┬─────┘ └───────┬───────┘ └────┬─────┘ API) drives all three
└────────────────────┼─────────────────────┘
│ each module stages raw JSONL
▼ into .annotate_staging/
┌─────────────────┐
│ validator │ ◀── checks everything
└────────┬────────┘
▼
┌─────────────────┐
│ writer │
└────────┬────────┘
▼
data/chunk-*/file-*.parquet
(+ meta/info.json tools)Tiga modul (plan, interjections, vqa) semuanya berkomunikasi dengan satu VLM yang berbagi. Setiap modul menyimpan outputnya ke disk, validator memeriksanya, dan satu writer tunggal menulis ulang shard dataset secara in-place.
Apa yang dihasilkan oleh pipeline
Setiap modul mengeluarkan beberapa jenis anotasi (“gaya”), yang diarahkan ke salah satu dari dua kolom bahasa:
| Gaya / atom | Kolom | Modul |
|---|---|---|
subtask (gaya Pi0.7 “bagaimana, bukan apa”) | language_persistent | plan |
plan (awal + penyegaran saat interupsi) | language_persistent | plan |
memory (kompresi gaya MEM) | language_persistent | plan |
task_aug (parafrasa dari tugas) | language_persistent | plan |
interjection | language_events | interjections |
atom tool-call ucapan (style=null, say) | language_events | interjections |
vqa (pasangan user / assistant) | language_events | vqa |
Cara subtask dibuat
Modul plan tidak meminta subtask dari VLM dalam satu langkah. Sebaliknya, modul ini menggunakan alur describe → segment dua langkah:
- Describe — VLM hanya mendeskripsikan apa yang benar-benar terlihat di kamera yang dipilih (tidak menebak-nebak tentang tugasnya).
- Segment — deskripsi tersebut dimasukkan kembali, dan VLM membagi episode menjadi subtask atomik yang berurutan.
Kedua pass melihat episode sebagai lembar kontak bertanda waktu — frame yang disampel pada frames_per_second (0,5s secara default) dan dikemas ke dalam grid JPEG dengan waktu setiap frame dicetak di sudutnya, sehingga VLM dapat mengutip waktu batas yang tepat secara langsung. Hal ini jauh lebih hemat dalam token visual dibandingkan satu gambar per frame, sehingga sampling dapat tetap padat; episode yang lebih panjang dari max_frames_per_prompt dipecah menjadi jendela pada kepadatan yang sama dan digabungkan. Kedua prompt juga membawa definisi batas peristiwa yang kausal (peristiwa baru dimulai ketika suatu objek dipegang / dilepaskan / mencapai lokasi baru / penutup berubah status / isi bergerak) untuk memperjelas di mana pemotongan dilakukan.
Secara opsional, jalur ketiga seeded-relabel (--plan.subtask_seeded_relabel)
mengunjungi ulang setiap span dengan contact sheet segmen sebelumnya/saat ini/berikutnya dan
melakukan koreksi minimal pada label, menggunakan label pertama sebagai prior — batas tetap
dipertahankan dan hanya peristilahan yang diperjelas, dengan biaya satu panggilan tambahan per
subtask.
Span yang dihasilkan kemudian dirangkai menjadi cakupan episode penuh tanpa celah, sehingga setiap frame memiliki tepat satu subtask aktif. Lihat Running on Hugging Face Jobs untuk pengaturan produksi (kamera tunggal, contact sheet bertanda waktu, generasi subtask dengan jendela otomatis).
Alat
Penulis tidak menambahkan kolom tools ke parquet. Katalog alat berada di meta/info.json["tools"] (lihat Tools). Setelah setiap eksekusi, pipeline memastikan skema say kanonik ada dalam daftar tersebut, sambil mempertahankan alat apa pun yang Anda deklarasikan sebelumnya.
Ingin menambahkan alat Anda sendiri? Edit meta/info.json["tools"] secara langsung — pipeline akan mempertahankan apa pun yang sudah ada. Hal ini membuat alat tersebut terlihat oleh chat template, sehingga model dapat belajar untuk menghasilkan panggilan. Lapisan runtime yang benar-benar mengeksekusi panggilan yang dihasilkan (protokol Tool / TOOL_REGISTRY di bawah src/lerobot/tools/) belum diimplementasikan — dokumen Tools menandai bagian-bagian tersebut sebagai belum diimplementasikan.
Menjalankan di Hugging Face Jobs
Anotasi dataset sungguhan membutuhkan GPU yang cukup besar untuk menjalankan VLM, sehingga lerobot-annotate dapat mengirim dirinya ke Hugging Face Jobs — sama seperti lerobot-train. Tambahkan --job.target=<flavor> ke perintah persis yang akan Anda jalankan secara lokal, dan perintah tersebut akan berjalan di perangkat keras tersebut:
hf auth login # once
uv run lerobot-annotate \
--repo_id=user/my_dataset \
--new_repo_id=user/my_dataset_annotated \
--push_to_hub=true \
--vlm.model_id=Qwen/Qwen3.6-27B \
--vlm.num_gpus=1 \
--vlm.serve_command="vllm serve Qwen/Qwen3.6-27B --tensor-parallel-size 1 \
--max-model-len 32768 --gpu-memory-utilization 0.8 \
--uvicorn-log-level warning --port {port}" \
--vlm.serve_ready_timeout_s=1800 \
--vlm.chat_template_kwargs='{"enable_thinking": false}' \
--job.target=h200Itu mengirimkan tugas h200 GPU tunggal yang:
- dimulai dari image
vllm/vllm-openaidan menginstallerobotdi atasnya, - menjalankan satu server vLLM per GPU dan mengendalikannya melalui API yang kompatibel dengan OpenAI,
- menjalankan modul
plan/interjections/vqadi seluruh dataset, - dengan
--push_to_hub=true, mengunggah hasil ke--new_repo_id(atau kembali ke--repo_iddi tempatnya jika Anda tidak mengatur itu).
Perintah ini mengalirkan log pekerjaan; Ctrl-C memisahkan koneksi tanpa membatalkannya. Daftarkan flavor yang tersedia beserta harganya dengan hf jobs hardware.
Qwen3.6 dirilis dengan fitur berpikir yang diaktifkan, yang menghabiskan anggaran token yang dibutuhkan anotator untuk jawaban JSON-nya —
--vlm.chat_template_kwargs='{"enable_thinking": false}'menonaktifkannya. Tanpa--push_to_hub=true, dataset yang telah dianotasi akan dibuang saat pod keluar.
Opsi pekerjaan
| Bendera | Default | Fungsinya |
|---|---|---|
--job.target | local | Flavor HF Jobs untuk dijalankan (misalnya h200, h200x4). Dikurangi/local berjalan di sini. |
--job.image | vllm/vllm-openai:latest | Gambar runtime untuk pod. |
--job.timeout | 2h | Batas waktu nyata. Naikkan untuk dataset besar. |
--job.detach | false | Kirim dan keluar alih-alih streaming log. |
--job.lerobot_ref | main | Git ref dari lerobot yang diinstal pada pod — arahkan ke cabang untuk menguji perubahan. |
--job.tags | [] | Tag tambahan pada job dan pada dataset apa pun yang didorongnya (lerobot selalu ditambahkan). |
Untuk dataset yang lebih besar, skala ke h200x4 dan naikkan --vlm.parallel_servers / --vlm.num_gpus agar sesuai, serta berikan ruang lebih untuk pekerjaan tersebut, misalnya dengan --job.timeout=8h.
Eksekusi jarak jauh memerlukan --repo_id (pod menarik dataset dari Hub; --root hanya menamai direktori yang ada di mesin Anda). Dataset yang hanya ada di cache lokal Anda akan didorong terlebih dahulu ke repo private.
Opsi utama
Ini adalah flag yang paling sering Anda gunakan. Jalankan lerobot-annotate --help untuk opsi lainnya; nilai defaultnya disesuaikan untuk episode manipulasi yang singkat.
Dataset masuk / keluar
| Bendera | Default | Fungsinya |
|---|---|---|
--repo_id | — | Dataset Hub untuk anotasi (diunduh jika --root tidak disetel). |
--root | — | Anotasi direktori dataset lokal sebagai gantinya. |
--new_repo_id | — | Dorong hasil ke repo baru (membiarkan repo sumber tetap utuh). |
--push_to_hub | false | Unggah setelah anotasi (ke --new_repo_id, jika tidak kembali ke --repo_id). |
--only_episodes | semua | Anotasi hanya indeks episode ini (berguna untuk uji coba). |
--seed | 1729 | Menetapkan benih untuk RNG yang memilih timestamp interupsi + jenis pertanyaan VQA. |
Modul mana yang dijalankan
Setiap modul aktif secara default dan dapat diaktifkan/dinonaktifkan secara independen (atur ke false untuk melewatinya, misalnya untuk mengiterasi satu modul pada satu waktu):
| Bendera | Default | Matikan |
|---|---|---|
--plan.enabled | true | subtasks + rencana + memori + task_aug |
--interjections.enabled | true | interjeksi + atom ucapan |
--vqa.enabled | true | pasangan VQA |
VLM ( --vlm.* )
| Bendera | Default | Fungsinya |
|---|---|---|
--vlm.model_id | Qwen/Qwen3.6-27B | Model yang akan disajikan dan prompt. |
--vlm.camera_key | pertama images.* | Kamera mana yang menjadi dasar setiap prompt. |
--vlm.serve_command | auto | Perintah vllm serve … yang tepat (atur ukuran TP, memori GPU, --max-model-len di sini). |
--vlm.parallel_servers | 1 | Server independen untuk perutean round-robin (satu per GPU). |
--vlm.num_gpus | 0 | GPU per server (0 = satu per server). |
--vlm.client_concurrency | 16 | Permintaan in-flight di semua server. |
--vlm.max_new_tokens | 512 | Batas generasi per panggilan. |
--vlm.temperature | 0.2 | Suhu sampling. |
--vlm.reasoning_effort | null | Petunjuk anggaran berpikir (low/medium/high) diteruskan ke server yang kompatibel dengan OpenAI. |
Subtugas / rencana / memori ( --plan.* )
| Bendera | Default | Fungsinya |
|---|---|---|
--plan.frames_per_second | 2.0 | Laju pengambilan sampel frame untuk contact sheets (2.0 = satu frame setiap 0,5 detik). |
--plan.max_frames_per_prompt | 60 | Anggaran frame per panggilan VLM. Episode yang sampling-nya melebihi ini akan otomatis di-window pada kepadatan yang sama, kemudian di-stitch. |
--plan.contact_sheet_columns | 5 | Kolom per grid contact-sheet (contact_sheet_frames_per_sheet ubin, baris waktu utama). |
--plan.plan_max_steps | 8 | Batas atas subtask per episode. |
--plan.subtask_describe_first | true | Jalankan jalur grounding describe→segment (kualitas subtask terbaik; +1 panggilan/episode). |
--plan.subtask_seeded_relabel | false | Lewatan kedua: beri label ulang setiap subtask dari lembar kontak prev/current/next-nya, diinisiasi dengan label pertama (+1 panggilan/subtask). |
--plan.subtask_relabel_frames | 5 | Frame yang disampel secara seragam per lembar segmen pada jalur relabel (hanya digunakan ketika subtask_seeded_relabel=true). |
--plan.emit_plan | true | Keluarkan baris plan bernomor (false = hanya subtask + memori). |
--plan.emit_memory | true | Keluarkan baris memory (false = hanya subtask + rencana); simetris dengan emit_plan. |
--plan.n_task_rephrasings | 10 | Berapa banyak rephrasing task_aug yang akan dikeluarkan (0 untuk menonaktifkan). |
--plan.derive_task_from_video | if_short | Gunakan tugas dataset apa adanya (off), hanya saat hilang/pendek (if_short), atau selalu turunkan ulang dari video (always). |
Interjeksi + VQA
| Bendera | Default | Fungsinya |
|---|---|---|
--interjections.max_interjections_per_episode | 3 | Batas pasangan interupsi/ucapan per episode. |
--vqa.vqa_emission_hz | 1.0 | Seberapa sering pasangan VQA dipancarkan. |
--vqa.restrict_to_default_camera | false | Ground VQA hanya pada --vlm.camera_key (jika tidak, semua kamera). |
--vqa.coordinate_scale | 1000 | Grid bbox/keypoint VLM, disimpan sebagai [0, 1] (none = px). |
--executor.episode_parallelism | 16 | Episodes yang diproses secara bersamaan dalam setiap fase. |
Kontribusi modul baru
Pipeline ini dirancang untuk berkembang, dan kontribusi sangat diterima —
modul baru (misalnya, jejak lintasan atau affordances), template prompt baru, alur grounding yang lebih cerdas, atau perbaikan kualitas pada modul plan / interjections / vqa yang sudah ada.
Setiap modul berada di bawah src/lerobot/annotations/steerable_pipeline/modules/, berbagi klien VLM dan cache keyframe, menulis output mentahnya ke pohon staging, dan terhubung ke eksekutor sebagai fase tersendiri. Punya ide? Buka issue atau PR di repo.
Bagaimana resep mengonsumsi output
Anotasi dimaksudkan untuk dibaca oleh resep (lihat Kolom Bahasa dan Resep). Biasanya:
- cabang low-level / high-level / memory-update membaca
subtask/plan/memorydarilanguage_persistent. - cabang interupsi-respons membaca peristiwa
interjectionbeserta atom ucapan yang berpasangan (digabungkan menjadi satu giliran asisten melaluitool_calls_from) dan pembaruanplanyang sesuai pada timestamp yang sama. - cabang VQA membaca pasangan
(vqa, user)dan(vqa, assistant)darilanguage_events.
Mengapa state dan events dipisahkan
Dua gagasan membentuk desainnya:
- State persisten vs. event yang tepat. Baris persisten (
subtask,plan,memory) berlaku untuk seluruh episode dan menjawab “apa yang benar saat ini?”. Baris event (interjection,vqa, ucapan) hanya muncul pada satu frame yang timestamp-nya cocok. Timestamp disalin langsung dari sumber parquet — tidak pernah dihitung ulang dalam floating point. - Satu kali lintasan VLM. Ketiga modul berbagi satu klien VLM (klien yang kompatibel dengan OpenAI yang berkomunikasi dengan server vLLM dari job tersebut), sehingga Anda hanya membayar untuk satu pemuatan model per dataset, bukan tiga.
Menjalankan ulang satu modul
Setiap modul menaruh output mentahnya ke <root>/.annotate_staging/episode_{N:06d}/<module>.jsonl. Hal ini membuat iterasi prompt menjadi murah: menjalankan ulang satu modul hanya menimpa JSONL miliknya sendiri, lalu writer menyusun ulang parquet final. Nonaktifkan modul yang tidak Anda inginkan dengan --plan.enabled=false (dan juga --interjections.enabled / --vqa.enabled) untuk menguji satu per satu.
Yang diperiksa oleh validator
Sebelum writer dijalankan, StagingValidator memastikan:
- setiap baris event mendarat tepat pada timestamp frame yang sebenarnya;
- tidak ada pasangan ucapan / interjeksi yang tersisa tanpa pasangan;
plandiperbarui pada setiap timestamp interupsi;- baris
memoryjatuh pada batas subtask (peringatan, bukan kesalahan); - setiap
contentasisten VQA adalah JSON valid dalam salah satu bentuk bbox / keypoint / count / attribute / spatial; - setiap baris masuk ke kolom yang dipilih oleh
column_for_style(style).
Setiap kesalahan akan menghentikan writer. Lewati --skip_validation=true untuk menimpa perilaku ini saat debugging.
Dari mana ide setiap modul berasal
plan— subtask. Hi Robot (Shi 2025) untuk granularitas atom (“ambil satu helai selada”, “letakkan mangkuk ke kotak”); Pi0.7 (Physical Intelligence 2025) untuk detail “bagaimana, bukan apa”.plan— memori. MEM (Torne 2026): pertahankan hanya informasi relevan yang minimal — simpan hasil, buang atribut spesifik.interjections. Taksonomi skenario Hi Robot: tugas negatif, koreksi situasional, kendala spesifik, preferensi. Bicara adalah atom yang hanya berupa pemanggilan alat (tool_calls=[{type:function, function:{name:"say", arguments:{text:...}}}]).vqa. ECoT (Zawalski 2024) untuk fitur yang berakar pada data (bounding box[x_min, y_min, x_max, y_max]dan keypoints, disimpan sebagai fraksi gambar [0, 1]) dan Steerable VLA Policies (Zhao 2025) untuk grounding multi-abstraksi. Pi0.7 juga mengaitkan jawaban di berbagai tingkat abstraksi.
Saat meningkatkan modul, sesuaikan template prompt-nya di src/lerobot/annotations/steerable_pipeline/prompts/ alih-alih menulis ulang dari awal.
Kira-kira berapa biayanya
Per episode, pipeline melakukan sekitar plan_max_steps panggilan plan, max_interjections_per_episode panggilan interjection, dan vqa_emission_hz × episode_seconds panggilan VQA. Dengan nilai default (8 subtask, 3 interjection, 1 Hz × 3 pasangan) pada episode 30 detik, itu sekitar ~50 panggilan VLM.
Penyimpanan tetap kecil: language_persistent maksimal puluhan KB per episode (parquet melakukan dictionary-encoding pada satu entri yang berulang di seluruh frame), dan language_events kosong di sebagian besar frame — ukurannya berskala dengan jumlah emisi, bukan num_frames × num_emissions.