Skip to main content

MuJoCo Robot

CI PyPI version Python versions License: MIT

A general Python interface for robot simulations using MuJoCo. Its public API mirrors pybullet_robot so that controllers written against that backend can be run here with minimal changes. MujocoRobot is a single object to load, introspect, control, and read state from a robot, implemented directly on MjModel + MjData.

Installation

Requires Python >= 3.10 and mujoco >= 3.2.

pip install mujoco_robot
pip install "mujoco_robot[descriptions]"   # fetch robots by name (robot_descriptions)
pip install "mujoco_robot[ik]"             # optional whole-body IK backend (mink)

The core needs only mujoco, numpy and scipy. The Quickstart and most examples load robots with robot_loader_utils, which needs the [descriptions] extra.

From source:

git clone https://github.com/justagist/mujoco_robot && cd mujoco_robot
pip install -e .

Quickstart

import numpy as np
from mujoco_robot import MujocoRobot
from mujoco_robot.utils.robot_loader_utils import get_mjcf_from_awesome_robot_descriptions

robot = MujocoRobot(
    mjcf_path=get_mjcf_from_awesome_robot_descriptions("panda_mj_description"),
    ee_names=["hand"],
    run_async=False,   # step manually
    render=True,       # launch the passive viewer (needs a display)
)
robot.set_position_control_mode()

target = robot.get_actuated_joint_positions()
target[0] += 0.5
while robot.is_viewer_running():
    robot.set_joint_positions(target)
    robot.step()

Wrapping an existing world

