How to Generate SDF Simulation Models with Physics and Sensors Using Text-to-CAD
Use the SDF skill's gen_sdf() function with the builder helpers API to create Python generators that output validated SDFormat XML files complete with physics joints, inertial properties, and sensor definitions.
The text-to-CAD repository provides a specialized SDF skill that transforms concise Python scripts into simulator-ready SDFormat (.sdf) models. This workflow follows a source-as-truth pattern, allowing you to programmatically define complex simulation models with physics and sensors while eliminating common XML formatting errors through automated validation.
Understanding the SDF Skill Architecture
The SDF skill is built around three core components that work together to produce valid simulation models according to the architecture defined in skills/sdf/SKILL.md.
The gen_sdf() Source File
Every SDF model starts as a Python script containing a gen_sdf() function located in skills/sdf/scripts/sdf/.... This function serves as the single source of truth for your model definition. Rather than editing XML directly, you modify this Python file and regenerate the .sdf output, ensuring reproducibility and maintaining clear version control history.
Builder Helpers Library
The sdf.builder module provides typed helper functions that construct an xml.etree.ElementTree safely. Implemented in skills/sdf/scripts/sdf/builder.py and documented in skills/sdf/references/builder-helpers.md, these helpers validate numeric inputs, enforce correct vector lengths, and eliminate common LLM-generated XML bugs such as invalid poses, missing relative_to attributes, or non-finite numbers.
CLI Launcher and Validation
The command-line interface in skills/sdf/scripts/sdf/cli.py orchestrates the generation process. It parses your source file, executes gen_sdf(), writes the output .sdf, and runs the bundled validation checks defined in skills/sdf/scripts/sdf/validation.py. The CLI can optionally invoke gz sdf --check when the --gz-check flag is provided, and automatically hands off valid results to the CAD Viewer ($cad-viewer).
Step-by-Step Workflow to Generate SDF Models
Follow this systematic approach to create simulation models with physics and sensors:
-
Create a Python generator file (e.g.,
my_robot.py) containing agen_sdf()function that uses builder helpers to construct the XML tree. -
Add physics and sensor elements using
joint(),inertial(), andsensor()helpers to define mechanical properties and sensing capabilities. -
Run the CLI to generate and validate the SDF file using
python scripts/sdf my_robot.py -o my_robot.sdf. -
Validate and visualize the output through the bundled validator and optional CAD Viewer link for final visual confirmation.
Adding Physics and Sensors
To generate SDF simulation models with physics and sensors, you must explicitly define inertial properties, joint mechanics, and sensor configurations using the builder API.
Defining Joints and Inertial Properties
Attach inertial elements to each link using inertial(link, mass, inertia) with a 3x3 inertia matrix. Create mechanical connections using joint() with parameters for joint_type (e.g., revolute, prismatic), parent_link, child_link, and axis_xyz. Specify physical limits using the limits dictionary with keys for lower, upper, effort, and velocity.
Configuring Sensors
Add perception capabilities by calling sensor(parent, name, sensor_type, ...) on a link element. Required parameters include pose (position and orientation), topic (ROS/Gazebo topic name), and update_rate. The builder automatically validates that sensor types are non-empty and update rates are finite numbers, preventing runtime configuration errors.
Complete Code Example
Below is a complete Python generator that creates a robot with a revolute joint, inertial properties, and a lidar sensor:
# my_robot.py
from sdf.builder import (
sdf_root, model, link, joint, inertial,
sensor, visual, collision, box,
)
def gen_sdf():
# Root SDF document (version 1.12)
doc = sdf_root(version="1.12")
# Top-level model
robot = model(doc, "my_robot", static=False)
# Base link with box geometry
base = link(robot, "base_link")
base_vis = visual(base, "base_vis")
box(base_vis, (0.3, 0.2, 0.1))
base_col = collision(base, "base_col")
box(base_col, (0.3, 0.2, 0.1))
inertial(base, mass=2.0,
inertia=[[0.01, 0, 0],
[0, 0.02, 0],
[0, 0, 0.03]])
# Revolute joint connecting base to arm
joint(robot,
name="shoulder_joint",
joint_type="revolute",
parent_link="base_link",
child_link="arm_link",
axis_xyz=(0, 0, 1),
limits={"lower": -1.57, "upper": 1.57, "effort": 10, "velocity": 1.0},
pose=(0, 0, 0.1, 0, 0, 0))
# Arm link
arm = link(robot, "arm_link")
# Lidar sensor attached to arm
sensor(arm,
name="front_lidar",
sensor_type="lidar",
pose=(0.1, 0, 0.2, 0, 0, 0),
topic="/lidar",
update_rate=10.0)
return {"xml": doc}
Generate the SDF file and validate it:
python scripts/sdf my_robot.py -o my_robot.sdf
To additionally verify with Gazebo tools if installed:
python scripts/sdf my_robot.py --gz-check auto
Validation and Visualization
After generation, the CLI produces a compact validation report. The bundled validator from skills/sdf/scripts/sdf/validation.py checks for unique element names, finite numeric values, and proper coordinate frame usage. If the CAD Viewer skill is installed, the CLI automatically launches it and prints a URL in the format http://localhost:.../?dir=/abs/path/to/models&file=my_robot.sdf, allowing you to visually inspect sensor frames and verify static metadata. Note that the viewer displays geometry and transforms but does not execute physics plugins.
Summary
- Source-only editing: Modify only the Python
gen_sdf()file; never edit the generated.sdfdirectly to maintain reproducibility. - Builder helpers: Use functions from
sdf.builderto safely construct XML with automatic validation of poses, inertial properties, and sensor parameters. - Physics definition: Include
inertial()elements on all links andjoint()elements with proper types and limits to enable realistic simulation. - CLI workflow: Run
python scripts/sdf <source.py>to generate, validate, and optionally check withgz sdf --checkbefore visualizing in the CAD Viewer. - Zero external dependencies: The builder uses only Python standard library (
xml.etree.ElementTree), making the skill portable across any Python environment.
Frequently Asked Questions
How do I add a physics plugin to my SDF model?
Use the plugin() helper from sdf.builder inside your gen_sdf() function. Pass the parent element (typically the model), a unique name, the shared library filename, and a dictionary of parameters. For example: plugin(robot, name="gravity", filename="libgz-plugin-gravity.so", params={"gravity": "0 0 -9.81"}).
What validation checks does the bundled validator perform?
According to skills/sdf/scripts/sdf/validation.py, the validator ensures element names are unique within their scope, verifies that all numeric values are finite (not NaN or infinity), checks that pose vectors have exactly six elements, and validates that sensor update rates are positive numbers.
Can I use this workflow without Gazebo installed?
Yes. The SDF skill is deliberately dependency-free and does not require Gazebo or numpy. While the CLI can optionally invoke gz sdf --check if Gazebo is present, the bundled validation in validation.py and the CAD Viewer handoff work using only Python standard libraries.
Why does the CLI generate a CAD Viewer URL instead of running simulation?
The workflow follows the text-to-CAD principle that every SDF modification must be visually confirmed before simulation. The CAD Viewer (accessed via $cad-viewer) displays the static model geometry, coordinate frames, and sensor positions to catch errors early, while actual physics simulation requires running the generated .sdf in a simulator like Gazebo separately.
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 →