Import upstream snapshot d424a0c899f6b33cbd3daeb279913134349c0b63
Upstream: https://github.com/pollen-robotics/microduck_rl Upstream-Commit: d424a0c899f6b33cbd3daeb279913134349c0b63 Upstream-Branch: develop
This commit is contained in:
@@ -0,0 +1,110 @@
|
||||
from mjlab_microduck.tasks import mdp as microduck_mdp
|
||||
from mjlab_microduck.tasks.microduck_spin_env_cfg import (
|
||||
make_microduck_spin_env_cfg,
|
||||
MicroduckSpinRlCfg,
|
||||
)
|
||||
|
||||
|
||||
def test_cfg_uses_phase_command_with_runtime_default_period():
|
||||
cfg = make_microduck_spin_env_cfg()
|
||||
cmd = cfg.commands["twist"]
|
||||
assert isinstance(cmd, microduck_mdp.GroundPickPhaseCommandCfg)
|
||||
# 4.0 s = le défaut de --ground-pick-period : rien à passer au runtime
|
||||
assert cmd.period == 4.0
|
||||
# chaque épisode démarre à phase 0 (debout), comme le bouton au déploiement
|
||||
assert cmd.randomize_phase is False
|
||||
|
||||
|
||||
def test_cfg_has_the_spin_rewards():
|
||||
cfg = make_microduck_spin_env_cfg()
|
||||
for name in (
|
||||
"spin_rate_track",
|
||||
"spin_rate_l1",
|
||||
"spin_stay_in_place",
|
||||
"spin_wheel_differential",
|
||||
"spin_grounded",
|
||||
"leg_antisymmetry",
|
||||
):
|
||||
assert name in cfg.rewards, name
|
||||
# objectif principal avec un poids dominant
|
||||
assert cfg.rewards["spin_rate_track"].weight == 6.0
|
||||
# sur-place est un COÛT
|
||||
assert cfg.rewards["spin_stay_in_place"].weight < 0.0
|
||||
|
||||
|
||||
def test_stay_in_place_is_attenuated_during_the_launch_ramp():
|
||||
# Renforcé à -3.0, ce terme s'opposerait à l'injection de moment angulaire s'il
|
||||
# était plein tarif pendant la rampe de lancement : il doit y être atténué.
|
||||
cfg = make_microduck_spin_env_cfg()
|
||||
params = cfg.rewards["spin_stay_in_place"].params
|
||||
assert 0.0 < params["launch_scale"] < 1.0
|
||||
assert params["accel_end"] == microduck_mdp.SPIN_ACCEL_END
|
||||
# cible positive = anti-horaire (le sens est porté par l'enveloppe)
|
||||
assert microduck_mdp.SPIN_RATE_MAX > 0.0
|
||||
|
||||
|
||||
def test_angular_momentum_reward_is_removed():
|
||||
# Régression : angular_momentum_penalty pénalise la NORME 3D du moment
|
||||
# angulaire, elle combattrait directement le spin. Elle doit être absente.
|
||||
cfg = make_microduck_spin_env_cfg()
|
||||
assert "angular_momentum" not in cfg.rewards
|
||||
# body_ang_vel ne pénalise que x/y -> elle reste, elle mate le ballant
|
||||
assert "body_ang_vel" in cfg.rewards
|
||||
|
||||
|
||||
def test_head_yaw_is_free_to_act_as_a_flywheel():
|
||||
cfg = make_microduck_spin_env_cfg()
|
||||
pattern = cfg.rewards["neck_joint_pos_l2"].params["pattern"]
|
||||
assert "head_yaw" not in pattern
|
||||
|
||||
|
||||
def test_entry_velocity_allows_standstill_and_slow_roll():
|
||||
cfg = make_microduck_spin_env_cfg()
|
||||
# jamais via un push en mode reset (régression NaN du crouch)
|
||||
assert "entry_velocity" not in cfg.events
|
||||
lo, hi = cfg.events["reset_base"].params["velocity_range"]["x"]
|
||||
assert lo == 0.0 and hi > 0.0
|
||||
|
||||
|
||||
def test_symmetry_augmentation_is_disabled():
|
||||
# la symétrie G/D transformerait un spin à gauche en spin à droite
|
||||
assert MicroduckSpinRlCfg.algorithm.symmetry_cfg is None
|
||||
|
||||
|
||||
def test_leg_antisymmetry_shaping_decays():
|
||||
cfg = make_microduck_spin_env_cfg()
|
||||
stages = cfg.curriculum["leg_antisym_weight"].params["weight_stages"]
|
||||
weights = [s["weight"] for s in stages]
|
||||
assert weights[0] == cfg.rewards["leg_antisymmetry"].weight
|
||||
assert weights == sorted(weights, reverse=True)
|
||||
assert weights[-1] < weights[0]
|
||||
|
||||
|
||||
def test_actor_observation_keeps_the_61d_slot_layout():
|
||||
# condition pour que l'ONNX charge dans le slot du runtime. L'égalité exacte
|
||||
# des dimensions avec le crouch est vérifiée par test_obs_parity_with_roller_crouch
|
||||
# ci-dessous ; ici on vérifie la structure.
|
||||
cfg = make_microduck_spin_env_cfg()
|
||||
terms = cfg.observations["actor"].terms
|
||||
assert "base_lin_vel" not in terms
|
||||
assert "height_scan" not in terms
|
||||
for padded in ("head_command", "body_command"):
|
||||
assert padded in terms
|
||||
assert terms["head_command"].params["dim"] == 4
|
||||
assert terms["body_command"].params["dim"] == 6
|
||||
|
||||
|
||||
def test_obs_parity_with_roller_crouch():
|
||||
# Parité de layout obligatoire : sinon l'ONNX exporté ne charge pas dans le
|
||||
# slot du runtime. Contrairement au test de structure ci-dessus, celui-ci
|
||||
# compare l'ordre EXACT des termes, groupe par groupe.
|
||||
from mjlab_microduck.tasks.microduck_roller_crouch_env_cfg import (
|
||||
make_microduck_roller_crouch_env_cfg,
|
||||
)
|
||||
|
||||
spin = make_microduck_spin_env_cfg()
|
||||
crouch = make_microduck_roller_crouch_env_cfg()
|
||||
for grp in ("actor", "critic"):
|
||||
assert list(spin.observations[grp].terms.keys()) == list(
|
||||
crouch.observations[grp].terms.keys()
|
||||
), f"layout d'observation divergent sur le groupe {grp}"
|
||||
Reference in New Issue
Block a user