# How to Model Inertial, Visual, and Collision Properties for URDFs

> Learn to model inertial, visual, and collision properties in URDFs. Define mass, inertia, visual meshes, and collision geometry correctly for accurate robot simulations.

- Repository: [earthtojake/text-to-cad](https://github.com/earthtojake/text-to-cad)
- Tags: how-to-guide
- Published: 2026-07-28

---

**To model inertial, visual, and collision properties for URDFs, define an `<inertial>` block with positive mass and a physically valid inertia tensor, optional `<visual>` elements for display meshes, and optional `<collision>` elements for physics geometry, ensuring all origins are expressed in the link frame and mesh URIs resolve correctly.**

The `earthtojake/text-to-cad` repository includes a dedicated **URDF skill** that parses robot description XML and validates these three critical property types. Understanding the validation logic in [`skills/urdf/scripts/urdf/source.py`](https://github.com/earthtojake/text-to-cad/blob/main/skills/urdf/scripts/urdf/source.py) helps you author URDFs that pass strict physical plausibility checks and mesh resolution requirements.

## Core URDF Link Properties

Every rigid body in a URDF is defined within a `<link>` element. The `text-to-cad` validator enforces distinct rules for inertial, visual, and collision sub-elements.

### Inertial Properties (Required for Dynamic Links)

The `<inertial>` block defines the link's mass distribution and is mandatory for any link that participates in dynamics. According to `_validate_link_inertials()` in `source.py:226-230`, the validator performs three critical checks:

- **`<mass>`** must contain a **positive** floating-point value (`source.py:31-44`). Zero or negative values trigger validation errors.
- **`<inertia>`** must contain the six tensor components (`ixx`, `ixy`, `ixz`, `iyy`, `iyz`, `izz`). The diagonal terms must be **positive**, and the matrix must satisfy the **triangle inequalities** (`ixx + iyy > izz`, etc.) to ensure physical plausibility (`source.py:46-42`).
- **`<origin>`** specifies the center-of-mass (CoM) pose **expressed in the link frame**, not the joint frame. This is validated separately from joint origins (`source.py:226-230`).

### Visual Geometry (Optional)

The `<visual>` element defines the mesh used for rendering. The `_geometry_mesh_paths()` function (`source.py:100-115`) extracts geometry references and:

- Validates the `<origin>` sub-element if present.
- Requires a `<geometry>` child containing a `<mesh>` element (other geometry types are not processed by this skill).
- Resolves the filename via `classify_mesh_uri()` and `resolve_mesh_uri()`, supporting local relative paths, absolute paths, ROS package URIs, and remote URLs. Missing files raise errors; unresolved URIs emit warnings.

### Collision Geometry (Optional)

The `<collision>` element uses an identical structure to `<visual>` but defines the geometry used for physics engines and collision checking. The same mesh resolution logic applies (`source.py:100-115`). While optional, defining simplified collision meshes (separate from high-fidelity visual meshes) significantly improves simulation performance.

## The Validation Pipeline

The `read_urdf_source()` function (`source.py:70-95`) orchestrates the validation flow:

1. **Root Parsing** – Ensures the root element is `<robot>` and extracts the robot name.
2. **Link Collection** – Builds a list of all link names for cross-referencing.
3. **Inertial Validation** – Walks each `<link>` to validate inertial blocks as described above.
4. **Geometry Extraction** – Processes `<visual>` and `<collision>` sections to resolve mesh paths.
5. **Joint Validation** – Validates `<joint>` origins separately (`source.py:133-138`). This distinction is crucial because joint origins define the kinematic tree, while inertial origins define mass properties.
6. **Tree Consistency** – Verifies the kinematic structure forms a single rooted tree with no cycles (`source.py:175-190`). Inertial and collision data are only meaningful if the topology is valid.

## Complete URDF Example

Below is a minimal, valid URDF that demonstrates proper structuring of all three property types according to the `text-to-cad` validator requirements:

```xml
<robot name="example_robot">
  <!-- Base link with full property definitions -->
  <link name="base_link">
    <!-- Inertial: Required for dynamics -->
    <inertial>
      <!-- CoM expressed in base_link frame -->
      <origin xyz="0 0 0.02" rpy="0 0 0"/>
      <mass value="5.0"/>
      <inertia ixx="0.1" ixy="0.0" ixz="0.0"
               iyy="0.2" iyz="0.0" izz="0.3"/>
    </inertial>

    <!-- Visual: Optional display geometry -->
    <visual>
      <origin xyz="0 0 0.0" rpy="0 0 0"/>
      <geometry>
        <mesh filename="meshes/base_visual.stl"/>
      </geometry>
    </visual>

    <!-- Collision: Optional physics geometry -->
    <collision>
      <origin xyz="0 0 0.0" rpy="0 0 0"/>
      <geometry>
        <mesh filename="meshes/base_collision.stl"/>
      </geometry>
    </collision>
  </link>

  <!-- Connecting joint -->
  <joint name="base_to_arm" type="fixed">
    <parent link="base_link"/>
    <child link="arm_link"/>
    <origin xyz="0 0 0.1" rpy="0 0 0"/>
  </joint>

  <!-- Frame-only link (no inertial or geometry) -->
  <link name="arm_link"/>
</robot>

```

**Key implementation details from the source:**
- The mass value `5.0` satisfies the positive-mass constraint enforced at `source.py:31-44`.
- The inertia values satisfy `ixx > 0`, `iyy > 0`, `izz > 0`, and the triangle inequalities checked at `source.py:46-42`.
- Mesh filenames are relative to the URDF file location. If `meshes/base_visual.stl` is missing, the validator raises an error at `source.py:95-99`.

## Summary

- **Inertial properties** require positive mass, positive diagonal inertia terms, and satisfaction of triangle inequalities to represent physically valid rigid bodies.
- **Visual and collision properties** rely on mesh file resolution through `classify_mesh_uri()` and `resolve_mesh_uri()`, supporting local and remote paths.
- **Frame semantics** are strict: inertial origins are expressed in the link frame, while joint origins define the kinematic structure.
- **Tree validation** ensures the URDF describes a single rooted kinematic tree without cycles before processing geometric properties.

## Frequently Asked Questions

### What validation rules apply to URDF inertia tensors?

The validator requires the inertia matrix to have positive diagonal components (`ixx`, `iyy`, `izz`) and satisfy the triangle inequalities (`ixx + iyy > izz`, `ixx + izz > iyy`, `iyy + izz > ixx`). These checks, implemented in `source.py:46-42`, ensure the matrix is positive-definite and physically realizable.

### How does text-to-cad resolve mesh file paths in visual and collision elements?

The `_geometry_mesh_paths()` function (`source.py:100-115`) calls `classify_mesh_uri()` to identify whether the path is local, absolute, a ROS package URI, or remote, then uses `resolve_mesh_uri()` to locate the file. Missing files raise validation errors, while unresolvable URIs trigger warnings but allow parsing to continue.

### Why is the inertial origin expressed in the link frame rather than the joint frame?

According to the frame semantics validation in `source.py:226-230`, the `<inertial>` origin defines the center-of-mass relative to the link's own coordinate system. This is distinct from `<joint>` origins (`source.py:133-138`), which define the spatial relationship between parent and child links. Keeping these frames separate allows the inertia to remain constant regardless of how the link is connected to the kinematic tree.

### What happens if a URDF fails the tree consistency check?

If the validator detects cycles or disconnected subgraphs during the tree consistency verification (`source.py:175-190`), it rejects the URDF before processing inertial or collision data. This ensures that physical properties are only calculated for valid kinematic structures that simulation engines can actually simulate.