Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Expert skill for robot model creation and validation in URDF and SDF formats. Generate URDF files with proper link-joint hierarchy, create Xacro macros, calculate inertial properties, configure joint types, and validate models.
.claude/skills/a5c-ai-urdf-sdf-model/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-01 | ✗→✓ | ▲ Improved | 44% | 0% |
| case-02 | ✗→✓ | ▲ Improved | 77% | 0% |
| case-03 | ✗→✓ | ▲ Improved | 82% | 0% |
| case-12 | ✗→✓ | ▲ Improved | 694% | 0% |
| case-13 | ✗→✓ | ▲ Improved | 259% | 0% |
You are urdf-sdf-model - a specialized skill for robot model creation and validation in URDF (Unified Robot Description Format) and SDF (Simulation Description Format).
This skill enables AI-powered robot modeling including:
Generate URDF files with proper structure:
xml<?xml version="1.0"?> <robot name="my_robot" xmlns:xacro="http://www.ros.org/wiki/xacro"> <!-- Materials --> <material name="blue"> <color rgba="0.0 0.0 0.8 1.0"/> </material> <!-- Base Link --> <link name="base_link"> <visual> <geometry> <box size="0.5 0.3 0.1"/> </geometry> <material name="blue"/> </visual> <collision> <geometry> <box size="0.5 0.3 0.1"/> </geometry> </collision> <inertial> <mass value="10.0"/> <origin xyz="0 0 0" rpy="0 0 0"/> <inertia ixx="0.0833" ixy="0" ixz="0" iyy="0.2167" iyz="0" izz="0.2833"/> </inertial> </link> <!-- Wheel Joint --> <joint name="wheel_joint" type="continuous"> <parent link="base_link"/> <child link="wheel_link"/> <origin xyz="0.2 0.15 -0.05" rpy="-1.5708 0 0"/> <axis xyz="0 0 1"/> <limit effort="10" velocity="10"/> <dynamics damping="0.1" friction="0.1"/> </joint> <!-- Wheel Link --> <link name="wheel_link"> <visual> <geometry> <cylinder radius="0.05" length="0.02"/> </geometry> </visual> <collision> <geometry> <cylinder radius="0.05" length="0.02"/> </geometry> </collision> <inertial> <mass value="0.5"/> <inertia ixx="0.0003" ixy="0" ixz="0" iyy="0.0003" iyz="0" izz="0.0006"/> </inertial> </link> </robot>
Create modular robot descriptions with Xacro:
xml<?xml version="1.0"?> <robot name="my_robot" xmlns:xacro="http://www.ros.org/wiki/xacro"> <!-- Properties --> <xacro:property name="wheel_radius" value="0.05"/> <xacro:property name="wheel_width" value="0.02"/> <xacro:property name="wheel_mass" value="0.5"/> <!-- Inertia Macros --> <xacro:macro name="cylinder_inertia" params="m r h"> <inertia ixx="${m*(3*r*r+h*h)/12}" ixy="0" ixz="0" iyy="${m*(3*r*r+h*h)/12}" iyz="0" izz="${m*r*r/2}"/> </xacro:macro> <xacro:macro name="box_inertia" params="m x y z"> <inertia ixx="${m*(y*y+z*z)/12}" ixy="0" ixz="0" iyy="${m*(x*x+z*z)/12}" iyz="0" izz="${m*(x*x+y*y)/12}"/> </xacro:macro> <!-- Wheel Macro --> <xacro:macro name="wheel" params="prefix parent x_offset y_offset"> <joint name="${prefix}_wheel_joint" type="continuous"> <parent link="${parent}"/> <child link="${prefix}_wheel_link"/> <origin xyz="${x_offset} ${y_offset} 0" rpy="-1.5708 0 0"/> <axis xyz="0 0 1"/> <limit effort="10" velocity="10"/> <dynamics damping="0.1" friction="0.1"/> </joint> <link name="${prefix}_wheel_link"> <visual> <geometry> <cylinder radius="${wheel_radius}" length="${wheel_width}"/> </geometry> <material name="black"/> </visual> <collision> <geometry> <cylinder radius="${wheel_radius}" length="${wheel_width}"/> </geometry> </collision> <inertial> <mass value="${wheel_mass}"/> <xacro:cylinder_inertia m="${wheel_mass}" r="${wheel_radius}" h="${wheel_width}"/> </inertial> </link> <!-- Gazebo friction --> <gazebo reference="${prefix}_wheel_link"> <mu1>1.0</mu1> <mu2>1.0</mu2> <kp>1e6</kp> <kd>1.0</kd> </gazebo> </xacro:macro> <!-- Instantiate wheels --> <xacro:wheel prefix="front_left" parent="base_link" x_offset="0.15" y_offset="0.12"/> <xacro:wheel prefix="front_right" parent="base_link" x_offset="0.15" y_offset="-0.12"/> <xacro:wheel prefix="rear_left" parent="base_link" x_offset="-0.15" y_offset="0.12"/> <xacro:wheel prefix="rear_right" parent="base_link" x_offset="-0.15" y_offset="-0.12"/> </robot>
Calculate inertia tensors for common geometries:
pythonimport numpy as np def box_inertia(mass, x, y, z): """Calculate inertia tensor for a box centered at origin.""" ixx = mass * (y**2 + z**2) / 12 iyy = mass * (x**2 + z**2) / 12 izz = mass * (x**2 + y**2) / 12 return {'ixx': ixx, 'iyy': iyy, 'izz': izz, 'ixy': 0, 'ixz': 0, 'iyz': 0} def cylinder_inertia(mass, radius, height): """Calculate inertia tensor for a cylinder along z-axis.""" ixx = mass * (3 * radius**2 + height**2) / 12 iyy = mass * (3 * radius**2 + height**2) / 12 izz = mass * radius**2 / 2 return {'ixx': ixx, 'iyy': iyy, 'izz': izz, 'ixy': 0, 'ixz': 0, 'iyz': 0} def sphere_inertia(mass, radius): """Calculate inertia tensor for a solid sphere.""" i = 2 * mass * radius**2 / 5 return {'ixx': i, 'iyy': i, 'izz': i, 'ixy': 0, 'ixz': 0, 'iyz': 0} def mesh_inertia_from_stl(stl_file, mass, density=None): """Estimate inertia from STL mesh using convex hull approximation.""" # Use trimesh or similar library for accurate calculation import trimesh mesh = trimesh.load(stl_file) mesh.density = density if density else mass / mesh.volume return mesh.moment_inertia
Configure different joint types:
xml<!-- Revolute Joint (limited rotation) --> <joint name="arm_joint" type="revolute"> <parent link="base"/> <child link="arm"/> <origin xyz="0 0 0.1" rpy="0 0 0"/> <axis xyz="0 1 0"/> <limit lower="-1.57" upper="1.57" effort="100" velocity="1.0"/> <dynamics damping="0.5" friction="0.1"/> </joint> <!-- Continuous Joint (unlimited rotation) --> <joint name="wheel_joint" type="continuous"> <parent link="base"/> <child link="wheel"/> <axis xyz="0 0 1"/> <limit effort="10" velocity="10"/> </joint> <!-- Prismatic Joint (linear motion) --> <joint name="slider_joint" type="prismatic"> <parent link="base"/> <child link="slider"/> <origin xyz="0 0 0"/> <axis xyz="0 0 1"/> <limit lower="0" upper="0.5" effort="50" velocity="0.5"/> </joint> <!-- Fixed Joint (no motion) --> <joint name="sensor_mount" type="fixed"> <parent link="base"/> <child link="sensor"/> <origin xyz="0.1 0 0.05" rpy="0 0 0"/> </joint>
Add sensors to the robot model:
xml<!-- Camera Sensor --> <link name="camera_link"> <visual> <geometry> <box size="0.02 0.05 0.02"/> </geometry> </visual> </link> <joint name="camera_joint" type="fixed"> <parent link="base_link"/> <child link="camera_link"/> <origin xyz="0.2 0 0.1" rpy="0 0 0"/> </joint> <gazebo reference="camera_link"> <sensor type="camera" name="camera"> <update_rate>30.0</update_rate> <camera> <horizontal_fov>1.3962634</horizontal_fov> <image> <width>640</width> <height>480</height> <format>R8G8B8</format> </image> <clip> <near>0.02</near> <far>100</far> </clip> </camera> <plugin name="camera_plugin" filename="libgazebo_ros_camera.so"> <ros> <namespace>/robot</namespace> <remapping>image_raw:=camera/image_raw</remapping> <remapping>camera_info:=camera/camera_info</remapping> </ros> <frame_name>camera_link</frame_name> </plugin> </sensor> </gazebo> <!-- LiDAR Sensor --> <link name="lidar_link"> <visual> <geometry> <cylinder radius="0.03" length="0.04"/> </geometry> </visual> </link> <gazebo reference="lidar_link"> <sensor type="ray" name="lidar"> <pose>0 0 0 0 0 0</pose> <visualize>true</visualize> <update_rate>10</update_rate> <ray> <scan> <horizontal> <samples>360</samples> <resolution>1</resolution> <min_angle>-3.14159</min_angle> <max_angle>3.14159</max_angle> </horizontal> </scan> <range> <min>0.1</min> <max>10.0</max> <resolution>0.01</resolution> </range> </ray> <plugin name="lidar_plugin" filename="libgazebo_ros_ray_sensor.so"> <ros> <namespace>/robot</namespace> <remapping>~/out:=scan</remapping> </ros> <output_type>sensor_msgs/LaserScan</output_type> <frame_name>lidar_link</frame_name> </plugin> </sensor> </gazebo>
Validate URDF models:
bash# Check URDF syntax check_urdf robot.urdf # Process Xacro and check xacro robot.urdf.xacro > robot.urdf && check_urdf robot.urdf # Visualize URDF tree urdf_to_graphviz robot.urdf # View in RViz ros2 launch urdf_tutorial display.launch.py model:=robot.urdf.xacro # Convert URDF to SDF gz sdf -p robot.urdf > robot.sdf
Generate SDF for Gazebo:
xml<?xml version='1.0'?> <sdf version='1.7'> <model name='my_robot'> <link name='base_link'> <inertial> <mass>10.0</mass> <inertia> <ixx>0.0833</ixx> <iyy>0.2167</iyy> <izz>0.2833</izz> </inertia> </inertial> <collision name='base_collision'> <geometry> <box> <size>0.5 0.3 0.1</size> </box> </geometry> <surface> <friction> <ode> <mu>1.0</mu> <mu2>1.0</mu2> </ode> </friction> </surface> </collision> <visual name='base_visual'> <geometry> <box> <size>0.5 0.3 0.1</size> </box> </geometry> <material> <ambient>0.0 0.0 0.8 1</ambient> </material> </visual> </link> </model> </sdf>
This skill can leverage the following MCP servers for enhanced capabilities:
| Server | Description | Installation | |--------|-------------|--------------| | CAD-Query MCP | Parametric 3D modeling | mcpservers.org | | FreeCAD MCP | FreeCAD integration | GitHub | | Blender MCP | Mesh creation and editing | blender-mcp.com | | OpenSCAD MCP | Parametric modeling | playbooks.com |
This skill integrates with the following processes:
robot-urdf-sdf-model.js - Primary model creation processrobot-system-design.js - System architecture with modelsmoveit-manipulation-planning.js - MoveIt configurationgazebo-simulation-setup.js - Simulation model setupWhen executing operations, provide structured output:
json{ "operation": "create-urdf", "robotName": "my_robot", "status": "success", "validation": { "syntaxValid": true, "inertiasValid": true, "jointsValid": true }, "artifacts": [ "urdf/my_robot.urdf.xacro", "meshes/base_link.stl", "meshes/wheel.stl" ], "statistics": { "links": 5, "joints": 4, "sensors": 2 } }
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-01 | fail→pass | 29,067 | 29,325 | +1% | 1 | 1 | 0% | 7,111 | 10,230 | +44% | 0 | 0 | — |
case-02 | fail→pass | 25,274 | 26,804 | +6% | 1 | 1 | 0% | 5,360 | 9,469 | +77% | 0 | 0 | — |
case-03 | fail→pass | 29,837 | 27,292 | -9% | 1 | 1 | 0% | 6,196 | 11,298 | +82% | 0 | 0 | — |
case-04 | pass→pass | 46,787 | 30,246 | -35% | 1 | 1 | 0% | 7,649 | 9,828 | +28% | 0 | 0 | — |
case-05 | pass→pass | 20,025 | 18,426 | -8% | 1 | 1 | 0% | 3,535 | 7,209 | +104% | 0 | 0 | — |
case-06 | pass→pass | 20,061 | 20,775 | +4% | 1 | 1 | 0% | 3,160 | 8,964 | +184% | 0 | 0 | — |
case-07 | pass→pass | 11,265 | 12,847 | +14% | 1 | 1 | 0% | 1,585 | 6,230 | +293% | 0 | 0 | — |
case-08 | pass→pass | 9,299 | 12,538 | +35% | 1 | 1 | 0% | 2,161 | 6,082 | +181% | 0 | 0 | — |
case-09 | pass→pass | 5,446 | 11,800 | +117% | 1 | 1 | 0% | 1,161 | 5,900 | +408% | 0 | 0 | — |
case-10 | pass→pass | 9,290 | 4,043 | -56% | 1 | 1 | 0% | 761 | 4,976 | +554% | 0 | 0 | — |
case-11 | pass→pass | 7,521 | 5,064 | -33% | 1 | 1 | 0% | 1,345 | 5,083 | +278% | 0 | 0 | — |
case-12 | fail→pass | 8,761 | 4,865 | -44% | 1 | 1 | 0% | 656 | 5,206 | +694% | 0 | 0 | — |
case-13 | fail→pass | 11,594 | 10,580 | -9% | 1 | 1 | 0% | 1,454 | 5,219 | +259% | 0 | 0 | — |
case-14 | pass→pass | 6,286 | 6,620 | +5% | 1 | 1 | 0% | 1,290 | 5,689 | +341% | 0 | 0 | — |
case-15 | pass→pass | 4,317 | 9,168 | +112% | 1 | 1 | 0% | 932 | 5,202 | +458% | 0 | 0 | — |
case-16 | pass→pass | 10,045 | 8,633 | -14% | 1 | 1 | 0% | 2,085 | 5,983 | +187% | 0 | 0 | — |
case-17 | fail→pass | 15,411 | 10,275 | -33% | 1 | 1 | 0% | 2,109 | 5,452 | +159% | 0 | 0 | — |
case-18 | pass→pass | 11,074 | 5,953 | -46% | 1 | 1 | 0% | 1,178 | 5,414 | +360% | 0 | 0 | — |
case-19 | fail→pass | 12,771 | 8,730 | -32% | 1 | 1 | 0% | 1,614 | 5,081 | +215% | 0 | 0 | — |
case-20 | pass→pass | 11,026 | 8,645 | -22% | 1 | 1 | 0% | 2,012 | 5,907 | +194% | 0 | 0 | — |
case-21 | pass→pass | 8,014 | 4,440 | -45% | 1 | 1 | 0% | 566 | 4,982 | +780% | 0 | 0 | — |
case-22 | pass→pass | 15,588 | 9,829 | -37% | 1 | 1 | 0% | 2,179 | 6,396 | +194% | 0 | 0 | — |
case-23 | fail→pass | 4,110 | 8,131 | +98% | 1 | 1 | 0% | 874 | 4,992 | +471% | 0 | 0 | — |
case-24 | fail→pass | 5,488 | 3,761 | -31% | 1 | 1 | 0% | 1,020 | 5,077 | +398% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 24 cases were attempted. The headline lift of +38 percentage points is the difference between those two pass rates over the 24 comparable cases.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.