ROS 2 package for global GPS-based localization of a rover using robot_localization.
This package adds the global localization layer required to publish the transform:
map -> odom
It is designed to work together with an existing local EKF that already publishes:
odom -> base_link
The local EKF usually fuses wheel or track odometry with IMU data to produce a smooth local odometry estimate. This local estimate is continuous, but it can drift over time.
This package uses GPS data with navsat_transform_node and a second EKF to correct that drift in a global map frame.
Final TF tree:
map -> odom -> base_link
Existing local localization:
/odom + /imu/raw
|
v
Local EKF
|
v
/odometry/filtered
TF: odom -> base_link
Global localization added by this package:
/gps/fix + /imu/raw + /odometry/filtered
|
v
navsat_transform_node
|
v
/odometry/gps
|
v
Global EKF
|
v
/odometry/global
TF: map -> odom
This package expects the following topics:
/odometry/filtered nav_msgs/msg/Odometry
/imu/raw sensor_msgs/msg/Imu
/gps/fix sensor_msgs/msg/NavSatFix
/odometry/filtered should come from the local EKF.
/imu/raw should contain IMU data. Ideally, the IMU message should include a valid orientation.
/gps/fix should contain GPS data as sensor_msgs/msg/NavSatFix.
This package publishes:
/odometry/gps nav_msgs/msg/Odometry
/odometry/global nav_msgs/msg/Odometry
It also publishes the TF:
map -> odom
Before launching this package, the robot should already have:
odom -> base_link
Usually this is published by the local EKF.
The robot should also have static transforms for its sensors, for example:
base_link -> imu_link
base_link -> gps_link
This package depends on:
robot_localization
nav_msgs
sensor_msgs
tf2_ros
rclpy
Install robot_localization:
sudo apt update
sudo apt install ros-jazzy-robot-localizationIf you are not using ROS 2 Jazzy, replace jazzy with your ROS 2 distribution name.
rover_global_localization
├── config
│ ├── ekf_global.yaml
│ └── navsat.yaml
├── launch
│ └── global_localization.launch.py
├── rover_global_localization
│ └── __init__.py
├── resource
│ └── rover_global_localization
├── package.xml
├── setup.cfg
├── setup.py
└── README.md
navsat_transform_node converts GPS latitude/longitude into a local odometry message.
Important parameters:
use_odometry_yaw: false
zero_altitude: true
wait_for_datum: false
publish_filtered_gps: trueIf the IMU does not provide a valid orientation, use_odometry_yaw may need to be set to true.
The global EKF fuses:
/odometry/filtered
/odometry/gps
/imu/raw
The important frame configuration is:
map_frame: map
odom_frame: odom
base_link_frame: base_link
world_frame: map
publish_tf: trueThe key parameter is:
world_frame: mapThis makes the global EKF publish:
map -> odom
From the ROS 2 workspace:
cd ~/ros2_ws
colcon build --packages-select rover_global_localization
source install/setup.bashFirst, launch the local EKF and sensor drivers.
The following topics should already exist:
ros2 topic list | grep -E "imu|gps|odom"Expected topics include:
/imu/raw
/gps/fix
/odom
/odometry/filtered
Then launch the global localization package:
ros2 launch rover_global_localization global_localization.launch.pyCheck the GPS fix:
ros2 topic echo /gps/fix --onceCheck the IMU:
ros2 topic echo /imu/raw --onceCheck the local EKF output:
ros2 topic echo /odometry/filtered --onceCheck the GPS odometry output from navsat_transform_node:
ros2 topic echo /odometry/gps --onceCheck the global EKF output:
ros2 topic echo /odometry/global --onceCheck the local transform:
ros2 run tf2_ros tf2_echo odom base_linkCheck the global transform:
ros2 run tf2_ros tf2_echo map odomCheck the full transform chain:
ros2 run tf2_ros tf2_echo map base_linkLaunch RViz:
rviz2Set:
Fixed Frame = map
Recommended displays:
TF
Odometry: /odometry/filtered
Odometry: /odometry/global
NavSatFix: /gps/fix
Expected TF tree:
map
└── odom
└── base_link
├── imu_link
└── gps_link
Only one node should publish each TF.
Recommended setup:
Local EKF -> odom -> base_link
Global EKF -> map -> odom
The odometry node should not publish odom -> base_link if the local EKF already does it.
The global EKF should not replace the local EKF. The local EKF provides smooth short-term motion, while the global EKF corrects drift using GPS.
Check that all required inputs exist:
ros2 topic echo /gps/fix --once
ros2 topic echo /imu/raw --once
ros2 topic echo /odometry/filtered --onceAlso check that the IMU orientation is valid.
If the IMU orientation is invalid, try setting:
use_odometry_yaw: truein navsat.yaml.
Check that the global EKF is running:
ros2 node listCheck the global odometry output:
ros2 topic echo /odometry/global --onceCheck the TF:
ros2 run tf2_ros tf2_echo map odomMake sure the global EKF has:
world_frame: map
publish_tf: trueIf your IMU topic is /imu/data instead of /imu/raw, replace /imu/raw with /imu/data in:
config/ekf_global.yaml
launch/global_localization.launch.py
MIT