How to Build SDF Simulator Models Using Text‑to‑CAD: A Complete Guide
Text‑to‑CAD provides an SDF skill that converts Python scripts into validated SDF simulator models through a gen_sdf() function, XML validation, and optional Gazebo compliance checks.
The text-to-cad repository by earthtojake includes a specialized SDF skill that streamlines the creation of simulator-ready models for Gazebo and Ignition. Instead of manually authoring complex XML, developers write concise Python scripts that generate structured SDF output with automated validation and intelligent mesh resolution.
Understanding the SDF Skill Architecture
The SDF skill operates through three distinct layers that transform a user script into a production-ready simulator file.
The Three-Layer Pipeline
-
User Script Layer: Supplies a
gen_sdf()function returning SDF XML viaskills/sdf/scripts/sdf/source.py. This layer utilizes theSdfSourcedataclass to capture parsed models, including version strings, model and world names, plus lists of links, joints, and mesh file paths. -
CLI Driver Layer: Orchestrates execution through
skills/sdf/scripts/sdf/cli.py. Thegenerate_sdf_targets()function manages module loading, payload normalization via_normalize_sdf_payload(), and final report generation. -
Validation Layer: Ensures structural integrity through
skills/sdf/scripts/sdf/validation.py. Thevalidate_sdf_xml()function enforces SDF schema compliance, validates unique naming conventions via_raise_on_duplicates(), and resolves mesh references through_resolve_local_mesh_uri().
URI Resolution and External Assets
The validator handles multiple URI schemes intelligently. External references using model://, package://, http(s)://, and fuel:// pass through unchanged for runtime resolution, while file:// URIs and plain relative paths resolve against the script's base directory. This architecture allows mesh files to sit adjacent to source scripts or in centralized models/ directories without path ambiguity.
Creating Your First SDF Generator Script
Every generator script must expose a zero-argument gen_sdf() function returning one of three supported envelope formats: an ET.Element representing the <sdf> root, a raw XML string, or a dictionary containing an "xml" key with optional "metadata", "assumptions", and "warnings" fields.
Minimal Robot Example
Create a file named my_robot.sdf.py:
import xml.etree.ElementTree as ET
def gen_sdf():
sdf = ET.Element("sdf", version="1.7")
world = ET.SubElement(sdf, "world", name="default")
model = ET.SubElement(world, "model", name="simple_box")
link = ET.SubElement(model, "link", name="body")
visual = ET.SubElement(link, "visual", name="visual")
geometry = ET.SubElement(visual, "geometry")
mesh = ET.SubElement(geometry, "mesh")
uri = ET.SubElement(mesh, "uri")
uri.text = "model://my_robot/meshes/box.stl"
return sdf
This script constructs a basic SDF tree programmatically. The function returns an ET.Element object, which the CLI normalizes internally before validation and file generation.
Envelope Format with Metadata
For production workflows requiring traceability, return the dictionary envelope to embed provenance data:
def gen_sdf():
sdf = ET.Element("sdf", version="1.7")
world = ET.SubElement(sdf, "world", name="default")
model = ET.SubElement(world, "model", name="advanced_robot")
# ... additional link and joint definitions ...
return {
"xml": sdf,
"metadata": {"author": "Jane Doe", "version": "1.0.0"},
"assumptions": [
{"code": "mesh_placeholder", "message": "Mesh files provided at runtime"}
],
"warnings": [
{"code": "inertia_approx", "message": "Using approximate inertia tensor"}
]
}
The CLI processes this envelope through _normalize_sdf_payload() and embeds metadata via xml_with_text_to_cad_metadata() from the cadpy_metadata package, making outputs traceable to their Python source.
Running the CLI and Validating Output
The command-line interface in skills/sdf/scripts/sdf/cli.py automates module loading, validation, and file generation.
Basic Generation
Execute your script using the installed entry point:
text-to-cad sdf my_robot.sdf.py
Alternatively, invoke the module directly:
python -m text_to_cad.sdf my_robot.sdf.py
The _load_generator_module() function dynamically imports your script, temporarily injects its directory into sys.path, and invokes gen_sdf(). By default, output writes to the same directory with the .sdf extension.
Strict Validation with Gazebo
For deployment-ready simulator assets, enforce Gazebo's native validator:
text-to-cad sdf my_robot.sdf.py=output/robot.sdf \
--gz-check required \
--strict
The --gz-check required flag triggers run_gz_sdf_check() from skills/sdf/scripts/sdf/external.py, executing the external gz sdf --check command and merging its findings with internal validation. The --strict flag elevates warnings to fatal errors, ensuring non-zero exit codes on any validation issue.
Debugging Validation Failures
All errors surface as SdfSourceError instances containing human-readable messages with repo-relative file references. Common failures include duplicate names detected by _raise_on_duplicates() in skills/sdf/scripts/sdf/source.py or unresolvable mesh paths flagged by _resolve_local_mesh_uri().
Advanced Configuration and Target Management
The CLI supports complex generation workflows through flexible target specifications and metadata handling.
Multiple Target Generation
The _resolve_target_specs() function accepts multiple SOURCE=OUTPUT mappings:
text-to-cad sdf robot.sdf.py=models/robot.sdf gripper.sdf.py=models/gripper.sdf
You may also use the global --output flag to specify a destination directory for all generated files.
Local Mesh Path Resolution
Place mesh files relative to your script or use standard Gazebo URI schemes. The validator at skills/sdf/scripts/sdf/source.py distinguishes between external schemes (preserved as-is) and local paths (resolved against the script location), supporting workflows where .stl or .dae files reside in models/my_robot/meshes/ adjacent to the generator script.
Summary
- Build SDF simulator models by writing Python scripts containing a
gen_sdf()function that returns XML elements, raw strings, or metadata envelopes. - The CLI driver at
skills/sdf/scripts/sdf/cli.pyhandles dynamic module loading via_load_generator_module(), payload normalization, and orchestrates the validation pipeline. - Structural validation occurs in
skills/sdf/scripts/sdf/validation.pythroughvalidate_sdf_xml(), enforcing unique names, valid mesh URIs, and SDF schema compliance. - Use
--gz-check requiredto invoke Gazebo's native validator viaskills/sdf/scripts/sdf/external.pyand--strictto fail on warnings. - The
SdfSourcedataclass inskills/sdf/scripts/sdf/source.pyprovides the internal representation for models, worlds, links, and joints. - Metadata embedding via
cadpy_metadatautilities ensures generated files contain provenance blocks tracing back to the original Python source.
Frequently Asked Questions
What Python function must my script define to build SDF simulator models?
Your script must define a zero-argument callable named gen_sdf() that returns either an xml.etree.ElementTree.Element representing the SDF root, a raw XML string, or a dictionary containing an "xml" key. The CLI loader in skills/sdf/scripts/sdf/cli.py specifically looks for this function signature when executing _load_generator_module().
How does text-to-cad handle external mesh file references?
The validator in skills/sdf/scripts/sdf/source.py preserves external URI schemes including model://, package://, http(s)://, and fuel:// unchanged for runtime resolution by the simulator. Local file:// URIs and relative paths resolve against the script's directory via _resolve_local_mesh_uri(), allowing meshes to sit next to source files or in structured models/ directories.
Can I enforce Gazebo-specific validation rules when generating SDF files?
Yes. Pass --gz-check required to the CLI to invoke run_gz_sdf_check(), which executes the external gz sdf --check command through skills/sdf/scripts/sdf/external.py. Combine this with --strict to treat all warnings as fatal errors, ensuring the generated SDF meets Gazebo's strictest compliance requirements before deployment.
Where does the SDF skill store metadata about the generation process?
The CLI calls xml_with_text_to_cad_metadata() from the cadpy_metadata package located at packages/cadpy_metadata/src/cadpy_metadata/generator.py to embed a provenance block inside the generated XML. This block captures the original Python source identity and any assumptions or warnings provided in the envelope's "metadata" field, creating an auditable trail from script to simulator model.
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 →