ROS 2 Humble Navigation Stack with Slam Toolbox on Raspberry Pi 5

DIY Robotics
Date:November 9, 2026
Topic:
ROS 2 Humble Navigation Stack with Slam Toolbox on Raspberry Pi 5
⏱ 3 min read

The Raspberry Pi 5 changed the economics of mobile robotics. For the first time, a $80 single-board computer delivers enough CPU cycles to run a full ROS 2 Humble navigation stack—SLAM, localization, and path planning—without offloading to a laptop. I spent three weeks tuning Slam Toolbox and Nav2 on a Pi 5 driving a differential-drive base. Here is the minimal viable path from a fresh OS image to autonomous waypoint following.

Hardware Baseline

ComponentSpec
SBCRaspberry Pi 5 8 GB
Storage256 GB NVMe via Pimoroni NVMe Base
LidarRPLidar A1M8 (USB)
MCUSTM32F4 running micro-ROS
Battery2S 5000 mAh LiPo + 5 V 5 A UBEC

Active cooling is non-negotiable. The Pi 5 throttles at 80 °C; the official active cooler keeps sustained compiles under 65 °C. Mount the Lidar high enough to clear the chassis lip—0.2 m is the practical minimum for 270° FOV.

OS and ROS 2 Install

bash
# Ubuntu 24.04 Server (64-bit) on NVMe
sudo apt update && sudo apt install -y ros-humble-desktop
# Add real-time kernel for deterministic control loops
sudo apt install -y linux-image-rt-rpi-2712
# Pin CPU governor to performance
echo 'GOVERNOR="performance"' | sudo tee /etc/default/cpufrequtils
💡
TipUse the NVMe boot path. SD cards cannot sustain the write throughput of SLAM map serialization.

Slam Toolbox Configuration

Slam Toolbox replaces the legacy gmapping/Cartographer combo with a single, maintenance-mode codebase. The async solver keeps the Pi’s four Cortex-A76 cores fed without starving the control loop.

yaml
slam_toolbox:
  ros__parameters:
    solver_plugin: solver_plugins::CeresSolver
    ceres_linear_solver: SPARSE_NORMAL_CHOLESKY
    mode: mapping
    map_frame: map
    odom_frame: odom
    base_frame: base_link
    scan_topic: /scan
    resolution: 0.05
    max_laser_range: 12.0
    minimum_time_interval: 0.5
    transform_publish_period: 0.02
    map_update_interval: 1.0
    enable_interactive_mode: true
⚠️
WarningSet <code>minimum_time_interval: 0.5</code>. The default 0.1 floods the CPU with scan matching threads and starves Nav2.

Nav2 defaults assume a desktop GPU. On the Pi 5 you must shrink the costmap footprint, reduce planner frequency, and disable unused plugins.

yaml
controller_server:
  ros__parameters:
    controller_plugins: ["FollowPath"]
    FollowPath:
      plugin: "nav2_regulated_pure_pursuit_controller/RegulatedPurePursuitController"
      desired_linear_vel: 0.35
      lookahead_dist: 0.6
      max_angular_vel: 1.8
planner_server:
  ros__parameters:
    planner_plugins: ["GridBased"]
    GridBased:
      plugin: "nav2_navfn_planner/NavfnPlanner"
      tolerance: 0.1
      use_astar: false
      allow_unknown: true
local_costmap:
  local_costmap:
    ros__parameters:
      update_frequency: 5.0
      publish_frequency: 2.0
      width: 3
      height: 3
      resolution: 0.05
      plugins: ["voxel_layer", "inflation_layer"]
      inflation_layer:
        inflation_radius: 0.3
        cost_scaling_factor: 3.0
ℹ️
NoteRegulated Pure Pursuit is lighter than DWB and handles differential-drive kinematics natively.

Bringup and First Map

bash
# Terminal 1: hardware bridge
ros2 launch my_robot_base hardware.launch.py
# Terminal 2: SLAM
ros2 launch slam_toolbox online_async_launch.py params_file:=slam.yaml
# Terminal 3: Nav2 (localization only)
ros2 launch nav2_bringup localization_launch.py map:=my_map.yaml
# Terminal 4: teleop for mapping run
ros2 run teleop_twist_keyboard teleop_twist_keyboard

Drive slowly. Fast rotations induce motion blur on the A1M8 and break scan matching. After 2–3 minutes save the map: ros2 run nav2_map_server map_saver_cli -f my_map.

Autonomous Waypoint Test

Switch to full navigation bringup. Send a goal via RViz2 or the action client:

python
from nav2_simple_commander.robot_navigator import BasicNavigator
nav = BasicNavigator()
goal = PoseStamped()
goal.header.frame_id = 'map'
goal.pose.position.x = 2.5
goal.pose.position.y = -1.0
nav.goToPose(goal)
while not nav.isTaskComplete():
    feedback = nav.getFeedback()
    print(f'Distance remaining: {feedback.distance_remaining:.2f} m')

Performance Numbers

MetricValue
CPU (mapping)65 % across 4 cores
CPU (nav only)38 %
RAM usage1.2 GB / 8 GB
Map serialization1.4 s (50 m²)
Replan latency120 ms avg

✦

Clone the reference repo, flash the NVMe, and you have a repeatable baseline. From here: add AMCL tuning for kidnap recovery, integrate a depth camera for obstacle avoidance, or swap the planner for Smac Hybrid-A* if you need reverse maneuvers. The Pi 5 finally makes “laptop-free” a shipping feature, not a demo trick.

💡
TipPin the ROS 2 apt repository to Humble. Rolling upgrades mid-project break ABI compatibility between Slam Toolbox and Nav2.
Share𝕏 Twitterin LinkedInin Whatsapp