Skip to Content

Mapping & Navigation

Prerequisites: This guide assumes you can already launch BonicBot A2 and drive it with teleop. If not, complete Getting Started with ROS2 first.

This page covers three sequential capabilities: Mapping with SLAM (building a 2D map by driving through a space), Autonomous Mapping (frontier-based exploration with no manual driving), and Autonomous Navigation (goal-directed path planning on a saved map).


Part 1 — Mapping with SLAM

SLAM (Simultaneous Localization and Mapping) builds a 2D occupancy grid of the environment while concurrently estimating the robot’s position within it, using lidar and odometry as its only inputs. The resulting map can be saved and reused for navigation, path planning, or spatial analysis.

This section requires three terminals (real robot) or four terminals (simulation). Open each terminal by running docker exec -it bonicbot-dev bash in a new window, or use the Antigravity terminal panel.


Workspace Setup

If the workspace has not been cloned and built yet:

mkdir -p ~/dev_ws/src cd ~/dev_ws/src git clone https://github.com/Autobonics/bonicbot-a2-ros.git cd ~/dev_ws colcon build --symlink-install source install/setup.bash

Completed this in Getting Started? Proceed to the next section.


Simulation Setup — Launch the Environment

Physical robot users: Skip this section. The robot_system launch is already running when you start ROS via the BonicBot A2 app — proceed directly to Terminal 1 — Start SLAM.

The simulation world must contain structural geometry (walls, obstacles) to provide meaningful lidar returns for the mapping algorithm. An empty world will not work. If this is your first time running Gazebo, see Run the Robot in Simulation for setup details.

ros2 launch my_bot robot_system.launch.py use_sim_time:=true world:=obsworld.sdf

Leave this terminal running. It hosts the simulated robot and environment; all subsequent nodes connect to it.


Terminal 1 — Start SLAM

Instructions

Launch SLAM Toolbox in online asynchronous mode:

ros2 launch my_bot online_async_launch.py use_sim_time:=false # real robot # ros2 launch my_bot online_async_launch.py use_sim_time:=true # simulation

Online asynchronous mode processes lidar scans and odometry in real time, continuously building the occupancy grid. “Async” means scan processing is not frame-locked to the sensor rate — the node stays responsive under variable system load.

Leave this running. It subscribes to /scan and the robot’s TF tree, and publishes the growing map to /map.

Terminal 2 — Visualize the Map in RViz

Instructions

rviz2

Configure the display:

  1. Set Fixed Frame to map
  2. Add → TF and Add → RobotModel (set Description Topic to /robot_description)
  3. Add → By Topic → /map (Map display)
  4. Add → By Topic → /scan (LaserScan display)

The map expands in the viewport as the robot moves.

Terminal 3 — Drive and Build the Map

Instructions

Install teleop_twist_keyboard if not already present:

sudo apt install ros-humble-teleop-twist-keyboard

Inside the Docker container, elevated privileges are already available — sudo may be omitted if unavailable.

Launch teleop:

ros2 run teleop_twist_keyboard teleop_twist_keyboard

Click the terminal to focus it, then drive the robot through the space. SLAM fills in walls and free space in real time wherever the lidar scans. Regions the robot has not passed near remain unknown (grey) on the map.

Drive every area you want represented in the final map. Unvisited regions will not appear.


Save the Map

Once map coverage is satisfactory, save it from any available terminal (cd ~/dev_ws first):

ros2 run nav2_map_server map_saver_cli -f my_map_save --ros-args -p use_sim_time:=false # real robot # ros2 run nav2_map_server map_saver_cli -f my_map_save --ros-args -p use_sim_time:=true # simulation

This writes two files to the current directory:

  • my_map_save.pgm — the occupancy grid image
  • my_map_save.yaml — metadata (resolution, origin, threshold values)

Verify the output:

ls ~/dev_ws/

Shutting Down

Press Ctrl+C in Terminal 1 (SLAM) and Terminal 2 (RViz). Simulation users may stop the world terminal or leave it running to proceed directly to Part 3.


Part 2 — Autonomous Mapping

Autonomous Mapping with Explore Lite

Explore Lite implements frontier-based exploration: it identifies boundaries between mapped and unknown space, selects the nearest reachable frontier, and dispatches a Nav2 goal to drive toward it. As each frontier is reached, SLAM expands the map and new frontiers emerge further out. The cycle continues until no reachable frontiers remain.

This section requires four terminals (real robot) or five terminals (simulation). Start each layer in sequence and wait for it to fully initialize before proceeding.


Simulation Setup

Physical robot users: Skip to Terminal 1 — Start SLAM.

Launch the simulation environment as described in Simulation Setup — Launch the Environment. Leave this terminal running.


Terminal 1 — Start SLAM

# Real robot ros2 launch slam_toolbox online_async_launch.py \ params_file:=$(ros2 pkg prefix my_bot)/share/my_bot/config/mapper_params_online_async.yaml \ use_sim_time:=false # Simulation — wait ~5 s after the world is up, then run with use_sim_time:=true

SLAM Toolbox starts publishing the live occupancy grid to /map.


Terminal 2 — Start Nav2

Wait ~5 seconds after SLAM is running, then:

# Real robot ros2 launch nav2_bringup navigation_launch.py \ params_file:=$(ros2 pkg prefix my_bot)/share/my_bot/config/nav2_params.yaml \ use_sim_time:=false # Simulation (uses a separate params file) # ros2 launch nav2_bringup navigation_launch.py \ # params_file:=$(ros2 pkg prefix my_bot)/share/my_bot/config/nav2_params_sim.yaml \ # use_sim_time:=true

