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.
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 .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()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 jointsOwnership 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.
- 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]).
Runnable demos in examples/ (each takes --headless to run without the viewer):
KUKA iiwa14 follows a joint-space sinusoid (position control).
Panda traces a circle with Cartesian impedance control.
Contact wrench visualised as the robot presses on a block.
UR5e tracks a moving target with differential_ik.
Go1 whole-body squat demo by doing IK for all four feet and body simultaneously.
- 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.
pip install -e ".[dev]" # test + lint + build tooling
ruff check .
pytest -qMIT (c) Saif Sidhik




