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
Upstream: https://github.com/michaelgillett/mjlab Upstream-Commit: c19f713c415a699a79d71cd96aa13c3104a05047 Upstream-Branch: main
142 lines
4.8 KiB
Python
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"
|
|
)
|