mjlab/tests/test_velocity_task.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

200 lines
6.9 KiB
Python

"""Tests specific to velocity tasks."""
import pytest
from mjlab.asset_zoo.robots import G1_ACTION_SCALE, GO1_ACTION_SCALE
from mjlab.envs.mdp.actions import JointPositionActionCfg
from mjlab.tasks.registry import list_tasks, load_env_cfg
from mjlab.tasks.velocity.mdp import UniformVelocityCommandCfg
@pytest.fixture(scope="module")
def velocity_task_ids() -> list[str]:
"""Get all velocity task IDs."""
return [t for t in list_tasks() if "Velocity" in t]
@pytest.fixture(scope="module")
def g1_velocity_task_ids(velocity_task_ids: list[str]) -> list[str]:
"""Get all G1 velocity task IDs."""
return [t for t in velocity_task_ids if "G1" in t]
@pytest.fixture(scope="module")
def go1_velocity_task_ids(velocity_task_ids: list[str]) -> list[str]:
"""Get all Go1 velocity task IDs."""
return [t for t in velocity_task_ids if "Go1" in t]
@pytest.fixture(scope="module")
def rough_velocity_task_ids(velocity_task_ids: list[str]) -> list[str]:
"""Get all rough terrain velocity task IDs."""
return [t for t in velocity_task_ids if "Rough" in t]
@pytest.fixture(scope="module")
def flat_velocity_task_ids(velocity_task_ids: list[str]) -> list[str]:
"""Get all flat terrain velocity task IDs."""
return [t for t in velocity_task_ids if "Flat" in t]
def test_velocity_tasks_have_twist_command(velocity_task_ids: list[str]) -> None:
"""All velocity tasks should have a velocity command."""
for task_id in velocity_task_ids:
cfg = load_env_cfg(task_id)
assert "twist" in cfg.commands, f"Task {task_id} missing 'twist' command"
twist_cmd = cfg.commands["twist"]
assert isinstance(twist_cmd, UniformVelocityCommandCfg), (
f"Task {task_id} twist command is not UniformVelocityCommandCfg"
)
def test_g1_velocity_has_required_sensors(g1_velocity_task_ids: list[str]) -> None:
"""G1 velocity tasks should have feet/ground and self collision sensors."""
for task_id in g1_velocity_task_ids:
cfg = load_env_cfg(task_id)
assert cfg.scene.sensors is not None, f"Task {task_id} has no sensors"
sensor_names = {s.name for s in cfg.scene.sensors}
assert "feet_ground_contact" in sensor_names, (
f"Task {task_id} missing feet_ground_contact sensor"
)
assert "self_collision" in sensor_names, (
f"Task {task_id} missing self_collision sensor"
)
def test_go1_velocity_has_required_sensors(go1_velocity_task_ids: list[str]) -> None:
"""Go1 velocity tasks should have feet/ground and collision sensors."""
for task_id in go1_velocity_task_ids:
cfg = load_env_cfg(task_id)
assert cfg.scene.sensors is not None, f"Task {task_id} has no sensors"
sensor_names = {s.name for s in cfg.scene.sensors}
assert "feet_ground_contact" in sensor_names, (
f"Task {task_id} missing feet_ground_contact sensor"
)
if "Rough" in task_id:
for name in (
"self_collision",
"thigh_ground_touch",
"shank_ground_touch",
"trunk_ground_touch",
):
assert name in sensor_names, f"Task {task_id} missing {name} sensor"
def test_flat_velocity_tasks_have_plane_terrain(
flat_velocity_task_ids: list[str],
) -> None:
"""Flat velocity tasks should have terrain_type='plane' and no terrain_generator."""
for task_id in flat_velocity_task_ids:
cfg = load_env_cfg(task_id)
assert cfg.scene.terrain is not None, f"Task {task_id} has no terrain config"
assert cfg.scene.terrain.terrain_type == "plane", (
f"Task {task_id} terrain_type={cfg.scene.terrain.terrain_type}, expected 'plane'"
)
assert cfg.scene.terrain.terrain_generator is None, (
f"Task {task_id} has terrain_generator, expected None for flat terrain"
)
def test_rough_velocity_tasks_have_generator_terrain(
rough_velocity_task_ids: list[str],
) -> None:
"""Rough velocity tasks should have generator terrain."""
for task_id in rough_velocity_task_ids:
cfg = load_env_cfg(task_id)
assert cfg.scene.terrain is not None, f"Task {task_id} has no terrain config"
assert cfg.scene.terrain.terrain_type == "generator", (
f"Task {task_id} terrain_type={cfg.scene.terrain.terrain_type}, "
"expected 'generator'"
)
assert cfg.scene.terrain.terrain_generator is not None, (
f"Task {task_id} has no terrain_generator, expected one for rough terrain"
)
def test_rough_velocity_training_has_curriculum_enabled() -> None:
"""Rough velocity training tasks should have terrain curriculum enabled."""
rough_training_tasks = [
"Mjlab-Velocity-Rough-Unitree-G1",
"Mjlab-Velocity-Rough-Unitree-Go1",
]
for task_id in rough_training_tasks:
cfg = load_env_cfg(task_id)
assert cfg.scene.terrain is not None, f"Task {task_id} has no terrain config"
assert cfg.scene.terrain.terrain_generator is not None, (
f"Task {task_id} has no terrain_generator"
)
assert cfg.scene.terrain.terrain_generator.curriculum is True, (
f"Task {task_id} curriculum={cfg.scene.terrain.terrain_generator.curriculum}, "
"expected True"
)
def test_rough_velocity_play_has_curriculum_disabled() -> None:
"""Rough velocity play tasks should have terrain curriculum disabled."""
rough_training_tasks = [
"Mjlab-Velocity-Rough-Unitree-G1",
"Mjlab-Velocity-Rough-Unitree-Go1",
]
for task_id in rough_training_tasks:
cfg = load_env_cfg(task_id, play=True)
assert cfg.scene.terrain is not None, (
f"Task {task_id} (play mode) has no terrain config"
)
assert cfg.scene.terrain.terrain_generator is not None, (
f"Task {task_id} (play mode) has no terrain_generator"
)
assert cfg.scene.terrain.terrain_generator.curriculum is False, (
f"Task {task_id} (play mode) curriculum={cfg.scene.terrain.terrain_generator.curriculum}, "
"expected False"
)
def test_g1_velocity_has_correct_action_scale(g1_velocity_task_ids: list[str]) -> None:
"""G1 velocity tasks should use G1_ACTION_SCALE."""
for task_id in g1_velocity_task_ids:
cfg = load_env_cfg(task_id)
assert "joint_pos" in cfg.actions, f"Task {task_id} missing 'joint_pos' action"
joint_pos_action = cfg.actions["joint_pos"]
assert isinstance(joint_pos_action, JointPositionActionCfg), (
f"Task {task_id} joint_pos action is not JointPositionActionCfg"
)
assert joint_pos_action.scale == G1_ACTION_SCALE, (
f"Task {task_id} action scale mismatch, expected G1_ACTION_SCALE"
)
def test_go1_velocity_has_correct_action_scale(
go1_velocity_task_ids: list[str],
) -> None:
"""Go1 velocity tasks should use GO1_ACTION_SCALE."""
for task_id in go1_velocity_task_ids:
cfg = load_env_cfg(task_id)
assert "joint_pos" in cfg.actions, f"Task {task_id} missing 'joint_pos' action"
joint_pos_action = cfg.actions["joint_pos"]
assert isinstance(joint_pos_action, JointPositionActionCfg), (
f"Task {task_id} joint_pos action is not JointPositionActionCfg"
)
assert joint_pos_action.scale == GO1_ACTION_SCALE, (
f"Task {task_id} action scale mismatch, expected GO1_ACTION_SCALE"
)