ROS: An In-Depth Discussion

by Gazza

In our previous article entitled, "Setting up a Simulated Environment for the Robot Operating System (ROS)," we covered a lot of ground relatively quickly.

Although the main focus of the previous article was to get the simulation up and running, this article will attempt to explain in more detail what is actually going on.  Let's begin where we launch the TurtleBot3 and the virtual world.

In ROS Noetic, the simulated environment is called Gazebo Classic.  Gazebo Classic will go end of life on January 31, 2025.  As an aside, in the newer ROS 2 versions, the virtual environment is referred to as Gazebo Simulation.

turtlebot3_world.launch.py

The turtlebot3_world.launch.py file is what spawns the virtual world and robot.1

In the launch file, there are a few important parameters, the first being: use_sim_time

In simulation, this should be set to true.  In this case, the value defaults to true but should be changed to false when adapting the launch files to a physical robot.  This is important due in part to the ROS message system using quality control services to reject outdated messages.  In fact, you can get ROS error messages in ROS stating that the message is too far in the future.  I always found that amusing.

The x_pos, y_pos, and z_pos parameters are also important since they control the starting position of the robot.  With regard to X, Y, and Z, if the robot is directly in front of you, +X would drive forward and -X would drive backward.  Sliding left is +Y, while sliding right would be -Y.  Gravity pulls the robot in -Z direction.  Thus, lifting the robot would be a +Z vector.

Running multiple robots requires each to have a unique starting potion.  Also, the default value of z_pos is 0.0, but I have found that giving it a value of 0.1 is beneficial when changing worlds.  If your robot falls through the floor, adjusting the z_pos usually fixes the issue.

Furthermore, the launch file calls the turtlebot3_waffle.gazebo.xacro file.  This particular file can be used to add additional sensors to the robot.  For instance, we typically add a rear facing camera so we can see obstacles when we back up.2

turtlebot3_teleop_key.launch

If the robot drives too slowly using the teleop_key inputs, then the main file of interest is: turtlebot3_teleop_key.3

Note that the max velocity (BURGER_MAX_LIN_VEL) for the burger robot is set at 0.22 m/s, while the maximum velocity for the waffle robot is 0.26 m/s.  Note that on a physical robot we typically set the values to 1.0 m/s.  Also, on a differential drive, the MAX_ANG_VEL is often quite higher approaching 4.0 for wheeled robots and 8.0 for track robots.

It is also possible to change the step of key presses by modifying the variables LIN_VEL_STEP_SIZE and ANG_VEL_STEP_SIZE.

It is also possible to change the keys from w, a, s, d, and x, but those are ingrained in my muscle memory from playing Doom and Quake, so I never changed these.

Finally, I would like to bring to the reader's attention the fact that the spacebar can be used to force stop the robot.  I mention this because I have always used the s key and just noticed that space was an option as well.  As mentioned in the previous article, the teleop_key window needs to have focus to drive the robot with the keyboard.

turtlebot3_slam.launch

In our previous article, we passed the parameter slam_methods:=gmapping to use the gmapping package for SLAM.

Recall that SLAM stands for Simultaneous Localization and Mapping.  There are other options besides gmapping, including, but not limited to: cartographer, hector, and karto.

For this article, we will just focus on gmapping.  As an aside, the maintainers of gmapping have not ported it to ROS 2 at the time of writing.  However, unofficial releases are available.

The purpose of gmapping is to use the laser scan topic /scan and create an occupancy grid.  The occupancy grid topic is named /map and exists in the map frame.

This is probably a good time to introduce frames.  Note that more than half of my problems in ROS are related to frames.  Let us start with the robot.

The frame of the robot is typically called base_link.  The base_link frame is an arbitrary location on the robot.  Personally, I typically choose the center of the bottom plate for the base_link frame.  All sensors are mounted as children to the base_link frame.  Specifically, the TurtleBot3 laser has a frame called base_scan whose parent is base_link.

The parent of base_link is often base_footprint.  The base_footprint frame typically has an offset to the ground.

Typically, I think of base_footprint as ground clearance.  Note that base_link and base_footprint are mobile frames that move with the robot.  The parent of base_footprint is often the odom frame.

The odom frame is a static frame.  In our use case, the odom frame is using wheel encoders to track the robot's pose in a local coordinate system.  The robot's origin when powered on is always: [0, 0, 0]

Note that the turtlebot3_world.launch.py file establishes base_link, base_footprint, and odom frames.  In simulation, wheel encoders are used to track the base_link frame as it moves about the virtual world.

Using wheel encoders for odometry in simulation is typically good enough, but on a physical robot, we typically combine the wheel encoders with an IMU or rely on the lidar or camera(s) for odometry.  This brings us back full circle to SLAM.

