ROS Occupancy Grid: A City Map a Mobile Robot Can Navigate
An occupancy grid is the simplest way to tell a robot where a wall is and where a passage is. It is a grid of equal square cells, and each cell has exactly three possible states: free, occupied, unknown. Nothing else — no elevation, no names, no object classes. This is what the path planner in ROS expects, this is what laser-based localisation runs on, and this is usually what SLAM hands you after driving through a building. For outdoor work you can get one straight from a city map instead of driving a robot around the block first.
What the map consists of
Two files. The first is a greyscale image: .pgm or .png. The second is a YAML
description, without which the image is meaningless, because the image carries
neither scale nor georeference:
image: city_block.pgm
resolution: 0.05
origin: [-25.0, -18.0, 0.0]
negate: 0
occupied_thresh: 0.65
free_thresh: 0.196
resolution— metres per cell. 0.05 means 5 cm per pixel: a block of 500 by 360 metres at that resolution is an image of 10000 by 7200 pixels.origin— the coordinates of the lower-left corner of the image in metres, plus a rotation in radians as the third value. An error inorigindoes not break the map, it displaces the robot: the whole world is shifted by the difference.negate— whether the brightness interpretation is inverted.occupied_threshandfree_thresh— the two thresholds that turn continuous brightness into three states.
Three states and two thresholds
Pixel brightness becomes an occupancy probability: a black pixel is occupied, a
white one free. Then the thresholds apply. A value above occupied_thresh counts
as an obstacle, a value below free_thresh as free space, and everything in
between is declared unknown.
In the map message the planner receives, those states are already numbers: 0 for free, 100 for occupied, -1 for unknown. The difference between occupied and unknown matters: a robot will never enter the occupied, while it may enter the unknown carefully if the planner is configured that way. So the grey field around a block is not a defect, it is an honest "no data here".
What becomes an obstacle
From a city map, building footprints, fences, water bodies and retaining walls naturally fall into occupied cells — everything closed and impassable. Roadway, pavements and courtyards become free. Debatable cases are settled by the task itself: a lawn is an obstacle for a wheeled robot and not for a tracked one.
Two places where a city map and a robot disagree:
- Passage width. A robot with a 60 cm body will not fit through a 50 cm gap, and on a 5 cm grid that difference is visible; on a 20 cm grid a 50 cm gap may vanish or, worse, merge into the wall. Resolution is chosen from the robot footprint, not from the file size.
- What is absent from the data is absent from the map. Poles, barriers, steps and parked cars are usually not in a city map. A static map is a skeleton; live obstacles are picked up by the lidar in real time.
Checks before the first run
- Open the image and confirm that walls are black, driveable space white and the unknown grey. An inverted map is the most common trouble: the robot treats the road as a wall.
- Check the pixel size against the area size in metres: width in pixels times
resolutionmust equal the width of the area. - Verify
origin: place the robot at a known point and see whether it lands correctly on the map. A shift by one constant vector is anoriginerror, not a localisation one. - Load the map with the map server and view it in the visualiser together with the scan cloud: map walls and scanned walls must sit on top of each other.
- Make sure the working area is enclosed by a boundary. A map with a hole at the edge lets the planner steer the robot beyond the data.
Where to get such a map for outdoor work
SLAM produces an excellent indoor map, but driving a whole city block in advance is expensive and sometimes impossible. So a static street map is easier to derive from a city map: order your area as an occupancy grid and use it as the starting map. On its first outing the robot already knows where the blocks are and fills in the details with its own sensors.
If the task is not "drive through" but "understand lanes and traffic rules", an occupancy grid is the wrong tool — you need a map with road topology: Lanelet2 or the driving-scene formats from the simulation overview.