AEGLOSLAB ROS 2 Jazzy 대전

제8부 항법

8.2 지도와 SLAM Toolbox

8.2.1 이 장에서 배우는 것 / 선수 지식

지도는 항법의 기준이다. 전역 경로를 계획하고(8.5), 자기 위치를 알고(8.3), 정적 장애물을 아는(8.4) 근거가 전부 지도에서 온다. 지도 품질이 낮으면 그 위의 모든 것이 부정확해진다.

그리고 지도 품질 문제는 원인이 지도 밖에 있는 경우가 많다. 오도메트리가 부정확하거나(7.6), 라이다에 운동 왜곡이 있거나(7.5.4), 시계가 안 맞으면(7.7) 지도가 어긋난다. 그래서 이 장은 "지도를 어떻게 만드는가"만큼 "왜 문제인가" 에 지면을 쓴다.

배우는 것:

  • 지도 표현 — OccupancyGrid와 파일 형식.
  • map_server — 지도 제공과 QoS.
  • SLAM Toolbox — 모드와 주요 설정.
  • 직렬화 — 포즈 그래프 저장·재사용.
  • 루프 클로저 — 왜 중요하고 언제 실패하는가.
  • 지도 품질 문제 — 증상에서 원인으로.
  • 다중 세션·평생 지도 — 환경이 변할 때.

선수 지식: 7.5(라이다·운동 왜곡), 7.6(오도메트리), 5.1.7(REP 105), 1.6.3.3(TRANSIENT_LOCAL), 1.12(라이프사이클), 8.1(Nav2 구조).

정직: SLAM Toolbox의 파라미터는 많고 배포판마다 추가·변경된다. 아래 파라미터 이름·기본값은 Jazzy에 설치된 slam_toolbox의 기본 config와 직접 대조했다(실행 확인 — 8.2.13에 결과를 정리했다). 전체 목록은 패키지가 배포하는 config 파일이 정본이다. SLAM 알고리즘 이론은 자매서 robotics 제9부(RB-195~RB-241)가 다룬다(집필 상태는 A.5.9). SLAM 알고리즘 이론은 자매서 robotics 제9부(RB-195~RB-241)가 다룬다(집필 상태는 A.5.9).

8.2.2 지도 표현

nav_msgs/OccupancyGrid가 2D 지도의 표준이다.

header:
  frame_id: "map"
info:
  resolution: 0.05          # m/cell
  width: 2000               # cells
  height: 1500
  origin:                   # 지도 좌하단의 map 프레임 좌표
    position: {x: -50.0, y: -37.5, z: 0.0}
    orientation: {w: 1.0}
data: [...]                 # width * height, int8

셀 값의 의미:

값의미
0자유(free)
100점유(occupied)
-1미지(unknown)
1~99확률(대개 안 씀)

셀 ↔ 좌표 변환:

$$x = \text{origin}_x + (i + 0.5) \cdot r, \qquad i = \left\lfloor \frac{x - \text{origin}_x}{r} \right\rfloor$$

데이터는 행 우선이며 좌하단이 인덱스 0이다:

$$\text{index} = j \cdot \text{width} + i$$

함정: origin은 지도의 좌하단 셀의 위치다. 지도 중심이 아니다. 이것을 혼동하면 좌표 변환이 지도 크기의 절반만큼 어긋난다.

파일 형식:

# map.yaml
image: map.pgm
mode: trinary
resolution: 0.05
origin: [-50.0, -37.5, 0.0]     # x, y, yaw
negate: 0
occupied_thresh: 0.65
free_thresh: 0.25
필드의미
imagePGM·PNG 파일(회색조)
modetrinary·scale·raw (실행 확인 — nav2_map_server의 MapMode 열거형)
resolutionm/cell
origin좌하단 위치 + yaw
negate흑백 반전
occupied_thresh·free_thresh회색조 → 점유/자유 판정

PGM에서 어두운 픽셀이 장애물, 밝은 픽셀이 자유 공간, 중간이 미지다(negate: 0일 때).

견해: 지도를 이미지 편집기로 수정하는 것이 실무에서 흔하고 유용하다. SLAM이 잘못 인식한 반사면, 일시적으로 있던 물건, 유리벽(라이다가 통과) 등을 수동으로 그려 넣거나 지운다. 다만 원본을 보관하고 수정 이력을 남기라 — 나중에 왜 그렇게 됐는지 모르게 된다.

