# 2.3 Create Launch Files to Display the URDF in RViz

{% embed url="<https://youtu.be/oEnJqHY-rv4>" %}

In our last tutorial, we launch the URDF file based on `urdf-tutorial` package as follows:

{% code overflow="wrap" %}

```
sudo apt install ros-jazzy-urdf-tutorial
ros2 launch urdf_tutorial display.launch.py model:=/home/robotlabs/ros2_ws/src/mec_mobile/mec_mobile_description/urdf/robots/robot_3d.urdf.xacro
```

{% endcode %}

Now, let's create our own launch file and load our own *.rviz* file as well.

## Add RViz Configuration File

ROS provides several tools to visualize various data like `rqt` and `RViz`. We can start the RViz with the `rviz2` command, then we can add the `Robot Model` view, browse for our URDF file and type `base_link` as Fixed Frame etc. \
![alt text](https://github.com/MOGI-ROS/Week-3-4-Gazebo-basics/raw/main/assets/rviz.png)

Although is possible to use RViz with command like this, it's not the most convenient way, as we can move all these tasks into a ROS launch file with loading a *.rviz* file. Let's add the RViz configuration file first and then use it in our launch file.&#x20;

```
cd ~/ros2_ws/src/mec_mobile/mec_mobile_description/ 
mkdir rviz && cd rviz
```

Create a file inside rviz folder and name it with **mec\_mobile\_description.rviz**  &#x20;

<details>

<summary><strong>mec_mobile_description.rviz</strong></summary>

```
Panels:
  - Class: rviz_common/Displays
    Name: Displays
  - Class: rviz_common/Views
    Name: Views
Visualization Manager:
  Displays:
    - Class: rviz_default_plugins/Grid
      Name: Grid
      Value: true
    - Alpha: 0.8
      Class: rviz_default_plugins/RobotModel
      Description Topic:
        Value: /robot_description
      Name: RobotModel
      Value: true
    - Class: rviz_default_plugins/TF
      Name: TF
      Value: true
  Global Options:
    Fixed Frame: base_link
  Tools:
    - Class: rviz_default_plugins/MoveCamera
  Value: true
  Views:
    Current:
      Class: rviz_default_plugins/Orbit
      Distance: 1.7
      Name: Current View
      Pitch: 0.33
      Value: Orbit (rviz)
      Yaw: 5.5
Window Geometry:
  Height: 800
  Width: 1200
```

</details>

Note that we don't need to understand all the details of this configuration file. It can be generated automatically through RViz.&#x20;

The RViz configuration files set up an RViz with a grid, a robot model, and coordinate frame visualizations etc. It also enables the camera movement tool and sets the initial camera view to an orbit view, which allows orbiting around a focal point in the scene.&#x20;

When RViz is launched with this configuration file, it will display the robot model and allow interaction and visualization of the robot.

## Launch file based on urdf-tutorial

The source of `urdf-tutorial` package is [here](https://github.com/ros/urdf_tutorial/tree/ros2). This package does exactly what we need, but <mark style="color:red;">it's not good to build up our tools onto it, so we can create our own launch file based on this package</mark>.

Let's create the `check_urdf.launch.py` file in the launch folder inside **mec\_mobile\_description**:

```
cd ~/ros2_ws/src/mec_mobile/mec_mobile_description/ 
mkdir launch && cd launch
```

<details>

<summary>check_urdf.launch.py</summary>

```python
import os
from launch import LaunchDescription
from launch.actions import DeclareLaunchArgument, IncludeLaunchDescription
from launch.substitutions import LaunchConfiguration, PathJoinSubstitution
from launch_ros.substitutions import FindPackageShare


def generate_launch_description():
    
    # Set urdf package path, replace it with your package
    pkg_urdf_path = FindPackageShare('mec_mobile_description')
    default_rviz_config_path = PathJoinSubstitution([pkg_urdf_path, 'rviz', 'mec_mobile_description.rviz'])

    # Show joint state publisher GUI for joints
    gui_arg = DeclareLaunchArgument(name='gui', default_value='true', choices=['true', 'false'],
                                    description='Flag to enable joint_state_publisher_gui')
    
    # RViz config file path
    rviz_arg = DeclareLaunchArgument(name='rvizconfig', default_value=default_rviz_config_path,
                                    description='Absolute path to rviz config file')
    

    # URDF model path within your package
    model_arg = DeclareLaunchArgument(
        'model', default_value='robot_3d.urdf.xacro',
        description='Name of the URDF description to load'
    )

    # Use built-in ROS2 URDF launch package with our own arguments
    urdf = IncludeLaunchDescription(
        PathJoinSubstitution([FindPackageShare('urdf_launch'), 'launch', 'display.launch.py']),
        launch_arguments={
            'urdf_package': 'mec_mobile_description',
            'urdf_package_path': PathJoinSubstitution(['urdf','robots', LaunchConfiguration('model')]),
            'rviz_config': LaunchConfiguration('rvizconfig'),
            'jsp_gui': LaunchConfiguration('gui')}.items()
    )

    launchDescriptionObject = LaunchDescription()

    launchDescriptionObject.add_action(gui_arg)
    launchDescriptionObject.add_action(rviz_arg)
    launchDescriptionObject.add_action(model_arg)
    launchDescriptionObject.add_action(urdf)

    return launchDescriptionObject
```

</details>

This launch file is very useful to visualize our URDF during development. Now add the *launch* to the **CMakeLists.txt** file and build the workspace to try it:

```
ros2 launch mec_mobile_description check_urdf.launch.py 
```

## Create another Launch File

We can also create a custom launch file to launch a mobile robot in RViz as [ros2 official docs](https://docs.ros.org/en/jazzy/Tutorials/Intermediate/Launch/Launch-Main.html). Launch files in ROS2 allows you to start multiple nodes and set parameters with a single command.

Open a terminal window, and move to the mobile robot directory.

```
cd ~/ros2_ws/src/mec_mobile/mec_mobile_description/ 
mkdir launch && cd launch
```

Add a file with VS Code in the launch folder named **robot\_state\_publisher.launch.py** (<mark style="color:red;">with</mark> <mark style="color:red;"></mark><mark style="color:red;">**robot state**</mark> <mark style="color:red;"></mark><mark style="color:red;">and</mark> <mark style="color:red;"></mark><mark style="color:red;">**joint state publisher**</mark> <mark style="color:red;"></mark><mark style="color:red;">nodes more than the previous check\_urdf.launch.py</mark>):

<details>

<summary><strong>robot_state_publisher.launch.py</strong></summary>

```python
# This file launches the robot state publisher, joint state publisher,
# and RViz2 for visualizing the robot.
 
from launch import LaunchDescription
from launch.actions import DeclareLaunchArgument
from launch.conditions import IfCondition, UnlessCondition
from launch.substitutions import Command, LaunchConfiguration, PathJoinSubstitution
from launch_ros.actions import Node
from launch_ros.parameter_descriptions import ParameterValue
from launch_ros.substitutions import FindPackageShare
 
# Define the arguments for the XACRO file
ARGUMENTS = [
    DeclareLaunchArgument('prefix', default_value='',
                          description='Prefix for robot joints and links'),
    DeclareLaunchArgument('use_gazebo', default_value='false',
                          choices=['true', 'false'],
                          description='Whether to use Gazebo simulation')
]
 
 
def generate_launch_description():
    # Define filenames
    urdf_package = 'mec_mobile_description'
    urdf_filename = 'robot_3d.urdf.xacro'
    rviz_config_filename = 'mec_mobile_description.rviz'
 
    # Set paths to important files
    pkg_share_description = FindPackageShare(urdf_package)
    default_urdf_model_path = PathJoinSubstitution(
        [pkg_share_description, 'urdf', 'robots', urdf_filename])
    default_rviz_config_path = PathJoinSubstitution(
        [pkg_share_description, 'rviz', rviz_config_filename])
 
    # Launch configuration variables
    jsp_gui = LaunchConfiguration('jsp_gui')
    rviz_config_file = LaunchConfiguration('rviz_config_file')
    urdf_model = LaunchConfiguration('urdf_model')
    use_rviz = LaunchConfiguration('use_rviz')
    use_sim_time = LaunchConfiguration('use_sim_time')
 
    # Declare the launch arguments
    declare_jsp_gui_cmd = DeclareLaunchArgument(
        name='jsp_gui',
        default_value='true',
        choices=['true', 'false'],
        description='Flag to enable joint_state_publisher_gui')
 
    declare_rviz_config_file_cmd = DeclareLaunchArgument(
        name='rviz_config_file',
        default_value=default_rviz_config_path,
        description='Full path to the RVIZ config file to use')
 
    declare_urdf_model_path_cmd = DeclareLaunchArgument(
        name='urdf_model',
        default_value=default_urdf_model_path,
        description='Absolute path to robot urdf file')
 
    declare_use_rviz_cmd = DeclareLaunchArgument(
        name='use_rviz',
        default_value='true',
        description='Whether to start RVIZ')
 
    declare_use_sim_time_cmd = DeclareLaunchArgument(
        name='use_sim_time',
        default_value='false',
        description='Use simulation (Gazebo) clock if true')
 
    robot_description_content = ParameterValue(Command([
        'xacro', ' ', urdf_model, ' ',
        'prefix:=', LaunchConfiguration('prefix'), ' ',
        'use_gazebo:=', LaunchConfiguration('use_gazebo')
    ]), value_type=str)
 
    # Subscribe to the joint states of the robot, and publish the 3D pose of each link.
    start_robot_state_publisher_cmd = Node(
        package='robot_state_publisher',
        executable='robot_state_publisher',
        name='robot_state_publisher',
        output='screen',
        parameters=[{
            'use_sim_time': use_sim_time,
            'robot_description': robot_description_content}])
 
    # Publish the joint state values for the non-fixed joints in the URDF file.
    start_joint_state_publisher_cmd = Node(
        package='joint_state_publisher',
        executable='joint_state_publisher',
        name='joint_state_publisher',
        parameters=[{'use_sim_time': use_sim_time}],
        condition=UnlessCondition(jsp_gui))
 
    # Depending on gui parameter, either launch joint_state_publisher or joint_state_publisher_gui
    start_joint_state_publisher_gui_cmd = Node(
        package='joint_state_publisher_gui',
        executable='joint_state_publisher_gui',
        name='joint_state_publisher_gui',
        parameters=[{'use_sim_time': use_sim_time}],
        condition=IfCondition(jsp_gui))
 
    # Launch RViz
    start_rviz_cmd = Node(
        condition=IfCondition(use_rviz),
        package='rviz2',
        executable='rviz2',
        output='screen',
        arguments=['-d', rviz_config_file],
        parameters=[{'use_sim_time': use_sim_time}])
 
    # Create the launch description and populate
    ld = LaunchDescription(ARGUMENTS)
 
    # Declare the launch options
    ld.add_action(declare_jsp_gui_cmd)
    ld.add_action(declare_rviz_config_file_cmd)
    ld.add_action(declare_urdf_model_path_cmd)
    ld.add_action(declare_use_rviz_cmd)
    ld.add_action(declare_use_sim_time_cmd)
 
    # Add any actions
    ld.add_action(start_joint_state_publisher_cmd)
    ld.add_action(start_joint_state_publisher_gui_cmd)
    ld.add_action(start_robot_state_publisher_cmd)
    ld.add_action(start_rviz_cmd)
 
    return ld
```

</details>

## Edit CMakeLists.txt

Now we need to edit **CMakeLists.txt** for the **mec\_mobile\_description** package, so build system can find our new folders, launch and rviz.

```
cd ~/ros2_ws/src/mec_mobile/mec_mobile_description/ 
```

Add following code with VS Code to **CMakeLists.txt**:

```
# Copy necessary files to designated locations in the project
install (
  DIRECTORY launch meshes urdf rviz
  DESTINATION share/${PROJECT_NAME}
)
```

## Build the Package

Now let’s build the package.

```
cd ~/ros2_ws
colcon build
```

## Launch the Launch Files

Launch your launch files:

```
ros2 launch mec_mobile_description robot_state_publisher.launch.py
```

You can also add launch arguments. To see the available launch arguments, type:

```
ros2 launch mec_mobile_description robot_state_publisher.launch.py --show-args
```

If you want to launch the robots with no GUIs, do this:

{% code overflow="wrap" %}

```
ros2 launch mec_mobile_description robot_state_publisher.launch.py jsp_gui:=false use_rviz:=false
```

{% endcode %}

> Note: The **ARGUMENTS** in launch.py defines the robot’s configurable parameters.

## Launch File Explanation

<details>

<summary>robot_state_publisher.launch.py</summary>

Starting with the imports, we bring in essential ROS 2 launch utilities.

```python
from launch import LaunchDescription
from launch.actions import DeclareLaunchArgument
from launch.conditions import IfCondition, UnlessCondition
```

The launch file’s core purpose is to arrange multiple nodes:

* **Robot State Publisher:** Broadcasts the robot’s current pose
* **Joint State Publisher:** Manages the robot’s joint positions
* **RViz:** Provides the 3D visualization

The **ARGUMENTS** list defines the robot’s configurable parameters:

```python
ARGUMENTS = [
    DeclareLaunchArgument('prefix', default_value='',
                          description='Prefix for robot joints and links'),
    DeclareLaunchArgument('add_world', default_value='true',
                          description='Whether to add world link'),
    # ... other arguments
]
```

<mark style="color:red;">These arguments allow users to customize the robot’s configuration without changing the code</mark> – like using command-line switches to modify a program’s behavior.

The **generate\_launch\_description()** function is where the magic happens. First, it sets up the file paths:

```
urdf_package = 'mec_mobile_description'
urdf_filename = 'robot_3d.urdf.xacro'
rviz_config_filename = 'mec_mobile_description.rviz'
```

The robot’s description is loaded from a **XACRO file (an XML macro file)** and converted into a **robot\_description\_content** parameter:

```
robot_description_content = ParameterValue(Command([
    'xacro', ' ', urdf_model, ' ',
    'prefix:=', LaunchConfiguration('prefix'),
    # ... other parameters
]), value_type=str
```

**Then we create three key nodes**:

1\. Robot State Publisher:

This node takes the **robot description** and **joint states** and **publishes the 3D poses** **of all robot links** – like a real-time monitoring system for each part of the robot.

```python
start_robot_state_publisher_cmd = Node(
    package='robot_state_publisher',
    executable='robot_state_publisher',
    name='robot_state_publisher',
    output='screen',
    parameters=[{
        'use_sim_time': use_sim_time,
        'robot_description': robot_description_content}])
```

2\. Joint State Publisher (with optional GUI):

This node **publishes joint positions** – imagine it as the robot’s muscle control center. The GUI version allows manual control of these joints.

```python
start_joint_state_publisher_cmd = Node(
    package='joint_state_publisher',
    executable='joint_state_publisher',
    name='joint_state_publisher',
    parameters=[{'use_sim_time': use_sim_time}],
    condition=UnlessCondition(jsp_gui))
```

3\. RViz:

This is our visualization tool, loading a pre-configured layout specified in the RViz config file.

```python
start_rviz_cmd = Node(
    condition=IfCondition(use_rviz),
    package='rviz2',
    executable='rviz2',
    name='rviz2',
    output='screen',
    arguments=['-d', rviz_config_file])
```

Finally, everything is assembled into a LaunchDescription and returned:

```
ld = LaunchDescription(ARGUMENTS)
# Add all the actions...
return ld
```

This launch file structure is common in ROS 2 applications, providing a clean way to start multiple nodes simultaneously.

That’s all.

</details>


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://robotlabs.gitbook.io/docs/ros/ros2-jazzy/2.3-create-launch-files-to-display-the-urdf-in-rviz.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
