Set Up the Odometry for a Simulated Mobile Robot in ROS 2

In this tutorial, I will show you how to set up the odometry for a mobile robot. This tutorial is the second tutorial in my Ultimate Guide to the ROS 2 Navigation Stack (also known as Nav2)

The official tutorial for setting up odometry is on this page, but I will walk you through the entire process, step-by-step.

You can get the entire code for this project here.

Let’s get started!

Prerequisites

Odometry in ROS 2

In robotics, odometry is about using data from sensors to estimate the change in a robot’s position, orientation, and velocity over time relative to some point (e.g. x=0, y=0, z=0). Odometry information is normally obtained from sensors such as wheel encoders, IMU (Inertial measurement unit), and LIDAR

In ROS, the coordinate frame most commonly used for odometry is known as the odom frame. Just like an odometer in your car which measures wheel rotations to determine the distance your car has traveled from some starting point, the odom frame is the point in the world where the robot first starts moving.

A robot’s position and orientation within the odom frame becomes less accurate over time and distance because sensors like IMUs (that measure acceleration) and wheel encoders (that measure the number of times each wheel has rotated) are not perfect. 

For example, imagine your robot runs into a wall. Its wheels might spin repeatedly without the robot moving anywhere (we call this wheel slip). The wheel odometry would indicate a further distance traveled by the robot than reality. We have to adjust for these inaccuracies.

To learn all about coordinate frames for mobile robots (including the odom frame), check out this post.

To correct for the inaccuracies of sensors, most ROS applications have an additional coordinate frame called map that provides globally accurate information and corrects drift that happens within the odom frame.

The ROS 2 Navigation Stack requires:

  1. Publishing of nav_msgs/Odometry messages to a ROS 2 topic
  2. Publishing of the coordinate transform from odom (parent frame) -> base_link (child frame) coordinate frames.

Getting both of these pieces of data published to the ROS system is our end goal in setting up the odometry for a robot.

Simulate the Odometry System Using Gazebo

Let’s set up the odometry for the simulated robot we created in the last tutorial. We will use Gazebo, an open-source 3D robotics simulator.

Set Up the Prerequisites

Gazebo is automatically included in ROS 2 installations. To test that Gazebo is installed, open a new terminal window, and type:

gazebo

You should see an empty world:

1-empty-gazebo-world

If you don’t see any output after a minute or two, follow these instructions to install Gazebo

Close Gazebo by going to the terminal window and pressing CTRL + C.

Now, open a new terminal window, and install the gazebo_ros_pkgs package.

sudo apt-get update
sudo apt-get upgrade
sudo apt install ros-foxy-gazebo-ros-pkgs

The syntax for the above command is:

sudo apt install ros-<ros2-distro>-gazebo-ros-pkgs

….where you replace <ros2-distro> with the ROS 2 distribution that you are using. I am using ROS 2 Foxy Fitzroy, so I use “foxy”.

Create an SDF File for the Robot

Later in this tutorial series, we will be using the robot_localization package to enable the robot to determine where it is in the environment. To use this package, we need to create an SDF file.

Why should we create an SDF file instead of using our URDF File? In working with ROS for many years, I have found that URDFs don’t always work that well with Gazebo. 

For example, when I ran through the official ROS 2 Navigation Stack robot localization demo, I found that the filtered odometry data was not actually generated.

So, we need to use an SDF file for Gazebo stuff and a URDF file for ROS stuff. We will try to make sure our SDF file generates a robot that is as close as possible to the robot generated by the URDF file.

Open a new terminal window, and type:

cd ~/dev_ws/src/basic_mobile_robot/models

If you have colcon_cd set up, you can also type:

colcon_cd basic_mobile_robot
cd models

Create Model.config

Let’s create a folder for the SDF model.

mkdir basic_mobile_bot_description

Move inside the folder.

cd basic_mobile_bot_description

Create a model.config file.

gedit model.config

Type this code inside this model.config file. You can see this file contains fields for the name of the robot, the version, the author (that’s you), your email address, and a description of the robot.

Save the file, and then close it.

