Skip to content
  • Home
  • About the Blog
  • About the Author
  • Sitemap

Abdur Rosyid's Blog

Just a few notes on mechanical engineering and robotics

Fusing Wheel Odometry and IMU Data Using robot_localization in ROS

July 22, 2021 by Abdur Rosyid

Accurate robot localization is very important for robot SLAM and navigation. Using only wheel odometry typically does not provide accurate localization of a mobile ground robot because of the uncertainty resulting from the wheels’ slip and drift. For this reason, it is quite common to fuse the wheel odometry data and the IMU data. In general, it can be said that the sensor data is noisy due to the sensor’s uncertainty. And this uncertainty typically increases with time and more distance from the start position. Hence, data fusion is beneficial. A ROS package called robot_localization is quite common to be used to perform this fusion to improve the localization’s accuracy.

The robot_localization package is a generic state estimator based on EKF and UKF with sensor data fusion capability. It can fuse unlimited number of sensors as long as the sensors provide any of the following messages:

  • nav_msgs/Odometry
  • geometry_msgs/PoseWithCovarianceStamped
  • geometry_msgs/TwistWithCovarianceStamped
  • sensor_msgs/Imu

The robot_localization package provides two nodes based on the estimation algorithm used:

  • ekf_localization_node – Implementation of an extended Kalman filter (EKF)
  • ukf_localization_node – Implementation of an unscented Kalman filter (UKF)

Here is the steps to implement robot_localication to fuse the wheel odometry and IMU data for mobile robot localization.

Step 1: Create your robot_localization package. Let’s call it “my_fused_localization”. Type on the terminal:

1
2
3
4
5
cd ~/catkin_ws/src
catkin_create_pkg my_fused_localization
cd my_fused_localization
mkdir launch
mkdir config

This will create the new package having the following structure:

1
2
3
4
5
my_fused_localization:
  |--launch
  |--config
  |--package.xml
  |--CMakeLists.txt

Step 2: Create a launch file to run the robot_localization node. Let’s say we want to use the ekf_localization_node, then we can run this node by using the following launch file. Create the launch file inside the “launch” folder.

1
2
3
4
5
6
<launch>
  <!-- Run the EKF Localization node -->
  <node pkg="robot_localization" type="ekf_localization_node" name="ekf_localization">
        <rosparam command="load" file="$(find my_fused_localization)/config/ekf_localization.yaml"/>
  </node>
</launch>

Step 3: Create the configuration file for the robot_localization node. As we can see in the launch file above, we need to write a configuration file for the ekf_localization_node. Let’s say it is called “ekf_localization.yaml”. This configuration file looks like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
#Configuration for EKF localization node
 
frequency: 10
two_d_mode: true
publish_tf: false
 
# Coordinate frame specification
odom_frame: odom
base_link_frame: base_link
world_frame: odom
#map_frame: map
 
# The odom0 configuration
odom0: /noisy_odom
odom0_config: [false, false, false,
               false, false, false,
               true,  true,  false,
               false, false, true,
               false, false, false,]
odom0_differential: false
 
# The imu0 configuration
imu0: /imu/data
imu0_config: [false, false, false,
              false, false, false,
              false, false, false,
              false, false, true,
              true,  false, false,]
imu0_differential: false
 
