From 1d9e98bf4e41fbbd6305d1bbb3f000ed6802b65f Mon Sep 17 00:00:00 2001 From: "-T.K.-" Date: Sun, 26 Jul 2026 17:31:58 -0700 Subject: [PATCH] feat: physics-rate PD actuators, named collision geoms, lite_biped_debug Three related changes to the MJCF generator and one new variant. 1. actuators instead of The joint-level PD belongs inside the physics step. MuJoCo evaluates actuators every mj_step, which mirrors the real RobStride drive closing its onboard loop at ~1 kHz independently of the host rate, and lets implicitfast integrate the damping term implicitly -- the reason a stiff PD stays stable at a coarse timestep, and how mjlab trains. A makes ctrl a raw torque, forcing the host to sample the PD once per controller_manager tick, which made the sim's PD rate an artifact of the CM rate. Measured downstream in the arms sim (worst-joint peak-to-peak after standby settles): 0.00065 rad at 200 Hz with the PD in mj_step, vs 0.0371 rad of 25-37 Hz wrist chatter when that same 200 Hz loop computed the PD itself, and 0.0024 rad at 400 Hz that way. So it is both chatter-free and 3.6x quieter than the previous best arrangement. kp/kv are left at MuJoCo's defaults on purpose: the operative gains are mode-dependent and arrive over ros2_control's stiffness/damping command interfaces, so MujocoSystem overwrites gainprm/biasprm every tick. No ctrlrange/ctrllimited is emitted, and that is load-bearing: an RL policy is allowed to command a position setpoint beyond the mechanical joint limit to hold a saturating torque, so clamping the setpoint would silently cap the achievable torque. forcerange still bounds the output. 2. Name collision geoms _collision_ MuJoCo leaves URDF-imported geoms anonymous, but the training env selects them BY NAME -- mjlab's friction DR matches r"^(left|right)_foot_collision_\d+$" -- and an unnamed geom can never match, so that randomization was silently doing nothing. Only fills blanks; an explicit name in the MJCF still wins. 3. New variant: lite_biped_debug Identical to lite_biped except for a deliberately simplified foot collider: one 70x130x20 mm box per foot, replacing 11 spheres. Verified flush with the sole (gap 0.0 mm with the origin rpy applied) and covering the full 130 mm mesh length. Note the debug foot is much smaller than lite_biped's (130 mm vs 213 mm long), so its support polygon is roughly a third of the area -- a policy will not transfer between the two variants unchanged. Link and joint names match lite_biped exactly (the three new CAD parts merge into the existing left_foot/right_foot links), so ros2_control.json, the MJCF body list and the training-side tracked-body list all still resolve. physics .json is byte-identical, keeping timestep 0.005 and the solver settings matched to mjlab's training config. Regenerated every variant's MJCF. 1531 tests pass. Co-Authored-By: Claude Opus 4.8 (1M context) Claude-Session: https://claude.ai/code/session_012QKJQ9hze1hMWXJpdPcjp3 --- CMakeLists.txt | 1 + robot_assets/workflow/urdf_to_mjcf.py | 9 + robots/lite/mjcf/lite.xml | 2 +- robots/lite_biped/mjcf/lite_biped.xml | 52 +- robots/lite_biped_debug/cad/config.json | 28 ++ .../cad/joint_properties.json | 27 ++ robots/lite_biped_debug/cad/physics.json | 13 + robots/lite_biped_debug/cad/ros2_control.json | 59 +++ .../cad/scad/left_foot_palm_debug.scad | 4 + .../cad/scad/left_hip_roll.scad | 4 + .../cad/scad/left_knee_pitch.scad | 4 + .../cad/scad/right_foot_palm_debug.scad | 4 + .../cad/scad/right_hip_roll.scad | 4 + .../cad/scad/right_knee_pitch.scad | 4 + .../meshes/visual/left_ankle_pitch_visual.stl | Bin 0 -> 25384 bytes .../meshes/visual/left_ankle_yaw_visual.stl | Bin 0 -> 524684 bytes .../meshes/visual/left_foot_visual.stl | Bin 0 -> 1084884 bytes .../meshes/visual/left_hip_pitch_visual.stl | Bin 0 -> 407884 bytes .../meshes/visual/left_hip_roll_visual.stl | Bin 0 -> 557084 bytes .../meshes/visual/left_hip_yaw_visual.stl | Bin 0 -> 455784 bytes .../meshes/visual/left_knee_pitch_visual.stl | Bin 0 -> 398584 bytes .../meshes/visual/pelvis_visual.stl | Bin 0 -> 2018884 bytes .../visual/right_ankle_pitch_visual.stl | Bin 0 -> 25384 bytes .../meshes/visual/right_ankle_yaw_visual.stl | Bin 0 -> 524084 bytes .../meshes/visual/right_foot_visual.stl | Bin 0 -> 1085184 bytes .../meshes/visual/right_hip_pitch_visual.stl | Bin 0 -> 408184 bytes .../meshes/visual/right_hip_roll_visual.stl | Bin 0 -> 557184 bytes .../meshes/visual/right_hip_yaw_visual.stl | Bin 0 -> 455784 bytes .../meshes/visual/right_knee_pitch_visual.stl | Bin 0 -> 398184 bytes .../mjcf/lite_biped_debug.xml | 109 +++++ .../urdf/lite_biped_debug.urdf | 450 +++++++++++++++++ .../xacro/lite_biped_debug.description.xacro | 459 ++++++++++++++++++ .../xacro/lite_biped_debug.ros2_control.xacro | 214 ++++++++ .../xacro/lite_biped_debug.urdf.xacro | 28 ++ robots/lite_pro/mjcf/lite_pro.xml | 2 +- 35 files changed, 1449 insertions(+), 28 deletions(-) create mode 100644 robots/lite_biped_debug/cad/config.json create mode 100644 robots/lite_biped_debug/cad/joint_properties.json create mode 100644 robots/lite_biped_debug/cad/physics.json create mode 100644 robots/lite_biped_debug/cad/ros2_control.json create mode 100644 robots/lite_biped_debug/cad/scad/left_foot_palm_debug.scad create mode 100644 robots/lite_biped_debug/cad/scad/left_hip_roll.scad create mode 100644 robots/lite_biped_debug/cad/scad/left_knee_pitch.scad create mode 100644 robots/lite_biped_debug/cad/scad/right_foot_palm_debug.scad create mode 100644 robots/lite_biped_debug/cad/scad/right_hip_roll.scad create mode 100644 robots/lite_biped_debug/cad/scad/right_knee_pitch.scad create mode 100644 robots/lite_biped_debug/meshes/visual/left_ankle_pitch_visual.stl create mode 100644 robots/lite_biped_debug/meshes/visual/left_ankle_yaw_visual.stl create mode 100644 robots/lite_biped_debug/meshes/visual/left_foot_visual.stl create mode 100644 robots/lite_biped_debug/meshes/visual/left_hip_pitch_visual.stl create mode 100644 robots/lite_biped_debug/meshes/visual/left_hip_roll_visual.stl create mode 100644 robots/lite_biped_debug/meshes/visual/left_hip_yaw_visual.stl create mode 100644 robots/lite_biped_debug/meshes/visual/left_knee_pitch_visual.stl create mode 100644 robots/lite_biped_debug/meshes/visual/pelvis_visual.stl create mode 100644 robots/lite_biped_debug/meshes/visual/right_ankle_pitch_visual.stl create mode 100644 robots/lite_biped_debug/meshes/visual/right_ankle_yaw_visual.stl create mode 100644 robots/lite_biped_debug/meshes/visual/right_foot_visual.stl create mode 100644 robots/lite_biped_debug/meshes/visual/right_hip_pitch_visual.stl create mode 100644 robots/lite_biped_debug/meshes/visual/right_hip_roll_visual.stl create mode 100644 robots/lite_biped_debug/meshes/visual/right_hip_yaw_visual.stl create mode 100644 robots/lite_biped_debug/meshes/visual/right_knee_pitch_visual.stl create mode 100644 robots/lite_biped_debug/mjcf/lite_biped_debug.xml create mode 100644 robots/lite_biped_debug/urdf/lite_biped_debug.urdf create mode 100644 robots/lite_biped_debug/xacro/lite_biped_debug.description.xacro create mode 100644 robots/lite_biped_debug/xacro/lite_biped_debug.ros2_control.xacro create mode 100644 robots/lite_biped_debug/xacro/lite_biped_debug.urdf.xacro diff --git a/CMakeLists.txt b/CMakeLists.txt index 034dd96..0e941cc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,6 +13,7 @@ set(ROBOTS lite lite_bimanual lite_biped + lite_biped_debug lite_dummy lite_pro ) diff --git a/robot_assets/workflow/urdf_to_mjcf.py b/robot_assets/workflow/urdf_to_mjcf.py index a9dc34d..ab02e5a 100644 --- a/robot_assets/workflow/urdf_to_mjcf.py +++ b/robot_assets/workflow/urdf_to_mjcf.py @@ -336,9 +336,18 @@ def is_collision(geom: ET.Element) -> bool: def walk(body: ET.Element, in_foot: bool) -> None: nonlocal n_foot, n_other foot = in_foot or (body.get("name") in foot_bodies) + # Name collision geoms _collision_. MuJoCo leaves URDF-imported + # geoms anonymous, but the training env selects them BY NAME -- mjlab's + # friction DR matches r"^(left|right)_foot_collision_\d+$" -- and an + # unnamed geom can never match, so the randomization would silently do + # nothing. Only fills blanks; an explicit name in the MJCF wins. + collision_index = 0 for geom in body.findall("geom"): if not is_collision(geom): continue + if not geom.get("name"): + geom.set("name", f"{body.get('name')}_collision_{collision_index}") + collision_index += 1 if foot: geom.set("condim", "3") geom.set("friction", foot_friction_str) diff --git a/robots/lite/mjcf/lite.xml b/robots/lite/mjcf/lite.xml index 5a0cfbc..291da4d 100644 --- a/robots/lite/mjcf/lite.xml +++ b/robots/lite/mjcf/lite.xml @@ -462,4 +462,4 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/robots/lite_biped/mjcf/lite_biped.xml b/robots/lite_biped/mjcf/lite_biped.xml index 3900d3d..0d145ba 100644 --- a/robots/lite_biped/mjcf/lite_biped.xml +++ b/robots/lite_biped/mjcf/lite_biped.xml @@ -35,7 +35,7 @@ - + @@ -44,7 +44,7 @@ - + @@ -57,17 +57,17 @@ - - - - - - - - - - - + + + + + + + + + + + @@ -83,7 +83,7 @@ - + @@ -92,7 +92,7 @@ - + @@ -105,17 +105,17 @@ - - - - - - - - - - - + + + + + + + + + + + diff --git a/robots/lite_biped_debug/cad/config.json b/robots/lite_biped_debug/cad/config.json new file mode 100644 index 0000000..ad5b4ab --- /dev/null +++ b/robots/lite_biped_debug/cad/config.json @@ -0,0 +1,28 @@ +{ + "output_format": "urdf", + "output_filename": "lite_biped_debug", + + "document_id": "e9ee61a2e2678af2088d9f31", + "workspace_id": "26c0b8ae0ee1fec90698ed7b", + "assembly_name": "Biped", + + "configuration": "Configuration=Sim Biped", + "robot_name": "lite_biped_debug", + + "ignore": {}, + "include_configuration_suffix": false, + "round_decimals": 12, + + "joint_properties": { + "default": { + "max_effort": 100, + "max_velocity": 100, + "friction": 0.1 + } + }, + + "merge_stls": true, + "simplify_stls": true, + "max_stl_size": 2, + "use_scads": true +} \ No newline at end of file diff --git a/robots/lite_biped_debug/cad/joint_properties.json b/robots/lite_biped_debug/cad/joint_properties.json new file mode 100644 index 0000000..c463beb --- /dev/null +++ b/robots/lite_biped_debug/cad/joint_properties.json @@ -0,0 +1,27 @@ +{ + "(left|right)_hip_(pitch|roll)": { + "friction_loss": 0.1, + "armature": 0.008, + "effort_limit": 36.0 + }, + "(left|right)_hip_yaw": { + "friction_loss": 0.1, + "armature": 0.005, + "effort_limit": 17.0 + }, + "(left|right)_knee_pitch": { + "friction_loss": 0.1, + "armature": 0.008, + "effort_limit": 36.0 + }, + "(left|right)_ankle_(yaw|pitch)": { + "friction_loss": 0.1, + "armature": 0.002, + "effort_limit": 14.0 + }, + "(left|right)_ankle_roll": { + "friction_loss": 0.1, + "armature": 0.001, + "effort_limit": 5.5 + } +} diff --git a/robots/lite_biped_debug/cad/physics.json b/robots/lite_biped_debug/cad/physics.json new file mode 100644 index 0000000..9b21246 --- /dev/null +++ b/robots/lite_biped_debug/cad/physics.json @@ -0,0 +1,13 @@ +{ + "_comment": "MuJoCo