From URDF File to ROS 2 Package: Organizing Your Robot Model for Success

Prerequisites:

Before we dive into creating a URDF package, make sure you have the following in place:

  1. A Completed URDF File:

You should have a valid URDF file describing your robot. If you haven't created one yet, check out my previous article on Creating a URDF with Autodesk Fusion.

  1. ROS2 Installed:

This guide assumes you're working on ROS2 (any recent distribution like Kilted, Jazzy, Iron or Humble). If you haven't installed ROS2 yet, follow my previous article on ROS2 Installation.

  1. Text Editor:

Any text editor should do (VS Code, gedit, nano, vim...etc) for editing your URDF and launch files.

Step 0: Creating a ROS2 Workspace

SKIP if you already have one

Create a New Workspace Directory

Open a terminal and create a new workspace directory (commonly named ros2_ws)

Navigate to the workspace root

Build the (currently empty) workspace:

Source the Workspace

So ROS2 can find your packages:

Step 1: Create a New Description Package

I'll be using

ament_python for the build_type and

MIT for the License

Navigate to your Workspace's src directory:

Create a New Package

I'm Using the CanadaArm2 from the previous Tutorial, so I'll call it the canadarm2_description

Step 2: Add Your meshes and urdf Folders to the Package

To keep the robot model organized and portable, place all the model assets - such as mesh files and URDF files - inside the description package. This makes it easy to share and reuse the robot accross different projects.

Create the Folders:

Navigate to the package directory and create the urdf and meshes folders:

Copy Your Files

Copy URDF Files into urdf folder:

Copy All mesh files into the meshes folder:

Most Commonly its .stl, .dae, .obj or .ply files.

Now Your Package should like:

Step 3: Edit package.xml and setup.py

To ensure your package is properly recognised by ROS2 and all your assets are installed, you need to update package.xml and setup.py

A bit more of detail on why we do this crucial step:

  1. Seamless Dependency Installation with rosdep:

When you specify all required dependencies in your package.xml, new users can simply run:

to automatically install every system and ROS package dependency needed to build and run your package—even on a fresh ROS install. This saves time, reduces errors, and ensures a smoother onboarding experience for collaborators and users.

  1. Cross Platform Compatibility:

rosdep translates dependency keys into the correct package names for different operating systems, making your package more portable and easier to use across Ubuntu, Debian, Fedora, and other supported platforms

  1. Reliable Builds and Reproducibility:

A complete and correct package.xml ensures that all build tools and automation systems (like CI/CD pipelines) can resolve dependencies and build your package without manual intervention.

  1. Easier Maintenance and Collaboration:

Clear dependency management helps collaborators understand what your package needs, reduces troubleshooting time, and keeps your project maintainable as it grows.

Edit package.xml :

Open package.xml in VS Code and update the following:

  • Description , Maintainer and License

  1. name: The unique name of your package.

  2. version: Current version of your package.

  3. description: Brief summary of what the package provides.

  4. maintainer: Who maintains the package (with email).

  5. license: The license under which the package is released (MIT is permissive and open-source friendly).

  • Dependencies

  1. robot_state_publisher: Publishes the state of the robot as described by your URDF.

  2. joint_state_publisher_gui: GUI for interactively moving joints.

  3. gazebo_ros: Required if you plan to simulate your robot in Gazebo.

  4. rviz2: For visualizing the robot in RViz.

  5. rclpy: Python client library for ROS 2 (needed if you write Python nodes or launch files).

gazebo_ros is a dependency which has been deprecated on Jazzy and Above, So Humble is the last ROS2 Distribution it will work with

My package.xml looks like:

Edit setup.py :

Open package.xml in VS Code and update the following:

  1. Include All Resource Files:

  • Defines a helper function package_files to gather all files (recursively) under a given directory.

  • Collects all files from urdf, meshes, launch, and (optionally) rviz folders.

  • Ensures that every asset (URDFs, meshes, launch files, RViz configs) is included in the package installation.

Guarantees that all model and configuration files are installed and available to users, no matter where the package is built or installed.

  1. Declaring Data Files for Installation :

  • Ensures that the ROS 2 package index and package.xml are installed in the correct locations.

  • Dynamically adds all URDF, mesh, launch, and RViz files to the installation, if they exist.

ROS 2 tools (like ros2 launch and ros2 pkg) expect files in these standard locations.

Makes your package portable and ensures all resources are available after installation.

  1. The setup() Function :

  • Standard Python package metadata: name, version, description, maintainer, license.

  • find_packages(exclude=['test']) finds any Python modules (not typically used in pure description packages, but keeps the structure flexible).

  • data_files ensures all your non-code resources (URDFs, meshes, etc.) are installed.

  • install_requires and tests_require specify Python dependencies.

  • entry_points is left empty, as this package doesn’t provide Python executables/scripts.

Makes your package installable via colcon build and ensures all assets are available at runtime.

Follows ROS 2 conventions for Python-based packages, which is best practice for description packages.

My setup.py looks like:

Step 4: Create Launch Files for Rviz and Gazebo

