Upstream: https://github.com/pollen-robotics/microduck_rl Upstream-Commit: d424a0c899f6b33cbd3daeb279913134349c0b63 Upstream-Branch: develop
8.0 KiB
Design — Roller Crouch-Glide (« s'accroupir en glissant » au bouton)
Date : 2026-07-17 Statut : conception validée, prêt pour le plan d'implémentation
Contexte
Le robot microduck sait patiner (policy roller, tâche Mjlab-Velocity-Flat-MicroDuck-Rollers).
On veut un nouveau geste : sur un appui bouton, il s'accroupit et continue de glisser
sur son élan (comme un patineur en position basse), maintient ~1 s, puis se relève
tout seul et reprend le patinage.
Contrainte forte de l'utilisatrice : ne pas modifier le runtime Rust
(apirrone/microduck_runtime, installé en binaire). Le geste doit donc réutiliser un
mécanisme déjà présent dans le runtime.
Découverte clé : le runtime a déjà un slot « comportement one-shot déclenché au
bouton » : --ground-pick. Il est déclenché par le bouton A (front montant),
joue une policy ONNX pilotée par une phase pendant une durée fixe, puis revient
automatiquement à la policy principale. Surtout, il utilise exactement le même
layout d'observation 61D que la policy roller — les deux sont interchangeables au
runtime. C'est le véhicule idéal, sans une ligne de Rust.
Compromis accepté : le geste est one-shot (durée fixe, pas de « bascule maintenue »). La durée de l'accroupi est fixée par la période du slot.
Approche retenue (approche B)
Créer une nouvelle tâche mjlab entraînée sur le robot rollers, qui joue
descente → glisse accroupi → remontée, piloté par la phase du slot ground-pick.
L'exporter en ONNX et la charger via --ground-pick. Aucune modif Rust.
Fichiers concernés
| Fichier | Action |
|---|---|
src/mjlab_microduck/tasks/microduck_roller_crouch_env_cfg.py |
Nouveau. L'env, hybride roller + ground-pick. |
src/mjlab_microduck/tasks/mdp.py |
Ajout de la reward crouch_glide_height_by_phase. |
src/mjlab_microduck/tasks/__init__.py |
Ajout : enregistrer Mjlab-RollerCrouch-Flat-MicroDuck. |
Réutilisation (ne rien réinventer)
- Physique / robot roller ←
microduck_velocity_rollers_env_cfg.py:MICRODUCK_WALK_ROLLERS_ROBOT_CFG(14 joints actifs + 4 roues passives), capteur de contact sur lesroller_blade, DR friction des roulements (randomize_wheel_friction+ curriculum), obs 14-dim (roues exclues viaSceneEntityCfg("robot", joint_names=(r"^(?!passive_).*",))),action.scale=1.0,kp_fw=200. - Machinerie phase / one-shot ←
microduck_ground_pick_env_cfg.py: commandemicroduck_mdp.GroundPickPhaseCommandréutilisée telle quelle (produit le[cos(2πφ), sin(2πφ), 0]que le runtime enverra dans le slot twist), padding head/body à zéro (zero_command_padding), terminaisonrobot_state_is_nan,reset_action_history. - DR sim2real ← repris du roller env sans changement (IMU misalignment obs-level, encoder bias, masse/inertie, friction BAM, armature, pushes doux ±0.2).
Le cœur : cible de hauteur « en trapèze » pilotée par la phase
Seule vraie nouveauté. Au lieu de descendre la bouche (ground-pick), on pilote la
hauteur du tronc (com_height du trunk_base) selon la phase, avec un palier bas :
hauteur
haute ┐ ┌── debout (rend la main à la policy roller)
│ \ /
basse│ \_______________/ accroupi + glisse (palier 1 s)
└───────────────────────► phase
0 0.375 0.625 1
- φ ∈ [0, 0.375] : descente vers la hauteur accroupie
- φ ∈ [0.375, 0.625] : maintien accroupi (= 1 s sur une période de 4 s) → glisse
- φ ∈ [0.625, 1.0] : remontée vers la pose roller debout
Nouvelle reward crouch_glide_height_by_phase(env, command_name, height_low, height_high, hold_lo=0.375, hold_hi=0.625, std=...) dans mdp.py :
lit la phase depuis la commande, calcule la hauteur-cible (interpolée haut→bas→haut,
plate sur le palier), récompense exp(-((h_mesurée - h_cible)/std)²).
S'inspirer des fonctions com_height_target (mdp.py:694) et des
interpolated/multistage height target déjà présentes.
Valeurs de départ : height_high ≈ 0.11 m (hauteur roller debout, cf. bande
com_height_target roller 0.0935–0.1235), height_low ≈ 0.075 m (accroupi ;
à affiner en play). La phase est reconstruite depuis atan2(sin, cos) de la commande.
Récompenses
| Reward | Rôle | Origine |
|---|---|---|
crouch_glide_height_by_phase |
Cible principale (haut→bas→haut) | nouveau |
wheel_speed (poids réduit ~2–3) |
Garder l'élan, ne pas freiner pendant l'accroupi | roller env (wheel_speed_reward) |
upright (≈2), body_ang_vel (−0.05), angular_momentum (−0.02) |
Équilibre / stabilité | roller env |
return_pose (fin de phase) |
Converger vers la pose roller debout pour rendre la main proprement | adapté de ground_pick_return_pose |
feet_flat (−2) |
Lames à plat → glisse stable | roller env |
action_rate_l2, neck_action_rate_l2, joint_torques_l2, self_collisions |
Lissage / transfert sim2real | les deux envs |
Explicitement PAS inclus : braking (on ne veut pas s'arrêter), mouth_ground_proximity
/ mouth_perpendicular_to_ground (on ne touche pas le sol), skating_air_time /
single_support / glide (pas de stride pendant le trick — on glisse passivement).
Entraînement
MicroduckRollerCrouchRlCfg= copie deMicroduckRollersRlCfg(MLP 512/256/128, ELU, obs_normalization, PPO,experiment_name="roller_crouch").- Enregistrer dans
tasks/__init__.py:register_mjlab_task(task_id="Mjlab-RollerCrouch-Flat-MicroDuck", ...). - Lancer :
uv run train Mjlab-RollerCrouch-Flat-MicroDuck \ --env.scene.num-envs 4096 --agent.max_iterations 8000 - Épisodes démarrés avec une vitesse d'entrée réaliste (le robot arrive en roulant), sinon il n'aura pas d'élan à conserver pendant l'accroupi. À câbler via un event de reset (vitesse initiale non nulle) ou un push au début d'épisode.
Export + déploiement (flags runtime exacts)
Export ONNX (le normaliseur est baké par export.py), puis :
microduck_runtime --variant pre-alpha --new-cmd-obs --roller \
--model output.onnx \
--new-dxl-imu --kp 200 --action-scale 0.8 \
--max-linear-vel 0.6 --max-linear-vel-backward 0.5 --max-angular-vel 0.0 \
--ground-pick roller_crouch.onnx \
--ground-pick-period 5.0 \
--ground-pick-kp-ratio 1.0 \
--ground-pick-action-scale 0.8
Bouton A → crouch-glide, puis retour auto à la policy roller.
Pièges de parité entraînement/déploiement (importants pour le sim2real) :
--ground-pick-kp-ratio 1.0: le défaut est 0.6 (baisse kp à 120 pendant le trick). On entraîne à kp=200 → il faut forcer 1.0 pour que ça corresponde.--ground-pick-action-scaledoit matcher l'action_scaled'entraînement (0.8 ci-dessus).--ground-pick-period 5.0doit matcher la période/longueur de mouvement entraînée (défaut 4.0, on le garde).
Risques et vérification
- One-shot, durée fixe : l'accroupi dure
ground-pick-periodpuis remonte tout seul. Pas de maintien libre — limite acceptée de l'approche B. - Élan pendant le trick : la phase remplace la commande de vitesse → pas de poussée active pendant l'accroupi. Si l'élan d'entrée est trop faible, il ralentit. D'où l'entraînement avec vitesse d'entrée réaliste.
- Vérification :
- En sim (
play) : il descend, garde les roues qui tournent pendant le palier, se relève sans tomber, et la pose finale rejoint proprement la pose roller debout. - Sur le vrai robot : lancer à petite vitesse, appuyer sur A, observer.
- Confirmer que la policy roller reprend la main proprement après le retour.
- En sim (
Questions ouvertes / à confirmer pendant l'implémentation
- Valeur exacte de
height_low(accroupi) — à régler en play. - Meilleure façon d'injecter la vitesse d'entrée à l'épisode (event reset vs push initial).
- Poids relatif
wheel_speedvscrouch_glide_height_by_phase(garder l'élan sans empêcher de s'accroupir).