LeRobot documentation

Représentations d'actions

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

Représentations d'actions

Ce guide explique les différentes manières de représenter les actions robotiques dans LeRobot, comment elles sont liées entre elles et quand utiliser chacune d’elles.

Espace articulaire vs espace de l'effecteur

Avant d’aborder les représentations d’actions, il est utile de comprendre les deux espaces de coordonnées dans lesquels les actions peuvent exister.

Espace conjoint

Les actions en espace articulaire spécifient directement les positions cibles pour chaque moteur. Pour un bras à 6 degrés de liberté avec une pince, une action en espace articulaire pourrait ressembler à :

action = [shoulder_pan: 45.0, shoulder_lift: -20.0, elbow: -30.0, wrist_pitch: 10.0, wrist_roll: 0.0, wrist_yaw: 5.0, gripper: 0.8]

L'espace articulaire est le mode par défaut dans LeRobot. Il est simple, ne nécessite aucun modèle cinématique et se mappe directement sur les commandes des moteurs. La plupart des configurations pour débutants (SO-100, Koch) utilisent des actions dans l'espace articulaire.

Espace de l'effecteur terminal (EE)

Les actions dans l’espace de l’effecteur final spécifient la position et l’orientation souhaitées de l’extrémité de l’outil du robot (pince) en coordonnées cartésiennes :

action = [x: 0.25, y: -0.10, z: 0.15, wx: 0.0, wy: 0.0, wz: 0.1, gripper: 0.8]

L'espace EE est plus intuitif pour des tâches telles que le pick-and-place, car il décrit directement l'endroit où le gripper doit se rendre, mais il nécessite un modèle cinématique (URDF) pour convertir entre les poses EE et les angles articulaires.

Conversion entre espaces

LeRobot fournit des étapes de processeur pour convertir entre les espaces articulaire et effecteur en utilisant la cinématique directe et inverse. Ces étapes s'appuient sur RobotKinematics, qui charge un modèle URDF de votre robot.

from lerobot.model.kinematics import RobotKinematics
from lerobot.robots.so_follower.robot_kinematic_processor import (
    ForwardKinematicsJointsToEE,
    InverseKinematicsEEToJoints,
)

kinematics = RobotKinematics(
    urdf_path="./SO101/so101_new_calib.urdf",
    target_frame_name="gripper_frame_link",
    joint_names=["shoulder", "elbow", "wrist_pitch", "wrist_roll", "wrist_yaw"],
)

# Joints → EE (for observations: "where is my gripper?")
fk_step = ForwardKinematicsJointsToEE(kinematics=kinematics, motor_names=[...])

# EE → Joints (for actions: "move my gripper here")
ik_step = InverseKinematicsEEToJoints(kinematics=kinematics, motor_names=[...])

Consultez examples/so100_to_so100_EE/ pour un exemple complet et fonctionnel d’enregistrement, de lecture et d’évaluation avec des actions dans l’espace EE sur un bras SO-100.

Actions absolues, relatives et delta

Quel que soit l'espace dans lequel vous travaillez, l'espace articulaire ou l'espace de l'effecteur, les valeurs d'action peuvent être exprimées de trois manières différentes. La terminologie suit UMI (Chi et al., 2024).

Actions absolues (par défaut de LeRobot)

Chaque action spécifie directement la position cible.

Exemple (espace conjoint, bloc de 4) :

current_state = [45.0, -30.0, 10.0]

action_chunk = [
    [46.0, -29.0, 11.0],   # go to 46, -29, 11
    [47.5, -27.0, 12.0],   # go to 47.5, -27, 12
    [49.0, -25.0, 13.5],   # go to 49, -25, 13.5
    [50.0, -24.0, 15.0],   # go to 50, -24, 15
]

Chaque valeur est une position cible dans le repère de coordonnées du robot. Simple et direct, mais nécessite un repère de coordonnées global cohérent. C'est le comportement par défaut dans LeRobot.

Actions relatives (utilisées par OpenPI / pi0)

