LeRobot documentation
إضافة سياسة
إضافة سياسة
يقودك هذا الدليل خطوة بخطوة في تنفيذ سياسة مخصصة وجعلها تعمل مع أدوات التدريب والتقييم والنشر في LeRobot. هناك مساران:
- إضافة (خارج الشجرة) — انشر سياستك كحزمة مستقلة
lerobot_policy_*. أسرع، ولا يتطلب طلب دمج، ويسهّل التكرار. مناسب للتجارب، أو للاستخدام الداخلي، أو عندما ترغب في النشر بشكل مستقل. - داخل الشجرة (مساهمة في LeRobot) — أضِف سياستك مباشرةً في
src/lerobot/policies/. يتطلب ذلك طلب دمج، لكنه يجعل سياستك مواطناً من الدرجة الأولى في المكتبة.
عادةً ما تكون مسارات الإضافات هي نقطة البداية الصحيحة — ثم تُرقّى إلى الشجرة الرئيسية بعد استقرار السياسة ووجود قيمة واضحة في توزيعها مع المكتبة.
في كلتا الحالتين، تبقى اللبنات الأساسية هي نفسها: فئة إعداد، وفئة سياسة، ومصنع معالج. يتناول النصف الأول من هذا الدليل تلك الأجزاء المشتركة، بينما يتناول النصف الثاني البنية التحتية الخاصة بكل مسار (المسار أ، المسار ب).
ملاحظة بشأن الأسلوب: يُعدّ مجال تعلّم الروبوتات مجالاً يتطور بنشاط مستمر، وقد يتغير شكل «السياسة» مع كل معمارية جديدة. إن الأعراف الموصوفة هنا موجودة لأنها تتيح لـlerobot-train وlerobot-eval العمل بشكل موحد عبر نماذج مختلفة جداً. عندما لا تتناسب سياسة جديدة فعلياً مع هذه الأعراف، ارفع الأمر (في طلبك الخاص بالدمج، أو في مشكلة) — فالأعراف ليست مقدسة.
تشريح سياسة
تتكوّن كل سياسة من ثلاثة عناصر أساسية. تستخدم الأسماء أدناه my_policy كاسم مؤقت — استبدله باسم سياستك. هذا الاسم حاسم: يجب أن يطابق السلسلة التي تمررها إلى @PreTrainedConfig.register_subclass، وسمة الفئة MyPolicy.name، ودالة المصنع make_<name>_pre_post_processors (تفصيل أكثر عن كل منها أدناه).
فئة التهيئة
ورث من PreTrainedConfig وسجّل نوع سياستك. إليك قالب — عدّل المعاملات والطرق حسب الحاجة لتناسب بنية سياستك ومتطلبات تدريبها.
# configuration_my_policy.py
from dataclasses import dataclass, field
import torch
from lerobot.configs import PreTrainedConfig
from lerobot.optim import AdamWConfig
from lerobot.optim import CosineDecayWithWarmupSchedulerConfig
@PreTrainedConfig.register_subclass("my_policy")
@dataclass
class MyPolicyConfig(PreTrainedConfig):
"""Configuration class for MyPolicy.
Args:
n_obs_steps: Number of observation steps to use as input
horizon: Action prediction horizon
n_action_steps: Number of action steps to execute
hidden_dim: Hidden dimension for the policy network
# Add your policy-specific parameters here
"""
dtype: torch.dtype | None = torch.bfloat16
horizon: int = 50
n_action_steps: int = 50
hidden_dim: int = 256
optimizer_lr: float = 1e-4
optimizer_weight_decay: float = 1e-4
def __post_init__(self):
super().__post_init__()
if self.n_action_steps > self.horizon:
raise ValueError("n_action_steps cannot exceed horizon")
def validate_features(self) -> None:
"""Validate input/output feature compatibility.
Call this explicitly from your policy's __init__ — the base class does not.
"""
if not self.image_features:
raise ValueError("MyPolicy requires at least one image feature.")
if self.action_feature is None:
raise ValueError("MyPolicy requires 'action' in output_features.")
def get_optimizer_preset(self) -> AdamWConfig:
return AdamWConfig(lr=self.optimizer_lr, weight_decay=self.optimizer_weight_decay)
def get_scheduler_preset(self):
"""Return a LRSchedulerConfig from lerobot.optim, or None."""
return None
@property
def observation_delta_indices(self) -> list[int] | None:
"""Relative timestep offsets the dataset loader provides per observation.
Return `None` for single-frame policies. For temporal policies that consume
multiple past or future frames, return a list of offsets, e.g. `[-20, -10, 0, 10]` for
3 past frames at stride 10 and 1 future frame at stride 10.
"""
return None
@property
def action_delta_indices(self) -> list[int]:
"""Relative timestep offsets for the action chunk the dataset loader returns."""
return list(range(self.horizon))
@property
def reward_delta_indices(self) -> None:
return Noneيجب أن يتطابق النص الذي تمرره إلى @register_subclass مع MyPolicy.name (القسم التالي)، وهو ما يقدّمه المستخدمون كـ --policy.type على سطر الأوامر. افتراضياً، استخدم AdamW من lerobot.optim لـ get_optimizer_preset ما لم تكن بحاجة حقيقية إلى خلاف ذلك. يصرّح PreTrainedConfig مسبقاً بـ dtype: torch.dtype | None؛ ومنح سياسةك دقة افتراضية يتطلب إعادة التصريح بها بقيمة torch.dtype (مثلاً dtype: torch.dtype | None = torch.bfloat16).
فئة السياسة
ورث من PreTrainedPolicy وعرّف صفحتين من صفات الفئة — كلاهما يُفحص بواسطة __init_subclass__:
# modeling_my_policy.py
import torch
import torch.nn as nn
from typing import Any
from lerobot.policies import PreTrainedPolicy
from lerobot.utils.constants import ACTION
from .configuration_my_policy import MyPolicyConfig
class MyPolicy(PreTrainedPolicy):
config_class = MyPolicyConfig # must match the string in @register_subclass
name = "my_policy"
def __init__(self, config: MyPolicyConfig, dataset_stats: dict[str, Any] = None):
super().__init__(config, dataset_stats)
config.validate_features() # not called automatically by the base class
self.config = config
self.model = ... # your nn.Module here
def reset(self):
"""Reset per-episode state. Called by lerobot-eval at the start of each episode."""
...
def get_optim_params(self) -> dict:
"""Return parameters to pass to the optimizer (e.g. with per-group lr/wd)."""
return {"params": self.parameters()}
def predict_action_chunk(self, batch: dict[str, torch.Tensor], **kwargs) -> torch.Tensor:
"""Return the full action chunk (B, chunk_size, action_dim) for the current observation."""
...
def select_action(self, batch: dict[str, torch.Tensor], **kwargs) -> torch.Tensor:
"""Return a single action for the current timestep (called every step at inference)."""
...
def forward(self, batch: dict[str, torch.Tensor]) -> tuple[torch.Tensor, dict | None]:
"""Compute the training loss.
Returns `(loss, output_dict)`. `output_dict` may be `None`; everything in it must be
logging-friendly Python natives (no tensors with gradients).
`batch["action_is_pad"]` is a bool mask of shape (B, horizon) that marks
timesteps padded because the episode ended before `horizon` steps; you
can exclude those from your loss.
"""
actions = batch[ACTION]
action_is_pad = batch.get("action_is_pad")
...
return loss, {"some_loss_component": some_loss_component.item()}الطرق التي تستدعيها حلقات التدريب/التقييم:
| الطريقة | يستخدمه | ما يفعله |
|---|---|---|
reset() -> None | lerobot-eval | امسح حالة كل حلقة في بداية كل حلقة. |
select_action(batch, **kwargs) -> Tensor | lerobot-eval | إرجاع الإجراء التالي (B, action_dim). يُستدعى في كل خطوة. |
predict_action_chunk(batch, **kwargs) -> Tensor | السياسة نفسها | أعدّ شريحة إجراء (B, chunk_size, action_dim). حاليًا، الدالة مجردة في الفئة الأساسية — ارفع NotImplementedError إذا لم يقسّم سياستك الإجراءات إلى شرائح. |
forward(batch, reduction="mean") -> tuple[Tensor, dict \| None] | lerobot-train | يعيد (loss, output_dict). تقبل reduction="none" إذا كنت ترغب في دعم وزن لكل عينة. |
get_optim_params() -> dict | المُحسِّن | أعد self.parameters() للسياسات البسيطة؛ وأعد قاموسًا للمعاملات المسماة للسياسات متعددة المُحسِّنات (انظر get_optim_params في modeling_act.py لمثال على معدل تعلّم لكل مجموعة). |
update() -> None (اختياري) | lerobot-train | يُستدعى بعد كل خطوة من خطوات المُحسِّن إذا كان معرّفًا. يُستخدم لـ EMA والشبكات المستهدفة ومخازن إعادة التشغيل (يستخدم TDMPC هذا). |
تكون الدُفعات (Batches) قوامات مسطّحة مفهرسة بثوابت lerobot.utils.constants: OBS_STATE (observation.state.<motor>)، OBS_IMAGES (observation.images.<camera>)، OBS_LANGUAGE، ACTION، وما إلى ذلك. أعد استخدام الثوابت — لا تبتكر بادئات جديدة.
إذا كان حجم نموذجك كبيراً بما يكفي لتبرير التدريب المتعدد المعالجات GPU المجزأ، فحدد أيضاً وحدات التفاف FSDP الخاصة به — وهي فئات الكتل المتكررة التي يعمل عليها التجزئة:
class MyPolicy(PreTrainedPolicy):
...
_fsdp_wrap_modules = ["MyTransformerBlock"]مع هذا الإعلان الواحد، يعمل --parallelism.dp_shard=N مباشرةً لسياساتك (لا يزال بإمكان المستخدمين تجاوز ذلك باستخدام --accelerator.fsdp.wrap_modules). في غياب أي مصدر للتغليف، تفشل عمليات التشغيل المجزأة عند الإقلاع عمداً.
دوال المعالج
يستخدم LeRobot خطوط معالجة PolicyProcessorPipeline لتطبيع المدخلات وإزالة تطبيع المخرجات حول سياستك. للاطلاع على مرجع عملي، راجع processor_act.py أو processor_diffusion.py.
انتبه بدقة هنا: المعالجات هي أكثر نقطة تسبب صعوبة في إعادة إنتاج النتائج. أي عدم تطابق في وضع التطبيع (IDENTITY مقابل MEAN_STD مقابل MIN_MAX مقابل QUANTILES/QUANTILE10) أو في الميزات التي يتم تطبيعها، سيؤدي إلى تدريب وتقييم دون ظهور أخطاء، لكنه يدمّر النتائج بصمت. تأكد من أن الأوضاع تتطابق مع الطريقة التي تم بها تدريب نقطة التفتيش، وأن الإحصاءات المطلوبة موجودة (مثل أن QUANTILES يتطلب q01/q99)، وأن المعالجات قبل وبعد المعالجة تبقى متسقة.
# processor_my_policy.py
from typing import Any
import torch
from lerobot.processor import PolicyAction, PolicyProcessorPipeline
def make_my_policy_pre_post_processors(
config,
dataset_stats: dict[str, dict[str, torch.Tensor]] | None = None,
) -> tuple[
PolicyProcessorPipeline[dict[str, Any], dict[str, Any]],
PolicyProcessorPipeline[PolicyAction, PolicyAction],
]:
preprocessor = ... # build your PolicyProcessorPipeline for inputs
postprocessor = ... # build your PolicyProcessorPipeline for outputs
return preprocessor, postprocessorمهم — تسمية الدالة: يكتشف LeRobot المعالج الخاص بك من خلال اسمه. يجب أن تكون الدالة مُسمّاة باسم make_{policy_name}_pre_post_processors (مطابقًا للسلسلة التي مرّرتها إلى @PreTrainedConfig.register_subclass).
اختياري: تعديل المعالجات المحمّلة من نقطة التفتيش
make_{policy_name}_pre_post_processors لا يعمل إلا عند بناء المعالجات من الصفر. في كل مرة يكون فيها نقطة تفتيش (checkpoint) معنية (سواء في الضبط الدقيق، أو الاستئناف، أو التقييم)، يقوم LeRobot بدلاً من ذلك بتحميل خطوط الأنابيب المُسلسلة المجاورة للأوزان وتطبيق تجاوزات المستدعي.
إذا كان عليك تعديل خطوط الأنابيب المحمّلة، أضف دالة اختيارية ثانية إلى نفس الوحدة، باسم make_{policy_name}_pre_post_processors_from_pretrained. يجد LeRobot هذه الدالة بنفس قاعدة التسمية ويستدعيها بدلاً من التحميل العام. أما السياسات التي لا تحتاج إلى أي شيء خاص فتكتفي بإهمالها.
from lerobot.processor import load_pretrained_policy_processors
from lerobot.utils.constants import (
POLICY_POSTPROCESSOR_DEFAULT_NAME,
POLICY_PREPROCESSOR_DEFAULT_NAME,
)
def make_my_policy_pre_post_processors_from_pretrained(
config,
pretrained_path: str,
*,
revision: str | None = None,
dataset_stats: dict[str, dict[str, torch.Tensor]] | None = None,
dataset_meta: Any | None = None,
preprocessor_overrides: dict[str, Any] | None = None,
postprocessor_overrides: dict[str, Any] | None = None,
preprocessor_config_filename: str = f"{POLICY_PREPROCESSOR_DEFAULT_NAME}.json",
postprocessor_config_filename: str = f"{POLICY_POSTPROCESSOR_DEFAULT_NAME}.json",
) -> tuple[
PolicyProcessorPipeline[dict[str, Any], dict[str, Any]],
PolicyProcessorPipeline[PolicyAction, PolicyAction],
]:
preprocessor, postprocessor = load_pretrained_policy_processors(
pretrained_path,
revision=revision,
preprocessor_overrides=preprocessor_overrides,
postprocessor_overrides=postprocessor_overrides,
preprocessor_config_filename=preprocessor_config_filename,
postprocessor_config_filename=postprocessor_config_filename,
)
... # reconcile the loaded pipelines with the current `config`
return preprocessor, postprocessorللأمثلة العملية، راجعوا processor_evo1.py (يعيد ترميز إحصاءات التطبيع)، وprocessor_groot.py (يتعامل مع نقاط التفتيش الخام من المصدر)، وprocessor_molmoact2.py (يُعيد تسمية مفاتيح التجاوز).
إرجاع تنبؤ إلى جانب الإجراءات (الإطارات المتخيَّلة، المهام الفرعية، الصناديق)
قد تُرجع السياسة ما تتوقعه أو تعتقده إلى جانب الإجراء الذي تتخذه، مثل الإطارات المتخيلة لنموذج العالم، أو مهمة فرعية متوقعة، أو صناديق تحديد، لذا يعرض --display_data ذلك مباشرةً في Rerun أو Foxglove. تتلقى select_action وpredict_action_chunk دفعةً وتُرجع إما متجه إجراء عاريًا (وهو الافتراضي لكل سياسة) أو PolicyOutput (lerobot.lerobot_types): دفعة مفهرسة مثل دفعة الإدخال، حيث يكون الإجراء تحت "action" وPolicyPrediction تحت "prediction". تكون التنبؤات مفهرسة مثل الإجراءات، بقيمة واحدة لكل بيئة، وكل عنصر مفهرس بما يشير إليه:
observation: الملاحظة المستقبلية المتوقعة، حسب مفتاح الملاحظة ("observation.images.top"-> إطارات[B, C, H, W]). يتم عرضها على أنهاprediction.images.top.language: سلسلة نصية واحدة لكل بيئة، حسب أسلوب التوثيق ("subtask"،"plan"،"memory")، وهي أنماط أعمدة اللغة في خط أنابيب التوثيق. يتم عرضها على أنهاprediction.subtask.boxes: تُرسم على صورة الملاحظة التي يحدد مفتاحها، مع إجابة bbox واحدة لكل بيئة في مخطط VQA للتعليقات ({"detections": [{"label", "bbox": [x1, y1, x2, y2]}]}، كسور الصورة من 0 إلى 1).
تقوم الجهات المستدعية بتطبيع نوعَي الإرجاع باستخدام policy_output_to_transition، والذي يحوّل PolicyOutput إلى EnvTransition الذي تستخدمه المعالجات، بحيث لا تتغير سوى السياسات التي تتنبأ بشيء ما. وتعرض العروض البيئة الأولى.
from lerobot.utils.constants import ACTION, PREDICTION
class MyPolicy(PreTrainedPolicy):
def select_action(self, batch, **kwargs) -> Tensor | PolicyOutput:
if not self._action_queue:
chunk, extras = self.model.sample(batch)
self._action_queue.extend(chunk.transpose(0, 1))
if self.config.return_prediction: # a config field of your choosing
# The prediction goes with the step that computed it; the others return a bare action.
prediction = {
"observation": {"observation.images.top": extras.next_frames}, # [B, C, H, W]
"language": {"subtask": extras.subtasks}, # list[str], one per env
"boxes": {
"observation.images.top": [
{"detections": [{"label": "cube", "bbox": xyxy}]} for xyxy in extras.cube_xyxy
]
},
}
return {ACTION: self._action_queue.popleft(), PREDICTION: prediction}
return self._action_queue.popleft()- يحتوي التنبؤ على قيم لهذه الخطوة. أما التنبؤ الذي يمتد عبر مقطع، مثل مقطع متخيَّل، فيُعاد جزءًا جزءًا: حيث تُعيد كل خطوة الجزء المطابق للإجراء الذي تخدمه.
- تبقى كل مدخلة معروضة حتى يتم إرجاعها مرة أخرى. أعد
detectionsفارغة لمسح صناديق الكاميرا. - تكون التنبؤات اختيارية من خلال إعدادات سياستك، ولا ينبغي أن تتسبب في أي تكلفة عند تعطيلها.
- لا تُغيّر التنبؤات الإجراءات أبدًا، ولا يمرّ الإجراء عبر المعالج اللاحق. أما النص الذي يجب أن يوجّه السياسة، مثل المهام الفرعية التالية، فيمرّ عبر
generate_text(فيما يلي).
سياسات قادرة على التعامل مع النصوص
تشارك سياسات اللغة وصفة العرض بين خطوتين من خطوات المعالجة:
RenderTrainingMessagesStepيحوّل تعليقات لغة البيانات إلى رسائل مُصيَّرة.RenderRuntimeMessagesStepيحوّل أوامر المستخدم إلى رسائل مُصيَّرة.
تطبّق المعالجات الخاصة بالسياسة بعد ذلك تنسيق المحادثة والتجزئة.
تخزّن إعدادات السياسة الوصفة المُحلولة recipe: TrainingRecipe | None (من lerobot.datasets.recipe). يمكن لمسار اختياري recipe_path تحميل وصفة بصيغة YAML؛
يجب حلّها قبل إنشاء المعالجات. تمكّن الوصفة المُحلولة تدريب الوصفة، ويجعل None مُصيّر التدريب إجراءً لا يُنجز شيئًا. قم بتسلسل الوصفة المُحلولة في الإعدادات والمعالج حتى لا يعتمد النشر على مسار YAML محلي.
يتطلب التصيير المدعوم بالوصفات إضافة dataset لأن الوصفات موجودة حاليًا تحت lerobot.datasets.
ضع كلا المُصيِّرَين قبل تنسيق المحادثة الأصلي للسياسة وتجزئة الرموز:
input_steps = [
RenderRuntimeMessagesStep(config.recipe),
RenderTrainingMessagesStep(config.recipe),
# observation transforms
# policy-native roles, image placement, chat template, and tokenization
# device placement
]يتم حفظ نفس خط الأنابيب وتحميله للتدريب والاستدلال. يتم تنفيذ العرض وقت التشغيل فقط عند تحديد query_kind صراحةً. يتخطى العرض أثناء التدريب استعلامات وقت التشغيل، وعند تكوين وصفة، يعالج العينات باستخدام التسميات الخام أو أهداف الإجراءات. تمر مدخلات الملاحظة فقط العادية دون تغيير. يُدعم التدريب بالنص المسمّى فقط دون الحاجة إلى متجه إجراء. يتطلب التدريب دون تسميات أهداف إجراءات لتفعيل مسار المهمة الاحتياطي.
تقوم المصنعية المشتركة فقط ببناء المعالجات أو تحميلها. يبني ضبط الدقة الوصفة اللغوية هذه المعالجات من الإعداد النشط وإحصاءات مجموعة البيانات، بحيث لا يمكن للمعالج المحفوظ تجاوز الوصفة الجديدة. يقوم الاستئناف والاستدلال بتحميل خطوط الأنابيب المحفوظة، بما في ذلك وصفاتها وإحصاءاتها. يحتفظ التدريب غير اللغوي بسلوك البناء وتحميل نقاط التفتيش الحالية. تستنتج المصانع الجديدة إعدادات التطبيع والإجراء النسبي بنفسها. تستخدم المصانع الجديدة الجهاز المُعدّ؛ وتطبق معالجة الدفعة المسبقة لمجموعة البيانات القائمة خريطة إعادة التسمية للتدريب.
messages_rendered يحتوي على رسائل دلالية موسّعة وفق الوصفة، وذلك قبل تنسيق المحادثة الأصلي أو تحويل النص إلى رموز. تُفهرس قوائم التدريب الجانبية هذه القائمة نفسها. وتوجد أدوات الرسائل في lerobot.utils.language.
تضيف التوسعة التفاعلية query_kind وquery_text. يدعم العرض وقت التشغيل:
vqa: الحفاظ على سؤال المُستدعي كرسالة مستخدم.next_subtask: اعرض وصفة نقطة التفتيش حتى، ولكن دون تضمين، دور المساعد الذي يشرف على${subtask}.
ثم يعرض هذا الإجراء مُفكّكه (decoder) من خلال واجهة برمجة التطبيقات (API) الحالية للإجراء:
class MyPolicy(PreTrainedPolicy):
def supports_text_generation(self) -> bool:
return True
def generate_text(self, batch) -> str:
token_ids = self.model.generate(**batch)
return self.tokenizer.decode(token_ids, skip_special_tokens=True)generate_text يستهلك مخرجات المعالج الجاهزة للنموذج؛ فلا يجوز له إعادة بناء أو إعادة تقسيم موجه المستدعي إلى رموز. أثناء التدريب، يستخدم مُقسّم الرموز الأصلي للسياسة target_message_indices للإشراف على مقاطع المساعد المحددة فقط. احفظ الوصفة في إعدادات سياسة اللغة حتى تُسلسل مع نقطة الحفظ، وتعيد عملية النشر بناء الموجه المستخدم للتدريب.
المسار أ: إضافة خارجية
أسرع طريقة لإطلاق سياسة: حزمها كتوزيع Python مستقل وتثبيتها إلى جانب LeRobot. لا يلزم تقديم طلب سحب (PR)، فأنت تتحكم في دورة الإصدار، ويمكنك النشر على PyPI ضمن نطاقك الخاص.
هيكل الحزمة
أنشئ حزمةً بادئةً بـ lerobot_policy_ (مهم!) تليها اسم سياستك:
lerobot_policy_my_policy/
├── pyproject.toml
└── src/
└── lerobot_policy_my_policy/
├── __init__.py
├── configuration_my_policy.py
├── modeling_my_policy.py
└── processor_my_policy.pypyproject.toml
[project]
name = "lerobot_policy_my_policy"
version = "0.1.0"
dependencies = [
# your policy-specific dependencies
]
requires-python = ">= 3.12"
[build-system]
build-backend = # your-build-backend
requires = # your-build-systemحزمة __init__.py
اعرض فئاتك في ملف __init__.py الخاص بالحزمة، واحمِ نفسك من غياب lerobot:
# __init__.py
"""Custom policy package for LeRobot."""
try:
import lerobot # noqa: F401
except ImportError:
raise ImportError(
"lerobot is not installed. Please install lerobot to use this policy package."
)
from .configuration_my_policy import MyPolicyConfig
from .modeling_my_policy import MyPolicy
from .processor_my_policy import make_my_policy_pre_post_processors
__all__ = [
"MyPolicyConfig",
"MyPolicy",
"make_my_policy_pre_post_processors",
]التثبيت والاستخدام
cd lerobot_policy_my_policy
pip install -e .
# Or install from PyPI if published
pip install lerobot_policy_my_policyبمجرد التثبيت، يتم دمج سياستك تلقائيًا مع أدوات التدريب والتقييم في LeRobot:
lerobot-train \
--policy.type my_policy \
--dataset.repo_id lerobot/pusht \
--env.type pusht \
--steps 200000 \
--policy.push_to_hub falseالمسار ب: المساهمة داخل الشجرة
عندما تستقر سياسة التعلم لديك ويوجد قيمة واضحة في نشرها مع المكتبة، يمكنك دمجها مباشرة في LeRobot. اقرأ أولاً دليل المساهمة العام وقالب طلب الدمج — فهناك ستجد توقعات الاختبار والجودة التي يجب أن يستوفيها كل طلب دمج (pre-commit run -a، pytest، قاعدة مراجعة المجتمع، إلخ). ما يلي هو الطبقة الخاصة بالسياسة فوق ذلك.
تخطيط داخل الشجرة
src/lerobot/policies/my_policy/
├── __init__.py # re-exports config + modeling + processor factory
├── configuration_my_policy.py # MyPolicyConfig + @register_subclass
├── modeling_my_policy.py # MyPolicy(PreTrainedPolicy)
├── processor_my_policy.py # make_my_policy_pre_post_processors
└── README.md # symlink → ../../../../docs/source/policy_my_policy_README.mdملاحظتان:
- يُعدّ
README.mdالمجاور للمصدر رابطاً رمزياً إلىdocs/source/policy_<name>_README.md— حيث يقع الملف الفعلي تحتdocs/. تتبع جميع السياسات الموجودة (act، smolvla، diffusion، …) هذا النهج؛ انسخ أحد تلك الروابط الرمزية. عادةً ما يكون ملف README الخاص بالسياسة بسيطاً: رابط الورقة البحثية + اقتباس BibTeX. - يوجد الشرح الموجّه للمستخدم — ما يجب تثبيته، وكيفية التدريب، ومعلمات الضبط، وأرقام المعايير المرجعية — بشكل منفصل في
docs/source/<my_policy>.mdxوهو مسجّل في_toctree.ymlتحت “Policies”.
أسماء الملفات محورية: فالصانع يستورد الكائنات بشكل كسول (lazy) بناءً على الاسم، ويُكتشف المعالج وفق اتفاقية make_<policy_name>_pre_post_processors.
التوصيل
هناك مكانان يحتاجان إلى معرفة سياسةك. كلاهما بالاسم.
policies/__init__.py— أعد تصديرMyPolicyConfigوأضِفه إلى__all__. هذا الاستيراد هو ما يسجّل سياستك: يُنفَّذ@PreTrainedConfig.register_subclass("my_policy")، ومنذ ذلك الحين يحلّ المصنع كل شيء بالتوافق. لا تعِد تصدير فئة النمذجة؛ فهي تُحمَّل بشكل كسول عبر المصنع (لذا يبقىimport lerobotسريعًا).templates/lerobot_modelcard_template.mdوREADME.mdالجذري — القالب هو ما يعالجه ناشر نهاية التدريب ليصبح بطاقة النموذج لكل نقطة تحقق مُدرَّبة بسياستك: أضف وصفًا من سطر واحد لسياستك في فروعmodel_name، واربطه فيpolicy_docsحتى تربط البطاقات بدليلك الخاص بصيغة MDX، وأضف اختياريًا صورة معمارية إلىdiagrams. ثم أضف سياستك إلى جدول النماذج فيREADME.mdالجذري، تحت الفئة المناسبة، مع رابط إلى صفحة مستنداتك.
استنسخ سياسةً قائمةً تشبه سياستك من حيث البنية؛ فالفارق بينهما ضئيل.
التبعيات الثقيلة / الاختيارية
تحتاج معظم السياسات إلى عمود فقري ثقيل (transformers، diffusers، أو SDK محدد لـ VLM). حيثما كان ذلك ممكنًا، يُفضَّل تحميله، على سبيل المثال، من transformers أو diffusers بدلاً من إعادة تنفيذ البنية داخل الشجرة.
المعيار هو التحكم على خطوتين: استيراد محمي بـ TYPE_CHECKING في أعلى الوحدة، وفحص وقت تشغيل بـ require_package في منشئ الكائن. modeling_diffusion.py هو المرجع القياسي:
from typing import TYPE_CHECKING
from lerobot.utils.import_utils import _diffusers_available, require_package
if TYPE_CHECKING or _diffusers_available:
from diffusers.schedulers.scheduling_ddim import DDIMScheduler
else:
DDIMScheduler = None # keeps the symbol bindable at import time
class DiffusionPolicy(PreTrainedPolicy):
def __init__(self, config):
require_package("diffusers", extra="diffusion")
super().__init__(config)
...بهذه الطريقة:
import lerobot.policiesيستمر في العمل دون الحاجة إلى تثبيت إضافي (حيث يُربط الرمز ببساطة بـNone).- ترى أدوات فحص الأنواع الرمز الحقيقي.
- إنشاء السياسة دون الإضافات يثير
ImportErrorواضحًا يشير إلىpip install 'lerobot[diffusion]'.
أضِف إضافة مطابقة إلى pyproject.toml ضمن [project.optional-dependencies] وشملها في الإضافة all حتى يستمر pip install 'lerobot[all]' في تثبيت كل شيء.
تجنّب نسخ ملف نمذجة — بل قم ببناء فئة فرعية منه
إذا كان عليك تعديل هيكل أساسي موجود مسبقًا في transformers (شرط مخصص، مدخلات إضافية، أو استبدال وحدة فرعية)، فلا تنسخ ملف modeling_*.py الخاص به. وبدلاً من ذلك، أنشئ فئة فرعية من أصغر وحدة موجودة في المصدر الأصلي، وأعد تعريف ما يتغير فقط. يُعد pi_gemma.py المرجع القياسي لهذا النمط: فهو يحقن شرط AdaRMS في PaliGemma/Gemma في حوالي 370 سطرًا من خلال إنشاء فئة فرعية من GemmaModel/PaliGemmaModel وإعادة تعريف دالة forward لطبقة المُفكِّك، بدلاً من إنشاء فرع من ملف النموذج الذي يتكون من حوالي 2000 سطر. كما أن إجراء تعديلات على نموذج أصلي محمَّل مقبول تمامًا (مثل تقصير الطبقات، توسيع المُرمِّز، التقاط الحالات المخفية — راجع evo1/internvl3_embedder.py، eo1/modeling_eo1.py، groot/groot_n1_7.py كمثال عملي). سيطلب المراجعون هذا النمط عند وصول طلب دمج يتضمن ملف نموذج منسوخًا؛ والاستثناء الوحيد المقبول هو نموذج غير موجود أصلًا في transformers.
المعايير المرجعية ونقطة تحقق منشورة
يكون من الأسهل بكثير مراجعة سياسة جديدة — وأكثر فائدةً بكثير — عندما تُنشر مع نقطة تحقق عاملة ورقم واحد على الأقل يمكنك إعادة إنتاجه.
اختر معيارًا واحدًا على الأقل من المعايير المدمجة. يوفّر LeRobot معايير محاكاة مع صور Docker مخصصة لكل معيار (LIBERO، LIBERO-plus، Meta-World، RoboTwin 2.0، RoboCasa365، RoboCerebra، RoboMME، VLABench وغيرها). اختر المعيار الذي يتوافق مع نمط سياسة النموذج — فسياسات VLAs عادةً ما تستخدم LIBERO أو VLABench؛ بينما تستخدم سياسات BC المعتمدة على الصور فقط LIBERO أو Meta-World. القائمة الكاملة متاحة تحت المعايير في شريط التصفح الجانبي للتوثيق.
ارفع نقطة التفتيش ومعالجات البيانات إلى Hub تحت lerobot/<policy>_<benchmark> (أو مساحة الأسماء الخاصة بك إذا لم يكن لديك صلاحية الكتابة؛ يمكن لمُحافظ على المستودع إنشاء نسخة مرآة). الأسهل هو التدريب باستخدام --policy.repo_id=<namespace>/<repo> و--policy.push_to_hub=true: حيث ينشر lerobot-train النموذج، وكلا المعالجين، وبطاقة النموذج في نهاية التشغيل. لنشر نقطة تفتيش موجودة مسبقاً لاحقاً، ارفع مجلد pretrained_model/ الخاص بها (مثل hf upload)، أو استخدم lerobot-convert-dcp --push_to_hub=... لنقاط التفتيش بصيغة مقسمة.
أبلغ عن النتائج في ملف MDX الخاص بسياساتك، مع الأمر الدقيق lerobot-eval والأجهزة المستخدمة حتى يتمكن أي شخص من إعادة التشغيل:
## Results
Evaluated on LIBERO with `lerobot/<policy>_libero`:
| Suite | Success rate | n_episodes |
| -------------- | -----------: | ---------: |
| libero_spatial | 87.5% | 50 |
| libero_object | 93.0% | 50 |
| libero_goal | 81.5% | 50 |
| libero_10 | 62.0% | 50 |
| **average** | **81.0%** | 200 |
Reproduce: `lerobot-eval --policy.path=lerobot/<policy>_libero --env.type=libero --env.task=libero_spatial --eval.n_episodes=50` (1× A100 40 GB).استخدم n_episodes ≥ 50 لكل مجموعة للحصول على تقديرات مستقرة لمعدلات النجاح.
إذا كانت سياستك مخصصة للروبوت الحقيقي فقط ولا ينطبق أي معيار محاكاة، فاستبدل تقييم المحاكاة بما يلي: مجموعة بيانات تدريب عامة على Hub، الأمر lerobot-train، نقطة التفتيش، ومعدل نجاح على روبوت حقيقي عبر ≥10 حلقات باستخدام lerobot-rollout --policy.path=....
قائمة التحقق من طلب الدمج
تتضمن التوقعات العامة ما هو مذكور في CONTRIBUTING.md وقالب طلب الإرسال. بالإضافة إلى ذلك، سيقوم المراجعون بالبحث عن:
- تغطي
MyPolicyوMyPolicyConfigما سبق ذكره؛ ويقبل__init_subclass__الفئة. - يعيد
policies/__init__.pyتصدير الإعداد (يؤدي هذا إلى تسجيل السياسة؛ يحلّ المصنع نمذجة/المعالج وفق اتفاقية التسمية). -
make_my_policy_pre_post_processorsيتبع اتفاقية التسمية. - توجد التبعيات الاختيارية خلف إضافة
[project.optional-dependencies]وحارسTYPE_CHECKING + require_package. - تم تحديث
tests/policies/؛ تم الالتزام بمنتج التوافق العكسي واختبارات محددة للسياسة. -
src/lerobot/policies/<name>/README.mdمرتبط برابط رمزي إلىdocs/source/policy_<name>_README.md؛ وdocs/source/<name>.mdxالموجه للمستخدمين مكتوب وأُضيف إلى_toctree.yml. -
lerobot-train --policy.type my_policy ...يعمل من البداية إلى النهاية لعدة خطوات على الأقل + يحفظ نقطة تفتيش يمكن تحميلها وتشغيلها بواسطةlerobot-evalأوlerobot-rollout. - يحتوي
templates/lerobot_modelcard_template.mdعلى حقل وصف ورابطpolicy_docsلسياساتك. - تُدرج جدول النماذج في ملف
README.mdالجذري سياسةَك في الفئة المناسبة، مع رابط إلى صفحة مستنداتك. - على الأقل تقييم معياري قابل للتكرار في ملف السياسة MDX مع نقطة تحقق منشورة (معايرة محاكاة، أو مجموعة بيانات روبوت حقيقية + نقطة تحقق).
أسرع طريقة للحصول على طلب دمج نظيف هي نسخ مجلد السياسة الموجودة الأقرب إلى سياستك، ثم إعادة تسميته، واستبدال المحتوى طريقةً بطريقة. لا تنتظر حتى يصبح كل شيء مصقولاً — افتح طلب دمج مسودةً في وقت مبكر وتكرّر العمل معنا؛ فالمراجعون يفضلون بكثير تقديم ملاحظات على فرع غير مكتمل بدلاً من فرع تم دمجه بالكامل.
أمثلة ومساهمات المجتمع
اطّلع على هذه الأمثلة لتطبيقات السياسات:
- سياسة DiTFlow — سياسة تحويل انتشار مع هدف مطابقة التدفق. جرّبها في هذا المثال: مثال DiTFlow
شكرًا لكم على تخصيص الوقت لإدراج سياسة جديدة في LeRobot. كل بنية معمارية تصل إلى main — وكل إضافة تنشرها المجتمع — تجعل المكتبة أكثر فائدة للشخص التالي، وأكثر تمثيلاً للاتجاه الذي يتجه نحوه تعلم الروبوتات. نتطلع إلى رؤية ما ستقدمونه. 🤗