Create Model.sdf

Now, let’s create an SDF (Simulation Description Format) file. This file will contain the tags that are needed to create an instance of the basic_mobile_bot model. We want to build it so that it is as close to the URDF robot representation as possible. Our robot has three wheels: two wheels in the back and a caster wheel in the front. 

The official tutorial for creating an SDF file is here (other good tutorials are here and here), but let’s do this together below. I put a lot of comments in the SDF file so that you can see what is going on. Don’t be intimidated by how the file looks. Just go through it slowly, one line at a time, section by section. There is no hurry.

Type the following command:

gedit model.sdf

Here is my sdf file. You can copy and paste those lines inside your sdf file.

Save the file and close it to return to the terminal.

You’ll notice there are some slight differences between the URDF and the SDF file formats.

You can see in the SDF file that we use an IMU sensor plugin to simulate IMU data. And, instead of using wheel encoders to calculate odometry from the motion of the wheels, we use Gazebo’s differential drive plugin and the joint state publisher plugin

The differential drive plugin will subscribe to velocity commands over the /cmd_vel topic and will publish odometry information to the /wheel/odometry topic. 

Remember, a differential drive robot is a mobile robot whose motion is based on two separately driven wheels that are on either side of the robot’s body.

The joint state publisher plugin will publish the angles of the drive wheel joints, which are continuously changing once the robot starts moving.

In a real robotics project, to calculate the odometry system, we would use an IMU sensor, and we would use wheel encoders. In this real-world project for example, I used wheel encoder data to generate an odometry message.

Add Meshes Folder

Now let’s have our robot look more realistic by adding a mesh to the base of the robot.

Open the file explorer by clicking on the Files icon on the left side of your terminal.

2-files-icon

Go to your basic_mobile_robot package.

Copy the meshes folder.

3-copy-the-meshes-folder

Paste the meshes folder inside the ~/dev_ws/src/basic_mobile_robot/models/basic_mobile_bot_description folder.

4-paste-meshes

Add the Path of the Model to the Bashrc File

Now we need to add the file path of the model to the bashrc file.

Open a terminal window, and type:

gedit ~/.bashrc

Add the following line to the bottom of the bashrc file:

export GAZEBO_MODEL_PATH=$GAZEBO_MODEL_PATH:/home/focalfossa/dev_ws/src/basic_mobile_robot/models/

The name of my Linux environment is focalfossa. Your environment may or may not have a different name.

Save the file and close it.

Test Your Robot

Now let’s run Gazebo so that we can see our model. Open a new terminal window, and type the following command:

gazebo

On the left-hand side, click the “Insert” tab.

On the left panel, find your robot. 

5-basic-mobile-bot

You should see your robot in the empty Gazebo environment. You can place it wherever you want by clicking inside the environment.

To test the robot, open the rqt_steering program. In a new terminal window, type the following command:

rqt_robot_steering

A GUI will pop up.

Change the topic on the GUI to /cmd_vel.

6-rqt-robot-steering

Move the sliders to move the robot forward/backward/right/left.

To see a list of ros topics, you can open a new terminal window, and type:

ros2 topic list
8-ros2-topic-list

You can see that our IMU data, wheel odometry data, and GPS data are all being published by the robot.

To see the wheel odometry data for example, you can type:

ros2 topic echo /wheel/odometry

You will see the current position and orientation of the robot relative to its starting point.

9-pose-starting-point

When you’re done, go back to the terminal windows, and type CTRL + C in all of them to close Gazebo and the steering program.

Create an SDF File for the World

Just like an SDF file can be used to define what a robot looks like, we can use an SDF file to define what the robot’s environment should look like. We want to make our robot’s environment look as realistic as possible.

This tutorial here shows you how to create your own Gazebo virtual world by dragging and dropping items into an empty world. We will walk through the process below.

Open a new terminal window, and type:

cd ~/dev_ws/src/basic_mobile_robot/worlds

If you have colcon_cd set up, you can also type:

colcon_cd basic_mobile_robot
cd worlds