해상도 선택:

해상도장점단점
0.02~0.03 m정밀·좁은 통로메모리·계산 급증
0.05 m균형일반적 기본
0.1 m경량좁은 곳 부정확

$$N_{\text{cells}} = \frac{A}{r^2}$$

해상도를 절반으로 하면 셀이 4배다. 100×100 m 공간이 0.05 m면 400만 셀(4 MB), 0.025 m면 1600만 셀(16 MB).

8.2.3 map_server

map_server:
  ros__parameters:
    yaml_filename: "/path/to/map.yaml"
    topic_name: "map"
    frame_id: "map"

/map 토픽은 TRANSIENT_LOCAL이다(1.6.3.3) — 늦게 기동된 노드도 지도를 받는다.

# 확인 시 QoS 옵션 필수(0.7.5)
ros2 topic echo /map --qos-durability transient_local --once --no-arr

서비스:

서비스용도
/map_server/load_map실행 중 지도 교체
/map_saver/save_map현재 지도 저장
# 지도 저장 (SLAM 중)
ros2 run nav2_map_server map_saver_cli -f ~/maps/my_map
# 또는 서비스로
ros2 service call /map_saver/save_map nav2_msgs/srv/SaveMap \
  "{map_topic: map, map_url: /home/user/maps/my_map}"

함정: map_server도 라이프사이클 노드다(1.12). 활성화되지 않으면 지도를 발행하지 않고, Nav2 전체가 대기한다. lifecycle_manager의 node_names에 포함되어 있는지 확인한다(8.1.4).

함정: 지도 파일 경로가 절대 경로이거나 package://여야 한다. 상대 경로는 실행 위치에 따라 실패한다(4.1.4).

8.2.4 SLAM Toolbox 모드

여러 모드가 있고 용도가 다르다.

모드설명용도
online_async실시간, 비동기 처리일반적 지도 작성
online_sync실시간, 동기 처리정확도 우선·느린 로봇
offlinebag 재생으로 후처리품질 최우선(11.3)
localization기존 지도에서 측위만AMCL 대안(8.3)
lifelong지도를 계속 갱신변화하는 환경

이 다섯이 흔히 소개되는 모드지만 패키지가 배포하는 실행 파일은 여섯 개다 — 여기 없는 map_and_localization과, 모드가 아닌 도구 merge_maps_kinematic이 더 있다(실행 확인, 8.2.13.1).

ros2 launch slam_toolbox online_async_launch.py \
  params_file:=/path/to/mapper_params_online_async.yaml

online_async와 online_sync의 차이:

축asyncsync
스캔 처리큐에 넣고 비동기즉시 처리
스캔 유실가능(지연되면)없음
실시간성좋음처리가 지연되면 지연
정확도약간 낮을 수 있음높음

견해: 먼저 online_async로 시작하고, 지도 품질이 부족하면 offline으로 후처리하라. bag을 기록해 두면(11.3) 파라미터를 바꿔 가며 여러 번 시도할 수 있고, 실시간 제약이 없어 더 좋은 결과를 얻는다. 현장에서 한 번에 완벽한 지도를 만들려 하지 말고, 데이터를 잘 기록하는 데 집중하라.

8.2.5 주요 설정

slam_toolbox:
  ros__parameters:
    # 프레임 (REP 105 — 5.1.7)
    odom_frame: odom
    map_frame: map
    # 패키지 기본값은 base_footprint — 명시가 안전(8.2.5)
    base_frame: base_link
    scan_topic: /scan

    mode: mapping                    # mapping · localization

    # 처리
    resolution: 0.05
    max_laser_range: 20.0
    minimum_time_interval: 0.5       # 스캔 처리 최소 간격
    transform_publish_period: 0.02   # map → odom 발행 주기
    map_update_interval: 5.0

    # 스캔 매칭 — 새 스캔을 언제 추가할 것인가
    minimum_travel_distance: 0.5     # m
    minimum_travel_heading: 0.5      # rad

    # 루프 클로저
    do_loop_closing: true
    loop_search_maximum_distance: 3.0
    loop_match_minimum_chain_size: 10
    loop_match_minimum_response_coarse: 0.35
    loop_match_minimum_response_fine: 0.45

    # 상관 스캔 매처
    correlation_search_space_dimension: 0.5
    correlation_search_space_resolution: 0.01
    correlation_search_space_smear_deviation: 0.1

위 20개 파라미터를 Jazzy의 기본 config(mapper_params_online_async.yaml)와 한 줄씩 대조했다(실행 확인): 이름은 20개 전부 실재하고, 기본값은 19개가 일치한다. 다른 하나는 base_frame으로, 패키지 기본값은 base_link가 아니라 base_footprint다 — 아래 함정을 보라. 이 config에는 총 49개 파라미터가 들어 있으므로, 위 목록은 널리 쓰이는 부분집합이다.

함정: base_frame 기본값이 base_footprint라는 점. 로봇 URDF에 base_footprint 링크가 없으면 SLAM Toolbox는 odom → base_footprint 조회에 실패하고, 증상은 "지도가 전혀 안 생긴다"로 나타난다. 원인이 파라미터 하나라는 것을 알기 어려워 시간을 크게 잃는 자리다(5.2의 tf 진단으로 이어진다). URDF에 base_footprint가 없다면 base_frame: base_link로 명시하라 — 기본값에 기대지 마라.

튜닝에서 중요한 것들:

파라미터영향
resolution지도 정밀도·메모리(8.2.2)
max_laser_range멀리 볼수록 정보가 많지만 노이즈도
minimum_travel_distance·_heading노드 밀도 — 작으면 정밀하지만 부하가 크다
minimum_time_interval처리 부하 제한
do_loop_closing누적 오차 보정(8.2.7)
correlation_search_space_*매칭 탐색 범위 — 오도메트리 품질이 낮으면 키운다

견해: minimum_travel_distance를 줄이면 지도가 정밀해지지만 그래프가 커져 느려진다. 넓은 공간은 0.5 m, 좁고 복잡한 곳은 0.2~0.3 m가 출발점이다. 오도메트리가 좋을수록 크게 설정해도 된다 — 이것이 7.6의 오도메트리 품질이 지도에 영향을 주는 경로 중 하나다.

8.2.6 직렬화와 재사용

SLAM Toolbox는 지도 이미지뿐 아니라 포즈 그래프를 저장할 수 있다.

# 포즈 그래프 저장 (서비스)
ros2 service call /slam_toolbox/serialize_map \
  slam_toolbox/srv/SerializePoseGraph "{filename: /home/user/maps/my_map}"
# → my_map.posegraph, my_map.data 생성

# 이미지 지도 저장 (Nav2용)
ros2 run nav2_map_server map_saver_cli -f /home/user/maps/my_map
# → my_map.yaml, my_map.pgm

두 형식의 차이:

형식내용용도
.pgm + .yaml최종 격자 지도Nav2·AMCL
.posegraph + .data포즈 그래프 + 스캔SLAM 재개·추가 매핑

포즈 그래프를 이어서 매핑:

slam_toolbox:
  ros__parameters:
    mode: mapping
    map_file_name: /home/user/maps/my_map     # 포즈 그래프 로드
    map_start_at_dock: true                    # 또는 map_start_pose

견해: 큰 공간을 여러 번에 나눠 매핑할 때 포즈 그래프 저장이 필수다. 한 번에 다 매핑할 수 없는 건물은 구역별로 매핑하고 이어 붙인다. 다만 이어 붙이는 지점에서 루프 클로저가 일어나야 정합이 맞으므로, 겹치는 구역을 충분히 확보하라.

localization 모드:

slam_toolbox:
  ros__parameters:
    mode: localization
    map_file_name: /home/user/maps/my_map
    map_start_at_dock: true

AMCL 대신 쓸 수 있다(8.3). 포즈 그래프 기반이라 스캔 매칭 정확도가 높지만, 계산 비용과 메모리가 더 든다(확인 필요: 실제 비교는 환경 의존).

8.2.7 루프 클로저

SLAM의 핵심 메커니즘이다.

예 아니오 출발점 주행하며 매핑 오도메트리 오차 누적 출발점 근처로 복귀 이전에 본 곳을 인식하는가? 루프 클로저 전체 그래프 보정 지도가 어긋난 채 남음

루프 클로저가 성공하면 누적 오차가 전역적으로 분산·보정된다. 실패하면 같은 복도가 두 개로 그려지거나 벽이 겹친다.

