Download scripts/crouch_pose_editor.py from kenpeter123/microduck_rl: direct link, hf CLI and curl.
- Browser
- Download file 3.49 kB
-
https://huggingface.co/kenpeter123/microduck_rl/resolve/main/scripts/crouch_pose_editor.py
- Command line
-
hf download hf://kenpeter123/microduck_rl/scripts/crouch_pose_editor.py
-
curl -L -o crouch_pose_editor.py https://huggingface.co/kenpeter123/microduck_rl/resolve/main/scripts/crouch_pose_editor.py
3.49 kB
| """Interactive crouch pose editor (roller robot). | |
| Ouvre le viewer MuJoCo avec le robot rollers debout. Dans le panneau "Control" | |
| du viewer, bouge les sliders (genoux/hanches/chevilles…) pour composer la pose | |
| ACCROUPIE voulue. La gravité est coupée et la base est maintenue droite + | |
| abaissée pour que le point le plus bas reste au sol (tu vois donc le tronc | |
| descendre quand tu plies les genoux). À la fermeture de la fenêtre, la pose est | |
| imprimée en dict CROUCH_POSE {nom_articulation: angle_rad} prêt à coller. | |
| Usage: | |
| uv run python scripts/crouch_pose_editor.py | |
| """ | |
| import re | |
| import time | |
| import mujoco | |
| import mujoco.viewer | |
| from mjlab_microduck.robot.microduck_constants import ( | |
| get_walk_rollers_spec, | |
| HOME_FRAME, | |
| ) | |
| def home_value(joint_name: str): | |
| for pattern, val in HOME_FRAME.joint_pos.items(): | |
| if re.search(pattern, joint_name): | |
| return float(val) | |
| return 0.0 | |
| # Modèle direct depuis le spec du robot (14 actionneurs <position> dans le XML). | |
| model = get_walk_rollers_spec().compile() | |
| data = mujoco.MjData(model) | |
| mujoco.mj_resetData(model, data) | |
| model.opt.gravity[:] = [0, 0, 0] # rien ne s'effondre : seuls les sliders bougent | |
| has_free = model.jnt_type[0] == mujoco.mjtJoint.mjJNT_FREE | |
| # Articulations actionnées (hors roues passives), avec adresse qpos. | |
| joints = [] | |
| for i in range(model.njnt): | |
| name = mujoco.mj_id2name(model, mujoco.mjtObj.mjOBJ_JOINT, i) | |
| if not name or "freejoint" in name or "passive_" in name: | |
| continue | |
| joints.append((name, model.jnt_qposadr[i])) | |
| # ctrl initial = pose HOME (les actionneurs position tiennent cette cible). | |
| for a in range(model.nu): | |
| aname = mujoco.mj_id2name(model, mujoco.mjtObj.mjOBJ_ACTUATOR, a) | |
| data.ctrl[a] = home_value(aname or "") | |
| if has_free: | |
| data.qpos[0:3] = [0.0, 0.0, 0.14] | |
| data.qpos[3:7] = [1.0, 0.0, 0.0, 0.0] | |
| base_xy = data.qpos[0:2].copy() | |
| base_quat = data.qpos[3:7].copy() | |
| robot_geoms = [g for g in range(model.ngeom) | |
| if model.geom_type[g] != mujoco.mjtGeom.mjGEOM_PLANE] | |
| mujoco.mj_forward(model, data) | |
| print("=== Crouch Pose Editor (rollers) ===") | |
| print(f"actionneurs: {model.nu} | base flottante: {has_free}") | |
| print("Ouvre le panneau 'Control' du viewer et bouge les sliders pour composer") | |
| print("la pose ACCROUPIE. Ferme la fenêtre quand c'est bon.\n") | |
| with mujoco.viewer.launch_passive(model, data) as viewer: | |
| while viewer.is_running(): | |
| if has_free: | |
| data.qpos[0:2] = base_xy | |
| data.qpos[3:7] = base_quat | |
| data.qvel[0:6] = 0.0 | |
| mujoco.mj_step(model, data) # actionneurs position -> les joints suivent ctrl | |
| if has_free: | |
| data.qpos[0:2] = base_xy | |
| data.qpos[3:7] = base_quat | |
| data.qvel[0:6] = 0.0 | |
| mujoco.mj_forward(model, data) | |
| try: | |
| zmin = min(float(data.geom_xpos[g, 2] - model.geom_rbound[g]) | |
| for g in robot_geoms) | |
| data.qpos[2] -= zmin | |
| mujoco.mj_forward(model, data) | |
| except Exception: | |
| pass | |
| viewer.sync() | |
| time.sleep(1.0 / 60.0) | |
| print("\n=== Pose accroupie capturée ===\n") | |
| print("CROUCH_POSE = {") | |
| for name, adr in joints: | |
| print(f' "{name}": {float(data.qpos[adr]):.4f},') | |
| print("}") | |
| if has_free: | |
| print(f"\n# hauteur de base finale (info) : z = {float(data.qpos[2]):.4f}") | |
| print("# Colle CROUCH_POSE ici et donne-le a Claude pour cabler la reward.") | |