Let’s create a folder for the SDF model.

mkdir basic_mobile_bot_world

Move inside the folder.

cd basic_mobile_bot_world

Create a world file.

gedit smalltown.world

Copy and paste this code inside this smalltown.world file. 

Save the file, and then close it.

The world file we just created has the following six sections (from top to bottom):

  1. Place objects in the world
  2. Define the lighting of the world
  3. Define the physics of the world
  4. Define the latitude, longitude, and elevation
  5. Define the user perspective of the world (i.e. aerial view)
  6. Import our own robot

Test Your World

Now let’s run Gazebo so that we can see our world model. Open a new terminal window, and type the following command. Make sure you are inside the worlds directory of your package:

gazebo smalltown.world

You should see the world with the robot inside of it. 

10-test-your-world

If you open a new terminal window, you can see that ROS automatically launched by typing the following command to see the list of active ROS topics:

ros2 topic list

If you go back to Gazebo, you can click on the World tab, and play around with the settings for GUI (user perspective), Spherical Coordinates (latitude, longitude, elevation), Physics, etc. If you want to save these settings, you will need to record the values and modify your smalltown.world file accordingly (I prefer to do this instead of going to File -> Save World).

12-modify-world-settings

Once you’re finished, go back to the terminal windows, and type CTRL + C in all of them to close Gazebo.

Edit the Launch File

Now that we have our robot and our world file, we need to modify the launch file.

colcon_cd basic_mobile_robot
cd launch
gedit basic_mobile_bot_v2.launch.py

Copy and paste this code into the file.

Save the file, and close it.

Build the Package

Now build the package by opening a terminal window, and typing the following command:

cd ~/dev_ws
colcon build

Launch the Robot

Open a new terminal, and launch the robot.

cd ~/dev_ws/
ros2 launch basic_mobile_robot basic_mobile_bot_v2.launch.py

It could take a while for Gazebo to build and load the virtual world, so be patient. You might also see some warnings that say: “Warning: Invalid frame ID “drivewhl_l_link” passed to canTransform argument source_frame – frame does not exist…”. Ignore this warning. It will go away as soon as Gazebo loads.

If Gazebo is not launching properly, you can terminate Gazebo as follows:

killall gazebo
killall gzserver
killall gzclient

Here is the output once everything is fully loaded:

13-everything-fully-loaded

To see the active topics, open a terminal window, and type:

ros2 topic list
14-ros2-topic-list

To see more information about the topics, execute:

ros2 topic info /imu/data
ros2 topic info /wheel/odometry
15-more-info-on-topics

The  /imu/data topic publishes sensor_msgs/Imu type messages, and the /wheel/odometry topic publishes nav_msgs/Odometry type messages. The information that is published on these topics comes from the IMU and differential drive Gazebo plugins that are defined inside our SDF file for the robot.

You will also see that both topics don’t have any subscribers yet. We will generate a robot_localization node in the next tutorial that will subscribe to both of these topics to provide a fused, locally accurate and smooth odometry information for the ROS 2 Navigation Stack.

When you’re finished, press CTRL+C in all terminal windows to stop all processes.

Troubleshooting

If you see an error that says “Warning: TF_OLD_DATA ignoring data from the past for frame”, it means that you need to make sure that all nodes are running on simulated time

It is likely that your robot state publisher has not had the use_sim_time parameter set to True

You can also type the following command (when ROS 2 is shutdown) to see if there are any ROS nodes that are running that shouldn’t be running. Often, you may have multiple joint state publishers that are conflicting with each other.

ros2 topic list

If you have a ROS 2 process that is running, be sure to kill it. Then restart the launch file.

In my launch file basic_mobile_bot_v2.launch.py, you can see how I set the simulated time to true for this node.

That’s it!

In our next tutorial, I will show you how to fuse the information from your IMU sensor and wheel odometry to create a smooth estimate of the robot’s location in the world. We call this sensor fusion.

How to Create a Simulated Mobile Robot in ROS 2 Using URDF