실패 원인:

원인설명
누적 오차가 너무 큼탐색 범위를 벗어남
특징 부족긴 복도·빈 공간
환경이 대칭·반복다른 곳을 같은 곳으로 오인
동적 장애물사람·물건이 매칭 방해
파라미터가 보수적매칭 임계가 높음
운동 왜곡스캔 자체가 왜곡(7.5.4)

대처:

방법설명
오도메트리 개선누적 오차를 줄이면 매칭이 쉬워진다(7.6)
루프를 자주 닫는다큰 루프 하나보다 작은 루프 여럿
loop_search_maximum_distance 증가더 넓게 탐색(비용 증가)
매칭 임계 완화거짓 양성 위험
천천히 주행운동 왜곡 감소

견해: 매핑 주행 전략이 지도 품질을 크게 좌우한다. 한 방향으로 길게 가고 돌아오는 것보다, 작은 루프를 여러 번 닫으며 확장하는 편이 낫다. 그리고 시작점으로 자주 돌아오라 — 오차가 커지기 전에 보정된다.

함정: 대칭적이거나 반복적인 환경(창고 선반·똑같은 복도)에서 거짓 루프 클로저가 발생하면 지도가 크게 손상된다. 매칭 임계를 낮추면 이 위험이 커지므로, 완화는 신중하게 하고 결과를 반드시 확인해야 한다.

8.2.8 지도 품질 문제 진단

증상에서 원인으로 역추적하는 표다.

증상원인 후보확인
벽이 두 겹·흐릿운동 왜곡(7.5.4)·오도메트리 오차정지 시 vs 이동 시 스캔
같은 복도가 두 개루프 클로저 실패그래프 시각화
지도가 회전하며 어긋남각속도 오차·자이로 문제제자리 회전 시험(7.6.7)
지도가 늘어남·줄어듦휠 반경 오차직진 시험(7.6.7)
유령 장애물동적 물체·반사·inf 처리(7.5.5)스캔 원본 확인
유리·거울 벽이 없음라이다가 통과수동 편집·다른 센서
낮은 장애물 누락라이다 높이3D 센서·범퍼
지도가 조금씩 밀림시계 불일치(7.7)tf2_monitor(5.1.10)
매핑이 전혀 안 됨tf·스캔 문제8.1.6의 필수 입력

견해: "지도 품질이 낮다"의 원인은 대개 SLAM 밖에 있다. 위 표에서 SLAM 파라미터 문제는 루프 클로저 정도이고, 나머지는 오도메트리·센서·시간 문제다. SLAM 파라미터를 조정하기 전에 8.1.10의 1~3단계를 검증하라.

진단 절차:

# 1. 정지 상태에서 스캔이 선명한가 (7.5.9)
ros2 topic echo /scan --no-arr --once

# 2. 오도메트리 검증 (7.6.7)
#    직진·회전·폐루프 시험

# 3. 매핑 중 RViz2 관찰
#    - 스캔이 지도와 정렬되는가
#    - 로봇이 튀지 않는가

# 4. tf 지연 (5.1.10)
ros2 run tf2_ros tf2_monitor odom base_link

8.2.9 평생 지도와 환경 변화

환경은 변한다. 가구가 옮겨지고, 재고가 쌓이고, 임시 벽이 생긴다.

접근설명적합
정적 지도 + 지역 코스트맵지도는 고정, 변화는 지역에서 처리(8.4)대부분
주기적 재매핑정기적으로 다시 만듦변화가 큼
lifelong 모드지도를 계속 갱신점진적 변화
다중 지도상황별 전환구역별 차이

견해: "정적 지도 + 지역 코스트맵"이 기본이고 대부분 충분하다. 지도는 벽·기둥 같은 구조를 담고, 움직이는 것은 지역 코스트맵이 실시간으로 처리한다(8.4). 지도에 임시 물건이 들어가면 오히려 해롭다 — 그것이 치워져도 지도에는 남아 경로를 막는다.

함정: 매핑 시 환경을 정리하라. 사람이 이동하거나 임시 물건이 있으면 지도에 남는다. 지울 수 있지만(이미지 편집), 처음부터 정리된 상태에서 매핑하는 것이 낫다.

