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.Elementrepresenting 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:
- ElementTree Element: Best for programmatic construction using Python's XML API
- Raw XML String: Useful for templating or embedding pre-written XML fragments
- 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:
skills/urdf/scripts/urdf/source.py: Containsread_urdf_source()for parsing URDF files, validating links and joints, and verifying mesh URI resolutionskills/urdf/scripts/urdf/cli.py: CLI driver that loads user scripts, callsgen_urdf, writes.urdffiles, and invokes the validatortests/python/skills/urdf/test_source.py: Unit tests covering link/joint validation and mesh handling logictests/python/skills/urdf/test_cli.py: Integration tests ensuring correct generation and error handling across the CLI workflowskills/urdf/scripts/packages/cadpy_metadata/src/cadpy_metadata/generator.py: Suppliestrack_generation_runused 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.pyautomates generation and invokes strict validation read_urdf_source()inskills/urdf/scripts/urdf/source.pyenforces 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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →