ZERO Notificatons

NO Feedback yet!!

okay

xparo
X.P.A.R.O



project - ROS Map Editor



A ROS map is two small files: a greyscale image and a YAML file that says how to read it. They look simple, and they are, but a few of their details decide whether your robot treats unexplored space as a wall, as unknown, or as open floor. This guide explains both files and shows, with measurements from Nav2's own map server, exactly how pixels become an occupancy grid.

The image: a PGM file

Maps are usually saved as PGM (portable greymap) images: a short text header followed by one byte per pixel.

P5
# CREATOR: map_saver
604 307
255
…604 × 307 bytes of pixel data…

P5 means binary greyscale, then come the width and height in pixels and the maximum value (255). Each pixel is one cell of the map. The image is stored top row first, but a ROS occupancy grid counts rows from the bottom, so the map server flips it: the bottom-left pixel of the image is cell (0, 0) of the map. Maps saved by Nav2 use three values: 254 (white) for free space, 0 (black) for obstacles and 205 (grey) for unknown space.

The metadata: a YAML file

Here is the YAML of the depot map that ships with Nav2 on ROS 2 Jazzy:

image: depot.pgm
mode: trinary
resolution: 0.05
origin: [-7.14, -7.83, 0]
negate: 0
occupied_thresh: 0.65
free_thresh: 0.25
FieldMeaning
imageThe image file, relative to the YAML file.
modeHow pixel values become occupancy: trinary (the default), scale or raw. See below.
resolutionMetres per pixel. 0.05 means each pixel is a 5 cm square, so this 604 × 307 image covers 30.2 × 15.35 m.
originThe pose of the lower-left corner of the map in the map frame: x, y in metres and a yaw angle. Many parts of ROS ignore the yaw, so keep it 0 and rotate the image instead.
negateIf 1, white means occupied and black means free. Almost always 0.
occupied_threshPixels darker than this occupancy probability count as obstacles.
free_threshPixels lighter than this occupancy probability count as free.

From pixel to occupancy

The map server first turns each pixel into an occupancy probability, where darker means more likely occupied:

p = (255 − pixel) ÷ 255 (with negate: 0) trinary: p > occupied_thresh → 100 (occupied) p < free_thresh → 0 (free) otherwise → −1 (unknown)

To be sure how this behaves in practice, we fed a strip of eight test pixels to the Nav2 map server on ROS 2 Jazzy (thresholds 0.65 and 0.196) and read back the occupancy grid it published:

Pixel050100150180205230254
trinary100100−1−1−1−100
scale100100914822000
raw050100−1−1−1−1−1
  • trinary gives three classes. It is what navigation maps normally use.
  • scale keeps in-between greys as intermediate values between the two thresholds. It is useful for masks such as speed zones, as our guide to Nav2 keep-out and speed zones shows.
  • raw uses the pixel value itself as the occupancy (0 to 100), and anything above 100 is unknown. Notice that the meaning is reversed compared with the other modes: a white pixel is not free in raw mode.

The free_thresh trap

Look at pixel 205, the grey that means "unknown". Its occupancy probability is 50 ÷ 255 ≈ 0.19608, a hair above 0.196. So it is read as unknown only if free_thresh is 0.196 or lower. With a higher value, it falls below the free threshold and becomes free space.

We checked this with the three maps that ship with Nav2. With free_thresh 0.196 (tb3_sandbox) and 0.1 (warehouse), all grey pixels loaded as unknown. With 0.25 (depot), all 8,894 grey pixels loaded as free. Maps saved with Nav2's map_saver_cli on Jazzy are written with 0.196 (we saved one with --free 0.19 and still got 0.196 in the YAML), so the trap mostly comes from hand-written or edited YAML files. Whether it matters depends on your planner: if it is configured not to plan through unknown space, turning unknown into free lets it plan routes through areas the robot has never seen.

The ROS Map Editor shows how your current thresholds will read white, grey and black pixels, and warns you when grey would become free.

Pixel to world coordinates

For a map with zero yaw, the centre of the pixel in column c and row r (counted from the top of an image H pixels high) is at:

x = origin_x + (c + 0.5) × resolution y = origin_y + (H − r − 0.5) × resolution

For the depot map, the top-left pixel (c = 0, r = 0) is at x = −7.14 + 0.025 = −7.115 m and y = −7.83 + (307 − 0.5) × 0.05 = 7.495 m. This is handy for checking where a goal will land, or for drawing a keep-out zone at known coordinates.

Saving and loading

# save the map currently published on /map as office.pgm + office.yaml
ros2 run nav2_map_server map_saver_cli -f ~/maps/office

# serve it (map_server is a lifecycle node; autostart activates it)
ros2 run nav2_map_server map_server --ros-args \
  -p yaml_filename:=$HOME/maps/office.yaml -p autostart_node:=true

map_saver_cli also accepts --fmt png, --mode scale or raw, and threshold options. In a full Nav2 bringup you normally pass the map with the map:= launch argument instead of starting the server yourself.

Keep the files together

The YAML refers to the image by name, so move and rename the two files together, and keep them under version control: they are small, and when a robot starts behaving strangely after a map edit, being able to diff and roll back the map is invaluable. To clean a map up before navigating, see how to clean up a SLAM map.

More guides

Oct. 4, 2026, 10:10 a.m.
Map Resolution and Origin: Practical Choices for Nav2
Read more..
Oct. 4, 2026, 10:11 a.m.
Mapping with slam_toolbox in ROS 2: From First Scan to Saved Map
Read more..
Oct. 4, 2026, 10:12 a.m.
Keep-Out Zones and Speed Limits in Nav2 with Costmap Filters
Read more..
Oct. 4, 2026, 10:13 a.m.
How to Clean Up a SLAM Map: Noise, Ghost Obstacles and Glass Walls
Read more..

If you have any query or problem
feel free to contact us
email: [email protected]