ROS 2 Nav2 SMAC Planner Guide 2026
SMAC (State Machine-based A*) planners generate kinematically feasible SE(2) paths — accounting for turning radius, non-holonomic constraints, and forward-only motion. Essential for car-like robots, forklifts, and any vehicle that can't spin in place.
Planner Selection: Which One?
| Planner | Algorithm | Robot Type | Speed |
|---|---|---|---|
| NavFn (Dijkstra/A*) | Grid-based A* | Diff-drive (any heading at goal) | Fast, ~50ms |
| SmacPlanner2D | Grid A* + footprint | Diff-drive (circular footprint OK) | Fast |
| SmacPlannerHybrid | Hybrid-A* (Reeds-Shepp/Dubins) | Car-like, Ackermann, forklifts | Moderate, ~200ms |
| SmacPlannerLattice | Lattice A* + motion primitives | Any kinematic model (legged, etc.) | Slower, configurable |
Rule: If your robot has a minimum turning radius > 0 (car, forklift, AGV), use SmacPlannerHybrid. If you need to precompute motion primitives for a custom kinematic model, use SmacPlannerLattice. Diff-drive robots that can spin in place: NavFn or SmacPlanner2D is simpler and faster.
Install
sudo apt install ros-jazzy-nav2-smac-planner # or for Humble: sudo apt install ros-humble-nav2-smac-planner
SmacPlannerHybrid: Full YAML Reference
Add to your nav2_params.yaml under the planner_server:
planner_server:
ros__parameters:
planner_plugins: ["GridBased"]
GridBased:
plugin: "nav2_smac_planner/SmacPlannerHybrid"
# --- Core kinematic constraints ---
minimum_turning_radius: 0.40 # meters. CRITICAL: measure your robot!
# Car: wheelbase / tan(max_steer_angle)
motion_model_for_search: "REEDS_SHEPP" # REEDS_SHEPP | DUBIN
# REEDS_SHEPP allows reverse motion
# DUBIN = forward only
# --- Angular resolution ---
angle_quantization_bins: 72 # 360/72 = 5° per bin. More bins = finer
# heading resolution but slower search.
# 72 (5°) is good for most robots.
# Forklifts with tight turns: try 72-144.
# --- Search space ---
max_iterations: 1000000 # Upper bound on A* node expansions
max_planning_time: 5.0 # seconds before planner gives up
tolerance: 0.5 # meters, goal tolerance
cache_obstacle_heuristic: True # reuse heuristic across replanning calls
# --- Analytic expansion (Reeds-Shepp shortcut to goal) ---
analytic_expansion_ratio: 3.5 # Try analytic shortcut every N iterations
analytic_expansion_max_length: 3.0 # meters, max analytic segment length
# --- Reversing ---
allow_unknown: True # plan through unknown cells
reverse_penalty: 2.0 # cost multiplier for reverse motion
# Higher = prefer forward-only paths
change_penalty: 0.0 # penalty for changing direction (fwd→rev)
non_straight_penalty: 1.20 # penalty for curved vs straight segments
cost_penalty: 2.0 # costmap lethal threshold amplifier
# --- Costmap ---
use_quadratic_cost_penalty: False
downsample_costmap: False
downsampling_factor: 1
# --- Smoother (optional post-processing) ---
smooth_path: True
smoother:
max_iterations: 1000
w_smooth: 0.3
w_data: 0.2
tolerance: 0.001
do_refinement: True
refinement_num: 2Calculating minimum_turning_radius
Formula for Ackermann / Car-like robots:
minimum_turning_radius = wheelbase / tan(max_steering_angle_radians) # Example: wheelbase = 0.35m, max_steer = 30° import math r = 0.35 / math.tan(math.radians(30)) # → 0.606m # Example: differential drive with minimum in-place turn radius # (e.g. due to mechanical stops — if 0, robot can spin in place, use NavFn)
Common mistake: Setting minimum_turning_radius: 0.0 for a car-like robot disables the constraint — the planner generates infeasible paths the controller can't follow, causing oscillation. Always measure with the actual robot's steering range.
REEDS_SHEPP vs DUBIN Motion Model
REEDS_SHEPP
- ✅ Forward AND reverse motion
- ✅ Shorter paths in tight spaces
- ✅ Good for forklifts and indoor AGVs
- ⚠️ Reverse segments may be unsafe with rear obstacles
DUBIN
- ✅ Forward-only — always safe for passengers
- ✅ Simpler for outdoor robots and cars
- ⚠️ May fail in very tight spaces (no reverse)
- ⚠️ Longer paths in cluttered environments
SmacPlannerLattice: YAML Reference
The lattice planner uses precomputed motion primitives for any kinematic model. Generate them with nav2_smac_planner's primitive generator first:
# Generate motion primitives (one-time setup)
ros2 run nav2_smac_planner lattice_primitives \
--output /tmp/my_robot_primitives.json \
--turning_radius 0.4 \
--num_of_headings 16 \
--grid_resolution 0.05
# nav2_params.yaml
planner_server:
ros__parameters:
planner_plugins: ["GridBased"]
GridBased:
plugin: "nav2_smac_planner/SmacPlannerLattice"
allow_unknown: True
tolerance: 0.25
max_iterations: 4000000
max_planning_time: 5.0
analytic_expansion_ratio: 3.5
analytic_expansion_max_length: 3.0
reverse_penalty: 2.1
change_penalty: 0.05
non_straight_penalty: 1.05
cost_penalty: 2.0
rotation_penalty: 5.0
retrospective_penalty: 0.025
lattice_filepath: "/tmp/my_robot_primitives.json"
cache_obstacle_heuristic: False
allow_reverse_expansion: False
smooth_path: True
smoother:
max_iterations: 1000
w_smooth: 0.3
w_data: 0.2
tolerance: 0.001Debugging & Tuning
Path ignores obstacles / cuts through walls
Increase cost_penalty (try 2.0→3.0). Check that the global costmap inflation_radius matches your robot footprint inscribed radius. SMAC uses the costmap cost values directly.
Planning fails ('No valid path found')
Increase max_iterations (1M → 2M) and max_planning_time (5→15s). Check minimum_turning_radius isn't too large for the corridor width. Try REEDS_SHEPP if DUBIN fails in tight spaces.
Path oscillates / controller can't follow
Enable smooth_path: True and increase smoother w_smooth. Also increase angle_quantization_bins (36→72) so heading transitions are finer. Check FollowPath controller max_vel_theta matches path curvature.
Visualize path headings in RViz2
ros2 topic echo /plan | grep pose | head -20 — check orientation quaternions are smooth. In RViz2: Add → Path (topic /plan). Check arrow markers if using rviz_visual_tools.
Next Steps
- → Nav2 Costmap2D guide — configure the global costmap the SMAC planner searches over
- → Nav2 Behavior Tree guide — wire SMAC into ComputePathToPose BT action nodes
- → RViz2 plugin guide — visualize planned paths and heading arrows with Interactive Markers