When the robot is one part of a larger simulation that already exists as (model, data) and is stepped by its host (other actuated joints, other free bodies, the host's own pre-step logic), scope MujocoRobot to the robot's joints:

robot = MujocoRobot(model=sim.model, data=sim.data,
                    joint_names=[f"arm/{j}" for j in AUBO_JOINTS],
                    ee_sites=["arm/tcp"], run_async=False)
# host steps: sim.step(); robot only reads and writes ctrl / qfrc_applied for its joints

Ownership rule: the host steps; the robot reads state and writes ctrl / qfrc_applied for its own joints only.

  • joint_names become actuated_joint_names, in that order. Every other joint, actuator and body is foreign: commanding or resetting it raises, and nothing writes to it. Actuators are matched to joints by their transmission, so their names need not match the joint names.
  • The root body (root_body, or by default the common ancestor of the joints) gives the base pose and velocity; the base is floating iff the root carries a free joint. The links are the root's subtree, so Jacobians, link poses, contacts, transparency and dynamics editing cover the robot alone.
  • Passing model/data changes nothing on construction unless default_joint_positions or default_base_position/default_base_orientation are given. In this mode run_async and place_on_ground default to False, and managed_pd defaults to False: a command to a joint with no actuator raises instead of quietly starting a PD loop on qfrc_applied. With managed_pd=True, the host calls robot.apply_managed_control() before each mj_step.
  • step() is only for a robot that owns stepping; do not call it when the host steps.
  • Torque mode on a scoped robot clamps only its own actuators' force range to zero, so the rest of the world keeps its actuation.
  • Arm-only IK: differential_ik(model, data, "arm/tcp", target, joint_names=robot.actuated_joint_names).

mujoco_robot.utils.model_builder has helpers for building such worlds with MjSpec:

from mujoco_robot.utils import model_builder as mb

world = mujoco.MjSpec()
arm = mb.load_spec(urdf_path="aubo_i10.urdf")
mb.attach_robot(world, arm, prefix="arm/", pos=[0.0, 0.0, 0.8], quat_xyzw=[0, 0, 0, 1])
mb.add_position_actuators(world, AUBO_JOINTS, kp=2000.0, kv=100.0, prefix="arm/")
model = world.compile()

add_position_actuators adds a general actuator per joint with gaintype=FIXED and biastype=AFFINE, so URDF robots (which ship no actuators) are position-controlled through ctrl. Note that MjSpec actuators default to biastype=NONE: setting biasprm alone silently gives a pure gain, not a position servo.

Features

  • Load an MJCF/URDF file, or wrap an existing (model, data), optionally scoped to one robot inside a larger host-stepped world; assemble a world (ground, light, props, force-torque sensors, attached robots, position actuators) with the MjSpec helpers in mujoco_robot.utils.model_builder.
  • Introspect: name<->id maps for bodies, joints, actuators, sites, geoms, and sensors, with per-joint qpos/dof addressing.
  • Lifecycle: manual step() or a background stepping thread, an optional passive viewer, joint/base resets, and place-on-ground.
  • State: joint, base, and link/site pose and velocity (quaternions as [x, y, z, w]), the geometric Jacobian, gravity-compensation torques, and a get_robot_states snapshot.
  • Control: position control via the model's actuators, and torque/impedance control via data.qfrc_applied, with a PVT-PD command and position/torque mode switching.
  • Contacts and force-torque sensing, plus cosmetics / domain-randomisation helpers (transparency, per-geom collision, dynamics editing).
  • Inverse kinematics: differential_ik for a single frame (from mujoco_robot.ik import differential_ik); mink for whole-body (optional [ik]).

Examples

Runnable demos in examples/ (each takes --headless to run without the viewer):

Joint Position Control

KUKA iiwa14 follows a joint-space sinusoid (position control).

Joint-space position control demo

Task Space Control

Panda traces a circle with Cartesian impedance control.

Task-space control demo

Measuring Contact Forces using FT Sensor

Contact wrench visualised as the robot presses on a block.

FT sensor demo

Single arm Inverse Kinematics

UR5e tracks a moving target with differential_ik.

IK demo

Whole body IK (via mink -- needs pip install "mujoco_robot[ik]")

Go1 whole-body squat demo by doing IK for all four feet and body simultaneously.

WB IK demo

Design notes

  • Quaternions: the external API uses [x, y, z, w] (scipy convention); values are converted to MuJoCo's [w, x, y, z] only at the boundary.
  • Angles: joint positions, velocities, and limits are in radians (a compiled model always stores radians, regardless of the MJCF compiler angle setting).
  • Control: writes data.ctrl when the joint has an actuator, otherwise falls back to data.qfrc_applied with a Python PD loop (managed_pd; on by default only when loading from a path).
  • Fixed vs floating base: a structural free joint (added via MjSpec), not a load-time flag.
  • Sensors: force-torque sensors are declared up front (ft_sensor_links); the toggle only gates reading, since MuJoCo sensors are compile-time.

Development

pip install -e ".[dev]"   # test + lint + build tooling
ruff check .
pytest -q

License

MIT (c) Saif Sidhik

Metadata

Release files for mujoco-robot 0.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for mujoco-robot 0.2.0
File Size Uploaded
mujoco_robot-0.2.0.tar.gz 50.4 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for mujoco-robot 0.2.0
File Interpreter ABI Platform
mujoco_robot-0.2.0-py3-none-any.whl Python 3 none any Details

Total release size: 87.0 kB

Release files / mujoco_robot-0.2.0.tar.gz

Download URL mujoco_robot-0.2.0.tar.gz
Size 50.4 kB
Tags Source
SHA-256 checksum
How to use checksums
7e0d8643f640a9019308d1e4853dda38ba53767e0f9529004c2a38273495f9b1
BLAKE2b-256 checksum
How to use checksums
f0cb13fc24732ee536c030c32caad3775ad49b87787ecfe584067df04eb2c3ae
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 16, 2026.

Transparency log

Release files / mujoco_robot-0.2.0-py3-none-any.whl

Download URL mujoco_robot-0.2.0-py3-none-any.whl
Size 36.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
e723eb1af2d59359f9122e99c522390bbc1fa32b7bad5e95f647f316c6d74061
BLAKE2b-256 checksum
How to use checksums
96ea99e8b12a382302136991d58115ff5680506d96f698ecff964808e9be5fff
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 16, 2026.

Transparency log

Release history Release notifications | RSS feed

0.3.0

2 release files

This release

0.2.0 This release

2 release files

0.1.1

2 release files

0.1.0

2 release files

0.0.1

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page