# Nav2 costmap tuning: footprint, inflation and layers

> Tune Nav2 costmaps: footprint, inflation radius, cost scaling, observation sources and a symptom-to-parameter table.

Canonical: https://roboticsengineering.in/nav2-costmap-tuning-guide/ | Published: 2026-10-28 | Updated: 2026-10-28 | Author: Jignesh Panchal

**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.

| Value | Meaning | Where it comes from |
| --- | --- | --- |
| 0 | Free | No obstacle, no inflation |
| 1–252 | Inflated cost | Distance from an obstacle |
| 253 | Inscribed | Robot body would touch an obstacle |
| 254 | Lethal | Sensor or static map obstacle |
| 255 | Unknown | No 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

| Layer | Purpose | Typical use |
| --- | --- | --- |
| Static layer | Map from SLAM or a saved map | Global costmap |
| Obstacle layer | 2D lidar or depth obstacles marked and cleared | Both costmaps |
| Voxel layer | 3D obstacle marking with height filtering | Local costmap with depth camera |
| Inflation layer | Cost gradient around obstacles | Both costmaps |
| Keepout / speed filter | Forbidden or slow zones | Site-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

| Symptom | Likely parameter |
| --- | --- |
| Robot hugs walls and clips corners | Inflation radius too small, cost scaling too steep |
| Robot refuses to enter a gap it fits | Inflation too large, or circle radius used instead of footprint |
| Robot plans through a wall corner | Footprint too small or offset from base_link |
| Local costmap stays empty | Wrong topic, QoS mismatch, sensor range too short |
| Robot stops in open floor | Depth camera marking the ground; set height band or max obstacle height |
| Ghost obstacles that never clear | Raytrace range shorter than obstacle range |
| Robot oscillates near goals | Controller parameters, not the costmap |
| Plan invalidates constantly | Update 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](https://roboticsengineering.in/ros2-nav2-tutorial/), [SLAM Toolbox tutorial](https://roboticsengineering.in/slam-toolbox-ros2-tutorial/), [lidar vs ultrasonic](https://roboticsengineering.in/lidar-vs-ultrasonic-vs-ir-robots/), [SLAM for beginners](https://roboticsengineering.in/slam-for-beginners/), [charging and docking](https://roboticsengineering.in/mobile-robot-charging-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](https://roboticsengineering.in/ros2-qos-tutorial/) first, then tune inflation.

Localisation error shows up as costmap drift, so the [Kalman filter explanation](https://roboticsengineering.in/kalman-filter-robot-localization/) is worth reading before tuning inflation.

A costmap that is not active yet explains many nav failures, and the [lifecycle guide](https://roboticsengineering.in/ros2-lifecycle-nodes-tutorial/) covers the bring-up order.

## Sources and further reading

- [Nav2 documentation](https://docs.nav2.org/) — costmap2D configuration and tuning guides.
- [ROS 2 documentation](https://docs.ros.org/) — TF2 and QoS behaviour.
- [Navigation2 source](https://github.com/ros-navigation/navigation2) — default parameters and examples.
- [ROS community](https://www.ros.org/) — navigation tuning discussions and packages.
