๐Ÿ”จ Model Building#

MotrixSim provides a programmatic model building API that allows you to load, transform, and combine multiple models before simulation. This is useful for:

  • Combining robot models with different end-effectors

  • Creating multi-robot scenes

  • Dynamically assembling models at runtime

Basic Concepts#

The model building API is available through the motrixsim.msd module:

Class/Function

Description

msd.from_file(path)

Load a model file (MJCF/URDF/MSD) and return a Scene object

msd.from_str(string, format)

Load a model from string

Scene.attach(other, ...)

Attach another model to this one

Scene.build()

Build the final SceneModel for simulation

The Scene object is a mutable representation of a model that can be transformed and combined before being compiled into an immutable SceneModel.

Basic Usage#

Loading and Building a Model#

The simplest usage is to load a model file and build it:

import motrixsim as mx

# Load and build in one chain
model = mx.msd.from_file("robot.xml").build()

# Or step by step
scene = mx.msd.from_file("robot.xml")
model = scene.build()

Loading from String#

You can also create a model from an MJCF/URDF string:

import motrixsim as mx

mjcf_string = """
<mujoco>
  <worldbody>
    <body name="box">
      <geom type="box" size="0.1 0.1 0.1"/>
    </body>
  </worldbody>
</mujoco>
"""

model = mx.msd.from_str(mjcf_string, format="mjcf").build()

Combining Models#

The attach method allows you to combine multiple models together:

import motrixsim as mx

# Load two models
robot = mx.msd.from_file("robot.xml")
gripper = mx.msd.from_file("gripper.xml")

# Attach gripper to robot's hand link
robot.attach(
    gripper,
    self_link_name="hand",      # Link in robot to attach to
    other_prefix="gripper_",    # Prefix for gripper's names
    other_translation=[0.1, 0, 0]  # Offset
)

model = robot.build()

Attach Parameters#

Parameter

Type

Description

other

Scene

The model to attach (cloned internally, can be reused)

self_link_name

str

Link in this model to attach to. If None, merge at root level

other_link_name

str

Extract only this subtree from the other model

other_translation

[x, y, z]

Translation offset for the attached model

other_rotation

[x, y, z, w]

Rotation quaternion for the attached model

other_prefix

str

Prefix to add to all names in the attached model

other_suffix

str

Suffix to add to all names in the attached model

Creating Multiple Instances#

Since attach clones the other model internally, you can attach the same model multiple times:

import motrixsim as mx

scene = mx.msd.from_file("scene.xml")
robot = mx.msd.from_file("robot.xml")

# Create multiple robot instances at different positions
scene.attach(robot, other_prefix="robot1_", other_translation=[0, 0, 0])
scene.attach(robot, other_prefix="robot2_", other_translation=[2, 0, 0])
scene.attach(robot, other_prefix="robot3_", other_translation=[4, 0, 0])

model = scene.build()

Extracting Subtrees#

You can extract a specific subtree from a model before attaching:

import motrixsim as mx

robot = mx.msd.from_file("robot.xml")
full_arm = mx.msd.from_file("arm_with_base.xml")

# Only attach the "forearm" subtree, not the entire arm model
robot.attach(
    full_arm,
    self_link_name="shoulder",
    other_link_name="forearm",  # Extract from this link
    other_prefix="arm_"
)

model = robot.build()

Complete Example#

Hereโ€™s a complete example that creates a scene with multiple robots. For the full example, see examples/combine_msd.py.

import time

import motrixsim as mx

# Load the base scene
msd_scene = mx.msd.from_file("examples/assets/store/scene.xml")

# Load a dog model and attach an arm to it
msd_dog = mx.msd.from_file("examples/assets/boston_dynamics_spot/spot.xml")
msd_arm = mx.msd.from_file("examples/assets/stanford_tidybot_adhesion/tidybot.xml")
# Attach arm subtree to the dog's hip link
msd_dog.attach(
    msd_arm,
    self_link_name="hl_hip",
    other_link_name="bracelet_link",  # Extract subtree from arm
    other_rotation=[-0.71, 0, 0, 0.71],  # Rotate 90 degrees around X axis
    other_prefix="arm_",
)

# Attach multiple dogs to the scene at different positions
# Note: other is cloned internally, so msd_dog can be reused
msd_scene.attach(msd_dog, other_translation=[1, 0, 0], other_prefix="dog0_")
msd_scene.attach(msd_dog, other_translation=[2, 0, 0], other_prefix="dog1_")
msd_scene.attach(msd_dog, other_translation=[3, 0, 0], other_prefix="dog2_")

# Build the final model
model = msd_scene.build()

with mx.render.RenderApp("warn") as render:
    render.launch(model)
    data = mx.SceneData(model)

    while True:
        time.sleep(model.options.timestep)
        mx.step(model, data)
        render.sync(data)

MJCF Attach Element#

MotrixSim also supports the MJCF <attach> element for combining models in XML:

<mujoco>
  <asset>
    <model name="gripper" file="gripper.xml"/>
  </asset>
  <worldbody>
    <body name="robot">
      <!-- robot definition -->
      <body name="hand">
        <attach model="gripper" prefix="gripper_"/>
      </body>
    </body>
  </worldbody>
</mujoco>

For MJCF support details, see MJCF Files.

API Reference#

For detailed API documentation, see motrixsim.msd.