In this tutorial, I will show you how to create a simulated mobile robot using the Universal Robot Description Format (URDF), the standard ROS format for robot modeling. This tutorial is the first tutorial in my Ultimate Guide to the ROS 2 Navigation Stack (also known as Nav2). Here is what you will create:

2-basic-mobile-robot-rviz

Roboticists like to simulate robots before building them in order to test out different algorithms. You can imagine the cost of making mistakes with a physical robot can be high (e.g. crashing a mobile robot into a wall at high speed means lost money).

You can get the entire code for this project here.

Let’s get started!

Prerequisites

  • ROS 2 Foxy Fitzroy installed on Ubuntu Linux 20.04
    • If you are using another ROS 2 distribution, you will need to replace ‘foxy’ with the name of your distribution everywhere I mention ‘foxy’ in this tutorial. 
    • I highly recommend you get the newest version of ROS 2. If you are using a newer version of ROS 2, you can still follow all the steps in this tutorial. Everything will work just fine.
  • You have already created a ROS 2 workspace. The name of our workspace is “dev_ws”, which stands for “development workspace.” 

What is URDF?

A URDF (Universal Robot Description Format) file is an XML file that describes what a robot should look like in real life. It contains the complete physical description of the robot. Building the body of the robot is the first step when getting started with Nav2.

The body of a robot consists of two components:

  1. Links
  2. Joints

Links are the rigid pieces of a robot. They are the “bones”. 

Links are connected to each other by joints. Joints are the pieces of the robot that move, enabling motion between connected links.

Consider the human arm below as an example. The shoulder, elbow, and wrist are joints. The upper arm, forearm and palm of the hand are links.

link_joint

For a robotic arm, links and joints look like this.

link-joint-robotic-arm

You can see that a robotic arm is made of rigid pieces (links) and non-rigid pieces (joints). Servo motors at the joints cause the links of a robotic arm to move.

For a mobile robot with LIDAR, links and joints look like this:

mobile-robot-joints-links

The wheel joints are revolute joints. Revolute joints cause rotational motion. The wheel joints in the photo connect the wheel link to the base link.

Fixed joints have no motion at all. You can see that the LIDAR is connected to the base of the robot via a fixed joint (i.e. this could be a simple screw that connects the LIDAR to the base of the robot).

You can also have prismatic joints. The SCARA robot in this post has a prismatic joint. Prismatic joints cause linear motion between links (as opposed to rotational motion).

Install the ROS 2 Navigation Stack (Nav2)

Now that you know what a URDF file is, let’s get to work.

Right now, I want you to complete this tutorial to install the ROS 2 Navigation Stack. Once you complete that tutorial, return to this page.

Don’t worry, setting up the ROS 2 Navigation Stack doesn’t take very long. Make sure to complete the entire tutorial before you come back here.

Install Important ROS 2 Packages

Now we need to install some important ROS 2 packages that we will use in this tutorial. Open a new terminal window, and type the following commands, one right after the other.

sudo apt install ros-foxy-joint-state-publisher-gui
sudo apt install ros-foxy-xacro

The format of the commands above is:

sudo apt install ros-<ros2-distro>-joint-state-publisher-gui
sudo apt install ros-<ros2-distro>-xacro

You will need to replace <ros2-distro> with the ROS 2 distribution you are using. In this case, I am using ROS 2 Foxy Fitzroy, which is ‘foxy’ for short.

Create a ROS 2 Package

Let’s create a ROS 2 package inside our workspace.

In a new terminal window, move to the src (source) folder of your workspace.

cd ~/dev_ws/src

Now create the package using the following command.

ros2 pkg create --build-type ament_cmake basic_mobile_robot

Create Extra Folders

Move inside the package.

cd ~/dev_ws/src/basic_mobile_robot

Create some extra folders with the following names.

mkdir config launch maps meshes models params rviz worlds

Type the following command to verify the folders were created.

dir
1-create-folders-with-names

Now build the package by typing the following command:

cd ~/dev_ws
colcon build

Let’s add a script to our bashrc file which enables us to use the following command to move to the package from any directory inside our terminal window.

colcon_cd basic_mobile_robot