This brings up the full Nav2 stack — global and local costmaps, planner, and controller. Explore Lite sends goal poses through Nav2, so Nav2 must be fully active before starting exploration.


Terminal 3 — Open RViz

Wait ~15 seconds after Nav2 starts, then:

rviz2 -d /opt/ros/humble/share/nav2_bringup/rviz/nav2_default_view.rviz # Simulation: append --ros-args -p use_sim_time:=true

This opens RViz with Nav2’s default layout (map, costmaps, robot model, navigation displays). Watch the map grow as Explore Lite drives the robot.

This terminal only requires the base ROS 2 environment sourced — workspace sourcing is not needed since no my_bot packages are loaded here.


Terminal 4 — Start Explore Lite

Once the robot and an initial partial map are visible in RViz:

ros2 run explore_lite explore --ros-args \ --params-file $(ros2 pkg prefix explore_lite)/share/explore_lite/config/params.yaml \ -p costmap_topic:=/global_costmap/costmap \ -p costmap_updates_topic:=/global_costmap/costmap_updates \ -p min_frontier_size:=0.5 \ -p return_to_init:=false \ -p use_sim_time:=false # change to true for simulation

Parameter notes:

  • costmap_topic / costmap_updates_topic — Explore Lite’s defaults do not match Nav2’s published topic names; these overrides are required.
  • min_frontier_size:=0.5 — suppresses noise-level frontier segments, preventing the robot from pursuing map artifacts.
  • return_to_init:=false — the robot remains at its final position when exploration ends rather than returning to its start pose.

The robot begins navigating autonomously. In RViz, observe it select a frontier, drive toward it, and the map expand with each traversal. Exploration concludes automatically when no reachable frontiers remain.


Save the Map

Once exploration completes (or at any satisfactory coverage point), save the map using the same command documented in Save the Map above.


Part 3 — Autonomous Navigation

⚠️ Before proceeding: Stop Explore Lite (Terminal 4), Nav2 (Terminal 2), and SLAM (Terminal 1) with Ctrl+C if they are still running. Simulation users may keep the world terminal alive.

With a saved map, the robot can navigate autonomously to any goal pose within that space. Nav2 plans a global path, executes it with a local controller, and re-plans dynamically if the environment changes.

A single launch command starts Nav2 with the saved map loaded and RViz2 pre-configured.


Simulation Setup

Physical robot users: The base system starts on power-up. Proceed to Terminal 1.

If the Gazebo world is not already running from Part 1 or 2, launch it as described in Simulation Setup — Launch the Environment.


Terminal 1 — Launch Nav2 with the Saved Map

ros2 launch my_bot nav2_combined.launch.py use_sim_time:=false # real robot # ros2 launch my_bot nav2_combined.launch.py use_sim_time:=true # simulation

This brings up the full Nav2 stack with the saved map loaded, and opens RViz2 with the map, costmaps, and navigation displays pre-configured. Wait until the map is visible in RViz2 before proceeding.


How to Navigate

Step-by-step instructions

Step 1 — Set the Initial Pose

The robot has the map but does not yet know its position on it. An initial pose estimate is required before sending any navigation goals.

In RViz2, click 2D Pose Estimate in the toolbar, click the robot’s actual location on the map, and drag in the direction it is facing. Release to confirm.

AMCL’s particle cloud will converge around the estimated pose. If the robot model is visibly offset from the map geometry, re-estimate from a more accurate position.

An accurate initial pose is critical. An incorrect estimate causes the robot’s planned paths to be offset from the actual environment, leading to navigation failures.


Step 2 — Send a Navigation Goal

Click Navigation2 Goal in the RViz2 toolbar, click the target location on the map, and drag to set the arrival orientation. Release to confirm.

Nav2 will:

  1. Plan a collision-free global path using the static map and costmap
  2. Execute the path via the local controller, sending velocity commands to the robot
  3. Re-plan continuously if the robot deviates or a new obstacle is detected

The planned path appears as a line in RViz2. A new goal can be sent at any time; the active goal is immediately cancelled and a fresh path is computed.

Goal unreachable? Nav2 will time out if the goal is inside a wall, too close to an obstacle, or in an unmapped region. Select a point in open, mapped space.


Obstacle Avoidance

Monitor the interaction between the Global Path (the long-term planned route) and the Local Costmap (the small colored grid that moves with the robot):

  • Local Costmap — updates in real time from live lidar data, capturing obstacles not present in the saved map.
  • Dynamic re-planning — when a new obstacle is detected ahead, the local planner re-routes around it automatically. The robot decelerates, computes a new local trajectory, and continues toward the goal.

Cancel or Redirect

  • Change destination: Click Navigation2 Goal and select a new point. The current goal is cancelled immediately and a new path is planned.
  • Stop the robot: Click Cancel in the Navigation2 panel on the left side of RViz2.
  • Recovery behaviors: If the path is fully blocked, Nav2 executes recovery behaviors (e.g., in-place rotation, short reverse) to clear the costmap. If no path can be found after all attempts, the goal is cancelled and a failure is reported.

📖
Getting Started with ROS2
Teleop, topics, RViz, and basic simulation
⚙️
ROS2 Development Setup
Environment and Docker configuration
Last updated on