process_noise_covariance: [0.05,    0,    0,    0,    0,    0,    0,     0,     0,    0,    0,    0,    0,    0,    0,
                              0, 0.05,    0,    0,    0,    0,    0,     0,     0,    0,    0,    0,    0,    0,    0,
                              0,    0, 0.06,    0,    0,    0,    0,     0,     0,    0,    0,    0,    0,    0,    0,
                              0,    0,    0, 0.03,    0,    0,    0,     0,     0,    0,    0,    0,    0,    0,    0,
                              0,    0,    0,    0, 0.03,    0,    0,     0,     0,    0,    0,    0,    0,    0,    0,
                              0,    0,    0,    0,    0, 0.06,    0,     0,     0,    0,    0,    0,    0,    0,    0,
                              0,    0,    0,    0,    0,    0,0.025,     0,     0,    0,    0,    0,    0,    0,    0,
                              0,    0,    0,    0,    0,    0,    0, 0.025,     0,    0,    0,    0,    0,    0,    0,
                              0,    0,    0,    0,    0,    0,    0,     0,  0.04,    0,    0,    0,    0,    0,    0,
                              0,    0,    0,    0,    0,    0,    0,     0,     0, 0.01,    0,    0,    0,    0,    0,
                              0,    0,    0,    0,    0,    0,    0,     0,     0,    0, 0.01,    0,    0,    0,    0,
                              0,    0,    0,    0,    0,    0,    0,     0,     0,    0,    0, 0.02,    0,    0,    0,
                              0,    0,    0,    0,    0,    0,    0,     0,     0,    0,    0,    0, 0.01,    0,    0,
                              0,    0,    0,    0,    0,    0,    0,     0,     0,    0,    0,    0,    0, 0.01,    0,
                              0,    0,    0,    0,    0,    0,    0,     0,     0,    0,    0,    0,    0,    0, 0.015]
 
 
initial_estimate_covariance: [1e-9,    0,    0,    0,    0,    0,    0,    0,    0,    0,     0,     0,     0,    0,    0,
                                 0, 1e-9,    0,    0,    0,    0,    0,    0,    0,    0,     0,     0,     0,    0,    0,
                                 0,    0, 1e-9,    0,    0,    0,    0,    0,    0,    0,     0,     0,     0,    0,    0,
                                 0,    0,    0, 1e-9,    0,    0,    0,    0,    0,    0,     0,     0,     0,    0,    0,
                                 0,    0,    0,    0, 1e-9,    0,    0,    0,    0,    0,     0,     0,     0,    0,    0,
                                 0,    0,    0,    0,    0, 1e-9,    0,    0,    0,    0,     0,     0,     0,    0,    0,
                                 0,    0,    0,    0,    0,    0, 1e-9,    0,    0,    0,     0,     0,     0,    0,    0,
                                 0,    0,    0,    0,    0,    0,    0, 1e-9,    0,    0,     0,     0,     0,    0,    0,
                                 0,    0,    0,    0,    0,    0,    0,    0, 1e-9,    0,     0,     0,     0,    0,    0,
                                 0,    0,    0,    0,    0,    0,    0,    0,    0, 1e-9,     0,     0,     0,    0,    0,
                                 0,    0,    0,    0,    0,    0,    0,    0,    0,    0,  1e-9,     0,     0,    0,    0,
                                 0,    0,    0,    0,    0,    0,    0,    0,    0,    0,     0,  1e-9,     0,    0,    0,
                                 0,    0,    0,    0,    0,    0,    0,    0,    0,    0,     0,     0,  1e-9,    0,    0,
                                 0,    0,    0,    0,    0,    0,    0,    0,    0,    0,     0,     0,     0, 1e-9,    0,
                                 0,    0,    0,    0,    0,    0,    0,    0,    0,    0,     0,     0,     0,    0, 1e-9]

Description of the parameters in the configuration file

frequency: the real-valued frequency, in Hz, at which the filter produces a state estimate.

two_d_mode: If your robot is operating in a planar environment and you’re comfortable with ignoring the subtle variations in the ground (as reported by an IMU), then set this to true. It will fuse 0 values for all 3D variables (Z, roll, pitch, and their respective velocities and accelerations). This keeps the covariances for those values from exploding while ensuring that your robot’s state estimate remains affixed to the X-Y plane.

publish_tf: If true, the state estimation node will publish the transform from the frame specified by the world_frame parameter to its child. If the world_frame is the same as the map_frame it will publish the transform from the map_frame to the odom_frame and if the world_frame is the same as the odom_frame it will publish the transform from the odom_frame to the base_link_frame. Defaults to true.

Coordinate frame specification:

  • The default values for “map_frame”, “odom_frame”, and “base_link_frame” are “map”, “odom”, and “base_link”, respectively. The “world_frame” parameter defaults to the value of “odom_frame”.
  • If your system does not have a map_frame, just remove or comment it, and make sure “world_frame” is set to the value of “odom_frame”.
  • If you are only fusing continuous position data such as wheel encoder odometry, visual odometry, or IMU data, set “world_frame” to the value of your “odom_frame”. This is the default behavior for the state estimation nodes in robot_localization, and the most common use for it.

