Migrating from Isaac Lab#

Warning

This guide is a work in progress. As more users migrate, we will update this page with additional patterns and edge cases. If something is not covered, please open an issue on GitHub or start a discussion:

TL;DR#

Most Isaac Lab manager-based task configs can be ported to mjlab with only small changes:

  • The overall MDP structure is the same (managers for rewards, observations, actions, commands, terminations, events, curriculum).

  • The environment base classes are similar, but naming is slightly different.

  • The biggest change is configuration style: Isaac Lab uses nested @configclass definitions; mjlab uses dictionaries of config objects.

If you are familiar with Isaac Lab’s manager-based API, migration is mostly mechanical.

Key Differences#

1. Import Paths#

Isaac Lab:

from isaaclab.envs import ManagerBasedRLEnv

mjlab:

from mjlab.envs import ManagerBasedRlEnvCfg

Note

mjlab uses a consistent CamelCase naming convention (for example, RlEnv instead of RLEnv).

2. Configuration Structure#

Isaac Lab uses nested @configclass blocks for manager terms. mjlab instead uses plain dictionaries mapping names to config objects, which makes it easy to construct variants, merge configs, or generate them programmatically.

Isaac Lab:

@configclass
class RewardsCfg:
    """Reward terms for the MDP."""

    motion_global_anchor_pos = RewTerm(
        func=mdp.motion_global_anchor_position_error_exp,
        weight=0.5,
        params={"command_name": "motion", "std": 0.3},
    )
    motion_global_anchor_ori = RewTerm(
        func=mdp.motion_global_anchor_orientation_error_exp,
        weight=0.5,
        params={"command_name": "motion", "std": 0.4},
    )

mjlab:

rewards = {
    "motion_global_anchor_pos": RewardTermCfg(
        func=mdp.motion_global_anchor_position_error_exp,
        weight=0.5,
        params={"command_name": "motion", "std": 0.3},
    ),
    "motion_global_anchor_ori": RewardTermCfg(
        func=mdp.motion_global_anchor_orientation_error_exp,
        weight=0.5,
        params={"command_name": "motion", "std": 0.4},
    ),
}

cfg = ManagerBasedRlEnvCfg(
    scene=scene,
    rewards=rewards,
    # ... other manager dictionaries:
    # observations=..., actions=..., commands=..., terminations=...,
    # events=..., curriculum=...
)

This pattern applies to all managers:

  • rewards

  • observations

  • actions

  • commands

  • terminations

  • events

  • curriculum

3. Scene Configuration#

Scene setup is simpler in mjlab:

  • No Omniverse / USD scene graph, no prim_path management.

  • Assets are pure MuJoCo (MJCF) with modifier dataclasses applied to mujoco.MjSpec.

  • Lights, materials, textures, and sensors are configured as part of SceneCfg and robot configs.

Isaac Lab:

from whole_body_tracking.robots.g1 import G1_ACTION_SCALE, G1_CYLINDER_CFG
from isaaclab.scene import InteractiveSceneCfg
from isaaclab.sensors import ContactSensorCfg
from isaaclab.terrains import TerrainImporterCfg
import isaaclab.sim as sim_utils
from isaaclab.assets import ArticulationCfg, AssetBaseCfg

@configclass
class MySceneCfg(InteractiveSceneCfg):
    """Configuration for the terrain scene with a legged robot."""

    # ground terrain
    terrain = TerrainImporterCfg(
        prim_path="/World/ground",
        terrain_type="plane",
        collision_group=-1,
        physics_material=sim_utils.RigidBodyMaterialCfg(
            friction_combine_mode="multiply",
            restitution_combine_mode="multiply",
            static_friction=1.0,
            dynamic_friction=1.0,
        ),
        visual_material=sim_utils.MdlFileCfg(
            mdl_path="{NVIDIA_NUCLEUS_DIR}/Materials/Base/Architecture/Shingles_01.mdl",
            project_uvw=True,
        ),
    )
    # lights
    light = AssetBaseCfg(
        prim_path="/World/light",
        spawn=sim_utils.DistantLightCfg(
            color=(0.75, 0.75, 0.75), intensity=3000.0
        ),
    )
    sky_light = AssetBaseCfg(
        prim_path="/World/skyLight",
        spawn=sim_utils.DomeLightCfg(
            color=(0.13, 0.13, 0.13), intensity=1000.0
        ),
    )
    robot = G1_CYLINDER_CFG.replace(prim_path="{ENV_REGEX_NS}/Robot")

mjlab:

from dataclasses import replace

from mjlab.scene import SceneCfg
from mjlab.asset_zoo.robots.unitree_g1.g1_constants import get_g1_robot_cfg
from mjlab.utils.spec_config import ContactSensorCfg
from mjlab.terrains import TerrainImporterCfg

# Configure contact sensor
self_collision_sensor = ContactSensorCfg(
    name="self_collision",
    subtree1="pelvis",
    subtree2="pelvis",
    data=("found",),
    reduce="netforce",
    num=10,  # report up to 10 contacts
)

# Add sensor to robot config
g1_cfg = replace(get_g1_robot_cfg(), sensors=(self_collision_sensor,))

# Create scene
SCENE_CFG = SceneCfg(
    terrain=TerrainImporterCfg(terrain_type="plane"),
    entities={"robot": g1_cfg},
)

Key changes:

  • No USD prim_path or cloning; the scene is described directly in MuJoCo.

  • Materials, lights, and visual properties are applied via MjSpec-modifier dataclasses.

  • See mjlab.utils.spec_config in the repository for helpers that apply these changes for you.

Complete Example Comparison#

A good way to learn the pattern is to compare concrete tasks that have already been ported:

You will see that:

  • Manager dictionaries in mjlab mirror Isaac Lab’s config classes,

  • Reward, observation, command, and termination logic is almost identical,

  • Scene and asset setup are simplified to pure MuJoCo.

Migration Checklist#

Use this as a quick checklist when porting a task:

  1. Base class and imports

    • Replace Isaac Lab imports (for example, from isaaclab.envs import ManagerBasedRLEnv) with the corresponding mjlab imports (for example, from mjlab.envs import ManagerBasedRlEnvCfg).

  2. Manager configuration

    • Convert each Isaac Lab @configclass manager (RewardsCfg, ObservationsCfg, etc.) into a dictionary of config objects.

    • Pass these dictionaries into ManagerBasedRlEnvCfg.

  3. Scene and assets

    • Replace InteractiveSceneCfg with a SceneCfg instance.

    • Replace USD / prim_path logic with MuJoCo asset configs and scene entities (for example, a robot from asset_zoo).

  4. Sensors and contact handling

    • Convert Isaac Lab ContactSensorCfg to mjlab.utils.spec_config.ContactSensorCfg and attach it to the robot config.

  5. RL entry points

    • Make sure your training script or entry point uses the correct task id and environment config (for example, via Gymnasium registration or direct construction, depending on how your project is structured).

Tips and Support#

  1. Check the examples in the repository under:

    • src/mjlab/tasks/

  2. If you get stuck:

  3. Keep in mind MuJoCo vs Isaac Sim differences:

    • Some Omniverse / USD rendering features do not have direct equivalents.

    • Focus first on matching the physics and observations, then polish visuals if needed.