Nav2 costmap tuning: footprint, inflation and layers

Tune ROS 2 Nav2 costmaps: footprint, inflation radius, cost scaling, observation sources and a symptom-to-parameter table that saves test runs.

Reference
RE-997
Published
Read
6 min

About the author · Sources, testing and AI policy

Mobile robot with a lidar navigating a warehouse aisle between pallets

Most Nav2 problems are costmap problems. The planner and controller get blamed, but the robot is reacting to a map that is inflated too much, too little, or not being updated at all.

This guide explains what the costmap actually stores, the parameters that change robot behaviour, a tuning order that avoids chasing your own changes, and the symptoms that point to each parameter.

What a costmap contains

A costmap is a grid where each cell holds a cost from 0 to 255. Zero is free space. Values near 253 mark cells inside the robot footprint. 254 is lethal, meaning definitely occupied. In between is a gradient that tells the planner how uncomfortable a cell is.

ValueMeaningWhere it comes from
0FreeNo obstacle, no inflation
1–252Inflated costDistance from an obstacle
253InscribedRobot body would touch an obstacle
254LethalSensor or static map obstacle
255UnknownNo information

Two costmaps run at once. The global costmap covers the whole map and feeds the planner. The local costmap is a rolling window around the robot and feeds the controller. They can have different layers, resolutions and inflation settings, which is often the source of confusing behaviour.

The parameters that matter

  • resolution — cell size in metres. 0.05 is common; finer costs CPU and memory.
  • robot_radius — a circle approximation, simple but conservative or wrong for long robots.
  • footprint — a polygon, more accurate for non-circular robots.
  • inflation_radius — how far the cost gradient extends from an obstacle.
  • cost_scaling_factor — how quickly the cost decays with distance.
  • update_frequency — how often the costmap is recalculated.
  • transform_tolerance — how old a TF may be before the costmap refuses to update.

Inflation radius is the single most consequential setting. Set it too small and the robot clips corners and hugs walls. Set it too large and the robot refuses to enter doorways and narrow aisles that it physically fits through.

Footprint versus radius

A circle is safe but wasteful. A 1.2 m long, 0.6 m wide robot modelled as a circle of radius 0.6 cannot pass a 0.9 m gap that it fits through. A polygon footprint lets the planner use the real shape, but it must be accurate, including the bumper and any rear overhang.

footprint: "[ [0.60, 0.30], [0.60, -0.30],
              [-0.60, -0.30], [-0.60, 0.30] ]"
# points in metres, counter-clockwise, in base_link

If the footprint is wrong, the robot will plan paths that clip door frames, or refuse paths it could take. Measure the robot with bumpers, sensors and any rear-mounted equipment fitted.

The layers

LayerPurposeTypical use
Static layerMap from SLAM or a saved mapGlobal costmap
Obstacle layer2D lidar or depth obstacles marked and clearedBoth costmaps
Voxel layer3D obstacle marking with height filteringLocal costmap with depth camera
Inflation layerCost gradient around obstaclesBoth costmaps
Keepout / speed filterForbidden or slow zonesSite-specific rules

With a 2D lidar, an obstacle layer on both costmaps is usually enough. With a depth camera, a voxel layer filters the floor and keeps only obstacles within a height band, which avoids marking the ground as lethal.

Observation sources

observation_sources: scan
scan:
  topic: /scan
  max_obstacle_height: 2.0
  clearing: true
  marking: true
  obstacle_max_range: 3.0
  raytrace_max_range: 4.0
  data_type: LaserScan

obstacle_max_range is how far a return is trusted as an obstacle. raytrace_max_range is how far empty space is cleared along the beam. If clearing range is shorter than marking range, old obstacles never disappear and the robot gradually believes the world is full.

Keep raytrace range at or beyond obstacle range, and make sure the topic exists with the expected QoS. A costmap that never sees the scan looks exactly like a costmap with no obstacles.

