LeRobot documentation
خط أنابيب الترميز
خط أنابيب الترميز
lerobot-annotate يراقب فيديو كل حلقة باستخدام نموذج رؤية ولغة (VLM) ويكتب تعليقات بلغة طبيعية مرة أخرى في مجموعة البيانات الخاصة بك. يملأ العمودين اللغويين من صفحة Language Columns and Recipes — language_persistent وlanguage_events — مباشرةً في data/chunk-*/file-*.parquet.
باختصار: وجّهه إلى مجموعة بيانات LeRobot، وسيضيف مهام فرعية، وخططاً، وذاكرةً، وتعليقات، وكلاماً، وأسئلةً وأجوبةً بصرية يمكن تدريب سياسة عليها.
كيف تتكامل الأجزاء
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)تتواصل ثلاثة وحدات (plan، interjections، vqa) جميعها مع نموذج VLM مشترك واحد. تُحفظ مخرجات كل وحدة على القرص، ثم يتحقق مُتحقق من صحتها، ويعيد كاتب واحد كتابة شرائح مجموعة البيانات في مكانها.
ما ينتجه خط الأنابيب
تصدر كل وحدة أنواعًا قليلة من التعليقات التوضيحية (الأنماط)، وتُوجَّه إلى إحدى عمودَي اللغة:
| النمط / الذرة | عمود | وحدة |
|---|---|---|
subtask (نمط Pi0.7: "كيف وليس ماذا") | language_persistent | plan |
plan (التهيئة الأولية + التحديث عند المقاطعة) | language_persistent | plan |
memory (ضغط بنمط MEM) | language_persistent | plan |
task_aug (إعادة صياغة المهمة) | language_persistent | plan |
interjection | language_events | interjections |
أداة استدعاء الكلام (style=null, say) | language_events | interjections |
vqa (زوج مستخدم / مساعد) | language_events | vqa |
كيف يتم توليد المهام الفرعية
لا يطلب وحدة plan من نموذج الرؤية واللغة المهام الفرعية دفعة واحدة. بدلاً من ذلك، تستخدم تدفقاً من خطوتين: وصف → تقسيم:
- الوصف — يقوم نموذج VLM بسرد ما يراه فعليًا في الكاميرا المختارة (دون تخمين حول المهمة).
- Segment — يُعاد إدخال ذلك الوصف، ويقوم نموذج الرؤية-اللغة بتقسيم الحلقة إلى مهام فرعية ذرّية متتالية.
تتعامل كلا المسارين مع الحلقة على أنها أوراق اتصال مُوقّتة — وهي إطارات مُستخرجة بمعدل frames_per_second (0.5 ثانية افتراضيًا) ومُجمّعة في شبكات JPEG مع حرق وقت كل إطار في زاويته، مما يتيح لنموذج اللغة البصري (VLM) الاستشهاد بأوقات الحدود الدقيقة مباشرةً. هذا النهج أرخص بكثير من حيث رموز الرؤية مقارنةً باستخدام صورة لكل إطار، مما يسمح بإبقاء الاستخراج كثيفًا؛ فالحلقات الأطول من max_frames_per_prompt تُقسَّم إلى نوافذ بنفس الكثافة ثم تُدمج. كما تحمل كلا التوجيهات تعريفًا سببيًا لـ حدود الأحداث (يبدأ حدث جديد عندما يصبح كائن ما ممسوكًا / يُحرَّر / يصل إلى موقع جديد / يتغير حالة غطاء / تتحرك المحتويات) لتوضيح أماكن التقطيع بدقة أكبر.
اختياريًا، تمريرة ثالثة seeded-relabel (--plan.subtask_seeded_relabel)
تعيد زيارة كل مقطع مع أوراق الاتصال (contact sheets) للمقطع السابق/الحالي/التالي
وتصحح التسمية تصحيحًا أدنى، مستخدمةً التسمية الأولى كسابق (prior) — فهي تحافظ على
الحدود ثابتة وتحدّد الصياغة فقط، مقابل تكلفة استدعاء إضافي واحد لكل مهمة فرعية.
تُدمج المقاطع الناتجة بعد ذلك لتكوين تغطية كاملة للحلقة دون فجوات، بحيث يوجد لكل إطار فرعية واحدة نشطة بالضبط. راجع التشغيل على Hugging Face Jobs لمعرفة إعدادات الإنتاج (كاميرا واحدة، أوراق اتصال مختومة بالوقت، توليد فرعية تلقائي النوافذ).
الأدوات
لا يضيف الكاتب عمود tools إلى parquet. بل يوجد كتالوج الأدوات في meta/info.json["tools"] بدلاً من ذلك (انظر الأدوات). بعد كل تشغيل، يتأكد خط المعالجة من وجود مخطط say القياسي في تلك القائمة، مع الحفاظ على أي أدوات أعلنتها مسبقاً.
هل تريد إضافة أداة خاصة بك؟ عدّل meta/info.json["tools"] مباشرةً — فخط المعالجة يحافظ على ما هو موجود بالفعل. وهذا يجعل الأداة مرئية لقالب المحادثة، بحيث يمكن للنموذج أن يتعلم توليد الاستدعاء. أما طبقة وقت التشغيل التي تنفّذ فعليًا استدعاءً مولّدًا (بروتوكول Tool / TOOL_REGISTRY تحت src/lerobot/tools/) فلم تُنفَّذ بعد — ويوثّق مستند Tools تلك الأجزاء على أنها غير منفّذة بعد.
التشغيل على Hugging Face Jobs
يتطلب توثيق مجموعة بيانات حقيقية بطاقة GPU كبيرة بما يكفي لتشغيل نموذج VLM، لذا يمكن لـ lerobot-annotate أن يُوجّه نفسه إلى Hugging Face Jobs — تمامًا كما يفعل lerobot-train. أضف --job.target=<flavor> إلى الأمر الدقيق الذي كنت ستشغّله محليًا، وسيُنفَّذ على ذلك العتاد بدلًا من ذلك:
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=h200يقوم ذلك بإرسال مهمة h200 على وحدة معالجة رسومات واحدة:
- يبدأ من صورة
vllm/vllm-openaiويقوم بتثبيتlerobotفوقها، - يُشغّل خادم vLLM واحدًا لكل وحدة معالجة رسومية ويتحكم فيه عبر واجهة برمجة التطبيقات المتوافقة مع OpenAI،
- تشغّل وحدات
plan/interjections/vqaعبر مجموعة البيانات، - مع
--push_to_hub=true، يُحمّل النتيجة إلى--new_repo_id(أو يعيدها إلى--repo_idفي مكانها إذا تركت ذلك غير مضبوط).
يُبث الأمر سجلات المهمة؛ Ctrl-C يفصل دون إلغائها. اطلع على الأنواع المتاحة وأسعارها باستخدام hf jobs hardware.
يأتي Qwen3.6 مفعّلًا خاصية التفكير، مما يستهلك ميزانية الرموز التي يحتاجها المُعلِّم لإجابته بصيغة JSON — يُعطَّل ذلك عبر
--vlm.chat_template_kwargs='{"enable_thinking": false}'. وبدون--push_to_hub=trueيتم التخلّي عن مجموعة البيانات المُعلَّمة عند خروج العُقدة.
خيارات الوظائف
| علم | افتراضي | ما يفعله |
|---|---|---|
--job.target | local | نكهة HF Jobs التي سيتم التشغيل عليها (مثل h200، h200x4). يتم هنا تشغيل local أو عند الإغفال. |
--job.image | vllm/vllm-openai:latest | صورة وقت التشغيل للوحدة. |
--job.timeout | 2h | الحد الأقصى للوقت الفعلي. ارفعه في حالة استخدام مجموعات بيانات كبيرة. |
--job.detach | false | أرسل وانتهِ بدلاً من بثّ السجلات. |
--job.lerobot_ref | main | مرجع Git الخاص بـ lerobot المثبت على pod — وجّهه إلى فرع لاختبار التغييرات. |
--job.tags | [] | وسوم إضافية على المهمة وعلى أي مجموعة بيانات تُدفع إليها (lerobot يُضاف دائمًا). |
لمجموعة بيانات أكبر، قم بالتوسع إلى h200x4 وارفع --vlm.parallel_servers / --vlm.num_gpus بما يتناسب مع ذلك، ومنح المهمة مساحة أكبر على سبيل المثال باستخدام --job.timeout=8h.
تتطلب التشغيلات عن بُعد --repo_id (حيث يقوم العُقدة بسحب مجموعة البيانات من Hub؛ بينما يحدد --root مجلدًا موجودًا على جهازك فقط). تُدفع مجموعة البيانات الموجودة في ذاكرة التخزين المؤقت المحلية فقط إلى مستودع خاص أولاً.
خيارات رئيسية
هذه هي الأعلام التي ستلجأ إليها في أغلب الأحيان. نفّذ lerobot-annotate --help لمعرفة كل ما عدا ذلك؛ فالقيم الافتراضية مُعدّة خصيصًا لحلقات التلاعب القصيرة.
مجموعة البيانات في / خارج
| علم | افتراضي | ما يفعله |
|---|---|---|
--repo_id | — | مجموعة بيانات Hub للتعليم (تُحمَّل إذا لم يُضبط --root). |
--root | — | قم بتعليق دليل مجموعة بيانات محلية بدلاً من ذلك. |
--new_repo_id | — | ادفع النتيجة إلى مستودع جديد (يُترك المستودع المصدر دون تغيير). |
--push_to_hub | false | الرفع بعد التوسيم (إلى --new_repo_id، وإلا فإلى --repo_id). |
--only_episodes | الكل | قم بتعليق فهارس الحلقات هذه فقط (مفيد للتشغيل التجريبي). |
--seed | 1729 | يُهيّئ مولّدات الأرقام العشوائية (RNGs) التي تختار طوابع زمنية المقاطعات وأنواع أسئلة VQA. |
الوحدات التي تعمل
كل وحدة مفعّلة افتراضيًا ويمكن تبديلها بشكل مستقل (اضبطها على false لتخطيها، مثلًا للتكرار على وحدة واحدة في كل مرة):
| علم | افتراضي | إيقاف |
|---|---|---|
--plan.enabled | true | المهام الفرعية + الخطة + الذاكرة + task_aug |
--interjections.enabled | true | أدوات التعجب + ذرات الكلام |
--vqa.enabled | true | أزواج VQA |
نموذج اللغة المرئي (VLM) ( --vlm.* )
| علم | افتراضي | ما يفعله |
|---|---|---|
--vlm.model_id | Qwen/Qwen3.6-27B | النموذج الذي سيتم تقديمه والاستعلام. |
--vlm.camera_key | أول images.* | الكاميرا التي يُستند إليها كل موجه. |
--vlm.serve_command | auto | أمر vllm serve … الدقيق (اضبط حجم TP وذاكرة GPU و--max-model-len هنا). |
--vlm.parallel_servers | 1 | خوادم مستقلة للتوجيه بالتناوب (خادم واحد لكل وحدة معالجة رسومية). |
--vlm.num_gpus | 0 | عدد وحدات معالجة الرسومات لكل خادم (0 = وحدة واحدة لكل خادم). |
--vlm.client_concurrency | 16 | الطلبات الجارية عبر جميع الخوادم. |
--vlm.max_new_tokens | 512 | حد التوليد لكل استدعاء. |
--vlm.temperature | 0.2 | درجة حرارة العينة. |
--vlm.reasoning_effort | null | تلميح ميزانية التفكير (low/medium/high) المُعاد إلى الخوادم المتوافقة مع OpenAI. |
المهام الفرعية / الخطة / الذاكرة ( --plan.* )
| علم | افتراضي | ما يفعله |
|---|---|---|
--plan.frames_per_second | 2.0 | معدل أخذ العينات من الإطارات لصفحات الاتصال (2.0 = إطار واحد كل 0.5 ثانية). |
--plan.max_frames_per_prompt | 60 | ميزانية الإطارات لكل استدعاء لنموذج VLM. تُطبَّق النوافذ تلقائيًا على الحلقات التي يتجاوز فيها أخذ العينات هذه الميزانية بنفس الكثافة، ثم تُخاط معًا. |
--plan.contact_sheet_columns | 5 | الأعمدة في شبكة ورقة الاتصال (contact_sheet_frames_per_sheet بلاطات، صف زمني رئيسي). |
--plan.plan_max_steps | 8 | الحد الأقصى لعدد المهام الفرعية لكل حلقة. |
--plan.subtask_describe_first | true | نفّذ مسار التأسيس من الوصف إلى التقسيم (أفضل جودة للمهام الفرعية؛ +1 استدعاء/حلقة). |
--plan.subtask_seeded_relabel | false | المرور الثاني: إعادة تسمية كل مهمة فرعية من أوراق الاتصال السابقة/الحالية/التالية الخاصة بها، مع التهيئة باستخدام التسمية الأولى (+1 استدعاء/مهمة فرعية). |
--plan.subtask_relabel_frames | 5 | الإطارات المُستخرجة بشكل متساوٍ لكل ورقة مقطع في مسار إعادة التسمية (تُستخدم فقط عندما يكون subtask_seeded_relabel=true). |
--plan.emit_plan | true | أصدر صفوف plan المرقّمة (false = المهام الفرعية والذاكرة فقط). |
--plan.emit_memory | true | أصدر صفوف memory (false = المهام الفرعية والخطة فقط)؛ وهو متماثل مع emit_plan. |
--plan.n_task_rephrasings | 10 | عدد إعادة الصياغات task_aug التي يجب إصدارها (0 لتعطيلها). |
--plan.derive_task_from_video | if_short | استخدم مهمة مجموعة البيانات كما هي (off)، فقط عندما تكون مفقودة/قصيرة (if_short)، أو أعد اشتقاقها دائمًا من الفيديو (always). |
التدخلات + VQA
| علم | افتراضي | ما يفعله |
|---|---|---|
--interjections.max_interjections_per_episode | 3 | الحد الأقصى لعدد أزواج المقاطعة/الكلام في كل حلقة. |
--vqa.vqa_emission_hz | 1.0 | تكرار إصدار أزواج VQA. |
--vqa.restrict_to_default_camera | false | اعتمد VQA على --vlm.camera_key فقط (وإلا فكل كاميرا). |
--vqa.coordinate_scale | 1000 | شبكة bbox/keypoint الخاصة بـ VLM، مخزنة في نطاق [0, 1] (none = بكسل). |
--executor.episode_parallelism | 16 | الحلقات التي تتم معالجتها بالتوازي داخل كل مرحلة. |
المساهمة في وحدات جديدة
صُمِّم خط الأنابيب ليتم توسيعه، ونرحب بالمساهمات بشدة —
سواء كان ذلك عبر وحدة جديدة تمامًا (مثل تتبع المسارات أو الإمكانات)، أو قالب
موجّه جديد، أو تدفق تثبيت أكثر ذكاءً، أو إصلاحات جودة للوحدات الموجودة plan / interjections / vqa.
توجد كل وحدة تحت src/lerobot/annotations/steerable_pipeline/modules/، وتشارك عميل VLM وذاكرة التخزين المؤقت للإطارات الرئيسية، وتكتب مخرجاتها الخام إلى شجرة التجميع، وتُدمج في المنفّذ كمرحلة مستقلة. لديك فكرة؟ افتح مشكلة أو طلب سحب في المستودع.
كيف تستهلك الوصفات المخرجات
تُصمَّم التعليقات ليتم قراءتها بواسطة الوصفات (انظر أعمدة اللغات والوصفات). عادةً:
- تقرأ فروع المستوى المنخفض / المستوى العالي / تحديث الذاكرة
subtask/plan/memoryمنlanguage_persistent. - تقرأ فرع المقاطعة-الاستجابة أحداث
interjectionبالإضافة إلى ذرّة الكلام المزدوجة (المدمجة في دور مساعد واحد عبرtool_calls_from) وتحديثplanالمطابق عند نفس الطابع الزمني. - تقرأ فرع VQA أزواج
(vqa, user)و(vqa, assistant)منlanguage_events.
لماذا يتم فصل الحالة والأحداث
تتخذ التصميم شكله فكرتان:
- الحالة الدائمة مقابل الأحداث الدقيقة. الصفوف الدائمة (
subtask،plan،memory) تنطبق على الحلقة بأكملها وتُجيب عن سؤال «ما الذي صحيح الآن؟». أما الصفوف الحدثية (interjection،vqa، الكلام) فتظهر فقط في الإطار الوحيد الذي يطابق طابعه الزمني. تُنسخ الطوابع الزمنية مباشرةً من ملف parquet المصدر — ولا تُعاد حسابها أبدًا باستخدام الفاصلة العائمة. - مرور واحد لنموذج الرؤية-اللغة. تتشارك جميع الوحدات الثلاث عميلًا واحدًا لنموذج الرؤية-اللغة (العميل المتوافق مع OpenAI الذي يتواصل مع خادم vLLM الخاص بالمهمة)، لذا تدفع مقابل تحميل نموذج واحد لكل مجموعة بيانات، وليس ثلاثة.
إعادة تشغيل وحدة واحدة
يُخزّن كل وحدة مخرجاتها الخام في <root>/.annotate_staging/episode_{N:06d}/<module>.jsonl. وهذا يجعل تكرار المحفزات رخيص التكلفة: فإعادة تشغيل وحدة واحدة تُعيد كتابة ملف JSONL الخاص بها فقط، ثم يعيد الكاتب تركيب ملف parquet النهائي. عطّل الوحدات التي لا ترغب في تشغيلها باستخدام --plan.enabled=false (وبالمثل --interjections.enabled / --vqa.enabled) لاختبار وحدة واحدة في كل مرة.
ما يفحصه المُتحقّق
قبل تشغيل الكاتب، يتحقق StagingValidator مما يلي:
- تقع كل صفّ حدثٍ تمامًا على طابع زمنيّ لإطارٍ حقيقيّ؛
- لا تبقى أي أزواج من الكلام/التصريحات معزولة؛
- يُحدَّث
planعند كل طابع زمني للتدخل؛ - تقع صفوف
memoryعلى حدود المهام الفرعية (إنها تحذير وليس خطأً)؛ - كل محتوى مساعد VQA
contentهو JSON صالح بأحد الأشكال التالية: bbox / keypoint / count / attribute / spatial؛ - تذهب كل صف إلى العمود الذي تحدده
column_for_style(style).
تؤدي أي أخطاء إلى إيقاف الكاتب. مرّر --skip_validation=true لتجاوز ذلك أثناء التصحيح.
من أين تأتي أفكار كل وحدة
plan— المهام الفرعية. روبوت Hi (Shi 2025) لحجم الذرّة (مثل “التقاط قطعة خس واحدة”، “وضع الوعاء في الصندوق”); وPi0.7 (Physical Intelligence 2025) لتفاصيل “كيف وليس ماذا”.plan— الذاكرة. MEM (Torne 2026): احتفظ بالمعلومات ذات الصلة الحد الأدنى فقط — حافظ على النتائج، وأزل الخصائص المحددة.interjections. تصنيف سيناريوهات Hi Robot: مهمة سلبية، تصحيح مرتبط بالسياق، قيد محدد، تفضيل. الكلام هو ذرّة مخصّصة لاستدعاء الأدوات فقط (tool_calls=[{type:function, function:{name:"say", arguments:{text:...}}}]).vqa. ECoT (Zawalski 2024) للميزات المرسّخة (صناديق الحدود[x_min, y_min, x_max, y_max]ونقاط المفاتيح، المخزّنة ككسور من الصورة ضمن المجال [0, 1]) وسياسات VLA القابلة للتوجيه (Zhao 2025) للترسيخ متعدد المستويات التجريدية. كما يرسّخ Pi0.7 الإجابات عبر مستويات التجريد المختلفة.
عند تحسين وحدة، عدّل قالب موجهاتها في src/lerobot/annotations/steerable_pipeline/prompts/ بدلاً من إعادة كتابتها من الصفر.
كم تبلغ تكلفته تقريباً
في كل حلقة، يُجري خط الأنابيب حوالي plan_max_steps من استدعاءات التخطيط، وmax_interjections_per_episode من استدعاءات المقاطعة، وvqa_emission_hz × episode_seconds من استدعاءات VQA. مع القيم الافتراضية (8 مهام فرعية، 3 مقاطعات، 1 هرتز × 3 أزواج) في حلقة مدتها 30 ثانية، يكون ذلك حوالي 50 استدعاءً لنموذج VLM.
تبقى مساحة التخزين صغيرة: language_persistent لا يتجاوز بضع عشرات من الكيلوبايت لكل حلقة (حيث يقوم parquet بتشفير القاموس للدخول الواحد الذي يتكرر عبر الإطارات)، وlanguage_events فارغ في معظم الإطارات — حيث تتناسب حجمه مع عدد الإطلاقات، وليس مع num_frames × num_emissions.