The parent of the odom frame is the map frame and is provided by the gmapping package we just launched.  The map frame is also a static frame and is used to compensate for drift in the odom frame.  The parameters for gmapping are located in the gmapping_params.yaml file.4

Note that the default parameters are quite good for most situations.  However, on the physical robot, I usually increase the parameters xmin, ymin, xmax, and ymax to -50, -50, 50, and 50 respectively, based on the range of the lidar equipped.

move_base.launch

After launching move_base, we used the "2D Nav Goal" in rviz to set a waypoint to which the robot navigated.

To accomplish this feat, move_base used a series of maps and planners.  Specifically, there are global and local varieties of costmaps and planners used in move_base.

When we set a waypoint on the map, we are effectively setting the goal on the global costmap.  The robot in turn uses a global planner to plan the robot's path.  Obstacle avoidance is accomplished using a local costmap and a local planner.  Note that the global costmap typically updates once a second, while the local costmap usually updates five times faster.  Thus, the local costmap is typically five to ten meters in size, while the global costmap can approach kilometer size for large areas.

The move_base.launch file is structured to call YAML files for each of the planners and costmaps.5

The first loaded YAML is the common_costmap_param.yaml file.6  This file includes parameters that are used by both the local and global costmaps.  As a result, it is loaded twice into each respective namespace.  This file includes the range used to detect obstacles.  In this case, obstacle_range is set to three meters.  Always set raytrace_range to be longer than obstacle_range.  Ray tracing is used to mark the map as clear between the robot's current position and the detected obstacle.  This file also includes the robot's footprint, which is used to detect collisions.

The parameters inflation_radius and cost_ scaling_factor are used to add padding to the obstacles to keep the robot from colliding.  If your robot cannot navigate through a doorway or hallway, then increasing the cost_scaling_factor (i.e., 10) is preferred over reducing the inflation_radius.

Finally, this file includes the parameter observation_sources which is used to determine obstacles.  In our simulated robot, it is 2D lidar that outputs the topic /scan.

The next file loaded by the move_base.launch file is the local_costmap_params.yaml file.7

The local costmap is what the robot uses to avoid obstacles.  It is typically smaller in range and updates faster than the global costmap.  This is true for mapping large areas.  Note that the width and height parameters match the obstacle range in the previous YAML file.

The global_costmap_params.yaml is loaded next.8

It has a similar structure to the local_costmap_params.yaml.  The key difference is that static_map is set to true for the global_costmap_params.yaml file.  The static map is generated by SLAM, and global costmap inflates the obstacles determined by SLAM.  The two costmaps are used by global and local planners to reach the waypoints.

Speaking of planners, the dwa_local_planner_params.yaml contains all the parameters for the local planner.9

This file sets both the linear and angular velocity of the robot.  It also has parameters to determine when a goal is reached.  The final file that is loaded is the move_base_params.yaml file and contains parameters associated with the frequency and patience of the path planners.  The default values are sufficient for testing in simulation.

Dockerfile and devcontainer.json

Lastly, these files were generated using the "Dev Container" extension in VScode.

Specifically, after clicking the Open a Remote Window button in VScode, and clicking New Dev Container, the Select Dev Container Configuration window pops up.  In the search box, I typically type ros and select ROS by ijnek.  Next I select Trust and Create Dev Container.  This creates a dev container along with the aforementioned Dockerfile and devcontainer.json files used in this article.

What Is Next?

There is a lot here to digest here, so I think I will save localization for the next article.

Also, I really liked the cover for volume 41:3 and in its honor, I was going to cover simulated quadruped robots in ROS 1.  If this is something you are interested in reading about, please write in.

Else, with Gazebo Classic and ROS Noetic being EOL when this article publishes, future articles will be based on ROS 2 Humble.

References

  1. github.com/ROBOTIS-GIT/turtlebot3_simulations/blob/master/turtlebot3_gazebo/launch/turtlebot3_world.launch
  2. github.com/ROBOTIS-GIT/turtlebot3/blob/master/turtlebot3_description/urdf/turtlebot3_waffle.gazebo.xacro
  3. github.com/ROBOTIS-GIT/turtlebot3/blob/master/turtlebot3_teleop/nodes/turtlebot3_teleop_key
  4. github.com/ROBOTIS-GIT/turtlebot3/blob/master/turtlebot3_slam/config/gmapping_params.yaml
  5. github.com/ROBOTIS-GIT/turtlebot3/blob/master/turtlebot3_navigation/launch/move_base.launch
  6. github.com/ROBOTIS-GIT/turtlebot3/blob/master/turtlebot3_navigation/param/costmap_common_params_waffle.yaml
  7. github.com/ROBOTIS-GIT/turtlebot3/blob/master/turtlebot3_navigation/param/local_costmap_params.yaml
  8. github.com/ROBOTIS-GIT/turtlebot3/blob/master/turtlebot3_navigation/param/global_costmap_params.yaml
Return to $2600 Index