Loading and Simulating URDF Robot Models Using the Newton Library: A Complete Guide

Use newton.ModelBuilder.add_urdf() to import URDF robots into Newton simulations with full control over root articulation, coordinate frames, and collision geometry.

The Newton physics library provides a streamlined pipeline for loading and simulating URDF robot models using the Newton library's high-level API. Whether you're importing quadrupeds, manipulators, or custom mobile robots, the ModelBuilder.add_urdf() method handles XML parsing, asset resolution, and physics body creation in a single call. This guide walks through the implementation details in newton/_src/sim/builder.py and newton/_src/utils/import_urdf.py to help you configure complex robot imports correctly.

The Entry Point: ModelBuilder.add_urdf

The primary interface for URDF import lives in newton/_src/sim/builder.py at lines 2056–2079. The add_urdf method validates incoming arguments and delegates to the core parser:

def add_urdf(
    self,
    source: str,
    *,
    xform: Transform | None = None,
    floating: bool | None = None,
    base_joint: dict | None = None,
    parent_body: int = -1,
    scale: float = 1.0,
    hide_visuals: bool = False,
    parse_visuals_as_colliders: bool = False,
    up_axis: AxisType = Axis.Z,
    force_show_colliders: bool = False,
    enable_self_collisions: bool = True,
    ignore_inertial_definitions: bool = False,
    joint_ordering: Literal["bfs", "dfs"] | None = "dfs",
    bodies_follow_joint_ordering: bool = True,
    collapse_fixed_joints: bool = False,
    mesh_maxhullvert: int | None = None,
    force_position_velocity_actuation: bool = False,
    override_root_xform: bool = False,
):
    """Parses a URDF file and adds the bodies and joints to the given ModelBuilder."""
    from ..utils.import_urdf import parse_urdf  # noqa: PLC0415

    return parse_urdf(self, source, ...)

Key design decisions:

  • Early validation occurs via ModelBuilder._validate_base_joint_params before any XML parsing begins.
  • Mutually exclusive arguments (floating vs base_joint) are enforced at the builder level to prevent invalid articulation states.
  • Thin wrapper philosophy keeps the public API stable while allowing the parser implementation to evolve.

Core Import Pipeline: parse_urdf

The heavy lifting happens in newton/_src/utils/import_urdf.py within the parse_urdf function (lines 68–90 for the signature, 188–199 for transform handling). This function orchestrates seven distinct stages:

1. Argument Validation and Base Joint Resolution

Before touching the URDF XML, the parser calls builder._validate_base_joint_params to resolve the root connection strategy:

  • floating=True: Creates a FREE joint (6-DOF) connecting the root link to world.
  • base_joint=dict(...): Defines a custom joint (FIXED, REVOLUTE, etc.) with specific pose and limits.
  • parent_body: When set to a valid body index, the imported root becomes a child of that existing body, enabling hierarchical composition.

These options are mutually exclusive; providing both floating and base_joint raises a ValueError.

2. Coordinate Frame Alignment

URDF files may use X, Y, or Z as the "up" axis. Newton handles this via quat_between_axes (lines 188–199):

axis_xform = wp.transform(wp.vec3(0.0), quat_between_axes(up_axis, builder.up_axis))
if xform is None:
    xform = axis_xform
else:
    # Compose the axis alignment with the user-provided transform

    xform = wp.transform_multiply(axis_xform, xform)

This ensures that a URDF designed for Y-up (common in some ROS environments) imports correctly into a Z-up Newton scene without manual rotation.

3. Asset Resolution and XML Sanitization

External mesh references (.obj, .stl) are fetched via download_asset_tmpfile using the requests library. The XML content then passes through sanitize_xml_content to remove Byte Order Marks (BOMs) and leading comments that could confuse the parser.

4. Topology Construction

The topological_sort function (from newton/_src/utils/topology.py) orders joints according to the joint_ordering parameter:

  • "bfs": Breadth-first search (layer-by-layer from root).
  • "dfs": Depth-first search (chain-by-chain, default).

Setting bodies_follow_joint_ordering=True ensures body indices align with joint indices for cleaner bookkeeping.

5. Body and Joint Creation

For each <link> and <joint> in the sorted topology, the parser calls:

  • ModelBuilder.add_body() – creates rigid bodies with inertial properties.
  • ModelBuilder.add_joint() – creates articulation joints with limits, damping, and actuation settings.

Options like collapse_fixed_joints=True merge fixed joints into parent bodies to reduce DOF count, while mesh_maxhullvert controls convex hull simplification for collision meshes.

6. Custom Attribute Parsing

URDF-specific tags map to Newton's attribute system via parse_custom_attributes in newton/_src/utils/import_utils.py. This allows custom XML properties to propagate into the simulation model as ModelBuilder.CustomAttribute entries.

7. Finalization

Once parsing completes, calling builder.finalize() returns a Model instance ready for simulation. This model contains optimized data structures for GPU-accelerated physics stepping.

Configuring URDF Import Options

Newton exposes granular control over the import process through the add_urdf parameter set:

Parameter Effect Typical Use Case
floating Boolean to create a FREE joint at the root. Mobile robots (drones, quadrupeds) that need 6-DOF world movement.
base_joint Dict defining a specific joint type (FIXED, REVOLUTE, etc.). Manipulators mounted to a static base or a moving platform with constrained DOF.
parent_body Integer index of an existing body to attach the URDF root. Composing complex scenes (e.g., attaching a gripper to a robot arm).
override_root_xform Boolean to replace rather than compose the root transform. Cloning articulations at specific poses without accumulating transforms.
up_axis Enum (Axis.X, Axis.Y, Axis.Z) matching the URDF's coordinate system. Importing ROS URDFs (often Y-up) into Z-up simulation environments.
enable_self_collisions Boolean to toggle collision filtering between links of the same model. Disabling for performance in complex robots where self-collision is unlikely.
collapse_fixed_joints Boolean to merge fixed joints into parent bodies. Reducing DOF count for static structural elements.
mesh_maxhullvert Integer limiting vertices in convex hull approximations. Optimizing collision detection for high-poly visual meshes.

Practical Implementation: Loading and Simulating a URDF Robot

Below is a complete, runnable script demonstrating the standard workflow from import to visualization:

import newton
import numpy as np

# -------------------------------------------------

# 1️⃣  Create a ModelBuilder (Z-up by default)

# -------------------------------------------------

builder = newton.ModelBuilder()

# -------------------------------------------------

# 2️⃣  Import the URDF

# -------------------------------------------------

# You can pass a file path or the raw XML string.

# Example: a quadruped robot shipped with the examples.

urdf_path = newton.examples.get_asset("quadruped.urdf")

builder.add_urdf(
    source=urdf_path,
    floating=True,                     # root becomes a FREE joint (6‑DOF)

    scale=1.0,                         # optional scaling factor

    hide_visuals=False,                # keep visual meshes visible

    up_axis=newton.Axis.Z,             # URDF’s up axis (most URDFs use Z)

    enable_self_collisions=False,      # disable self‑collision for speed

)

# -------------------------------------------------

# 3️⃣  Finalise the model

# -------------------------------------------------

model = builder.finalize()

# -------------------------------------------------

# 4️⃣  Create a simulation scene

# -------------------------------------------------

sim = newton.Simulation()
sim.add_model(model)

# -------------------------------------------------

# 5️⃣  Run a few timesteps

# -------------------------------------------------

dt = 0.001
for _ in range(1000):
    sim.step(dt)

# -------------------------------------------------

# 6️⃣  Visualise with the built‑in viewer (Rerun backend)

# -------------------------------------------------

viewer = newton.Viewer()
viewer.set_simulation(sim)
viewer.run()       # blocks until the window is closed

Advanced Composition: Attaching URDFs to Existing Bodies

Newton supports hierarchical scene composition by attaching imported URDF roots to existing bodies via the parent_body parameter. This is enforced by _validate_base_joint_params to ensure only the most recent articulation can serve as the parent.


# First robot (free‑floating base)

builder.add_urdf("base_robot.urdf", floating=True)

# Get index of a body to attach the second robot to (e.g., body 3)

parent_idx = 3  

# Import a gripper and attach it with a custom fixed joint

builder.add_urdf(
    "gripper.urdf",
    parent_body=parent_idx,
    base_joint=dict(
        type="FIXED",
        pose=newton.Transform.identity(),
    ),
    floating=False,   # cannot be FREE because we attach to an existing body

)

When using parent_body, the imported URDF's root link becomes a child of the specified existing body, enabling complex multi-robot scenarios and end-effector tooling.

Summary

  • ModelBuilder.add_urdf in newton/_src/sim/builder.py provides the single public entry point for URDF import, validating arguments before delegating to the core parser.
  • parse_urdf in newton/_src/utils/import_urdf.py executes a seven-stage pipeline: validation, axis alignment, asset resolution, XML sanitization, topological sorting, body/joint creation, and custom attribute parsing.
  • Root articulation is controlled via mutually exclusive floating (FREE joint) or base_joint (custom joint) parameters, with optional parent_body attachment for hierarchical composition.
  • Coordinate system handling automatically aligns URDF up-axes (X, Y, or Z) with the builder's coordinate frame using quat_between_axes.
  • Performance optimizations include collapse_fixed_joints to reduce DOF count, mesh_maxhullvert for convex hull simplification, and enable_self_collisions flags for collision filtering.

Frequently Asked Questions

How do I load a URDF file in Newton?

Call newton.ModelBuilder.add_urdf() with the file path or XML string as the source argument. The method automatically resolves external mesh assets, validates joint configurations, and creates the corresponding bodies and joints in the builder. After importing, call builder.finalize() to obtain a simulation-ready Model instance.

What is the difference between floating and base_joint parameters?

The floating parameter is a boolean that, when True, creates a FREE joint (6-DOF) connecting the URDF root to the world, suitable for drones or mobile robots. The base_joint parameter accepts a dictionary defining a specific joint type (FIXED, REVOLUTE, PRISMATIC, etc.) with custom poses and limits, used for mounting manipulators to static bases or platforms. These parameters are mutually exclusive; providing both raises a validation error.

Can I attach multiple URDF robots to each other in Newton?

Yes, use the parent_body parameter to attach a URDF's root link to an existing body in the builder. Specify the target body index as parent_body, and set floating=False (since the root is constrained by the parent attachment). You can optionally define a base_joint to specify how the URDF connects to the parent body, such as a FIXED joint for rigid tooling or a REVOLUTE joint for articulated wrists.

How does Newton handle URDF files with different up-axis conventions?

Newton automatically re-orients geometry using the up_axis parameter in add_urdf(), which accepts newton.Axis.X, Axis.Y, or Axis.Z. The parser computes the rotation between the URDF's up axis and the builder's coordinate system using quat_between_axes, then applies this transformation to all imported bodies and joints. This allows seamless import of ROS-style Y-up URDFs into Z-up simulation environments without manual rotation calculations.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →