"""Tests for g1_constants.py.""" import re import mujoco import numpy as np import pytest from mjlab.asset_zoo.robots.unitree_g1 import g1_constants from mjlab.entity import Entity from mjlab.utils.string import resolve_expr @pytest.fixture(scope="module") def g1_entity() -> Entity: return Entity(g1_constants.get_g1_robot_cfg()) @pytest.fixture(scope="module") def g1_model(g1_entity: Entity) -> mujoco.MjModel: return g1_entity.spec.compile() # fmt: off @pytest.mark.parametrize( "actuator_config,stiffness,damping", [ (g1_constants.G1_ACTUATOR_5020, g1_constants.STIFFNESS_5020, g1_constants.DAMPING_5020), (g1_constants.G1_ACTUATOR_7520_14, g1_constants.STIFFNESS_7520_14, g1_constants.DAMPING_7520_14), (g1_constants.G1_ACTUATOR_7520_22, g1_constants.STIFFNESS_7520_22, g1_constants.DAMPING_7520_22), (g1_constants.G1_ACTUATOR_4010, g1_constants.STIFFNESS_4010, g1_constants.DAMPING_4010), (g1_constants.G1_ACTUATOR_WAIST, g1_constants.STIFFNESS_5020 * 2, g1_constants.DAMPING_5020 * 2), (g1_constants.G1_ACTUATOR_ANKLE, g1_constants.STIFFNESS_5020 * 2, g1_constants.DAMPING_5020 * 2), ], ) # fmt: on def test_actuator_parameters(g1_model, actuator_config, stiffness, damping): for i in range(g1_model.nu): actuator = g1_model.actuator(i) actuator_name = actuator.name matches = any( re.match(pattern, actuator_name) for pattern in actuator_config.target_names_expr ) if matches: assert actuator.gainprm[0] == stiffness assert actuator.biasprm[1] == -stiffness assert actuator.biasprm[2] == -damping assert actuator.forcerange[0] == -actuator_config.effort_limit assert actuator.forcerange[1] == actuator_config.effort_limit def test_keyframe_base_position(g1_model) -> None: data = mujoco.MjData(g1_model) mujoco.mj_resetDataKeyframe(g1_model, data, 0) mujoco.mj_forward(g1_model, data) np.testing.assert_array_equal(data.qpos[:3], g1_constants.KNEES_BENT_KEYFRAME.pos) np.testing.assert_array_equal(data.qpos[3:7], g1_constants.KNEES_BENT_KEYFRAME.rot) def test_keyframe_joint_positions(g1_entity, g1_model) -> None: """Test that keyframe joint positions match the configuration.""" key = g1_model.key("init_state") expected_joint_pos = g1_constants.KNEES_BENT_KEYFRAME.joint_pos assert expected_joint_pos is not None expected_values = resolve_expr(expected_joint_pos, g1_entity.joint_names, 0.0) for joint_name, expected_value in zip( g1_entity.joint_names, expected_values, strict=True ): joint = g1_model.joint(joint_name) qpos_idx = joint.qposadr[0] actual_value = key.qpos[qpos_idx] np.testing.assert_allclose( actual_value, expected_value, rtol=1e-5, err_msg=f"Joint {joint_name} position mismatch: " f"expected {expected_value}, got {actual_value}", ) def test_foot_collision_geoms(g1_model) -> None: foot_pattern = r"^(left|right)_foot[1-7]_collision$" for i in range(g1_model.ngeom): geom = g1_model.geom(i) geom_name = geom.name # Foot collision geoms should have condim=3, priority=1, and friction=0.6. if re.match(foot_pattern, geom_name): assert geom.condim == 3 assert geom.priority == 1 assert geom.friction[0] == 0.6 def test_non_foot_collision_geoms(g1_model) -> None: foot_pattern = r"^(left|right)_foot[1-7]_collision$" for i in range(g1_model.ngeom): geom = g1_model.geom(i) geom_name = geom.name if "_collision" not in geom_name: continue # Non-foot collision geoms should have condim=1 if not re.match(foot_pattern, geom_name): assert geom.condim == 1 def test_collision_geom_count(g1_model) -> None: # There should be 7 geoms (capsules) per foot. collision_geoms = [ g1_model.geom(i).name for i in range(g1_model.ngeom) if "_collision" in g1_model.geom(i).name ] assert len(collision_geoms) > 0 foot_geoms = [ name for name in collision_geoms if re.match(r"^(left|right)_foot[1-7]_collision$", name) ] assert len(foot_geoms) == 14 def test_g1_entity_creation(g1_entity) -> None: assert g1_entity.num_actuators == 29 assert g1_entity.num_joints == 29 assert g1_entity.is_actuated assert not g1_entity.is_fixed_base def test_g1_actuators_configured_correctly(g1_model): """Verify that all G1 actuators have correct control and force limiting. All 29 G1 actuators should have ctrllimited=False (allowing setpoints beyond joint limits) and forcelimited=True (limiting forces to effort limits). """ for i in range(g1_model.nu): actuator_name = g1_model.actuator(i).name assert g1_model.actuator_ctrllimited[i] == 0, ( f"Actuator '{actuator_name}' has ctrllimited=True, expected False" ) assert g1_model.actuator_forcelimited[i] == 1, ( f"Actuator '{actuator_name}' has forcelimited=False, expected True" )