Skip to content

Latest commit

 

History

History
122 lines (93 loc) · 4.08 KB

File metadata and controls

122 lines (93 loc) · 4.08 KB

MuJoCo Menagerie Python Library

pip install mujoco-menagerie
uvx mujoco-menagerie view unitree_g1
import mujoco_menagerie as mm

Browse

mm.names()
mm.robots('humanoid')  # or arm, quadruped, mobile_manipulator, ...
r = mm.get('franka_emika_panda')  # no download yet
r.display_name, r.license, r.oid
r.entry_names  # ('hand', 'mjx_hand', ..., 'scene'): every top-level XML that compiles
r.default_scene, r.default_model

Load

model = mm.load('unitree_g1')  # the default scene: floor, lights, robot
model = r.model('mjx_scene')  # any entry point
model = r.model(r.default_model)  # the robot alone
spec = r.spec('panda')  # mujoco.MjSpec, to edit before compiling

Edit

spec = mm.get('unitree_g1').spec('g1')
for joint in spec.joints:
    if joint.name.endswith('_knee_joint'):
        joint.armature = 0.01
spec.body('pelvis').add_site(name='imu')
model = spec.compile()

Attach a hand to an arm

arm = mm.get('kuka_iiwa_14').spec()
hand = mm.get('wonik_allegro').spec('left_hand')
hand.body('palm').pos[:] = (0, 0, 0.095)
arm.attach(hand, prefix='allegro/', site=arm.site('attachment_site'))
model = arm.compile()

Place two copies of a robot

ur5e = mm.get('universal_robots_ur5e')
scene = mujoco.MjSpec()
scene.option.integrator = mujoco.mjtIntegrator.mjINT_IMPLICITFAST  # match the arm, or attach warns
for prefix, y in (('left/', -0.5), ('right/', 0.5)):
    arm = ur5e.spec('ur5e')
    frame = scene.worldbody.add_frame(pos=[0, y, 0])
    frame.attach_body(arm.body('base'), prefix, '')
model = scene.compile()

Files

r.path()  # the model directory
r.xml('scene')  # one XML
r.files('scene')  # every file scene.xml depends on; copy these to vendor, and note r.oid
r.assets('scene')  # the same as {relative path: bytes}, for MjModel.from_xml_string

assets() raises for robotis_op3, ufactory_lite6 and sharpa_wave, whose meshes share basenames MuJoCo cannot tell apart in memory.

Cache

mm.prefetch(['unitree_g1'])  # download ahead of time; no argument means everything, 253 MB
mm.prune()  # delete models this version no longer references
mm.clear()  # delete the whole cache
mm.load('unitree_g1', cache=mm.Cache(dir='/shared/menagerie'))
argument environment default
cache directory mm.Cache(dir=) MENAGERIE_CACHE_DIR ~/.cache/mujoco_menagerie, ~/Library/Caches/mujoco_menagerie on macOS, %LOCALAPPDATA%\mujoco_menagerie\cache on Windows
archive source mm.Cache(base_url=) MENAGERIE_BASE_URL the GitHub release
checkout to use instead mm.Cache(root=) MENAGERIE_ROOT unset

A warm cache can be read-only. Concurrent cold starts on one model are safe: a model directory is either complete or absent.

Uninstalling the package leaves the cache in place, as with pip and uv; run mujoco-menagerie clear first to remove it.

Command line

uvx mujoco-menagerie names | info NAME | path NAME [ENTRY] | view NAME [ENTRY] | prefetch [NAME...] | verify [NAME...] | prune | clear [-y]

Speed, laptop, local mirror

import mujoco_menagerie 0.09 s
mm.load('unitree_g1'), cold 0.9 s
mm.load('unitree_g1'), warm 0.27 s
mm.prefetch() 16 s

Develop

make registry  # derive python/src/mujoco_menagerie/registry.json from the checkout
make python-test
MENAGERIE_ROOT=. uv run --project python python -c "import mujoco_menagerie as mm; mm.load('unitree_g1')"

Release

Every merge to main that touches a model directory or python/ is published as YYYY.M.N by release.yml, which tags, builds and uploads the model archives, re-verifies them, then builds, smoke tests and publishes the wheel and pushes the tag. The version comes from the tag, so git checkout v2026.9.3 && make build rebuilds that release. Run it by hand from the Actions tab. It needs a PyPI trusted publisher (workflow release.yml, environment pypi) and a pypi GitHub environment.

The package is Apache-2.0. Each model ships its own LICENSE; r.license reports it.