How to Create URDF Robot Description Files Using CAD Skills

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 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 handles script execution, output normalization, and file persistence.

Execute a generator script with the default output path:

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:

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 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, 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:


# 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:

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:

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:

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 automates generation and invokes strict validation
  • read_urdf_source() in 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 before validation occurs.

How does the system validate mesh file references?

The read_urdf_source() function in 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.

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 →