microduck_rl/scripts/view_slope_terrain.py
Upstream Snapshot 47372443ff Import upstream snapshot d424a0c899f6b33cbd3daeb279913134349c0b63
Upstream: https://github.com/pollen-robotics/microduck_rl
Upstream-Commit: d424a0c899f6b33cbd3daeb279913134349c0b63
Upstream-Branch: develop
2026-08-28 15:41:56 +08:00

102 lines
4.0 KiB
Python

"""Observer la rampe du mode pente (roller_slope) dans le viewer MuJoCo.
Construit UNIQUEMENT le terrain « plat + rampe » (FlatRampTerrainCfg), sur
plusieurs rangées de difficulté croissante (raideur 2° -> 20°), et ouvre le
viewer natif MuJoCo. Aucune politique entraînée n'est nécessaire — c'est fait
pour valider à l'œil la géométrie (jointure plat/rampe, sens de descente).
Usage :
uv run python scripts/view_slope_terrain.py
uv run python scripts/view_slope_terrain.py --rows 6 --ramp-max 8 --runout 4
uv run python scripts/view_slope_terrain.py --build-only # test sans GUI
Dans le viewer : molette pour zoomer, clic-gauche glisser pour orbiter,
clic-droit glisser pour translater. Chaque rangée est une rampe de plus en plus
raide (difficulté 0 -> 1), de longueur tirée au hasard dans [ramp-min, ramp-max],
et se termine par un plat de sortie. « Devant » (+x) doit descendre.
"""
import argparse
import mujoco
import mujoco.viewer
from mjlab.terrains.terrain_generator import TerrainGenerator, TerrainGeneratorCfg
from mjlab_microduck.tasks.slope_terrain import (
FlatRampTerrainCfg,
RAMP_DEG_MIN,
RAMP_DEG_MAX,
)
def build_model(rows, size, flat_length, ramp_range, runout, deg_min, deg_max):
"""Construit le modèle MuJoCo du terrain seul (rows rampes de raideur croissante)."""
cfg = TerrainGeneratorCfg(
seed=0,
size=size,
num_rows=rows,
num_cols=1,
curriculum=True, # difficulté croissante le long des rangées
difficulty_range=(0.0, 1.0),
add_lights=True,
sub_terrains={
"flat_ramp": FlatRampTerrainCfg(
flat_length=flat_length,
ramp_length_range=ramp_range,
runout_length=runout,
deg_min=deg_min,
deg_max=deg_max,
)
},
)
generator = TerrainGenerator(cfg)
spec = mujoco.MjSpec()
generator.compile(spec)
model = spec.compile()
return model
def main():
p = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
p.add_argument("--rows", type=int, default=5, help="Nb de rangées = nb de raideurs affichées (défaut 5)")
p.add_argument("--size", type=float, nargs=2, default=(15.0, 4.0), help="Taille d'une tuile (x y) en m")
p.add_argument("--flat-length", type=float, default=2.0, help="Longueur du plat de départ (m)")
p.add_argument("--ramp-min", type=float, default=3.0, help="Longueur horizontale mini de la rampe (m)")
p.add_argument("--ramp-max", type=float, default=8.0, help="Longueur horizontale maxi de la rampe (m)")
p.add_argument("--runout", type=float, default=4.0, help="Longueur du plat de sortie (m)")
p.add_argument("--deg-min", type=float, default=RAMP_DEG_MIN, help=f"Raideur min en degrés (défaut {RAMP_DEG_MIN})")
p.add_argument("--deg-max", type=float, default=RAMP_DEG_MAX, help=f"Raideur max en degrés (défaut {RAMP_DEG_MAX})")
p.add_argument("--build-only", action="store_true", help="Construit le modèle et quitte (test sans GUI)")
args = p.parse_args()
model = build_model(
rows=args.rows,
size=tuple(args.size),
flat_length=args.flat_length,
ramp_range=(args.ramp_min, args.ramp_max),
runout=args.runout,
deg_min=args.deg_min,
deg_max=args.deg_max,
)
data = mujoco.MjData(model)
mujoco.mj_forward(model, data)
print(
f"Terrain construit : {args.rows} rampes, raideur {args.deg_min}°->{args.deg_max}°, "
f"longueur rampe {args.ramp_min}-{args.ramp_max}m + sortie {args.runout}m, "
f"{model.ngeom} géométries."
)
if args.build_only:
print("--build-only : OK, pas de GUI.")
return
print("Ouverture du viewer MuJoCo (Ctrl+C pour quitter)…")
with mujoco.viewer.launch_passive(model, data, show_left_ui=False, show_right_ui=False) as viewer:
while viewer.is_running():
mujoco.mj_forward(model, data)
viewer.sync()
if __name__ == "__main__":
main()