Where ‘cd’ means “change directory”.

Note that in ROS 1, we typed roscd to change directories. In ROS 2, we use the colcon_cd command instead of roscd.

Open a terminal window, and type the following commands, one right after the other:

echo "source /usr/share/colcon_cd/function/colcon_cd.sh" >> ~/.bashrc
echo "export _colcon_cd_root=~/dev_ws" >> ~/.bashrc

Now open a new terminal window.

To go directly to the basic_mobile_robot ROS 2 package, type:

colcon_cd basic_mobile_robot

This command above will get you straight to the basic_mobile_robot package from within any directory in your Linux system. The format is:

colcon_cd [ROS 2 package name]

Create the URDF File

In this section, we will build our mobile robot step-by-step. Our robot will be defined in a Universal Robot Descriptor File (URDF), the XML file that represents a robot model.

Open a new terminal window, and type:

colcon_cd basic_mobile_robot
cd models

Make sure you have the Gedit text editor installed.

sudo apt-get install gedit

Create a new file named basic_mobile_bot_v1.urdf.

gedit basic_mobile_bot_v1.urdf

Inside this file, we define what our robot will look like (i.e. visual properties), how the robot will behave when it bumps into stuff (i.e. collision properties), and its mass (i.e. inertial properties). 

Type this code inside the URDF file.

Save and close the file.

All robots are made up of links and joints. If you go through the URDF file, you will see that we have defined each link and each joint of our two-wheeled robot. 

The links of the mobile robot include the robot base (often called “chassis”), the two drive wheels, the front caster wheel, the IMU, and the GPS. We also have a virtual link known as the base footprint. The base footprint (as shown on this post) is the link directly under the center of the robot.

Each link of the robot is connected by a joint. The only two joints that move are the revolute joints for the left and right wheels that rotate, causing the wheel links to turn.

I use xacro in this file, which stands for XML macro language. Xacro helps avoid clutter in the URDF file by enabling us to define string constants that we can reuse throughout the URDF file (e.g. “wheel_radius”).

To learn more about URDF files, check out the official documentation

Now go to the meshes folder.

cd ~/dev_ws/src/basic_mobile_robot/meshes

Add the following STL files to your meshes folder. A mesh is a file that allows your robot to look more realistic (rather than just using basic shapes like boxes and spheres).

Rather than use the STL files I provided, you can use a program like SolidWorks to generate your own STL files. You can also use a program called Blender to create DAE files. URDF files support meshes in either STL or DAE format.

Add Dependencies

Let’s add some packages that our project will depend on. Go to the package.xml file.

cd ~/dev_ws/src/basic_mobile_robot
gedit package.xml

After the <buildtool_depend> tag, add the following lines:

<exec_depend>joint_state_publisher</exec_depend>
<exec_depend>robot_state_publisher</exec_depend>
<exec_depend>rviz</exec_depend>
<exec_depend>xacro</exec_depend>

Save the file, and close it.

We will be using the Joint State Publisher and the Robot State Publisher. We will also be using RViz to visualize our robot model.

Create the Launch File

Let’s create a launch file. This launch file will be used by ROS 2 to load the necessary nodes for our package. 

colcon_cd basic_mobile_robot
cd launch
cd ~/dev_ws/src/basic_mobile_robot/launch/
gedit basic_mobile_bot_v1.launch.py

Copy and paste this code into the file.

Save the file, and close it.

If you go through the launch file, you will see that we first set the path to the important files and folders our robot needs in order to launch properly.

After we set the paths to the files and folders, we set our LaunchConfiguration variables. You can think of these variables as our options for how we want to launch our robot simulation. 

For example, imagine we want to launch our robot using the ROS 2 launch file, but we want to make sure that RViz (the robot visualization tool provided by ROS) remains turned OFF. We can create a flag called ‘use_rviz’. When we set ‘use_rviz’ to True, RViz launches, and when we set it to False, RViz does not launch.