A tuning order that avoids chaos

  • Fix the footprint first, measured with all hardware fitted.
  • Verify the scan and TF arrive with correct timestamps.
  • Tune inflation on the global costmap until paths keep a sensible distance.
  • Then tune the local costmap, usually smaller inflation and shorter ranges.
  • Only then touch controller speeds and lookahead.
  • Change one parameter, replay the same bag, compare the same route.

Record a bag of a route that fails. Replaying it offline lets you compare parameter sets without burning battery and gives you an honest before and after.

Symptom to parameter

SymptomLikely parameter
Robot hugs walls and clips cornersInflation radius too small, cost scaling too steep
Robot refuses to enter a gap it fitsInflation too large, or circle radius used instead of footprint
Robot plans through a wall cornerFootprint too small or offset from base_link
Local costmap stays emptyWrong topic, QoS mismatch, sensor range too short
Robot stops in open floorDepth camera marking the ground; set height band or max obstacle height
Ghost obstacles that never clearRaytrace range shorter than obstacle range
Robot oscillates near goalsController parameters, not the costmap
Plan invalidates constantlyUpdate frequency too low or TF tolerance too tight

Inflation and cost scaling together

Inflation radius sets how far the gradient reaches. Cost scaling sets how fast it falls. A large radius with a high scaling factor creates a narrow high-cost band and a long low-cost tail; a small radius with a low factor makes a broad, expensive hill close to the obstacle.

A practical starting point for an indoor robot is an inflation radius around 0.55 m and a cost scaling factor between 3 and 10, with a footprint that matches the robot. Then adjust based on behaviour in the narrowest real doorway, not in simulation alone.

Test with simulated obstacles first

Before changing parameters on the real robot, load a map in RViz and place obstacles with the Nav2 panel or by publishing a point cloud. You can watch the costmap inflate, see which cells become lethal, and confirm the planner refuses a gap, all without moving the robot. That catches footprint and inflation errors in minutes rather than in a test run.

Then repeat with a recorded scan from the real site. Simulation uses clean geometry; the real lidar has noise, glass, reflective surfaces and people walking through. A configuration that works only on synthetic obstacles is not finished.

When the map is the problem

If the static map has thick walls, smeared furniture or missing rooms, no amount of inflation tuning will fix navigation. Check the map against the building, and remap if the lidar was tilted or the robot drove too fast during mapping. A clean map and an honest footprint solve more navigation complaints than any parameter sweep.

Related: Nav2 tutorial, SLAM Toolbox tutorial, lidar vs ultrasonic, SLAM for beginners, charging and docking.

What is a costmap in ROS 2 Nav2?

A grid where each cell holds a cost from 0 to 255 representing how safe it is to drive there. Layers mark obstacles, inflate them and combine with the static map to guide the planner and controller.

What does the inflation radius do?

It sets how far the cost gradient extends from an obstacle. Too small and the robot clips corners; too large and it refuses gaps it physically fits through.

Should I use robot_radius or footprint?

Use a polygon footprint for any robot that is not roughly circular. A circle is conservative and can block narrow passages that a correctly modelled robot could pass.

Why is my local costmap empty?

Usually a wrong topic, a QoS mismatch, or a sensor range shorter than the nearby obstacles. Check that the scan is arriving and that the observation source is configured with the right data type.

Why does my robot stop in open space?

Often a depth camera marking the floor as an obstacle. Use a voxel layer with a height band or set a maximum obstacle height so the ground is ignored.

A costmap that never updates is often a QoS mismatch on the scan topic rather than a bad parameter. Check the QoS tutorial first, then tune inflation.

Localisation error shows up as costmap drift, so the Kalman filter explanation is worth reading before tuning inflation.

A costmap that is not active yet explains many nav failures, and the lifecycle guide covers the bring-up order.

Sources and further reading

Pass it onWhatsAppLinkedIn

Keep reading

Continue learning

The Build Note

One short email when something is worth your bench time.

A new build sheet, a part price that moved, or a mistake worth avoiding. No daily drip, no recycled news.

One short email when a build or a price is worth your time. Unsubscribe in one click.