정직: lifelong 모드는 지도를 계속 갱신하지만 메모리·계산이 증가하고 장기 안정성 검증이 필요하다(확인 필요). 장시간 운용 전에 시험 기간을 두는 편이 안전하다.

8.2.10 매핑 실무 절차

예 아니오 1. 사전 검증 tf · 오도메트리 · 스캔 2. 환경 정리 3. bag 기록 시작 11.3 4. 천천히 주행 작은 루프 반복 5. 출발점 복귀 6. 지도 확인 품질 충분? offline 재처리 또는 재주행 7. 저장 pgm + posegraph 8. 수동 보정 필요 시 9. 측위 시험 8.3

주행 지침:

지침이유
천천히(0.2~0.5 m/s)운동 왜곡 감소(7.5.4)
회전을 천천히회전 왜곡이 더 심각하다
벽을 따라특징이 많은 곳
작은 루프 반복루프 클로저 기회(8.2.7)
막다른 곳도 방문완전한 지도
출발점 자주 복귀오차 보정

견해: bag 기록이 매핑 실무의 핵심이다(3단계). 현장에 다시 갈 수 없는 경우가 많고, 기록이 있으면 파라미터를 바꿔 가며 여러 번 재처리할 수 있다. /scan·/tf·/tf_static·/odom을 기록하면 충분하며, 용량도 작다(11.3).

ros2 bag record /scan /tf /tf_static /odom /imu/data -o mapping_session_01

8.2.13 패키지가 실제로 담고 있는 것 — 실측

앞 절들이 모드 다섯 가지를 설명했다. Jazzy에 설치된 slam_toolbox가 실제로 무엇을 배포하는지 확인한 결과다(실행 확인).

8.2.13.1 실행 파일은 여섯 개다

ls /opt/ros/jazzy/lib/slam_toolbox/
실행 파일대응하는 모드
async_slam_toolbox_nodeonline_async
sync_slam_toolbox_nodeonline_sync
localization_slam_toolbox_nodelocalization
lifelong_slam_toolbox_nodelifelong
map_and_localization_slam_toolbox_node(앞 표에 없던 것)
merge_maps_kinematic(모드가 아닌 도구)

offline은 실행 파일이 따로 없고 offline_launch.py가 비동기 노드를 다른 설정으로 실행한다 — launch 파일은 다섯 개(online_async·online_sync·offline·localization·merge_maps_kinematic)다.

8.2.13.2 앞 표에 없던 두 가지

map_and_localization_slam_toolbox_node — 지도를 이어서 넓히면서 동시에 그 지도 안에서 측위한다. localization이 "기존 지도를 고정하고 측위만" 하는 것과 달리, 이쪽은 알려진 구역에서는 측위하고 새 구역에 들어가면 지도를 확장한다. lifelong이 "같은 공간을 계속 갱신"하는 것과도 다르다 — 이쪽은 공간을 넓히는 쪽이다.

merge_maps_kinematic — 따로 만든 지도 여러 장을 한 장으로 합친다. 넓은 시설을 구역별로 나눠 매핑한 뒤 이어 붙이는 용도다. RViz에서 각 지도를 끌어 위치를 맞추고 서비스 호출로 병합하는 대화형 도구라, 자동 파이프라인에 넣기보다 사람이 한 번 하는 작업에 맞는다.

견해: 한 번에 전부 매핑하지 말고 구역을 나눠 매핑한 뒤 병합하는 편이 낫다. 큰 시설을 한 세션에 매핑하면 중간에 한 번 실패했을 때 전부 다시 해야 하고, 누적 오차도 커진다. 구역별로 나누면 실패한 구역만 다시 매핑하면 되고, 각 지도의 품질을 따로 확인할 수 있다. 다만 병합 이음매에서 정렬 오차가 생기므로, 구역 경계는 특징이 뚜렷한 곳(모서리·기둥)으로 정하라.

8.2.13.3 mode 두 개가 서로 다른 것을 가리킨다

이 장에는 mode라는 이름의 파라미터가 두 군데 나오는데 의미가 전혀 다르다. 혼동하기 쉬운 자리라 명시해 둔다(둘 다 실행 확인).

어디의 mode인가값뜻
map yaml(map_server)trinary·scale·raw회색조 픽셀을 점유/자유/미지로 해석하는 방식
SLAM Toolbox 파라미터mapping·localization지도를 만들 것인가 쓸 것인가

