Occupancy Grid란 무엇인가
자율주행 로봇(AMR)이 복도를 돌아다니려면 가장 먼저 답해야 할 질문이 있습니다. "여기는 벽인가, 빈 공간인가?" 사람은 눈으로 보면 끝이지만, 로봇은 이 세계를 숫자로 가지고 있어야 합니다. 그 가장 기본적인 표현이 occupancy grid(점유 격자) 입니다.
이 글은 이 시리즈의 출발점이라 조금 길게, 기초를 제대로 다집니다 — 격자가 무엇인지부터, 셀 크기를 어떻게 정하는지, 왜 확률이어야 하는지, 2D로 충분한지, 그리고 실로봇과 시뮬레이션이 이 격자를 만드는 방식이 어떻게 다른지까지.
세계를 격자로 쪼갠다
occupancy grid는 환경을 일정한 크기(예: 5cm)의 정사각형 셀로 나눈 2D 지도입니다. 각 셀은 세 가지 상태 중 하나를 가집니다.
- 점유(occupied) — 벽, 선반, 장애물이 있는 곳
- 자유(free) — 로봇이 지나갈 수 있는 빈 공간
- 미지(unknown) — 센서가 아직 관측하지 못한 곳
도식 렌더링 중…
아래는 작은 창고를 격자로 표현한 모습입니다(■=벽/선반, ·=자유공간):
■ ■ ■ ■ ■ ■ ■ ■ ■ ■
■ · · · · · · · · ■
■ · ■ ■ · · ■ ■ · ■
■ · ■ ■ · · ■ ■ · ■ ← 선반 두 줄
■ · · · · · · · · ■
■ · · · ▣ · · · · ■ ← ▣ = 로봇 현재 위치
■ ■ ■ ■ ■ ■ ■ ■ ■ ■
핵심 직관은 단순합니다 — 연속된 현실 세계를 "지나갈 수 있는 칸 / 없는 칸"의 바둑판으로 바꾼 것입니다. 그런데 이 단순한 그림 뒤에는 세 가지 결정이 숨어 있습니다: 셀을 얼마나 잘게 나눌까(해상도), 각 셀을 0/1로 볼까 확률로 볼까, 평면(2D)으로 충분할까. 하나씩 봅니다.
셀 크기를 얼마로? — 해상도의 트레이드오프
격자에서 가장 먼저 정해야 할 값은 셀 한 칸의 크기(resolution) 입니다. 너무 크지도 작지도 않아야 하는데, 양쪽 다 실패합니다.
20cm 셀: 문(0.8m) = 약 4칸 → 벽과 뭉개져 "막힌 줄"
5cm 셀: 문(0.8m) = 16칸 → "지나갈 수 있음"이 또렷
1cm 셀: 16배 더 정밀하지만 메모리·연산은 격자 면적이라 ∝ 1/해상도²
- 너무 크면(coarse) — 벽 사이의 좁은 틈이나 문이 인접 셀과 뭉개져 사라집니다. 로봇이 지나갈 수 있는 문을 "막혔다"고 보거나, 반대로 얇은 벽을 못 보고 들이받습니다.
- 너무 작으면(fine) — 격자 칸 수는 해상도의 제곱으로 늘어납니다. 5cm→1cm면 셀이 25배. 메모리·경로계산이 폭증합니다.
💡 해상도는 "로봇 크기"와 "중요한 최소 특징"에 맞춘다 — 폭 0.5m 로봇이 0.8m 문을 지나야 한다면, 그 차이(0.3m)를 표현할 만큼은 잘아야 합니다. 보통 3~10cm가 실내 AMR의 균형점입니다. "정밀할수록 좋다"가 아니라, 내가 구별해야 하는 가장 작은 것보다 한 단계 잘면 충분합니다.
왜 "확률"인가 — 센서는 거짓말한다
앞의 바둑판 그림은 셀이 깔끔하게 점유/자유로 나뉜 것처럼 보이지만, 현실은 그렇지 않습니다. 센서는 노이즈투성이입니다. LiDAR 한 번의 측정이 유리창 반사일 수도, 지나가던 사람일 수도, 먼지일 수도 있습니다. 단 한 번의 관측으로 "여긴 벽"이라 단정하면 지도가 흔들립니다.
그래서 각 셀은 0/1 플래그가 아니라 0~1 사이의 점유 확률을 가집니다. 같은 셀을 여러 번 관측하며 증거를 누적합니다 — 계속 "막혔다"고 나오면 확률이 1에 가까워지고, 엇갈리면 어중간하게 남습니다. 사람이 지나가 잠깐 막혔다가 비면, 이후의 "비었다" 관측이 확률을 다시 내립니다(동적 장애물 처리).
이 누적을 싸게 하는 트릭이 log-odds입니다 — 확률의 곱셈을 덧셈으로 바꿔, 관측마다 더하기 한 번이면 됩니다.
# 센서가 같은 셀을 관측할 때마다 점유 확률 갱신 (log-odds)
def update_cell(log_odds, hit):
l_occ, l_free = 0.85, -0.4 # 관측당 증감
log_odds += l_occ if hit else l_free
return clamp(log_odds, -5, 5) # 발산 방지
def is_occupied(log_odds):
return 1 / (1 + exp(-log_odds)) > 0.65 # 확률 → 점유 판정
⚠️ 한 번의 관측을 믿지 마라 — 점유 격자가 "확률"인 이유가 이것입니다. 센서 한 프레임으로 지도를 갱신하면 노이즈가 그대로 벽이 됩니다. 증거가 쌓여야 확신하는 구조라야, 잡음 속에서도 안정적인 지도가 나옵니다.
2D면 충분한가 — 평면의 가정
대부분의 실내 AMR은 바닥이 평평하다고 가정합니다. 그러면 로봇 높이에서 자른 2D 격자 한 장이면 충분하죠. 가볍고 빠릅니다. 하지만 이 가정이 깨지는 곳이 있습니다.
도식 렌더링 중…
- 2D가 못 보는 것: 책상(다리는 바닥, 상판은 공중) 같은 돌출(overhang), 계단, 복층, 경사진 야지. 로봇 높이만 보는 2D 격자는 머리 위 상판이나 발밑 단차를 놓칩니다.
- 그때는 3D: OctoMap 같은 voxel(3D 셀) 표현이나 3D costmap을 씁니다. 드론·사족보행·험지 차량이 여기 해당합니다.
핵심은 — 격자는 "현실의 모델"이지 현실 자체가 아니다라는 것. 2D는 평면 가정 위에서만 맞고, 그 가정을 벗어나면 차원을 올려야 합니다.
왜 격자여야 하는가
이 모든 복잡함을 감수하고도 격자를 쓰는 이유는 계산하기 압도적으로 쉽기 때문입니다.
- 경로 계획기(A*, Dijkstra)가 "셀 → 인접 셀" 그래프 위에서 최단 경로를 바로 풉니다
- 장애물 주변에 안전 여유(inflation)를 셀 단위로 깔 수 있습니다
- 센서 업데이트가 "이 셀의 확률을 올린다/내린다"로 국소화됩니다 — 지도 전체를 다시 안 봐도 됩니다
이 occupancy grid가 바로 다음 단계인 static map → Nav2 costmap의 원천이 됩니다(다음 글에서 다룹니다).
실로봇은 SLAM, 시뮬은 정답을 안다
같은 격자라도 만드는 방법이 실로봇과 시뮬레이션에서 완전히 다릅니다 — 그리고 이 차이가 시뮬의 큰 장점이 됩니다.
- 실로봇: 지도를 받지 못합니다. 돌아다니며 SLAM으로 직접 만듭니다 — 연속된 LiDAR 스캔을 정렬(scan matching)해 자기 위치와 지도를 동시에 추정하고, 한 바퀴 돌아 같은 곳에 오면 누적 오차를 보정(loop closure)합니다. 격자는 이 긴 과정의 산출물이고, 노이즈·드리프트가 섞입니다.
- 시뮬레이션: 우리는 환경의 정답(ground truth)을 이미 가지고 있습니다. imSim은 절차적 환경을 생성하는 단계에서 씬(USD)의 모든 asset 경계(bounding box)를 읽어 격자를 곧장 래스터화합니다 — SLAM도, 드리프트도, 기다림도 없이.
도식 렌더링 중…
💡 완벽한 지도가 공짜라는 건 검증의 무기다 — 실로봇은 "내비게이션이 틀린 건지, 지도가 틀린 건지" 구분하기 어렵습니다. 시뮬은 지도가 정답이라, 내비게이션 문제를 매핑 문제와 분리해서 볼 수 있습니다. 격자가 완벽하니, 로봇이 벽에 부딪히면 그건 순수하게 주행 로직의 문제입니다.
그래서 이 격자가 제대로 준비되는 게 중요합니다. 만약 격자가 비어 있거나 전부 free인 빈 지도(empty map)로 대체되면, 로봇은 벽을 모른 채 길이 아닌 곳으로 직진합니다 — 이건 실제로 우리가 겪었던 회귀 버그이고, 다음 글에서 다룹니다.
한 줄 정리
📌 Occupancy grid = 연속된 현실을 "지나갈 수 있는 칸 / 없는 칸"의 확률 바둑판으로 바꾼 것. 셀 크기는 로봇·최소 특징에 맞추고(너무 크면 문이 사라지고 작으면 폭증), 각 셀은 노이즈에 견디려 확률로 누적하며(log-odds), 평면 가정이 깨지면 3D로 올린다. 실로봇은 SLAM으로 힘겹게 만들지만, 시뮬은 ground truth로부터 완벽한 격자를 공짜로 얻어 — 매핑과 주행 문제를 분리해 검증한다.
참고 · References
- ROS 2 Nav2 — Costmap 2D · Static Layer (공식 문서)
- H. Moravec & A. Elfes, “High resolution maps from wide-angle sonar” (ICRA 1985) — occupancy grid 원전
- A. Hornung et al., “OctoMap: An efficient probabilistic 3D mapping framework” (Autonomous Robots 2013)
- SLAM Toolbox (ROS 2) — 실로봇 점유격자 생성