[sensor]: For each sensor, you need to define this parameter based on the topic published by the sensor.

[sensor]_config: is defined by a Boolean 5×3 matrix as follows:

1
2
3
4
5
[  X,         Y,        Z,
  Roll,     Pitch,     Yaw,
  dX/dt,    dY/dt,    dZ/dt,
dRoll/dt, dPitch/dt, dYaw/dt,
d2X/dt2,   d2Y/dt2,  d2Z/dt2]

[sensor]_differential: With this parameter, you specify whether the pose variables should be integrated differentially. If a given value is set to true, then for a measurement at time t from the sensor in question, we first subtract the measurement at time t−1, and convert the resulting value to a velocity. This setting is especially useful if your robot has two sources of absolute pose information, e.g., yaw measurements from odometry and an IMU. In that case, if the variances on the input sources are not configured correctly, these measurements may get out of sync with one another and cause oscillations in the filter, but by integrating one or both of them differentially, we avoid this scenario.

process_noise_covariance: commonly denoted Q, is used to model uncertainty in the prediction stage of the filtering algorithms. The values on the diagonals are the variances for the state vector which include pose, then velocities, then linear acceleration. It can be difficult to tune, and has been exposed as a parameter for easier customization. This parameter can be left alone, but you will achieve superior results by tuning it. In general, the larger the value for Q relative to the variance for a given variable in an input message, the faster the filter will converge to the value in the measurement.

initial_estimate_covariance: This parameter allows to set the initial value for the state estimate covariance matrix, which will affect how quickly the filter converges. Here’s the rule you should follow: if you are measuring a variable, make the diagonal value in initial_estimate_covariance larger than that measurement’s covariance. So, for example, if your measurement’s covariance value for the variable in question is 1e-6, make the initial_estimate_covariance diagonal value 1e-3 or something like that.

Step 4: To run the localization node, launch the launch file you created in Step 1. Let’s say your launch file is called “start_filter.launch”, the launch the launch file by typing the following command:

1
roslaunch my_fused_localization start_filter.launch

Adding move_base for navigation

If you want to move the robot by using this localization, you can additionally run the “move_base” node in the launch file:

1
2
3
4
5
6
7
8
<launch>
  <!-- Run the EKF Localization node -->
  <node pkg="robot_localization" type="ekf_localization_node" name="ekf_localization">
        <rosparam command="load" file="$(find my_fused_localization)/config/ekf_localization.yaml"/>
  </node>
  <!--- Run Move Base -->
  <include file="$(find my_move_base)/launch/my_move_base.launch" />
</launch>

The “my_move_base” launch file above is a launch file which runs the “move_base” node. This node should be wrapped in a separate package called “my_move_base”.

Official documentation: http://docs.ros.org/en/melodic/api/robot_localization/html/state_estimation_nodes.html

Post navigation

Previous Post:

2D Navigation of a Mobile Robot in ROS

Next Post:

Fusing Wheel Odometry, IMU Data, and GPS Data Using robot_localization in ROS

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

Categories

  • STEM 101
  • Robotics
  • Kinematics
  • Dynamics
  • Control
  • Robot Operating System (ROS)
  • Robot Operating System (ROS2)
  • Software Development
  • Mechanics of Materials
  • Finite Element Analysis
  • Fluid Mechanics
  • Thermodynamics

Recent Posts

  • Pull Request on Github
  • Basics of Git and Github
  • Conda vs Docker
  • A Conda Cheat Sheet
  • Installing NVIDIA GPU Driver on Ubuntu

Archives

  • June 2025
  • July 2021
  • June 2021
  • March 2021
  • September 2020
  • April 2020
  • January 2015
  • April 2014
  • March 2014
  • March 2012
  • February 2012
  • June 2011
  • March 2008
© 2026 Abdur Rosyid's Blog | WordPress Theme by Superbthemes