map yaml 쪽 세 값의 정확한 동작은 nav2_map_server의 MapMode 열거형에 이렇게 정의되어 있다:

값동작
trinary임계값 위는 점유(100), 아래는 자유(0), 그 사이는 전부 미지(-1)
scale사이 구간을 0~100으로 선형 보간한다. 알파 채널이 1.0 미만이면 미지
raw밝기 0~100을 그대로 점유 확률로 쓴다. 101 이상은 미지

함정: trinary가 기본이고, 사이 값을 전부 미지로 만든다. 수동으로 지도를 편집할 때 회색으로 칠한 영역이 "절반 점유"가 아니라 미지가 된다 — 플래너는 미지 영역을 설정에 따라 통과 금지로 볼 수 있어(8.4), "왜 여기로 경로가 안 나오나"의 원인이 된다. 사이 값을 실제 확률로 쓰려면 scale이나 raw로 바꿔야 한다.

8.2.11 함정 카탈로그

#함정증상대처
1origin을 중심으로 오해좌표가 절반 어긋남좌하단임을 인지
2/map을 기본 QoS로 확인안 보임transient_local(0.7.5)
3map_server 미활성화Nav2 대기라이프사이클 확인
4지도 경로가 상대 경로배포본에서 실패절대·package://
5빠르게 주행하며 매핑운동 왜곡·지도 흐림천천히
6큰 루프 하나만루프 클로저 실패작은 루프 반복
7매핑 중 환경 미정리임시 물건이 지도에사전 정리
8bag 미기록재처리 불가항상 기록
9오도메트리 미검증 상태로 매핑지도 왜곡사전 검증(7.6.7)
10매칭 임계 과도 완화거짓 루프 클로저신중히·결과 확인
11해상도를 근거 없이 높임메모리·계산 급증0.05 m 기본
12수동 편집 이력 미기록나중에 이유 모름원본·이력 보관
13지도 문제를 SLAM 파라미터로만 접근해결 안 됨하위 계층 검증
14base_frame 기본값 방치URDF에 base_footprint 없으면 지도가 안 생김base_frame 명시(8.2.5)
15수동으로 칠한 회색 영역trinary 기본이라 미지로 해석scale·raw 검토(8.2.13.3)

8.2.12 체크포인트

  • OccupancyGrid의 셀 값 세 가지와 origin의 정확한 의미를 말할 수 있는가.
  • 셀↔좌표 변환식과 인덱스 계산을 쓸 수 있는가.
  • 지도 파일의 두 부분과 occupied_thresh의 역할은?
  • 해상도가 셀 수에 미치는 영향을 식으로 말할 수 있는가. 일반적 기본값은?
  • /map 토픽의 QoS와, 확인 시 필요한 옵션은?
  • SLAM Toolbox의 다섯 모드와 각각의 용도를 말할 수 있는가. 실행 파일은 왜 여섯 개인가. online_async와 offline을 어떻게 조합하는가.
  • minimum_travel_distance가 무엇에 영향을 주며, 오도메트리 품질과 어떻게 연결되는가.
  • 포즈 그래프와 이미지 지도의 차이와 각각의 용도는?
  • 루프 클로저가 하는 일과 실패 원인 여섯 가지를 말할 수 있는가. 매핑 주행 전략이 왜 중요한가.
  • 거짓 루프 클로저가 발생하는 환경과 그 위험은?
  • 지도 품질 문제 아홉 가지 증상을 원인과 짝지을 수 있는가. "원인은 대개 SLAM 밖에 있다"의 의미는?
  • 환경 변화에 대한 네 가지 접근과, 기본이 되는 것은? 지도에 임시 물건이 들어가면 왜 해로운가.
  • 매핑 실무 절차 아홉 단계와 주행 지침 여섯 가지를 말할 수 있는가. bag 기록이 왜 핵심인가.
  • SLAM Toolbox가 배포하는 실행 파일은 몇 개이며 모드와 어떻게 다른가. map yaml의 mode 세 값은 각각 무엇을 하는가(8.2.13).

다음 장 8.3은 이 지도 위에서 자기 위치를 찾는 AMCL을 다룬다. 파티클 필터의 동작, 발산과 복구, 그리고 튜닝.