Create Directories for launch and rviz :

Navigate to your package directory and create the launch and rviz folders:

Create Rviz Launch File:

Inside the launch folder, create a file named display.launch.py:

Imports and Setup:

- Imports: Brings in ROS 2 launch system classes, OS utilities, and ament index tools for finding package resources.

File and Path Setup:

  • package_name: The ROS 2 package containing your robot description.

  • urdf_file: The filename of your robot’s URDF.

  • rviz_config_file: The filename of your RViz configuration.

  • pkg_share: Finds the installed location of your package.

  • urdf_path/rviz_path: Full paths to your URDF and RViz config files.

  • robot_description_content: Reads the entire URDF file into a string for use by the robot state publisher.

LaunchDescription and Nodes

Node 1: Static Transform Publisher

  • Publishes a fixed transform from the map frame to the base_link frame.

  • The arguments specify translation (0,0,0) and rotation (0,0,0), so the frames are coincident.

  • This is useful for establishing a root frame for your robot in the TF tree.

Node 2: Joint State Publisher GUI

  • Launches a GUI to interactively move the robot’s joints.

  • Essential for visualizing kinematic chains and testing joint limits in RViz.

Node 3: Robot State Publisher

  • Publishes the robot’s kinematic transforms based on the URDF and joint states.

  • Makes the robot model appear correctly in RViz and other ROS tools.

Node 4: RViz

  • Starts RViz 2 with your specified configuration file.

  • The config file can pre-load displays (like RobotModel, TF, etc.) for convenience.

display.launch.py

The full launch file looks like:

Create Gazebo Launch File:

Imports and Setup

  • Imports: Brings in all necessary Python and ROS 2 launch libraries for including other launch files, setting environment variables, and running ROS nodes.

File and Path Setup

  • pkg_share: Finds the installed location of your description package.

  • urdf_file: Full path to your robot’s URDF file.

  • gazebo_launch_file: Path to the standard Gazebo launch file provided by gazebo_ros.

  • gazebo_model_path: The parent directory of your package, set as the GAZEBO_MODEL_PATH so Gazebo can find your custom meshes.

  • robot_description_content: Reads the URDF file contents for use by the robot_state_publisher.

LaunchDescription and Actions

SetEnvironmentVariable:GAZEBO_MODEL_PATH

  • Purpose: Tells Gazebo where to look for model files (e.g., meshes referenced by your URDF).

  • Why: Ensures all custom meshes used by your robot are found and loaded correctly in the simulation.

IncludeLaunchDescription: Start Gazebo

  • Purpose: Starts the Gazebo simulator using its standard launch file.

  • Why: Brings up the Gazebo GUI and simulation environment, ready for your robot to be spawned.

  • Extra: The verbose argument enables more detailed logging from Gazebo.

Node: robot_state_publisher

  • Purpose: Publishes the robot’s TF transforms using the URDF.

  • Why: Keeps the simulation’s TF tree updated, so all ROS tools (including Gazebo plugins) know the robot’s kinematic structure.

  • Note:use_sim_time: True makes the node use Gazebo’s simulated clock.

Node: spawn_entity.py

  • Purpose: Spawns your robot into the Gazebo world.

  • How: Reads the robot’s URDF from the robot_description topic (published by robot_state_publisher) and creates the robot in the simulation.

  • Entity Name: The spawned robot will be named canadarm2 in Gazebo.

gazebo.launch.py

The full launch file looks like:

Step 5: Update Your URDF to Use package:// Paths for Meshes

To ensure your robot’s mesh files are found correctly—no matter where your package is installed—you need to reference them in your URDF using the package:// URI scheme. This is a ROS convention that makes your package portable and robust.

Why Use package://?

  • Portability: The package:// syntax allows ROS to find your mesh files based on the package name, not the absolute or relative path. This means your URDF will work on any system, as long as your package is installed.

  • Best Practice: This is the standard approach for all ROS robot description packages.

How to Update Your URDF

Suppose your mesh file is located at:

In your URDF, reference it like this:

Replace all mesh file paths in your URDF that look like:

  • meshes/shoulder_link.stl

  • ../meshes/shoulder_link.stl

  • /home/user/ros2_ws/src/my_robot_description/meshes/shoulder_link.stl

With:

Step 6: Compile and Validate Package

With your package organized and all files in place, it’s time to build your workspace and check that everything works as expected.

1. Compile the Workspace

From the root of your workspace, run:

  • The --symlink-install option is handy during development, as it lets you edit files without needing to rebuild every time.

2. Source the Workspace

After a successful build, source your workspace so ROS 2 can find your new package:

3. Validate the URDF and Package

A. Launch in RViz

Test your robot visualization:

  • RViz should open, and you should see your robot model.

  • If you set up the joint state publisher GUI, try moving some joints and watch the model update.

B. Launch in Gazebo

Test your robot in simulation:

  • Gazebo should start, and your robot should appear in the simulated world.

Happy Simulating :D

Troubleshooting:

Missing Dependencies:

Humble:

Jazzy:

Reply

or to participate.