The code for this section is located in navigation_bot_05 section of the code archive.
In this section, we are going to finalize the design of our robot, so that it can use different planner and controller algorithms to perform navigation tasks.
Note that the launch file can now take bunch of parameters: file name for the world, file name for the map to load in RViz and so on. One of such parameters is slam, which can be True or False. Depending on that parameters, we either launch slam tools or not. In our particular case, we use this parameter to either create a lidar-based map and save it, or to load a pre-saved map in RViz.
If you look in sam_bot code in ROS2 official tutorials, you will see that they bring up Gazebo in two steps: one to start Gazebo server, another one to start a Gazebo client. While I understand the reasons behind this approach, I do not like it.
Strictly speaking, for this project, we do not need to start Gazebo server and client as two separate processes. As a mater of fact, they take much longer to start than a combined Gazebo. However, we might need this functionality in future, and ROS2 example uses it, so let's at least take a look:
# world_path defined in globals.py declare_world_cmd = DeclareLaunchArgument( 'world', default_value=world_path, description='Full path to world model file to load') # Specify the actions start_gazebo_server_cmd = ExecuteProcess( condition=IfCondition(use_simulator), cmd=['gzserver', '-s', 'libgazebo_ros_init.so', world], cwd=[launch_dir], output='screen') start_gazebo_client_cmd = ExecuteProcess( condition=IfCondition(PythonExpression([use_simulator, ' and not ', headless])), cmd=['gzclient'], cwd=[launch_dir], output='screen')
Then, we need to pass the objects we created to LaunchDescription; to start Gazebo only (as for this short demo we do not need anything else), I have commented out all unrelated commands in main_simulation_launch.py:
# Create the launch description and populate ld = LaunchDescription() # Declare the launch options #ld.add_action(declare_namespace_cmd) #ld.add_action(declare_use_namespace_cmd) #ld.add_action(declare_slam_cmd) #ld.add_action(declare_map_yaml_cmd) ld.add_action(declare_use_sim_time_cmd) #ld.add_action(declare_params_file_cmd) #ld.add_action(declare_bt_xml_cmd) #ld.add_action(declare_autostart_cmd) #ld.add_action(declare_rviz_config_file_cmd) ld.add_action(declare_use_simulator_cmd) #ld.add_action(declare_use_robot_state_pub_cmd) #ld.add_action(declare_use_rviz_cmd) ld.add_action(declare_simulator_cmd) ld.add_action(declare_world_cmd) # Add any conditioned actions ld.add_action(start_gazebo_server_cmd) ld.add_action(start_gazebo_client_cmd) # Add the actions to launch all of the navigation nodes #ld.add_action(start_robot_state_publisher_cmd) #ld.add_action(rviz_cmd) #ld.add_action(bringup_cmd) return ld
So, it works.
However, this way of starting Gazebo will create problems in future. Let me repeat: this is an official example, and yet it can create problems when loading a model: try using this code with the code for a robot (next chapter) and your Gazebo will likely hang with "Waiting for service / spawn_entity" message. What should we do?
In my earlier tutorials, we used the following code that brings up the Gazebo "in one piece":
gazebo = IncludeLaunchDescription( PythonLaunchDescriptionSource([os.path.join( get_package_share_directory('gazebo_ros'), 'launch', 'gazebo.launch.py')]), )
Let's see what ROS2 has in the gazebo.launch.py (the package gazebo_ros should be located in /opt/ros/foxy/share/gazebo_ros).
gazebo.launch.py:
def generate_launch_description(): return LaunchDescription([ DeclareLaunchArgument('gui', default_value='true', description='Set to "false" to run headless.'), DeclareLaunchArgument('server', default_value='true', description='Set to "false" not to run gzserver.'), IncludeLaunchDescription( PythonLaunchDescriptionSource([ThisLaunchFileDir(), '/gzserver.launch.py']), condition=IfCondition(LaunchConfiguration('server')) ), IncludeLaunchDescription( PythonLaunchDescriptionSource([ThisLaunchFileDir(), '/gzclient.launch.py']), condition=IfCondition(LaunchConfiguration('gui')) ), ])
So it looks like we can selectively bring up Gazebo client, server or both by using appropriate parameters; let's use our "old" code with the following changes:
gazebo = IncludeLaunchDescription( PythonLaunchDescriptionSource([os.path.join( get_package_share_directory('gazebo_ros'), 'launch', 'gazebo.launch.py')]), launch_arguments={ 'gui':'True', 'server':'True' }.items() )
It works and it doesn't conflict with the code in the next section.