Skip to main content

mujoco-spec-editing

MuJoCo procedural model authoring, editing, and tuning with MjSpec (`mujoco.MjSpec` in Python and `mjSpec` in C++): bodies, geoms, joints, sites, cameras, sub-model attachment with prefix namespaces, actuators (position servos, motors, velocity drives, trntype), baseline option/default overrides (implicitfast, elliptic cones, solref, solimp, armature, damping), collision bitmasks (contype, conaffinity), friction (condim), default classes (MjsDefault), procedural meshes, deletion, spec recompilation (spec.recompile). Use only for authoring or tuning MuJoCo MJCF/MjSpec models, adding bodies/geoms/actuators, and attaching sub-models. Don't use for designing custom non-MuJoCo XML/scene-graph APIs when MuJoCo `MjSpec` is not used, stepping simulations (mujoco-python), or rendering (mujoco-rendering).

Source facts

Repository
google-deepmind/mujoco
Last source activity
November 7, 2026 at 17:05
Detected SKILL.md language
English
Stars
17,679
Forks
2,174

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
mujoco-spec-editing
description
MuJoCo procedural model authoring, editing, and tuning with MjSpec (`mujoco.MjSpec` in Python and `mjSpec` in C++): bodies, geoms, joints, sites, cameras, sub-model attachment with prefix namespaces, actuators (position servos, motors, velocity drives, trntype), baseline option/default overrides (implicitfast, elliptic cones, solref, solimp, armature, damping), collision bitmasks (contype, conaffinity), friction (condim), default classes (MjsDefault), procedural meshes, deletion, spec recompilation (spec.recompile). Use only for authoring or tuning MuJoCo MJCF/MjSpec models, adding bodies/geoms/actuators, and attaching sub-models. Don't use for designing custom non-MuJoCo XML/scene-graph APIs when MuJoCo `MjSpec` is not used, stepping simulations (mujoco-python), or rendering (mujoco-rendering).
# MuJoCo Spec Editing: Building & Modifying Models > [!TIP] > > **Official MuJoCo documentation (`doc/*.rst`):** Read short chapters like > [`../../programming/modeledit.rst`](../../programming/modeledit.rst) > (`.. _meOverview:`) and [`../../overview.rst`](../../overview.rst) directly > when in a MuJoCo checkout; for large reference files like > [`../../modeling.rst`](../../modeling.rst) (`.. _ProceduralModeling:`, > `.. _Mechanisms:`, `.. _CTuning:`), > [`../../XMLreference.rst`](../../XMLreference.rst), > [`../../python.rst`](../../python.rst) (`.. _PyModelEdit:`), and > [`../../APIreference/functions.rst`](../../APIreference/functions.rst), grep > the literal `.. _Label:` anchor. Outside a checkout, the same `doc/*.rst` tree > ships offline in `pip install mujoco` at > `pathlib.Path(mujoco.__file__).parent / 'doc'`; otherwise fetch > `https://mujoco.readthedocs.io/en/latest/<path>.html` (or `/en/stable/` for > released pages; `functions.rst` renders inside > `APIreference/APIfunctions.html`, and Sphinx anchors lowercase the label, > e.g. `.. _meOverview:` → `#meoverview`) and browse repo paths (`../../../`) > under `https://github.com/google-deepmind/mujoco/tree/main/`. > > **Related skills:** > > - [mujoco-python](../python/SKILL.md) — Simulation lifecycle, stepping, and > diagnostics. > - [mujoco-rendering](../rendering/SKILL.md) — Offscreen image rendering and > camera setup. > - [mujoco-studio](../studio/SKILL.md) — Interactive viewers and Studio > plugins. ## 1. Procedural Modeling with MjSpec `MjSpec` is a mutable C++ and Python data structure that mirrors the MJCF XML schema, allowing models to be constructed and modified programmatically. ```python import mujoco spec = mujoco.MjSpec() # Add a body with a freejoint and sphere geom body = spec.worldbody.add_body(name='ball', pos=[0, 0, 1.0]) body.add_freejoint() body.add_geom(type=mujoco.mjtGeom.mjGEOM_SPHERE, size=[0.1], rgba=[1, 0, 0, 1]) # Compile to MjModel model = spec.compile() ``` In C++: ```cpp #include <mujoco/mujoco.h> mjSpec* spec = mj_makeSpec(); mjsBody* body = mjs_addBody(mjs_findBody(spec, "world"), nullptr); body->pos[2] = 1.0; mjs_addFreeJoint(body); mjsGeom* geom = mjs_addGeom(body, nullptr); geom->type = mjGEOM_SPHERE; geom->size[0] = 0.1; mjModel* model = mj_compile(spec, nullptr); if (!model) printf("%s\n", mjs_getError(spec)); mj_deleteSpec(spec); ``` ## 2. Building Hierarchies: Bodies, Geoms, Joints & Sites ### Adding Elements ```python body = spec.worldbody.add_body(name='arm', pos=[0, 0, 0.5]) # Geom with capsule shape geom = body.add_geom( name='arm_geom', type=mujoco.mjtGeom.mjGEOM_CAPSULE, size=[0.05, 0.25], rgba=[0.2, 0.6, 1.0, 1.0], ) # Hinge joint joint = body.add_joint( name='shoulder', type=mujoco.mjtJoint.mjJNT_HINGE, axis=[0, 1, 0], range=[-1.57, 1.57], ) # Sensor site and camera site = body.add_site(name='grip_site', pos=[0, 0, 0.25]) cam = body.add_camera(name='tool_cam', pos=[0, -0.5, 0.25], xyaxes=[1, 0, 0, 0, 0, 1]) ``` ### Geom Dimensions Reference | Geom Type | `size` Parameter Semantics | | :--- | :--- | | `mjGEOM_SPHERE` | `[radius]` | | `mjGEOM_CAPSULE` | `[radius, half_length]` (or `[radius]` if using `fromto`) | | `mjGEOM_CYLINDER` | `[radius, half_length]` (or `[radius]` if using `fromto`) | | `mjGEOM_BOX` | `[half_x, half_y, half_z]` | | `mjGEOM_ELLIPSOID`| `[radius_x, radius_y, radius_z]` | | `mjGEOM_PLANE` | `[half_x, half_y, grid_spacing]` | > [!WARNING] > > When `fromto=[x1, y1, z1, x2, y2, z2]` is specified for capsules or cylinders, > `size` takes only **one value** (`[radius]`); length is calculated from endpoints. ### Orientation Keywords Only **one** orientation specification can be used at a time: - `quat=[w, x, y, z]` (native representation) - `euler=[a1, a2, a3]` (sequence set by `spec.compiler.eulerseq`, default `'xyz'`) - `axisangle=[x, y, z, angle]` - `xyaxes=[x_x, x_y, x_z, y_x, y_y, y_z]` - `zaxis=[x, y, z]` Angles in `euler` and `axisangle` are in degrees unless `spec.compiler.degree = False` (see [modeling.rst](../../modeling.rst), `.. _COrientation:`, and [XMLreference.rst](../../XMLreference.rst), `.. _body-geom:`). ## 3. Sub-model Composition & Attachments Compose models modularly by attaching sub-specs with prefix namespaces (see [programming/modeledit.rst](../../programming/modeledit.rst), `.. _meAttachment:` and `.. _meAttributeMerging:`): ```python parent = mujoco.MjSpec() child = mujoco.MjSpec() child_body = child.worldbody.add_body(name='gripper') child_body.add_geom(size=[0.05], type=mujoco.mjtGeom.mjGEOM_SPHERE) # Attach child spec to a mount site on the parent mount_site = parent.worldbody.add_site(name='mount', pos=[0, 0, 1.0]) parent.attach(child, site=mount_site, prefix='gripper_') # Child body is renamed to 'gripper_gripper' in parent ``` ### Cross-Spec Equality Constraints When linking bodies from two different child specs (e.g. dual-arm setups), attach both children to the common parent first, then declare constraints on the parent spec: ```python left_mount = parent.worldbody.add_frame(pos=[0, 0.3, 0]) right_mount = parent.worldbody.add_frame(pos=[0, -0.3, 0]) parent.attach(arm1_spec, frame=left_mount, prefix='left_') parent.attach(arm2_spec, frame=right_mount, prefix='right_') eq = parent.add_equality( type=mujoco.mjtEq.mjEQ_CONNECT, objtype=mujoco.mjtObj.mjOBJ_BODY, name1='left_hand', name2='right_hand', ) eq.data[0:3] = [0, 0, 0] # connect anchor, in name1's body frame ``` > [!NOTE] > > `attach()` requires exactly one of `frame=` or `site=`; omitting both raises > `ValueError`. ## 4. Deleting & Dynamically Modifying Elements ### Deleting Elements Pass the element object to `spec.delete()`; deleting a body removes its subtree: ```python spec.delete(spec.body('unwanted_body')) spec.delete(spec.geom('old_geom')) spec.delete(spec.joint('redundant_joint')) spec.delete(spec.actuator('motor')) ``` ### State-Preserving Recompilation When changing model architecture at runtime, use `spec.recompile` to keep existing physics state: ```python # Add new tool to robot spec.worldbody.add_geom(name='tool_tip', size=[0.02], type=mujoco.mjtGeom.mjGEOM_SPHERE) # Recompile keeping positions/velocities intact model, data = spec.recompile(model, data) ``` ## 5. Actuators & Servos Always explicitly set the transmission type (`trntype`): ```python # 1. Torque Motor motor = spec.add_actuator( name='torque_actuator', target='shoulder', trntype=mujoco.mjtTrn.mjTRN_JOINT, ) motor.set_to_motor() # 2. Stiff Position Servo (Recommended for position control) servo = spec.add_actuator( name='pos_servo', target='shoulder', trntype=mujoco.mjtTrn.mjTRN_JOINT, ) servo.set_to_position(kp=200.0, dampratio=1.0) # Pair with implicitfast (kv defaults to 0) servo.forcelimited = True servo.forcerange = [-50.0, 50.0] # Peak torque in Nm servo.ctrllimited = True servo.ctrlrange = [-1.57, 1.57] # Target angle in radians # 3. Velocity Actuator vel = spec.add_actuator( name='vel_driver', target='wheel_joint', trntype=mujoco.mjtTrn.mjTRN_JOINT, ) vel.set_to_velocity(kv=10.0) ``` > [!CAUTION] > > The helper functions `set_to_position()` and `set_to_motor()` configure gains > but do **not** set the transmission. You must always specify > `trntype=mujoco.mjtTrn.mjTRN_JOINT` (or another valid transmission type). See > [modeling.rst](../../modeling.rst) (`.. _CActuators:`, `.. _CActShortcuts:`) > and [XMLreference.rst](../../XMLreference.rst) (`.. _actuator:`). ## 6. Default Classes (Inheritance & Styling) Default classes (`MjsDefault`) provide cascaded styling and properties (see [modeling.rst](../../modeling.rst), `.. _CDefault:`, and [programming/modeledit.rst](../../programming/modeledit.rst), `.. _meDefault:`): ```python # Create child class under root 'main' class main_class = spec.default rubber = spec.add_default('rubber', main_class) rubber.geom.friction = [1.5, 0.01, 0.001] rubber.geom.density = 1200.0 # Assign class at creation body.add_geom(rubber, name='tire', type=mujoco.mjtGeom.mjGEOM_CYLINDER, size=[0.1, 0.05]) # Assign class post-creation (must pass MjsDefault object, NOT a string) other_geom.classname = rubber ``` ### Baseline Tuning Overrides (`<option>` & Root `<default>`) Several internal defaults (`Euler`, `pyramidal` cones with `impratio=1`, soft `solref="0.02 1"`, zero joint `armature`/`damping`) are kept for backwards compatibility rather than new robotics models. Override them at the top of a project (full rationale and priority-ordered parameter guide in [modeling.rst](../../modeling.rst), `.. _CTuning:`, `.. _CTuningDefaults:`, `.. _CTuningGlobal:`, `.. _CTuningModel:`): ```python spec.option.integrator = mujoco.mjtIntegrator.mjINT_IMPLICITFAST # DISCRETE for flex elasticity spec.option.cone = mujoco.mjtCone.mjCONE_ELLIPTIC spec.option.impratio = 10.0 spec.default.geom.solref = [0.005, 1.0] # Keep solref[0] >= 2 * timestep spec.default.joint.armature = 0.01 # Reflected rotor inertia (I_rotor * N^2) spec.default.joint.damping = 0.1 # Implicit in M + h*D at zero solver cost ```
View on GitHub
This SKILL.md is very large, so SkillsMP previews the first section here. View on GitHub