In order for us to be able to set the value of this flag from a terminal window (i.e. outside the launch file), we have to do two things:

  1. Turn the string ‘use_rviz’ into a LaunchConfiguration variable since we can’t assign boolean values like True and False to a string like ‘use_rviz’. By using the LaunchConfiguration command, use_rviz == ‘use_rviz’.
  2. We also need to set the default value for this flag so that our system knows what to do if we don’t set its value from a terminal window as an argument when we launch our launch file. The DeclareLaunchArgument command sets the default value of the string ‘use_rviz’, which is True in the case of our launch file…and therefore, the LaunchConfiguration variable use_rviz also has a default value of True.

After we set and declare our launch arguments, we launch our ROS 2 nodes (e.g. the robot state publisher, RViz, etc.)

Add the RViz Configuration File

Let’s add a configuration file that will initialize RViz with the proper settings so we can view the robot as soon as RViz launches.

Open a new terminal window. Type:

colcon_cd basic_mobile_robot
cd rviz
gedit urdf_config.rviz

Add this code.

Save the file, and close it.

Build the Package

Now we need to build the package. Open a new terminal window, and type:

cd ~/dev_ws/src/basic_mobile_robot/
gedit CMakeLists.txt

Add the following snippet to CMakeLists.txt file above the if(BUILD_TESTING) line.

install(
  DIRECTORY config launch maps meshes models params rviz src worlds
  DESTINATION share/${PROJECT_NAME}
)

Save the file and close it.

Build the project.

cd ~/dev_ws/
colcon build

In the future, if you would like to build the basic_mobile_robot package only, you can type:

colcon build --packages-select basic_mobile_robot

Launch the Robot in RViz

Open a new terminal, and launch the robot.

cd ~/dev_ws/
ros2 launch basic_mobile_robot basic_mobile_bot_v1.launch.py

——

By the way, if you want to see the available arguments you can pass to the launch file from the terminal window, type:

ros2 launch -s basic_mobile_robot basic_mobile_bot_v1.launch.py
1b-pass-launch-arguments

And if you want to set the value of an argument (e.g. launch the robot without RViz), you can do something like this (this is a single command):

ros2 launch basic_mobile_robot basic_mobile_bot_v1.launch.py use_rviz:='False'

——

Here is the output after we launch the robot in RViz:

2-basic-mobile-robot-rviz-1
2-basic-mobile-robot-rviz-2

The joint state publisher GUI has sliders that enable you to move the wheels.

3-joint-state-publisher

If something goes wrong with the launch, close everything down, restart Linux, and launch again.

Check whether you have properly set up the collision properties by enabling Collision Enabled under RobotModel on the left pane.

4-collision-enabled-1

You can see in the image that I have modeled the base_link as a box for collisions. I could have just as easily used the STL model itself (which was used for visualization) as the collision geometry, but I decided to go with the simpler box representation to give me more flexibility to alter the collision areas if necessary. Also, Gazebo (a popular robotics simulator) recommends using simpler collision geometry to increase performance.

View the Coordinate Frames

Let’s see the coordinate frames. We first need to install the necessary software.

sudo apt install ros-foxy-tf2-tools

Again, if you have a different distribution of ROS 2, you will need to repalace ‘foxy’ in the command above with the name of your distribution.

Check out the coordinate frames.

ros2 run tf2_tools view_frames.py

In the current working directory, you will have a file called frames.pdf. Open that file.

evince frames.pdf

Here is what my coordinate transform (i.e. tf) tree looks like:

5-frames-pdf

If we want to see the coordinate transformation from one link to another, we can type the following command. For example, what is the position and orientation of the front caster wheel relative to the base_link of the robot?

The syntax is:

ros2 run tf2_ros tf2_echo <parent frame> <child frame>

We open a terminal window, and type:

ros2 run tf2_ros tf2_echo base_link front_caster

Here is the output. With respect to the base_link reference frame, the front caster wheel is located at (x=0.217 meters, y=0 meters, and z=-0.1 meters). 

6-tf2-ros-base-link-front-caster

The rotation value is in quaternion format. You can see that the rotation values are all 0s, indicating that the orientation of both coordinate frames is equivalent (i.e. the x, y, and z axes of both coordinate frames point in the same direction).

7-base-link-front-caster-wheel
8-base-link-front-caster-wheel

Remember that, by convention:

  • x-axis is red
  • y-axis is green
  • z-axis is blue 

To see an image of the architecture of our ROS system, open a new terminal window, and type the following command:

rqt_graph

Select the “Nodes/Topics (all)” option in the upper-left part of the screen, and then click the refresh arrow right next to it.

9-refresh-arrow

You can see how the joint state publisher GUI manipulates the joint states (i.e. the angle of each wheel joint). This data feeds into the robot state publisher. The robot state publisher publishes the coordinate frame relationships to tf, and the updated robot model feeds into RViz, the robot model visualization program.

10-rosgraph

That’s it! 

When you’re done, press CTRL+C in all terminal windows to shut everything down.

In the next tutorial, we will set up the odometry for our robot so that we can make the robot move and answer questions like “where is my robot located as it moves around in the world?”. Stay tuned!

Robot State Publisher vs. Joint State Publisher

In this post, I will explain the difference between the Robot State Publisher and the Joint State Publisher ROS packages. 

In order to understand the difference between the packages, it is important you first understand that every robot is made up of two components:

  1. Joints
  2. Links

Links and Joints in Robotics

Links are the rigid pieces of a robot. They are the “bones”. 

Links are connected to each other by joints. Joints are the pieces of the robot that move, enabling motion between connected links.

Consider the human arm below as an example. The shoulder, elbow, and wrist are joints. The upper arm, forearm and palm of the hand are links.

link_joint

For a robotic arm, links and joints look like this.

link-joint-robotic-arm

You can see that a robotic arm is made of rigid pieces (links) and non-rigid pieces (joints). Servo motors at the joints cause the links of a robotic arm to move.

For a mobile robot with LIDAR, links and joints look like this:

mobile-robot-joints-links

The wheel joints are revolute joints. Revolute joints cause rotational motion. The wheel joints in the photo connect the wheel link to the base link.

Fixed joints have no motion at all. You can see that the LIDAR is connected to the base of the robot via a fixed joint (i.e. this could be a simple screw that connects the LIDAR to the base of the robot).

You can also have prismatic joints. The SCARA robot in this post has a prismatic joint. Prismatic joints cause linear motion between links (as opposed to rotational motion).

Difference Between the Robot State Publisher and the Joint State Publisher

Whenever we want a robot to complete a specific task (e.g. move a certain distance in an environment, pick up an object, etc.), we have to have a way to know the position and velocity of each joint at all times. The Joint State Publisher does exactly this.

The Joint State Publisher package keeps track of the position (i.e. angle in radians for a servo motor or displacement in meters for a linear actuator) and velocity of each joint of a robot and publishes these values to the ROS system as sensor_msgs/JointState messages.

The Robot State Publisher then takes two main inputs:

  1. The sensor_msgs/JointState messages from the Joint State Publisher. 
  2. A model of the robot in URDF file format.

The Robot State Publisher takes that information, outputs the position and orientation of each coordinate frame of the robot, and publishes this data to the tf2 package

The tf2 package is responsible for keeping track of the position and orientation of all coordinate frames of a robot over time. At any given time, you can query the tf2 package to find out the position and orientation of any coordinate frame (i.e. “child frame”) relative to another coordinate frame (i.e. “parent” frame).

For example, if we are using ROS 2 and want to know the position and orientation of the LIDAR link relative to the base of the robot, we would use the following command:

ros2 run tf2_ros tf2_echo base_link lidar_link

The syntax is:

ros2 run tf2_ros tf2_echo <parent frame> <child frame>

Joint State Publisher: Simulation vs. Real World

When you are creating robots in simulation using a tool like Gazebo, you are going to want to use the joint state publisher Gazebo plugin to publish the position and orientation of the joints (i.e. publish the sensor_msgs/JointState messages).

In a real-world robotics project, you will want to write your own joint state publisher. You can find examples of how to do this here, here, and here.