DWA vs TEB Planner: Choose the Right Obstacle Avoidance in 15 Minutes

Compare Dynamic Window Approach and Timed Elastic Band planners for ROS. Know which to use for your robot's speed, environment, and constraints.

Problem: Picking the Wrong Local Planner Costs You Weeks

You're setting up a mobile robot's navigation stack and need to choose a local planner. DWA and TEB both ship with ROS. Pick wrong and you'll spend weeks tuning a planner that was never right for your robot.

You'll learn:

  • How DWA and TEB work under the hood
  • Which performs better in tight spaces vs open environments
  • Concrete config starting points for each

Time: 15 min | Level: Intermediate


Why This Happens

Both planners solve the same problem — navigate to a local goal while avoiding obstacles in real-time — but they use fundamentally different math. That difference matters enormously depending on your robot's kinematics, environment, and latency requirements.

Most teams default to DWA because it's simpler. That's fine until the robot starts cutting corners in narrow corridors or failing to squeeze through doorways. Then they switch to TEB without understanding why it's better in that case, and spend days re-tuning.

Common symptoms of a wrong planner choice:

  • Robot oscillates near obstacles (DWA in tight spaces)
  • Navigation stutters or replans constantly (TEB on slow hardware)
  • Robot ignores kinematic constraints, jerks or skids
  • Paths are geometrically correct but mechanically awkward

How Each Planner Works

Dynamic Window Approach (DWA)

DWA samples velocity commands (v, ω) from a window of dynamically feasible velocities — ones the robot can physically reach given its current speed and acceleration limits. It scores each candidate using a cost function balancing goal heading, clearance, and forward velocity, then executes the best one.

Admissible velocities = {(v, ω) | v ∈ [v_current - a·dt, v_current + a·dt]}

Score = α·heading + β·clearance + γ·velocity

It's a reactive, single-timestep planner. It sees the next 1–2 seconds of motion. Fast and predictable, but myopic.

DWA strengths:

  • Very low CPU cost (~1–5ms per cycle on modest hardware)
  • Predictable, easy to tune
  • Works well in open or semi-structured environments
  • Solid for differential drive robots

DWA weaknesses:

  • Struggles with narrow passages (local minima)
  • Can't optimize over a full trajectory horizon
  • Poor handling of non-holonomic constraints in tight turns
  • No time parametrization — doesn't account for dynamic obstacles well

Timed Elastic Band (TEB)

TEB represents the robot's path as a sequence of poses connected by time intervals — the "elastic band." It then runs a sparse graph optimization (using g2o under the hood) to minimize travel time while satisfying obstacle clearance, kinematic constraints, and dynamic feasibility simultaneously.

Objective: min Σ (time intervals) 
Subject to:
  - Obstacle clearance ≥ d_min for each pose
  - |v| ≤ v_max, |ω| ≤ ω_max
  - |a| ≤ a_max (kinematic model)
  - Feasible for robot footprint (not just center point)

TEB optimizes over the entire trajectory horizon. It's globally aware within its local window.

TEB strengths:

  • Handles narrow corridors and doorways well
  • Supports car-like (Ackermann) robots natively
  • Optimizes full trajectory, not just next velocity command
  • Supports multiple topological paths simultaneously (homotopy classes)

