This section will focus on integrating a robot odometry system with Nav2. First, we are going to provide a brief introduction to odometry, plus the necessary messages and transforms that need to be published for NAV2 to function correctly.

Odometry is typically generated from a variety of sources and fused together using the robot_localization package. Some of these sources include wheel encoders, visual odometry (RTABMAP), and dead reckoning from an IMU. These sources are combined to create an odom frame. A transform from the odom frame to the base_footprint frame is then created using the robot_localization package.

Table of Contents

Odometry Introduction

The odometry system provides a locally accurate estimate of a robot’s pose and velocity based on its motion. The odometry information can be obtained from various sources such as IMU, LIDAR, RADAR, VIO, and wheel encoders. One thing to note is that IMUs drift over time while wheel encoders drift over the distance traveled, thus they are often used together to counter each other’s negative characteristics.

The odom frame and the transformation associated with its use a robot’s odometry system to publish localization information that is continuous but becomes less accurate over time or distance. In order to gain consistently accurate odometry information, a map frame provides globally accurate information that corrects the odom frame.

Odometry is published using the nav_msgs/Odometry message type. The content of that message is shown below:

# This represents estimates of position and velocity in free space.
# The pose in this message should be specified in the coordinate frame given by header.frame_id
# The twist in this message should be specified in the coordinate frame given by the child_frame_id

# Includes the frame id of the pose parent.
std_msgs/Header header

# Frame id the pose is pointing at. The twist is in this coordinate frame.
string child_frame_id

# Estimated pose that is typically relative to a fixed world frame.
geometry_msgs/PoseWithCovariance pose

# Estimated linear and angular velocity relative to child_frame_id.
geometry_msgs/TwistWithCovariance twist

Setting up Robot Odometry

For simulated robots, using Gazebo plugins for robot odometry is the simplest and most easily implemented route.

In order for Gazebo odometry to work, all links defined in your URDF must have associated inertial tags. There are Gazebo plugins for both differential drive robots and Ackermann steering robots. For the differential drive robot, the odometry is created using the same plugin that controls the movement of the robot. For the Ackermann robot, certain configurations have to be set such that the plugin will publish the odometry but not the wheel tf transforms.

First, ensure that the gazebo_ros_pkgs package is installed.

sudo apt install ros-humble-gazebo-ros-pkgs

Untitled

This should already be satisfied from robot dependencies but is a good check.

Next, we have to set up the Gazebo plugins that output the wheel odometry.

Differential Wheel Odometry

Adding wheel odometry is robot geometry dependent. Let's look to add the odometry to a differential drive robot. Using the Tracer robot as an example, add the following lines of code to your robot XACRO, here I got tracer_gazebo.xacro.

cd ~/humble_ws/src/tracer/tracer_description/models/xacro
gedit tracer_gazebo.xacro

Untitled

<aside> ⚠️ Some of these lines will already exist from calling on the differential drive control plugin, ensure that the plugin tags all match.

</aside>

			<gazebo>
            <plugin name="gazebo_ros_diff_drive" filename="libgazebo_ros_diff_drive.so">
                <ros>
                    <namespace>${robot_namespace}</namespace>
                </ros>
                <!-- Update rate in Hz -->
                <update_rate>50</update_rate>
                <!-- wheels -->
                <left_joint>left_wheel_joint</left_joint>
                <right_joint>right_wheel_joint</right_joint>
                <!-- kinematics -->
                <wheel_separation>${wheelbase}</wheel_separation>
                <wheel_diameter>${2*wheel_radius}</wheel_diameter>
                <!-- limits -->
                <max_wheel_torque>20</max_wheel_torque>
                <max_wheel_acceleration>1.0</max_wheel_acceleration>
                <!-- input -->
                <command_topic>cmd_vel</command_topic>
                <!-- output -->
                <publish_odom>true</publish_odom>
                <publish_odom_tf>false</publish_odom_tf>
                <publish_wheel_tf>true</publish_wheel_tf>
                <odometry_topic>wheel_odom</odometry_topic>
                <odometry_frame>odom</odometry_frame>
                <robot_base_frame>base_footprint</robot_base_frame>
            </plugin>