This is still very much a work in progress, if you want to contribute please check the Contributing Section.
There are some features that I intend to add once the project is stable enough, for now I will only focus on application on Humble, some help porting this plugin to newer ROS versions would be highly appreciated.
- Obstacle Permanence: Currently the obstacles get cleaned out once none of the robots can see them anymore, ideally the obstacle detections should remain until a robots clears them out. Much like how it currently works with the nav2 obstacle layer
- Nav2 Goal namespace selection tool for Rviz: Right now the only way to graphically set a goal is to manually modify the 2D goal pose functionality on rviz from the Toop Properties panel, while it's not possible to do the same with the NAV2 Goal tool.
- Secret one: I'm working on the concept of this one and if it works it could become a paper, so at the moment it will stay a secret.
A comprehensive ROS2 package that provides multi-robot obstacle sharing capabilities for the Nav2 navigation stack. This plugin enables robots in a fleet to share detected obstacles with each other, improving collective navigation awareness and safety.
The Multi-Robot Costmap Plugin consists of two main components:
- MultiRobotLayer: A Nav2 costmap plugin that integrates shared obstacle data into individual robot costmaps
- GlobalCostmapFusion: A standalone ROS2 node that collects laser scan data from multiple robots and publishes a unified obstacle grid
- Real-time obstacle sharing between multiple robots
- TF2-based robot pose tracking for accurate coordinate transformations
- Automatic map parameter detection from map server or service
- Configurable robot exclusion zones to prevent robots cross-detection and controller errors
- Thread-safe operation with mutex-protected data structures
- Robust error handling with transform timeout management
- Compatible with Nav2 lifecycle management
multi_robot_costmap_plugin/
├── CMakeLists.txt
├── package.xml
├── plugin.xml # Plugin registration file
├── include/
│ └── multi_robot_costmap_plugin/
│ ├── global_costmap_fusion.hpp # GlobalCostmapFusion node header
│ └── multi_robot_layer.hpp # MultiRobotLayer plugin header
├── src/
│ ├── global_costmap_fusion.cpp # GlobalCostmapFusion implementation
│ ├── global_costmap_fusion_node.cpp # Node executable wrapper
│ └── multi_robot_layer.cpp # MultiRobotLayer implementation
├── config/
│ └── multi_robot_costmap.yaml # Shared costmap configuration example
└── launch/
└── examples_launch.py # Demo launch file
- ROS2 Humble (tested) or later (not sure)
- Nav2 navigation stack
- TF2 for coordinate transformations
rclcppnav2_costmap_2dnav_msgssensor_msgsgeometry_msgstf2_rostf2_geometry_msgspluginlib
- Create a workspace and clone the repository:
mkdir -p ~/multi_robot_ws/src
cd ~/multi_robot_ws/src
git clone https://github.com/FilippoGuarda/multi_robot_costmap_plugin.git- Install dependencies:
cd ~/multi_robot_ws
rosdep install --from-paths src --ignore-src -r -y- Build the package:
colcon build --packages-select multi_robot_costmap_plugin
source install/setup.bash- Verify installation:
ros2 pkg list | grep multi_robot_costmap_pluginCreate a configuration file config/global_costmap_fusion.yaml:
global_costmap_fusion:
ros__parameters:
# Robot fleet configuration
robots: ["robot1", "robot2", "robot3", "robot4", "robot5", "robot6"]
# Physical robot parameters
robot_radius: 0.3 # Robot radius in meters
exclusion_buffer: 0.5 # Additional buffer around robots
# Update rates
update_frequency: 10.0 # Hz - obstacle grid publishing rate
robot_timeout: 5.0 # Seconds before marking robot inactive
# Frame and topic configuration
global_frame: "map" # Global reference frame
output_topic: "/shared_obstacles_grid"
scan_topic: "scan" # Laser scan topic suffix
map_topic: "/map" # Map topic for parameter extraction
map_service: "/map_server/map" # Map service fallback
base_frame_suffix: "base_footprint" # Robot base frame suffixAdd the MultiRobotLayer to your Nav2 costmap configuration:
global_costmap:
global_costmap:
ros__parameters:
plugins: ["static_layer", "multi_robot_layer", "inflation_layer"]
multi_robot_layer:
plugin: "multi_robot_costmap_plugin/MultiRobotLayer"
enabled: true
robot_namespaces: ["robot1", "robot2", "robot3", "robot4", "robot5", "robot6"]
obstacle_max_range: 2.5
obstacle_min_range: 0.3
footprint_padding: 0.1
default_robot_radius: 0.3# Example launch file integration
from launch import LaunchDescription
from launch_ros.actions import Node
from ament_index_python.packages import get_package_share_directory
import os
def generate_launch_description():
config_file = os.path.join(
get_package_share_directory('multi_robot_costmap_plugin'),
'config',
'global_costmap_fusion.yaml'
)
return LaunchDescription([
# GlobalCostmapFusion node
Node(
package='multi_robot_costmap_plugin',
executable='global_costmap_fusion_node',
name='global_costmap_fusion',
parameters=[config_file],
output='screen'
),
# Your Nav2 launch here...
])Coming Soon with complete simulation example
/{robot_id}/scan(sensor_msgs/LaserScan) - Laser scan data/map(nav_msgs/OccupancyGrid) - Map for parameter extraction
/shared_obstacles_grid(nav_msgs/OccupancyGrid) - Shared obstacle grid
map→{robot_id}/odom→{robot_id}/base_footprint{robot_id}/base_footprint→{robot_id}/laser_frame
/map_server/map(nav_msgs/GetMap) - Fallback for map parameters
The fusion node operates with the following workflow:
- Initialization: Subscribes to map topic/service to extract grid parameters
- Robot Tracking: Uses TF2 to continuously track robot poses at 10Hz
- Scan Processing: Processes laser scans from active robots
- Coordinate Transformation: Converts scan points to global coordinates using TF2
- Robot Filtering: Excludes points within robot exclusion zones
- Grid Publishing: Publishes accumulated obstacles as an occupancy grid
The costmap plugin integrates shared obstacle data:
- Lifecycle Management: Properly initializes and activates with Nav2
- Data Subscription: Subscribes to the shared obstacles grid
- Costmap Integration: Adds shared obstacles to the robot's local costmap
- Thread Safety: Ensures safe access to shared data structures
# For robots with different frame naming conventions
global_costmap_fusion:
ros__parameters:
base_frame_suffix: "base_link" # Instead of "base_footprint"# Optimize for large fleets
global_costmap_fusion:
ros__parameters:
update_frequency: 5.0 # Reduce for better performance
robot_timeout: 10.0 # Increase for unstable networks
exclusion_buffer: 0.3 # Reduce for denser operation# Enable detailed logging
global_costmap_fusion:
ros__parameters:
# Add to launch with --log-level debug1. Plugin Not Loading
# Check if plugin is registered
ros2 pkg list | grep multi_robot_costmap_plugin
# Verify plugin.xml is correct
cat install/multi_robot_costmap_plugin/share/multi_robot_costmap_plugin/plugin.xml2. No Shared Obstacles Published
# Check if robots are active
ros2 topic echo /shared_obstacles_grid --once
# Verify laser scan topics
ros2 topic list | grep scan
ros2 topic hz /robot1/scan3. Transform Errors
# Check TF tree
ros2 run tf2_tools view_frames
# Verify robot transforms
ros2 run tf2_ros tf2_echo map robot1/base_footprint4. Map Parameters Not Detected
# Check map availability
ros2 topic echo /map --once
ros2 service call /map_server/map nav_msgs/srv/GetMap- High CPU usage: Reduce
update_frequencyorobstacle_max_range - Memory consumption: Limit the number of obstacles accumulated
- Network bandwidth: Reduce grid resolution or update frequency
# Monitor node performance
ros2 topic hz /shared_obstacles_grid
# Check robot activity
ros2 param get /global_costmap_fusion robots
# Monitor transform updates
ros2 topic echo /tf --once | grep robot1
# Check costmap plugin status
ros2 lifecycle get /global_costmap/global_costmapThis project is licensed under the Apache License 2.0 - see the LICENSE file for details.
If you use this package in your research, please cite:
@software{multi_robot_costmap_plugin,
author = {Filippo Guarda},
title = {Multi-Robot Costmap Plugin for Nav2},
year = {2025},
url = {https://github.com/FilippoGuarda/multi_robot_costmap_plugin}
}- Fork the repository
- Create a feature branch:
git checkout -b feature-name - Commit changes:
git commit -am 'Add feature' - Push to branch:
git push origin feature-name - Submit a pull request
For questions, issues, or contributions:
- GitHub Issues: Report bugs or request features
- Discussions: Ask questions or share ideas