TEB weaknesses:

  • Higher CPU cost (~10–50ms per cycle depending on horizon length)
  • More parameters to tune (~30+ vs DWA's ~12)
  • Can produce oscillations if obstacle inflation is too tight
  • Optimization can fail or produce infeasible plans in very cluttered scenes

Decision Framework

Use this to pick quickly:

FactorUse DWAUse TEB
EnvironmentOpen, structuredNarrow, complex
Robot typeDifferential driveAckermann / car-like
CPU budgetTight (embedded)Available (x86/ARM)
Dynamic obstaclesRareFrequent
Tuning bandwidthLowHigh
ROS versionROS 1 or 2ROS 1 or 2 (teb_local_planner)

Short version: If your robot navigates warehouse aisles, apartments, or anywhere with doorways under 1.5m — use TEB. If it's in an open factory floor or outdoor environment — DWA is probably enough.


Solution

Step 1: Install and Configure DWA

DWA ships with nav2 (ROS 2) or navigation (ROS 1). For ROS 2:

# Already included in nav2, just set it in your nav2_params.yaml
sudo apt install ros-humble-nav2-dwb-controller

Minimal working config:

# nav2_params.yaml
controller_server:
  ros__parameters:
    controller_plugins: ["FollowPath"]
    FollowPath:
      plugin: "dwb_core::DWBLocalPlanner"
      
      # Velocity limits — match your robot's specs exactly
      min_vel_x: 0.0
      max_vel_x: 0.5        # m/s — start conservative
      max_vel_theta: 1.0    # rad/s
      min_speed_xy: 0.0
      
      # Acceleration limits — prevents wheel slip
      acc_lim_x: 2.5
      acc_lim_theta: 3.2
      decel_lim_x: -2.5
      
      # Trajectory sampling — more = better but slower
      vx_samples: 20
      vtheta_samples: 40
      sim_time: 1.7         # seconds ahead to simulate
      
      # Critic weights — tune these for your environment
      critics: ["RotateToGoal", "Oscillation", "BaseObstacle", "GoalAlign", "PathAlign", "PathDist", "GoalDist"]
      PathAlign.scale: 32.0
      GoalAlign.scale: 24.0
      PathDist.scale: 32.0
      GoalDist.scale: 24.0
      BaseObstacle.scale: 0.02

Expected: Robot follows global path with smooth velocity transitions and stops before obstacles.

If it fails:

  • Robot oscillates: Reduce vtheta_samples or increase BaseObstacle.scale
  • Ignores path: Increase PathAlign.scale and PathDist.scale
  • Too slow near obstacles: Increase max_vel_x and check decel_lim_x

Step 2: Install and Configure TEB

# ROS 2
sudo apt install ros-humble-teb-local-planner

# ROS 1
sudo apt install ros-noetic-teb-local-planner

Minimal working config for a differential drive robot:

# nav2_params.yaml (ROS 2) or local_planner_params.yaml (ROS 1)
TebLocalPlannerROS:
  
  # Robot geometry — must be accurate
  max_vel_x: 0.4
  max_vel_x_backwards: 0.2
  max_vel_theta: 0.8
  acc_lim_x: 0.5
  acc_lim_theta: 0.5
  
  # Footprint model — use "circular" first, then "polygon" when tuned
  footprint_model:
    type: "circular"
    radius: 0.25           # meters — slightly larger than real robot

  # Obstacle clearance
  min_obstacle_dist: 0.25  # Never go closer than this
  inflation_dist: 0.6      # Start inflating at this distance
  
  # Trajectory optimization
  no_inner_iterations: 5
  no_outer_iterations: 4
  optimization_activate: true
  
  # Horizon
  max_samples: 500
  max_global_plan_lookahead_dist: 3.0
  
  # Kinematic model
  cmd_angle_instead_rotvel: false   # true for Ackermann
  
  # Homotopy — enable for complex environments
  enable_homotopy_class_planning: true
  enable_multithreading: true
  max_number_classes: 4

Expected: Robot takes smooth, geometrically aware paths through narrow spaces, slowing near obstacles.

If it fails:

  • Optimization fails constantly: Increase min_obstacle_dist and reduce max_samples
  • Robot clips obstacles: Switch footprint_model.type from "circular" to "polygon" and provide actual vertices
  • High CPU usage: Set enable_homotopy_class_planning: false and reduce no_outer_iterations to 2

Step 3: Benchmark Both in Your Environment

Don't guess — measure. Use ROS's built-in logging:

# Record a navigation run
ros2 bag record /cmd_vel /odom /local_plan /cost_cloud -o nav_test

# Then compare:
# - /cmd_vel smoothness (fewer velocity reversals = better)
# - /local_plan length (shorter = more efficient)
# - CPU usage: htop while running

Run the same 5-10 goal sequence with each planner and compare:

# Quick analysis script — paste into a Jupyter notebook
import rosbag2_py
import numpy as np

# Load cmd_vel topic and compute jerk (rate of acceleration change)
# Higher mean jerk = less smooth = worse for hardware longevity

You should see: TEB produces lower jerk in narrow corridors. DWA produces lower CPU usage across all scenarios.


Verification

# Check planner is active
ros2 topic echo /local_plan --once

# Confirm no planning failures
ros2 topic echo /controller_server/transition_event

You should see: /local_plan publishing at 10–20Hz with a valid path. No FAILURE transitions in the controller server.


What You Learned

  • DWA is a velocity-space sampler — fast, predictable, myopic
  • TEB is a trajectory optimizer — slower, globally aware within its horizon, better for constrained spaces
  • The right choice depends on environment geometry and CPU budget, not on which is "newer"
  • Start with DWA and switch to TEB only when you hit specific failure modes (narrow passages, non-holonomic constraints)

Limitation: TEB's g2o optimizer can diverge in highly dynamic environments with many moving obstacles. In those cases, consider model-predictive control (MPC) planners like MPPI, which nav2 now ships as nav2_mppi_controller.

When NOT to use TEB: Embedded systems under 1GHz, environments where obstacles are purely dynamic (crowds), or when your team doesn't have bandwidth to tune 30+ parameters properly.


Tested on ROS 2 Humble, nav2 1.3.x, Ubuntu 22.04. TEB config applies to teb_local_planner 0.9+.