Chaque action du bloc est un décalage par rapport à l'état actuel au moment de la prédiction. Toutes les actions du bloc partagent le même point de référence :

current_state = [45.0, -30.0, 10.0]

relative_chunk = [
    [1.0,  1.0, 1.0],   # +1 from current → target 46, -29, 11
    [2.5,  3.0, 2.0],   # +2.5 from current → target 47.5, -27, 12
    [4.0,  5.0, 3.5],   # +4 from current → target 49, -25, 13.5
    [5.0,  6.0, 5.0],   # +5 from current → target 50, -24, 15
]

La conversion est simple : relative = absolute - current_state. Pour récupérer la valeur absolue : absolute = relative + current_state.

Pourquoi utiliser des actions relatives ? Le modèle apprend à prédire des décalages centrés autour de zéro, ce qui est plus facile à normaliser et conduit à un entraînement plus stable. Comme chaque bloc référence le même état actuel, il n'y a pas d'accumulation d'erreurs entre les blocs.

Actions Delta (différences séquentielles)

Chaque action est un décalage par rapport à l'action précédente (ou par rapport à l'état actuel pour la première étape) :

current_state = [45.0, -30.0, 10.0]

delta_chunk = [
    [1.0,  1.0, 1.0],   # current → 46, -29, 11
    [1.5,  2.0, 1.0],   # previous action → 47.5, -27, 12
    [1.5,  2.0, 1.5],   # previous action → 49, -25, 13.5
    [1.0,  1.0, 1.5],   # previous action → 50, -24, 15
]

Ici, chaque étape est relative à la précédente. Pour récupérer les positions absolues, il faut additionner tous les deltas précédents, ce qui signifie que les erreurs s'accumulent au fil du temps. UMI s'oppose explicitement à cette représentation pour cette raison.

Comparaison visuelle

La figure ci-dessous (basée sur une figure de UMI, Chi et al., 2024) illustre la différence clé. Avec une trajectoire relative, chaque action du bloc pointe vers la même origine (l'état actuel), de sorte qu'une nouvelle étape d'inférence réinitialise proprement la référence. Avec un delta, chaque action dépend de la précédente, ce qui entraîne une accumulation des erreurs. Les actions absolues nécessitent un repère de coordonnées global cohérent.

Relative Trajectory as Action Representation (UMI, Chi et al., 2024)

Utiliser des actions relatives dans LeRobot

LeRobot fournit RelativeActionsProcessorStep pour convertir entre actions absolues et relatives au sein du pipeline de traitement. C’est ainsi que pi0, pi0.5 et pi0_fast prennent en charge les actions relatives.

Remarque : Tous les modèles pi (pi0, pi0.5, pi0fast) appliquent la conversion relative _avant la normalisation (relative → normalize), de sorte que le normalisateur ne voit que des valeurs delta (relatives). Cela signifie que des statistiques d’actions relatives sont requises pour tous ces modèles lors de l’entraînement avec use_relative_actions=true. Dans pi0_fast, RelativeActionsProcessorStep ne modifie que l’action — l’observation d’état reste inchangée — de sorte que NormalizerProcessorStep s’exécute toujours avant le tokenizer d’état et que ce dernier continue de recevoir l’état normalisé comme prévu.

Fonctionnement

Lors de l'entraînement (prétraitement), les actions sont converties d'absolues en relatives avant que le modèle ne les voie :

raw absolute action → RelativeActionsProcessorStep → normalize → model

Lors de l'inférence (post-traitement), les prédictions du modèle sont converties en valeurs absolues avant d'être envoyées au robot :

model output → unnormalize → AbsoluteActionsProcessorStep → robot

Le AbsoluteActionsProcessorStep lit l’état actuel en cache depuis son RelativeActionsProcessorStep associé, les deux doivent donc être connectés (géré automatiquement par la fabrique de politique).

Activation des actions relatives pour la famille pi (pi0, pi0.5, pi0_fast)

Étape 1 : Précalculer les statistiques d'action relatives pour votre jeu de données :

lerobot-edit-dataset \
    --repo_id your_dataset \
    --new_repo_id your_dataset \
    --operation.type recompute_stats \
    --operation.overwrite true \
    --operation.relative_action true \
    --operation.chunk_size 50 \
    --operation.relative_exclude_joints "['gripper']"

Étape 2 : Entraîner avec les actions relatives activées :

lerobot-train \
    --dataset.repo_id=your_dataset \
    --policy.type=pi0 \
    --policy.use_relative_actions=true \
    --policy.relative_exclude_joints='["gripper"]'

Le paramètre relative_exclude_joints spécifie les articulations qui doivent rester dans l'espace absolu. Par exemple, les commandes de pince sont généralement binaires (ouverture/fermeture) et ne bénéficient pas d'un encodage relatif.

Pour les configurations de politique qui définissent action_feature_names, make_policy remplit cette liste à partir des métadonnées names de la fonction d’action du jeu de données. Les noms peuvent être une séquence plate ou un dictionnaire dont les valeurs sont toutes des listes ou des tuples. Par exemple, {"arm": ["shoulder", "elbow"], "hand": ["gripper"]} devient ["shoulder", "elbow", "gripper"], et non les clés de groupe. L’ordre d’insertion des groupes et l’ordre au sein de chaque groupe sont conservés ; déclarez donc les noms dans le même ordre que le vecteur d’action. Les correspondances nom-versus-index héritées conservent leur comportement d’ordre des clés.

Combiner les actions relatives avec RTC

RTC exécute l’inférence de la politique à haute fréquence et envoie les actions au robot dès qu’elles sont prédites, sans attendre la fin d’un bloc complet. Les actions relatives et RTC sont entièrement compatibles : comme chaque bloc en mode relatif référence l’état identique (capturé au début de l’inférence), chaque action prédite dans le bloc reste un décalage valide, même si le robot s’est déjà déplacé. Aucune gestion particulière n’est nécessaire — RelativeActionsProcessorStep met l’état en cache une fois par appel d’inférence et AbsoluteActionsProcessorStep l’applique à chaque action de la sortie diffusée.

Combinaison des actions relatives avec l’espace EE

Les actions relatives fonctionnent à la fois dans l'espace articulaire et l'espace de l'effecteur. Par exemple, si votre jeu de données stocke des actions EE, l'encodage relatif les convertit en décalages par rapport à la pose EE actuelle :

current_ee_state = [x: 0.25, y: -0.10, z: 0.15, gripper: 0.8]

absolute_ee_chunk = [
    [0.26, -0.09, 0.16, 0.8],
    [0.28, -0.07, 0.18, 0.8],
]

relative_ee_chunk = [
    [0.01,  0.01, 0.01, 0.0],   # offset from current EE pose
    [0.03,  0.03, 0.03, 0.0],   # offset from current EE pose
]

Résumé du pipeline de traitement

Voici comment les différents processeurs se composent. Chaque flèche représente une étape de processeur, et elles peuvent être enchaînées dans un RobotProcessorPipeline ou un PolicyProcessorPipeline :

                    ┌─────────────────────────────────────────┐
   Action Space     │   Joint Space  ←──IK──→  EE Space      │
                    │   ForwardKinematicsJointsToEE           │
                    │   InverseKinematicsEEToJoints           │
                    └─────────────────────────────────────────┘

                    ┌─────────────────────────────────────────┐
   Representation   │   Absolute  ←────→  Relative            │
                    │   RelativeActionsProcessorStep (pre)    │
                    │   AbsoluteActionsProcessorStep (post)   │
                    └─────────────────────────────────────────┘

                    ┌─────────────────────────────────────────┐
   Normalization    │   Raw  ←────→  Normalized               │
                    │   NormalizerProcessorStep (pre)         │
                    │   UnnormalizerProcessorStep (post)      │
                    └─────────────────────────────────────────┘

A typical training preprocessor might chain: raw absolute joint actions → relative → normalize. A typical inference postprocessor: unnormalize → absolute → (optionally IK to joints).

Références

Update on GitHub