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

> Effortlessly load and simulate URDF robot models in Newton using add urdf. Gain complete control over articulation, frames, and collision geometry for advanced physics.

- Repository: [Newton Physics/newton](https://github.com/newton-physics/newton)
- Tags: how-to-guide
- Published: 2026-03-19

---

**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`](https://github.com/newton-physics/newton/blob/main/newton/_src/sim/builder.py) and [`newton/_src/utils/import_urdf.py`](https://github.com/newton-physics/newton/blob/main/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`](https://github.com/newton-physics/newton/blob/main/newton/_src/sim/builder.py) at lines **2056–2079**. The `add_urdf` method validates incoming arguments and delegates to the core parser:

```python
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`](https://github.com/newton-physics/newton/blob/main/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**):

```python
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`](https://github.com/newton-physics/newton/blob/main/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`](https://github.com/newton-physics/newton/blob/main/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:

```python
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.

```python

# 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`](https://github.com/newton-physics/newton/blob/main/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`](https://github.com/newton-physics/newton/blob/main/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.