mjlab/tests/test_g1_constants.py
Upstream Snapshot 32a241c28f
Some checks failed
nightly / Test against latest dependencies (py3.10) (push) Has been cancelled
nightly / Test against latest dependencies (py3.13) (push) Has been cancelled
tests / tests (3.13, locked) (push) Has been cancelled
tests / tests (3.13, unlocked) (push) Has been cancelled
tests / pyright (3.10) (push) Has been cancelled
tests / lint-format (push) Has been cancelled
tests / tests (3.10, locked) (push) Has been cancelled
tests / tests (3.11, locked) (push) Has been cancelled
tests / tests (3.12, locked) (push) Has been cancelled
tests / pyright (3.11) (push) Has been cancelled
tests / pyright (3.12) (push) Has been cancelled
tests / pyright (3.13) (push) Has been cancelled
tests / ty-check (3.10) (push) Has been cancelled
tests / ty-check (3.11) (push) Has been cancelled
tests / ty-check (3.12) (push) Has been cancelled
tests / ty-check (3.13) (push) Has been cancelled
tests / stubs (push) Has been cancelled
tests / smoke-test (push) Has been cancelled
Docker / check_paths (push) Has been cancelled
docs / build (push) Has been cancelled
Docker / build (push) Has been cancelled
Import upstream snapshot c19f713c415a699a79d71cd96aa13c3104a05047
Upstream: https://github.com/michaelgillett/mjlab
Upstream-Commit: c19f713c415a699a79d71cd96aa13c3104a05047
Upstream-Branch: main
2026-08-28 15:42:17 +08:00

142 lines
4.8 KiB
Python

"""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"
)