# How to Create URDF Robot Description Files Using CAD Skills

> Learn to create URDF robot description files from CAD models using the earthtojake text-to-cad repository. Convert Python scripts to validated URDF files efficiently.

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

---

**The URDF skill in the earthtojake/text-to-cad repository transforms Python generator scripts into validated URDF files by extracting a `gen_urdf()` function and running it through a CLI pipeline that enforces schema compliance and mesh validation.**

Creating accurate URDF files for robotics simulation traditionally requires tedious manual XML editing. The text-to-cad repository streamlines this process through a dedicated URDF skill that lets you create URDF robot description files using CAD Skills and Python automation. This approach converts concise Python scripts into production-ready robot descriptions suitable for ROS and physics simulators.

## Understanding the URDF Skill Architecture

The workflow centers on three core phases: generator definition, CLI execution, and schema validation. According to the earthtojake/text-to-cad source code, the system loads your Python script as a temporary module, extracts the `gen_urdf` callable, and processes its output through a rigorous validation layer before writing the final `.urdf` file.

The entry point requires a **generator function** named exactly `gen_urdf` that accepts no arguments. The function may return:

- An **`xml.etree.ElementTree.Element`** representing the root `<robot>` tag
- A **raw XML string** containing the complete URDF document
- A Python **dict** with a single key `"xml"` containing the XML string (the generator envelope)

## Writing the gen_urdf() Generator Function

Your Python script must define the `gen_urdf()` function as the entry point for the URDF generation pipeline. This function serves as the bridge between procedural Python code and declarative robot description markup.

### Return Type Options

The [`skills/urdf/scripts/urdf/cli.py`](https://github.com/earthtojake/text-to-cad/blob/main/skills/urdf/scripts/urdf/cli.py) driver normalizes three distinct return formats:

1. **ElementTree Element**: Best for programmatic construction using Python's XML API
2. **Raw XML String**: Useful for templating or embedding pre-written XML fragments
3. **Envelope Dict**: Returns `{"xml": "<robot>...</robot>"}` for generator tracking metadata

## Running the URDF CLI Tool

The CLI driver located in [`skills/urdf/scripts/urdf/cli.py`](https://github.com/earthtojake/text-to-cad/blob/main/skills/urdf/scripts/urdf/cli.py) handles script execution, output normalization, and file persistence.

Execute a generator script with the default output path:

```bash
python -m urdf my_robot.py

```

This creates `my_robot.urdf` in the current working directory.

Specify custom output paths using inline syntax or the `-o` flag:

```bash
python -m urdf my_robot.py=custom_path/my_robot.urdf

# Or explicitly:

python -m urdf my_robot.py -o custom_path/my_robot.urdf

```

## Validation and Schema Compliance

Before finalizing any file, the system invokes `read_urdf_source()` from [`skills/urdf/scripts/urdf/source.py`](https://github.com/earthtojake/text-to-cad/blob/main/skills/urdf/scripts/urdf/source.py) to enforce strict compliance. This validation layer guarantees that generated files meet robotics pipeline requirements by checking:

- The file extension is `.urdf`
- The root element is `<robot>`
- Every link, joint, and mesh reference follows the URDF schema
- The joint graph forms a single rooted tree with no cycles
- All mesh URIs resolve to actual files (local, package, or remote)

Because validation occurs inside [`skills/urdf/scripts/urdf/source.py`](https://github.com/earthtojake/text-to-cad/blob/main/skills/urdf/scripts/urdf/source.py), any URDF produced through the skill is guaranteed to be well-formed before being handed to downstream tools such as ROS or Gazebo.

## Practical Implementation Examples

### Minimal Robot with ElementTree

Define a basic robot with a single box link and fixed joint:

```python

# my_robot.py

def gen_urdf():
    # Build a tiny robot with a single box link and a fixed joint.

    from xml.etree.ElementTree import Element, SubElement

    robot = Element("robot", {"name": "my_robot"})
    link = SubElement(robot, "link", {"name": "base_link"})
    visual = SubElement(link, "visual")
    geometry = SubElement(visual, "geometry")
    SubElement(geometry, "box", {"size": "0.1 0.1 0.1"})

    # Fixed joint connecting the base to world (implicit root)

    joint = SubElement(robot, "joint", {"name": "fixed_joint", "type": "fixed"})
    SubElement(joint, "parent", {"link": "world"})
    SubElement(joint, "child", {"link": "base_link"})
    return robot

```

### Using the Envelope Dict Pattern

Return a dictionary for generator tracking compatibility:

```python
def gen_urdf():
    return {"xml": """<robot name="my_robot">
        <link name="base_link">
            <visual><geometry><box size="0.1 0.1 0.1"/></geometry></visual>
        </link>
        <joint name="fixed_joint" type="fixed">
            <parent link="world"/><child link="base_link"/>
        </joint>
    </robot>"""}

```

### Referencing External Mesh Files

Include STL or DAE meshes using ROS package URIs:

```python
def gen_urdf():
    from xml.etree.ElementTree import Element, SubElement
    robot = Element("robot", {"name": "mesh_bot"})
    link = SubElement(robot, "link", {"name": "link1"})
    visual = SubElement(link, "visual")
    geometry = SubElement(visual, "geometry")
    # Local relative mesh file (e.g., models/meshes/part.stl)

    SubElement(geometry, "mesh", {"filename": "package://my_pkg/meshes/part.stl"})
    return robot

```

The CLI resolves `package://` URIs using the optional `package_map` argument of `read_urdf_source`, ensuring the mesh file exists before finalizing the URDF.

## Key Source Files in the Repository

The complete pipeline relies on these specific components:

- **[`skills/urdf/scripts/urdf/source.py`](https://github.com/earthtojake/text-to-cad/blob/main/skills/urdf/scripts/urdf/source.py)**: Contains `read_urdf_source()` for parsing URDF files, validating links and joints, and verifying mesh URI resolution
- **[`skills/urdf/scripts/urdf/cli.py`](https://github.com/earthtojake/text-to-cad/blob/main/skills/urdf/scripts/urdf/cli.py)**: CLI driver that loads user scripts, calls `gen_urdf`, writes `.urdf` files, and invokes the validator
- **[`tests/python/skills/urdf/test_source.py`](https://github.com/earthtojake/text-to-cad/blob/main/tests/python/skills/urdf/test_source.py)**: Unit tests covering link/joint validation and mesh handling logic
- **[`tests/python/skills/urdf/test_cli.py`](https://github.com/earthtojake/text-to-cad/blob/main/tests/python/skills/urdf/test_cli.py)**: Integration tests ensuring correct generation and error handling across the CLI workflow
- **[`skills/urdf/scripts/packages/cadpy_metadata/src/cadpy_metadata/generator.py`](https://github.com/earthtojake/text-to-cad/blob/main/skills/urdf/scripts/packages/cadpy_metadata/src/cadpy_metadata/generator.py)**: Supplies `track_generation_run` used by the CLI for reproducible build tracking

## Summary

- The URDF skill requires a `gen_urdf()` function that returns an ElementTree Element, raw XML string, or envelope dict
- The CLI tool in [`skills/urdf/scripts/urdf/cli.py`](https://github.com/earthtojake/text-to-cad/blob/main/skills/urdf/scripts/urdf/cli.py) automates generation and invokes strict validation
- `read_urdf_source()` in [`skills/urdf/scripts/urdf/source.py`](https://github.com/earthtojake/text-to-cad/blob/main/skills/urdf/scripts/urdf/source.py) enforces schema compliance, tree structure integrity, and mesh file resolution
- The system supports ROS-standard `package://` URIs for referencing external mesh assets

## Frequently Asked Questions

### What return type should I use for gen_urdf()?

You can return an `xml.etree.ElementTree.Element` for programmatic construction, a raw XML string for templating, or a dict with an `"xml"` key. All three formats are normalized by the CLI logic in [`skills/urdf/scripts/urdf/cli.py`](https://github.com/earthtojake/text-to-cad/blob/main/skills/urdf/scripts/urdf/cli.py) before validation occurs.

### How does the system validate mesh file references?

The `read_urdf_source()` function in [`skills/urdf/scripts/urdf/source.py`](https://github.com/earthtojake/text-to-cad/blob/main/skills/urdf/scripts/urdf/source.py) resolves all mesh URIs against local filesystem paths, ROS package maps, or remote URLs. It verifies file existence before the URDF is finalized, preventing broken references in downstream simulation tools.

### Can I specify a custom output path for the generated URDF?

Yes. Use the syntax `python -m urdf script.py=output.urdf` for inline path specification, or use the `-o` flag followed by the desired path. The CLI driver handles path resolution and ensures the `.urdf` extension is preserved for compatibility with ROS tools.

### Does the URDF skill support ROS package:// URIs?

Yes. When referencing meshes, you can use `package://my_pkg/meshes/part.stl` syntax. The validation logic resolves these URIs using the optional `package_map` argument in `read_urdf_source()`, making the output compatible with standard ROS package structures.