MuJoCo Robot
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_namesbecomeactuated_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/datachanges nothing on construction unlessdefault_joint_positionsordefault_base_position/default_base_orientationare given. In this moderun_asyncandplace_on_grounddefault to False, andmanaged_pddefaults to False: a command to a joint with no actuator raises instead of quietly starting a PD loop onqfrc_applied. Withmanaged_pd=True, the host callsrobot.apply_managed_control()before eachmj_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 theMjSpechelpers inmujoco_robot.utils.model_builder. - Introspect: name<->id maps for bodies, joints, actuators, sites, geoms, and sensors, with
per-joint
qpos/dofaddressing. - 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 aget_robot_statessnapshot. - 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_ikfor a single frame (from mujoco_robot.ik import differential_ik);minkfor 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).
Task Space Control
Panda traces a circle with Cartesian impedance control.
Measuring Contact Forces using FT Sensor
Contact wrench visualised as the robot presses on a block.
Single arm Inverse Kinematics
UR5e tracks a moving target with differential_ik.
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.
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 anglesetting). - Control: writes
data.ctrlwhen the joint has an actuator, otherwise falls back todata.qfrc_appliedwith 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.3.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| mujoco_robot-0.3.0.tar.gz | 53.3 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| mujoco_robot-0.3.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 92.8 kB
Release files / mujoco_robot-0.3.0.tar.gz
| Download URL | mujoco_robot-0.3.0.tar.gz |
|---|---|
| Size | 53.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
41000859123d1d7464f6b2aa964a1b755567fe797f636b5bfc2dd3563aeb0223
|
|
BLAKE2b-256 checksum How to use checksums |
13db817f984082252a957dace375c3c950276cd85d6d5b07840822e0f389797d
|
| 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 logRelease files / mujoco_robot-0.3.0-py3-none-any.whl
| Download URL | mujoco_robot-0.3.0-py3-none-any.whl |
|---|---|
| Size | 39.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
4aff2ed9251d549508afa37afb75f772c00c6d1fe5af281ff1654452c5d5f54b
|
|
BLAKE2b-256 checksum How to use checksums |
efb44b027633bbd0b8eb61f13368e9645330b6e8fdf65602c592a3e7ab261261
|
| 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