| name | urdf-fundamentals |
| description | Core URDF/Xacro syntax, joint types, kinematic structures, and URDF validation for robot descriptions |
URDF Fundamentals Skill
This skill provides comprehensive guidance on creating, structuring, and validating URDF (Unified Robot Description Format) files for robot descriptions.
When to Use This Skill
Use this skill when:
- Creating a new URDF from scratch
- Understanding URDF/Xacro syntax and structure
- Designing kinematic chains and joint hierarchies
- Adding links, joints, or coordinate frames
- Debugging URDF validation errors
- Implementing inertial properties and mass
- Understanding and using package paths and mesh references
URDF File Structure
Basic Robot Structure
Every URDF file has this basic structure:
<?xml version="1.0"?>
<robot name="robot_name">
<link name="base_link">
<visual>
<geometry>
<box size="0.5 0.3 0.1"/>
</geometry>
<material name="blue">
<color rgba="0 0 1 1"/>
</material>
</visual>
<collision>
<geometry>
<box size="0.5 0.3 0.1"/>
</geometry>
</collision>
<inertial>
<mass value="5.0"/>
<inertia ixx="0.1" ixy="0" ixz="0" iyy="0.1" iyz="0" izz="0.1"/>
</inertial>
</link>
<joint name="base_to_wheel" type="continuous">
<parent link="base_link"/>
<child link="wheel_link"/>
<origin xyz="0.2 0.2 0" rpy="0 1.57 0"/>
<axis xyz="0 1 0"/>
</joint>
<link name="wheel_link">
</link>
</robot>
Key Elements
<link> - Represents a rigid body in the robot:
name - Unique identifier
- Usually contains:
<visual>, <collision>, <inertial> elements
- The first link is typically the "base_link" (root of kinematic chain)
<joint> - Represents a connection between two links:
name - Unique identifier
type - Joint type (revolute, continuous, prismatic, fixed, etc.)
<parent> - Parent link name
<child> - Child link name
<origin> - Relative position and orientation
<axis> - Direction and rotation/translation axis
<limit> - Joint constraints (for movable joints)
<origin> - Specifies relative pose:
xyz - Position (x, y, z in meters)
rpy - Orientation (roll, pitch, yaw in radians)
Links in Detail
Visual Geometry
Used for display and rendering:
<link name="example_link">
<visual>
<geometry>
<box size="x y z"/>
<cylinder radius="r" length="l"/>
<sphere radius="r"/>
<mesh filename="package://robot_name/meshes/part.stl" scale="0.001 0.001 0.001"/>
</geometry>
<origin xyz="0 0 0" rpy="0 0 0"/>
<material name="blue">
<color rgba="0 0 1 1"/>
</material>
</visual>
</link>
Geometry types:
<box size="x y z"/> - Rectangular box in meters
<cylinder radius="r" length="l"/> - Cylinder with radius and length in meters
<sphere radius="r"/> - Sphere with radius in meters
<mesh filename="path" scale="sx sy sz"/> - External 3D mesh file (STL, DAE, OBJ)
Material colors (RGBA):
rgba="1 0 0 1" - Red
rgba="0 1 0 1" - Green
rgba="0 0 1 1" - Blue
rgba="1 1 1 1" - White
rgba="0 0 0 1" - Black
- Last value (alpha) is transparency: 1 = opaque, 0 = transparent
Collision Geometry
Used for physics simulation and collision detection. Should typically be simpler than visual geometry:
<link name="complex_part">
<visual>
<geometry>
<mesh filename="package://robot/meshes/detailed_part.stl"/>
</geometry>
</visual>
<collision>
<geometry>
<box size="0.5 0.3 0.1"/>
</geometry>
</collision>
</link>
Best practices:
- Keep collision geometry simple for performance
- Use multiple collision elements to approximate complex shapes:
<link name="gripper">
<collision>
<origin xyz="0 0 0"/>
<geometry><box size="0.1 0.2 0.05"/></geometry>
</collision>
<collision>
<origin xyz="0.08 0.1 0"/>
<geometry><box size="0.04 0.1 0.05"/></geometry>
</collision>
</link>
Inertial Properties
Define mass and rotational inertia for dynamics simulation:
<link name="base_link">
<inertial>
<mass value="5.0"/>
<inertia ixx="0.1" ixy="0" ixz="0" iyy="0.1" iyz="0" izz="0.2"/>
<origin xyz="0 0 0.05" rpy="0 0 0"/>
</inertial>
</link>
Computing inertia:
- For a box:
I = (1/12) * m * (h^2 + d^2) where h, d are dimensions
- For a cylinder:
I_x = I_y = (1/12) * m * (3*r^2 + h^2), I_z = (1/2) * m * r^2
- For a sphere:
I = (2/5) * m * r^2
Simplified approach:
If exact inertia is unknown, use rough estimates:
<inertial>
<mass value="1.0"/>
<inertia ixx="0.01" ixy="0" ixz="0" iyy="0.01" iyz="0" izz="0.01"/>
</inertial>
Joints in Detail
Joint Types
1. Fixed Joint
No movement - connects two links rigidly:
<joint name="fixed_joint" type="fixed">
<parent link="base_link"/>
<child link="sensor_link"/>
<origin xyz="0.1 0 0.05" rpy="0 0 0"/>
</joint>
Use for:
- Sensors mounted on the robot
- Non-moving parts
- Rigid attachments
2. Revolute Joint
Rotational movement with limits (like a door hinge):
<joint name="shoulder_joint" type="revolute">
<parent link="base_link"/>
<child link="arm_link"/>
<origin xyz="0 0 0.3" rpy="0 0 0"/>
<axis xyz="0 1 0"/>
<limit lower="-1.57" upper="1.57" effort="50" velocity="1.0"/>
</joint>
Parameters:
<axis> - Rotation axis (unit vector, typically one value = 1)
<limit>:
lower, upper - Min/max angles in radians
effort - Maximum torque in Newton-meters
velocity - Maximum angular velocity in rad/s
Common axes:
xyz="1 0 0" - Rotate around X (roll)
xyz="0 1 0" - Rotate around Y (pitch)
xyz="0 0 1" - Rotate around Z (yaw)
3. Continuous Joint
Unlimited rotation (like a wheel):
<joint name="wheel_joint" type="continuous">
<parent link="base_link"/>
<child link="wheel_link"/>
<origin xyz="0.2 0.15 0" rpy="0 1.57 0"/>
<axis xyz="0 1 0"/>
</joint>
Use for:
- Wheels that spin indefinitely
- Rotating shafts
- Continuous rotating joints
Note: No <limit> element needed
4. Prismatic Joint
Linear sliding movement (like a piston):
<joint name="linear_actuator" type="prismatic">
<parent link="base_link"/>
<child link="piston_link"/>
<origin xyz="0 0 0" rpy="0 0 0"/>
<axis xyz="0 0 1"/>
<limit lower="0" upper="0.2" effort="100" velocity="0.1"/>
</joint>
Parameters:
<axis> - Direction of movement (unit vector)
<limit>:
lower, upper - Min/max position in meters
effort - Maximum force in Newtons
velocity - Maximum linear velocity in m/s
5. Planar Joint
Movement in a 2D plane (advanced, less common)
6. Floating Joint
Free 6-DOF movement (for flying robots or underwater vehicles)
Joint Limits and Effort
Always specify limits for revolute joints:
<joint name="elbow" type="revolute">
<axis xyz="0 1 0"/>
<limit lower="-2.0" upper="2.0" effort="30" velocity="1.5"/>
</joint>
If you forget limits: Many simulators will refuse to load the file!
Kinematic Structures
Building a Kinematic Chain
A robot is organized as a tree of links connected by joints:
base_link (root)
|
| shoulder_joint (revolute)
|
arm_link
|
| elbow_joint (revolute)
|
forearm_link
|
| wrist_joint (revolute)
|
hand_link
URDF representation:
<robot name="robot_arm">
<link name="base_link">
<visual>
<geometry><box size="0.3 0.3 0.1"/></geometry>
</visual>
</link>
<joint name="shoulder_joint" type="revolute">
<parent link="base_link"/>
<child link="arm_link"/>
<origin xyz="0 0 0.15"/>
<axis xyz="0 0 1"/>
<limit lower="-1.57" upper="1.57" effort="50" velocity="1"/>
</joint>
<link name="arm_link">
<visual>
<geometry><cylinder radius="0.05" length="0.4"/></geometry>
</visual>
</link>
</robot>
Important Kinematic Rules
- Each link (except root) must have exactly ONE parent joint
- A link can have multiple child joints (branching is okay)
- No cycles allowed - Structure must be a tree
- Root link - Convention is to name it
base_link
- Unique names - Every link and joint must have a unique name
Example: Quadruped Robot
base_link (root)
/ | \ \
/ | \ \
fl_hip fr_hip rl_hip rr_hip
/ / / /
fl_udeg fr_uleg rl_uleg rr_uleg
/ / / /
fl_leg fr_leg rl_leg rr_leg
Coordinate Frames and Transforms
Understanding <origin>
The xyz and rpy in <origin> define the transform from parent to child:
<joint name="example" type="fixed">
<parent link="base_link"/>
<child link="camera_link"/>
<origin xyz="0.1 0 0.2" rpy="0 0 0"/>
</joint>
Rotations (rpy - Roll Pitch Yaw)
Three rotations applied in order: X (roll), Y (pitch), Z (yaw):
<origin xyz="0 0 0" rpy="0 1.57 0"/>
<origin xyz="1 2 3" rpy="0.785 0 0.785"/>
Converting degrees to radians: radians = degrees * π / 180
- 90° = 1.57 rad
- 45° = 0.785 rad
- 180° = 3.14 rad
Visual Origin vs Joint Origin
These are different:
<link name="camera">
<visual>
<origin xyz="0 0 0" rpy="0 0 0"/>
<geometry><box size="0.05 0.05 0.05"/></geometry>
</visual>
</link>
<joint name="camera_mount" type="fixed">
<origin xyz="0.1 0 0.2" rpy="0 0 0"/>
<parent link="base_link"/>
<child link="camera"/>
</joint>
Package and Mesh References
Using ROS Packages
Mesh files and resources use package-relative paths:
<mesh filename="package://my_robot/meshes/wheel.stl"/>
<mesh filename="/home/user/ros_ws/my_robot/meshes/wheel.stl"/>
<mesh filename="meshes/wheel.stl"/>
Package Discovery
The extension finds packages by:
- Scanning workspace for
package.xml files
- Using ROS package commands if available
- Building a package name → path mapping
Best practice: Always use package:// URIs in your URDF files.
Mesh File Paths
Typical project structure:
my_robot/
├── package.xml
├── urdf/
│ └── my_robot.urdf (or .xacro)
└── meshes/
├── base.stl
├── wheel.stl
└── gripper/
├── finger_left.stl
└── finger_right.stl
References in URDF:
<mesh filename="package://my_robot/meshes/base.stl"/>
<mesh filename="package://my_robot/meshes/wheel.stl"/>
<mesh filename="package://my_robot/meshes/gripper/finger_left.stl"/>
Mesh Scaling
Mesh units may differ from URDF (which uses meters):
<mesh filename="package://my_robot/meshes/part.stl" scale="0.001 0.001 0.001"/>
<mesh filename="package://my_robot/meshes/part.stl" scale="0.5 0.5 0.5"/>
Creating Complex Meshes with OpenSCAD
For complex geometry that cannot be created with basic shapes (boxes, cylinders, spheres), use OpenSCAD:
⚠️ IMPORTANT: Unit Mismatch
- OpenSCAD uses millimeters (mm)
- URDF uses meters (m)
- When referencing the STL in URDF, you MUST scale by
0.001 to convert mm → m
Workflow:
- Create a
.scad file with your geometry logic in millimeters (e.g., meshes/robot_foot.scad)
- The VS Code extension automatically converts
.scad → .stl when saved
- Reference the generated
.stl file in your URDF/Xacro (not the .scad)
- Always apply
scale="0.001 0.001 0.001" to convert from mm to meters
Example:
Create meshes/robot_foot.scad (dimensions in millimeters):
// robot_foot.scad - Complex foot geometry
// All dimensions are in MILLIMETERS
module robot_foot(length=100, width=50, height=20) {
difference() {
// Main foot body: 100mm × 50mm × 20mm
cube([length, width, height]);
// Mounting holes: 5mm radius
translate([20, width/2, 0])
cylinder(h=height, r=5);
translate([length-20, width/2, 0])
cylinder(h=height, r=5);
}
}
// Generate foot with dimensions in mm
robot_foot(length=120, width=60, height=25);
Reference in URDF (with 0.001 scale to convert mm → m):
<link name="foot">
<visual>
<geometry>
<mesh filename="package://my_robot/meshes/robot_foot.stl" scale="0.001 0.001 0.001"/>
</geometry>
</visual>
<collision>
<geometry>
<mesh filename="package://my_robot/meshes/robot_foot.stl" scale="0.001 0.001 0.001"/>
</geometry>
</collision>
</link>
Important notes:
- Always use
scale="0.001 0.001 0.001" when referencing OpenSCAD-generated STL files
- OpenSCAD source (
.scad) stays in millimeters - this is standard CAD convention
- Reference the
.stl file in URDF, never the .scad
- The
.scad file is the source; the .stl is what renders
- When you save the
.scad file, the extension automatically regenerates the .stl
- You can see a live preview of the geometry using the preview command
- For parametric geometry reuse, create OpenSCAD modules that other
.scad files can reference
URDF Validation and Best Practices
Common Errors
Error: "Duplicate link/joint name"
- Every link and joint name must be unique
- Check for typos or copy-paste mistakes
Error: "Parent link not found"
<joint name="wheel" type="continuous">
<parent link="baes_link"/>
<child link="wheel"/>
</joint>
Error: "Joint limits missing"
<joint name="arm" type="revolute">
<axis xyz="0 1 0"/>
</joint>
<joint name="arm" type="revolute">
<axis xyz="0 1 0"/>
<limit lower="-1.57" upper="1.57" effort="50" velocity="1"/>
</joint>
Error: "Mesh file not found"
- Verify mesh file exists
- Check spelling and path
- Ensure package name is correct
Validation Workflow
- Syntax check - XML structure is valid
- Preview - Use "URDF: Preview" to visualize
- Take screenshot - Verify geometry matches expectations
- Check kinematic tree - Ensure links and joints form valid tree
- Inspect console - Look for warning messages
Naming Conventions
Follow ROS naming standards:
<link name="base_link"/>
<link name="left_front_wheel"/>
<joint name="base_to_wheel"/>
<joint name="shoulder_joint"/>
<link name="BaseLink"/>
<link name="leftFrontWheel"/>
<joint name="baseToWheel"/>
Comments and Documentation
<?xml version="1.0"?>
<robot name="my_robot" xmlns:xacro="http://www.ros.org/wiki/xacro">
<link name="base_link">
<visual>
<geometry>
<box size="0.5 0.3 0.1"/>
</geometry>
<material name="base_color">
<color rgba="0.8 0.8 0.8 1"/>
</material>
</visual>
<collision>
<geometry>
<box size="0.5 0.3 0.1"/>
</geometry>
</collision>
</link>
<joint name="base_to_fl_wheel" type="continuous">
<parent link="base_link"/>
<child link="fl_wheel"/>
<origin xyz="0.15 0.15 -0.05" rpy="0 1.5708 0"/>
<axis xyz="0 1 0"/>
</joint>
<link name="fl_wheel">
<visual>
<geometry>
<cylinder radius="0.05" length="0.05"/>
</geometry>
<material name="black">
<color rgba="0.1 0.1 0.1 1"/>
</material>
</visual>
</link>
</robot>
Using URDF in the VS Code Extension
Preview Commands
- Right-click URDF file → "URDF: Preview" to open 3D viewer
- Right-click URDF file → "URDF: Take Screenshot" for documentation
- Right-click Xacro file → "URDF: Export" to generate expanded URDF
Validation Features
- XML schema automatically validates syntax
- Hover over elements for documentation
- Go to Definition (F12) jumps to referenced links/joints
- Diagnostics panel shows errors and warnings
Mesh File Viewer
The extension includes a custom 3D viewer for mesh files:
- Opens
.stl, .dae, .glb, .gltf files in 3D
- Supports interactive rotation, zoom, pan
- Displays mesh statistics and properties
Quick Reference: Joint Type Selection
| Joint Type | Movement | Use Case |
|---|
| Fixed | None | Sensors, non-moving attachments |
| Revolute | Rotational (limited) | Robot arms, articulated segments |
| Continuous | Rotational (unlimited) | Wheels, rotating shafts |
| Prismatic | Linear (limited) | Actuators, sliding mechanisms |
| Planar | 2D planar | Advanced wheeled robots |
| Floating | Free 6-DOF | Flying/underwater robots |
Best Practices Summary
✅ DO:
- Start with
base_link as the root
- Use
package:// URIs for mesh files
- Specify joint limits for revolute joints
- Use simple collision geometry
- Include inertial properties for dynamics
- Use meaningful link and joint names
- Take screenshots to verify geometry
- Add XML comments explaining design choices
- Validate in the extension's preview
- Use Xacro macros for repeated structures
❌ DON'T:
- Use absolute file paths
- Forget joint limits
- Make collision geometry too complex
- Reference non-existent mesh files
- Create cycles in the kinematic tree
- Use inconsistent naming conventions
- Skip inertial properties if doing simulation
- Make geometry without visualizing it first
- Assume mesh scales are in meters
- Leave namespacing issues unresolved