Skip to main content
Summary

지도는 로봇의 기억이다

SLAM을 실행하고 RViz 화면에 벽과 복도가 서서히 그려지는 순간은 꽤 짜릿하다. 처음에는 안개처럼 회색이던 공간이, 로봇이 한 바퀴 돌고 다시 출발점 근처로 돌아오면 검은 벽과 흰 바닥으로 정리된다. 마치 로봇이 낯선 건물을 더듬어 가며 자기만의 평면도를 그리는 것 같다.

하지만 여기서 중요한 질문이 남는다.

"이 지도를 어떻게 다음 실행에서도 다시 쓸 수 있을까?"

RViz에 보이는 지도는 화면 속 그림일 뿐이다. SLAM 노드가 종료되면, 별도로 저장하지 않은 지도는 사라진다. 자율주행 로봇에게 지도는 일회성 결과물이 아니다. 한 번 만들고 버리는 스크린샷이 아니라, 위치 추정과 경로 계획, 장애물 회피가 반복해서 참조하는 핵심 자산이다.

이번 장에서는 SLAM으로 만든 점유 격자 지도를 파일로 저장하고, 다시 불러와 위치 추정과 내비게이션에 사용하는 흐름을 익힌다. 여기서 다루는 지도는 주로 2D LiDAR SLAM에서 만들어지는 nav_msgs/msg/OccupancyGrid 형태의 지도다. RViz에서는 흰색, 검은색, 회색으로 보이지만, 내부적으로는 각 격자 셀이 "비어 있음", "점유됨", "알 수 없음"을 나타내는 숫자 데이터로 표현된다.

이제 로봇에게 기억을 남겨 보자.

지도 저장과 재사용

점유 격자 지도를 파일로 저장하기

앞 장에서 SLAM Toolbox를 실행해 /map 토픽이 발행되는 상태를 만들었다고 하자. SLAM Toolbox는 LiDAR 스캔과 오도메트리, TF를 이용해 2D 점유 격자 지도를 만들고, 이 지도를 ROS2 토픽으로 계속 내보낸다. Nav2 튜토리얼에서도 SLAM 노드는 /map 토픽과 map → odom 변환을 제공해야 하며, 생성된 지도는 nav2_map_server의 저장 도구로 파일화할 수 있다고 설명한다.1

가장 기본적인 저장 명령은 다음과 같다.

ros2 run nav2_map_server map_saver_cli -f ~/maps/office_map

이 명령에서 -f 뒤에는 저장할 파일 이름의 기준 경로를 적는다. 확장자는 붙이지 않는 편이 일반적이다. 위 명령이 성공하면 다음 두 파일이 만들어진다.

~/maps/office_map.pgm
~/maps/office_map.yaml

ROS2 환경과 설정에 따라 PNG 형식을 사용할 수도 있지만, 기본 예제에서는 PGM을 자주 본다. 중요한 것은 이미지 파일 하나만 저장되는 것이 아니라는 점이다. 로봇이 지도를 제대로 읽으려면 이미지와 메타데이터가 한 쌍으로 필요하다. 이미지 파일은 지도 모양을 담고, YAML 파일은 그 이미지가 실제 세계에서 어떤 크기와 위치를 갖는지 설명한다.

예를 들어 이미지 파일만 있다면 사람은 "아, 복도처럼 생겼네"라고 볼 수 있다. 그러나 로봇은 이렇게 물을 것이다.

"픽셀 하나가 몇 미터지? 이 그림의 왼쪽 아래는 실제 좌표로 어디지? 어느 정도 어두운 픽셀을 벽으로 봐야 하지?"

이 질문에 답하는 파일이 바로 YAML이다.

nav2_map_server는 Nav2 시스템의 다른 구성 요소에 지도를 제공하는 패키지다. 지도 로딩을 담당하는 map_server, 지도 저장을 담당하는 map_saver, 그리고 OccupancyGrid 지도 입출력을 처리하는 map_io 라이브러리로 구성된다.2 즉, map_saver_cli는 현재 ROS2 시스템에서 발행 중인 지도 토픽을 읽어 디스크에 저장하는 문지기 역할을 한다.

저장할 때 현재 디렉터리에 만들고 싶다면 다음처럼 간단히 실행할 수도 있다.

mkdir -p ~/robot_maps
cd ~/robot_maps
ros2 run nav2_map_server map_saver_cli -f lab

그러면 ~/robot_maps/lab.pgm과 ~/robot_maps/lab.yaml이 생성된다. 저장 직후에는 파일 목록을 확인하자.

ls -lh ~/robot_maps

예상 결과는 대략 다음과 같다.

lab.pgm
lab.yaml

여기서 첫 번째 디버깅 습관을 들이는 것이 좋다. 저장 명령이 성공했다는 로그만 믿지 말고, 실제 파일이 생성되었는지 확인한다. 지도 저장은 내비게이션 전체 흐름에서 작은 단계처럼 보이지만, 이 두 파일이 없으면 다음 단계인 AMCL 위치 추정과 Nav2 자율주행은 출발선에 설 수 없다.

PGM, PNG, YAML은 각각 무엇을 담고 있을까

저장된 지도 이미지 파일을 열어 보면 흑백 그림처럼 보인다. 검은색은 벽이나 장애물, 흰색은 로봇이 이동할 수 있는 자유 공간, 회색은 아직 알 수 없는 영역을 뜻한다. 물론 실제 내부 표현은 구현과 저장 모드에 따라 다를 수 있지만, 개념적으로는 이 세 가지를 떠올리면 된다.

지도 이미지가 "그림"이라면 YAML은 "그림을 읽는 법"이다. 일반적인 지도 YAML은 다음과 비슷하다.

image: lab.pgm
mode: trinary
resolution: 0.05
origin: [-3.20, -2.75, 0.0]
negate: 0
occupied_thresh: 0.65
free_thresh: 0.25

각 항목은 내비게이션에서 매우 중요한 의미를 갖는다. 특히 resolution과 origin은 지도 좌표를 실제 거리로 바꾸는 기준이므로, 값이 잘못되면 로봇이 지도 위에서 엉뚱한 위치에 나타난다.

image는 실제 지도 이미지 파일의 경로다. 보통 YAML 파일을 기준으로 상대 경로가 적힌다. 예를 들어 image: lab.pgm이라고 되어 있으면, lab.yaml과 같은 폴더에 있는 lab.pgm을 읽겠다는 뜻이다.

resolution은 픽셀 하나가 실제 세계에서 몇 미터인지 나타낸다. 0.05라면 픽셀 하나가 5cm라는 뜻이다. 해상도가 작을수록 더 세밀한 지도가 되지만, 지도 크기와 계산량도 늘어난다. 좁은 통로를 다루는 로봇에서는 해상도가 너무 거칠면 통로가 막힌 것처럼 보일 수 있고, 너무 세밀하면 저사양 컴퓨터에서 처리 부담이 커질 수 있다.

origin은 지도 이미지의 원점이 map 좌표계에서 어디에 놓이는지 나타낸다. 보통 [x, y, yaw] 형식이다. 여기서 x와 y는 미터 단위 위치이고, yaw는 평면에서의 회전 각도다. 많은 2D 실내 지도에서는 yaw가 0.0인 경우가 많지만, 값 자체의 의미는 알고 있어야 한다.

occupied_thresh는 어떤 셀을 장애물로 볼지 결정하는 임계값이다. 점유 확률이 이 값 이상이면 벽이나 장애물로 해석한다. 반대로 free_thresh는 어떤 셀을 자유 공간으로 볼지 결정하는 임계값이다. 이 값 이하이면 로봇이 지나갈 수 있는 공간으로 해석한다. 두 값 사이에 있는 셀은 불확실한 영역으로 남을 수 있다.

mode는 이미지 픽셀을 점유 격자로 해석하는 방식이다. 자주 보이는 값은 trinary다. 이름 그대로 지도를 대략 세 종류, 즉 점유·자유·미지 영역으로 나누어 해석한다. 일부 환경에서는 픽셀의 확률 정보를 더 보존하는 방식이 사용될 수 있지만, 처음에는 trinary 지도를 기준으로 이해하면 충분하다.

Nav2의 지도 YAML 형식은 ROS1 시절부터 이어진 형식을 유지하되, ROS2에서는 yaml_filename 같은 파라미터를 통해 map server가 어떤 YAML 파일을 읽을지 지정하는 방식으로 사용된다.3 따라서 YAML 안의 값은 단순한 설명문이 아니라, AMCL과 Nav2가 같은 스케일과 같은 좌표 기준으로 세계를 읽게 만드는 계약서다.

지도 파일을 열어 볼 때는 다음 세 가지를 특히 확인하자.

  • image가 실제 존재하는 이미지 파일을 가리키는가

  • resolution이 SLAM을 수행할 때 의도한 지도 해상도와 맞는가

  • origin이 비정상적으로 이상한 값으로 바뀌어 있지는 않은가

이 세 가지 중 하나라도 어긋나면 지도는 그럴듯하게 보이지만, 로봇은 지도 위에서 길을 잃는다. 사람에게는 종이 지도처럼 보이지만, 로봇에게는 축척이 틀어진 미로가 되는 셈이다.

저장 명령이 실패할 때 먼저 볼 것들

map_saver_cli를 실행했는데 지도가 저장되지 않을 때가 있다. 이때 당황해서 SLAM 파라미터부터 바꾸기 전에, 현재 시스템에서 지도 토픽이 제대로 발행되고 있는지 확인해야 한다.

먼저 /map 토픽이 있는지 본다.

ros2 topic list | grep map

그리고 메시지 타입을 확인한다.

ros2 topic info /map

정상적인 점유 격자 지도라면 타입은 보통 다음과 같다.

Type: nav_msgs/msg/OccupancyGrid

한 번만 메시지를 보고 싶다면 다음 명령을 사용할 수 있다.

ros2 topic echo /map --once

메시지가 오지 않는다면 저장 도구의 문제가 아니라, 저장할 지도 자체가 아직 시스템에 없는 것이다. SLAM 노드가 제대로 실행 중인지, use_sim_time 설정이 맞는지, LiDAR /scan과 TF가 정상인지 다시 확인해야 한다.

시뮬레이션에서 특히 흔한 문제는 시간 설정이다. Gazebo나 rosbag 재생을 사용한다면 SLAM 노드와 관련 노드들이 use_sim_time:=true로 실행되어야 한다. 이전 장에서 bag을 재생할 때 --clock을 사용했던 이유도 여기에 있다. 시간이 흐르지 않는 세계에서 로봇은 움직이지 않는 것처럼 보이고, 움직이지 않는 로봇에게는 저장할 지도도 생기지 않는다.

또 하나의 문제는 토픽 내구성, 즉 늦게 접속한 구독자가 마지막 지도 메시지를 받을 수 있는지 여부다. 일부 환경에서는 map_saver_cli가 /map의 마지막 메시지를 받지 못해 저장에 실패할 수 있다. TurtleBot4 문서에서는 네임스페이스를 쓰는 경우 map_saver_cli를 직접 호출하면서 map_subscribe_transient_local 파라미터를 설정하는 예를 보여 준다.4

ros2 run nav2_map_server map_saver_cli -f map_name \
  --ros-args -p map_subscribe_transient_local:=true

네임스페이스가 있는 로봇이라면 remapping이나 namespace도 함께 확인해야 한다.

ros2 run nav2_map_server map_saver_cli -f robot1_map \
  --ros-args -r __ns:=/robot1

물론 실제 프로젝트에서는 네임스페이스 구조에 맞게 토픽 이름과 노드 설정을 정리해야 한다. 중요한 것은 저장 도구가 "어느 /map을 읽고 있는가"를 명확히 아는 것이다. 여러 로봇이 있거나, SLAM 지도와 static map이 동시에 떠 있으면 이 질문은 생각보다 중요해진다.

저장한 지도를 다시 불러오기

지도를 저장했다면 다음 목표는 다시 불러오는 것이다. SLAM은 미지의 공간을 탐험하며 지도를 만드는 단계이고, 저장된 지도 기반 내비게이션은 이미 알고 있는 공간에서 현재 위치를 추정하고 목표까지 이동하는 단계다.

Nav2에서 저장된 지도를 불러올 때 핵심 역할을 하는 노드는 map_server다. map_server는 YAML 파일을 읽고, 이미지 파일을 함께 해석해 /map 토픽으로 지도를 발행한다. Nav2 문서에 따르면 map server는 토픽과 서비스 인터페이스를 통해 Nav2 시스템의 나머지 구성 요소에 지도를 제공하며, 실행 중 load_map 서비스로 지도를 바꾸는 것도 가능하다.5

가장 흔한 실행 형태는 Nav2 bringup이나 로봇별 navigation launch 파일에 map 인자를 넘기는 방식이다.

ros2 launch nav2_bringup bringup_launch.py \
  map:=/home/user/robot_maps/lab.yaml \
  use_sim_time:=true

로봇 패키지에 따라 명령은 조금 다를 수 있다. TurtleBot 계열 패키지에서는 다음처럼 보일 수 있다.

ros2 launch turtlebot3_navigation2 navigation2.launch.py \
  map:=$HOME/robot_maps/lab.yaml \
  use_sim_time:=true

중요한 것은 map 인자로 이미지 파일이 아니라 YAML 파일을 넘긴다는 점이다. PGM이나 PNG는 지도 그림이고, YAML은 그 그림을 지도답게 읽는 설명서다. map server는 YAML을 먼저 읽고, 그 안의 image 항목을 따라 실제 이미지 파일을 찾는다.

직접 map server 파라미터 파일을 작성하면 구조가 더 분명해진다.

map_server:
  ros__parameters:
    yaml_filename: "/home/user/robot_maps/lab.yaml"
    topic_name: "map"
    frame_id: "map"

Nav2 설정 문서에서도 yaml_filename, topic_name, frame_id 같은 map server 파라미터를 사용해 어떤 지도를 어떤 토픽과 프레임으로 발행할지 지정하는 예를 제공한다.6

여기서 frame_id는 보통 map이다. 앞에서 배운 TF 구조를 다시 떠올려 보자.

map → odom → base_link

저장된 지도는 map 좌표계에 고정된다. AMCL 같은 위치 추정 노드는 LiDAR 스캔과 저장된 지도를 비교해 로봇이 지도 안에서 어디에 있는지 추정하고, 그 결과로 map → odom 변환을 제공한다. 오도메트리는 짧은 시간 동안의 부드러운 움직임을 맡고, AMCL은 지도와 센서를 비교해 누적 오차를 바로잡는다.

이 구조가 맞아야 Nav2가 "현재 위치에서 목표 위치까지" 경로를 계획할 수 있다. 지도가 불러와져도 TF가 끊겨 있으면 로봇은 세계의 좌표와 자기 몸의 좌표를 연결하지 못한다.

경로 문제: YAML 기준 상대 경로와 실행 위치 기준 경로

지도 재사용에서 가장 자주 만나는 함정은 파일 경로다. 특히 image 항목이 상대 경로일 때 헷갈리기 쉽다.

다음 YAML을 보자.

image: lab.pgm
resolution: 0.05
origin: [-3.20, -2.75, 0.0]
negate: 0
occupied_thresh: 0.65
free_thresh: 0.25
mode: trinary

이 경우 lab.pgm은 보통 lab.yaml이 있는 폴더를 기준으로 찾는다고 이해하면 된다. 그래서 다음 구조는 자연스럽다.

robot_maps/
  lab.yaml
  lab.pgm

하지만 파일을 정리하다가 이렇게 바꾸면 문제가 생길 수 있다.

robot_maps/
  yaml/
    lab.yaml
  images/
    lab.pgm

이때 YAML 안의 image: lab.pgm을 그대로 두면 map server는 yaml/lab.pgm을 찾으려 할 수 있다. 실제 이미지는 images/lab.pgm에 있으므로 로딩에 실패한다. 이런 경우 YAML을 다음처럼 고쳐야 한다.

image: ../images/lab.pgm

또는 절대 경로를 사용할 수도 있다.

image: /home/user/robot_maps/images/lab.pgm

절대 경로는 명확하지만, 다른 컴퓨터나 로봇으로 프로젝트를 옮길 때 깨지기 쉽다. 반대로 상대 경로는 패키지나 폴더 전체를 함께 옮길 때 유리하지만, 폴더 구조를 바꾸면 함께 수정해야 한다.

실무에서는 지도 파일을 패키지 안에 넣어 관리하기도 한다.

my_robot_navigation/
  maps/
    lab.yaml
    lab.pgm
  launch/
    navigation.launch.py
  config/
    nav2_params.yaml

이 방식은 버전 관리가 쉽고, 여러 개발자가 같은 지도를 공유하기 좋다. 다만 ROS2 패키지로 설치할 때 maps 폴더가 install 공간에 복사되도록 설정해야 한다. Python 패키지라면 setup.py의 data_files, CMake 패키지라면 install(DIRECTORY maps DESTINATION share/${PROJECT_NAME}) 같은 설치 규칙을 챙겨야 한다.

지도 경로 문제를 점검할 때는 다음 순서가 좋다.

  • YAML 파일 경로가 launch 인자로 정확히 전달되었는지 확인한다.

  • YAML 안의 image 경로가 실제 이미지 파일을 가리키는지 확인한다.

  • 패키지 설치 후 실행한다면 install 공간에도 지도 파일이 복사되었는지 확인한다.

  • 확장자가 YAML 내용과 실제 파일 이름에서 일치하는지 확인한다.

특히 리눅스 파일 시스템은 대소문자를 구분한다. Lab.pgm과 lab.pgm은 다른 파일이다. 사람 눈에는 사소한 차이지만, map server에게는 완전히 다른 경로다.

지도 품질을 RViz에서 평가하기

지도 파일이 저장되었다고 해서 바로 좋은 지도인 것은 아니다. 내비게이션에 사용할 지도는 로봇이 실제 환경에서 자기 위치를 잃지 않고, 경로를 계획할 수 있을 정도로 안정적이어야 한다.

RViz에서 저장된 지도를 다시 불러온 뒤 천천히 살펴보자. 이때 중요한 것은 "예쁘게 보이는가"가 아니라 "로봇이 이 지도를 믿고 달릴 수 있는가"다.

먼저 벽의 연속성을 본다. 실제로는 직선 벽인데 지도에서는 톱니처럼 흔들리거나 중간중간 끊겨 있다면, LiDAR 스캔 정렬이 안정적이지 않았을 가능성이 있다. 벽이 끊긴 지점은 global planner가 통로로 착각할 수 있고, 반대로 실제 통로가 벽처럼 막혀 있으면 로봇은 갈 수 있는 길을 못 간다고 판단할 수 있다.

다음으로 열린 문과 유리 영역을 본다. LiDAR는 유리문, 거울, 검은 흡광 재질, 사람 다리 같은 대상에서 불안정한 데이터를 만들 수 있다. 어떤 날은 문이 열려 있었고, 다음 날은 닫혀 있다면 저장된 지도와 실제 환경이 달라진다. 지도에 찍힌 문짝 하나가 내비게이션 실패의 주인공이 되는 경우도 흔하다.

노이즈 점도 확인해야 한다. 넓은 자유 공간 한가운데 검은 점들이 흩어져 있다면, SLAM 중 잠깐 지나간 사람이나 의자 다리, 반사 오류가 지도에 남았을 수 있다. 작은 노이즈라도 inflation layer가 적용되면 로봇에게는 꽤 큰 위험 영역으로 부풀어 오른다. 나중에 Costmap 장에서 배우겠지만, 장애물 주변에는 안전거리 비용이 추가되기 때문이다.

스캔 정렬 어긋남도 중요한 신호다. 같은 벽이 두 겹으로 보이거나, 복도가 평행하게 겹쳐져 보이면 루프 클로저가 잘못되었거나 오도메트리와 스캔 매칭이 엇갈렸을 수 있다. 이런 지도는 로봇이 어느 벽을 진짜 벽으로 믿어야 할지 헷갈리게 만든다.

마지막으로 닫힌 공간의 왜곡을 본다. 로봇이 한 바퀴 돌아 출발점 근처로 돌아왔는데 지도의 시작점과 끝점이 어긋나 있다면, 루프가 제대로 닫히지 않은 것이다. 지도 전체가 살짝 휘어진 정도라면 작은 실습에서는 넘어갈 수 있지만, 실제 내비게이션에서는 장거리 이동 중 위치 추정이 흔들릴 수 있다.

SLAM 결과를 평가할 때는 장애물 경계가 분명한지, 실제 환경과 맞지 않는 영역이 없는지, 미탐색 회색 영역이 주행 공간 안에 남아 있지 않은지, 이동 장애물이 지도에 박제되지 않았는지 등을 확인하는 기준이 유용하다.7

좋은 지도는 반드시 완벽한 그림일 필요는 없다. 로봇이 이동할 구역의 벽과 주요 구조물이 안정적으로 표현되고, 통로와 방의 연결 관계가 실제와 맞으며, 위치 추정에 필요한 특징이 충분히 남아 있으면 된다. 지도는 예술 작품이 아니라, 로봇이 길을 찾기 위한 작업 도구다.

최종 지도와 편집 가능한 SLAM 데이터 구분하기

여기서 중요한 구분을 하나 해야 한다. PGM/PNG + YAML 지도는 내비게이션에 사용하기 좋은 최종 산출물이다. AMCL과 Nav2가 읽기 좋고, 파일 크기도 비교적 작으며, 배포하기 쉽다. 하지만 이 형식은 SLAM 과정의 모든 정보를 담지는 않는다.

SLAM Toolbox는 단순히 PGM 지도를 저장하는 것뿐 아니라, pose-graph와 메타데이터를 손실 없이 저장했다가 나중에 다시 불러와 같은 로봇이나 다른 로봇으로 이어서 매핑할 수 있는 기능도 제공한다.8

pose-graph는 로봇이 지나간 자세들과 그 사이의 관계를 그래프처럼 저장한 데이터라고 생각하면 된다. SLAM은 단순히 픽셀 그림을 그리는 과정이 아니다. 로봇이 어느 시점에 어디쯤 있었고, 어떤 LiDAR 스캔을 봤으며, 나중에 같은 장소를 다시 봤을 때 어떻게 경로를 보정했는지에 대한 구조가 있다. 이 구조가 pose-graph다.

비유하자면 PGM 지도는 완성된 지하철 노선도 이미지이고, pose-graph는 그 노선도를 만들기 위해 기록한 측량 지점과 연결 관계다. 노선도 이미지만 있으면 승객은 길을 찾을 수 있다. 하지만 노선을 새로 연장하거나, 잘못 그린 구간을 정밀하게 수정하려면 측량 기록이 있는 편이 훨씬 낫다.

따라서 상황에 따라 저장 방식을 구분해야 한다.

  • Nav2 내비게이션에서 사용할 안정적인 기준 지도가 필요하면 PGM/PNG + YAML을 저장한다.

  • 기존 지도를 나중에 이어서 확장하거나 SLAM 결과를 정교하게 수정하려면 SLAM Toolbox의 pose-graph 저장을 함께 고려한다.

  • 실험 재현과 알고리즘 비교가 목적이라면 rosbag으로 /scan, /tf, /tf_static, /odom 등을 기록해 원본 데이터도 보존한다.

처음에는 map_saver_cli로 지도 파일을 저장하는 것만으로 충분하다. 그러나 실제 로봇을 운영하다 보면 "이 복도 끝 방을 나중에 추가로 매핑하고 싶다", "가구 배치가 조금 바뀌었는데 전체를 다시 돌기에는 시간이 아깝다" 같은 요구가 생긴다. 그때 pose-graph 저장과 재로딩이 빛을 발한다.

지도를 재사용하는 전체 흐름

이제 SLAM으로 만든 지도가 Nav2 내비게이션에 들어가는 큰 흐름을 정리해 보자.

첫 번째 단계는 매핑이다. 로봇을 천천히 움직이며 SLAM을 실행한다. 이때 /scan, /odom, /tf가 안정적으로 들어오고, SLAM 노드가 /map과 map → odom 변환을 제공한다. RViz에서 지도가 충분히 그려지고 루프가 잘 닫혔는지 확인한다.

두 번째 단계는 저장이다. map_saver_cli로 현재 /map을 파일로 저장한다.

ros2 run nav2_map_server map_saver_cli -f ~/robot_maps/lab

세 번째 단계는 검수다. lab.pgm 또는 lab.png를 이미지 뷰어로 열어 보고, lab.yaml의 경로와 해상도, 원점, 임계값을 확인한다. RViz에서 다시 불러와 지도 품질도 확인한다.

네 번째 단계는 위치 추정이다. SLAM을 끄고, 저장된 지도를 map server로 불러온다. AMCL은 이 지도와 실시간 LiDAR 스캔을 비교해 로봇의 현재 위치를 추정한다. RViz에서 2D Pose Estimate로 초기 위치를 지정하면 AMCL이 입자들을 지도 위에 뿌리고, 로봇이 조금 움직이면서 더 정확한 위치로 수렴한다.

다섯 번째 단계는 내비게이션이다. Nav2의 planner는 지도와 costmap을 이용해 전역 경로를 만들고, controller는 로봇이 그 경로를 따라가도록 속도 명령을 낸다. 이때 저장된 지도는 global costmap의 static layer, 즉 이미 알고 있는 벽과 구조물의 기준 자료가 된다.

전체 흐름은 다음처럼 볼 수 있다.

SLAM 실행
  ↓
/map 생성
  ↓
map_saver_cli로 PGM/PNG + YAML 저장
  ↓
map_server로 YAML 로딩
  ↓
AMCL 위치 추정
  ↓
Nav2 경로 계획과 주행

이 흐름을 이해하면 SLAM과 Navigation이 따로 떨어진 기능이 아니라는 점이 보인다. SLAM은 로봇에게 세계의 지도를 만들어 주고, Navigation은 그 지도를 이용해 목표까지 움직인다. 지도 저장은 두 세계를 이어 주는 다리다.

환경이 바뀌면 지도도 다시 생각해야 한다

지도는 한 번 만들면 영원히 쓸 수 있을까? 안타깝지만 그렇지 않다. 로봇의 세계는 생각보다 자주 바뀐다. 의자가 이동하고, 책장이 들어오고, 문이 닫히고, 임시 칸막이가 생긴다. 어제의 지도는 오늘의 현실과 조금씩 달라진다.

그렇다고 작은 변화가 있을 때마다 전체 지도를 새로 만들어야 하는 것은 아니다. 중요한 것은 변화의 크기와 위치 추정 안정성이다.

예를 들어 회의실 안의 의자 배치가 조금 바뀐 정도라면 저장된 static map은 그대로 두고, Nav2의 local costmap이 실시간 LiDAR로 장애물을 반영하도록 운영할 수 있다. 이 경우 벽과 복도 구조는 그대로이므로 AMCL 위치 추정에는 큰 문제가 없다. 의자는 동적 장애물 또는 임시 장애물로 취급하면 된다.

반대로 벽 구조가 바뀌었거나, 큰 가구가 벽처럼 길게 배치되어 기존 지도와 실제 환경이 크게 달라졌다면 문제가 심각해진다. AMCL은 LiDAR 스캔을 기존 지도와 맞추려 하는데, 실제로 보이는 구조가 지도와 다르면 로봇 위치가 흔들린다. 로봇이 복도 한가운데 있는데 지도상으로는 벽 근처에 있다고 추정할 수도 있다.

변화가 지역적이라면 pose-graph를 다시 불러와 해당 구역만 확장하거나 부분 재매핑을 고려할 수 있다. SLAM Toolbox의 pose-graph 재사용 기능은 이런 장기 운영 시나리오에서 유용하다. 지도 전체를 폐기하지 않고, 기존 구조를 바탕으로 새로운 관측을 더해 갈 수 있기 때문이다.

하지만 다음과 같은 상황이라면 전체 지도를 새로 작성하는 편이 낫다.

  • 로봇이 같은 장소에서 반복적으로 위치를 잃는다.

  • RViz에서 LiDAR 스캔과 지도 벽이 지속적으로 어긋난다.

  • global costmap의 벽 위치와 실제 장애물 위치가 크게 다르다.

  • 기존 통로가 막혔거나, 새로운 통로가 생기는 등 구조적 변화가 있다.

  • 부분 수정 후에도 경로 계획 실패와 recovery behavior가 자주 발생한다.

지도는 로봇의 기억이지만, 오래된 기억이 항상 좋은 판단을 만들지는 않는다. 실제 환경과 맞지 않는 지도는 로봇에게 잘못된 확신을 준다. 사람도 낡은 건물 안내도를 믿고 걷다가 막힌 문 앞에서 멈추듯, 로봇도 오래된 지도 때문에 길을 잃는다.

지도 파일을 프로젝트 자산으로 관리하기

실습 단계에서는 ~/map, ~/my_map, ~/test2 같은 이름으로 지도를 저장해도 괜찮다. 하지만 프로젝트가 커지면 지도 파일도 코드처럼 관리해야 한다.

추천하는 방식은 지도 이름에 장소, 날짜, 목적을 담는 것이다.

maps/
  office_1f_2026_05_14.yaml
  office_1f_2026_05_14.pgm
  lab_room_nav_v1.yaml
  lab_room_nav_v1.pgm

지도 파일 옆에 간단한 README를 두는 것도 좋다.

maps/
  README.md
  office_1f_v1.yaml
  office_1f_v1.pgm

README에는 매핑한 날짜, 사용한 로봇, LiDAR 모델, SLAM 설정, 특이 사항을 적어 둔다. 예를 들어 "복도 끝 유리문은 LiDAR 반사가 불안정하여 지도에서 일부 노이즈가 있음", "회의실 문은 열린 상태로 매핑함", "해상도 0.05m" 같은 기록이 나중에 큰 도움이 된다.

지도는 바이너리 이미지 파일을 포함하므로 Git에서 충돌을 사람이 직접 해결하기 어렵다. 그래도 소규모 프로젝트에서는 지도 파일을 함께 버전 관리하는 편이 재현성에 좋다. 운영 환경에서는 지도 저장소를 따로 두고, 로봇이 실행 시 특정 버전의 지도를 내려받는 구조를 만들 수도 있다.

지도 파일을 관리할 때는 다음 원칙을 기억하자.

  • YAML과 이미지 파일은 항상 한 쌍으로 이동한다.

  • 지도 이름은 실험용 이름보다 장소와 버전을 드러내게 짓는다.

  • 내비게이션에 사용한 지도와 SLAM 실험 중간 결과를 구분한다.

  • 환경 변화가 생기면 어떤 지도 버전부터 영향을 받는지 기록한다.

이런 습관은 나중에 디버깅 시간을 크게 줄여 준다. "어제는 됐는데 오늘은 안 돼요"라는 말의 절반은 사실 "어제 어떤 지도를 썼는지 모르겠어요"에서 시작된다.

작은 실습: 저장한 지도 확인하기

간단한 실습 흐름을 하나 정리해 보자. 먼저 SLAM이 실행 중이고 /map이 보이는 상태에서 지도 저장 폴더를 만든다.

mkdir -p ~/robot_maps

지도를 저장한다.

ros2 run nav2_map_server map_saver_cli -f ~/robot_maps/test_lab

파일을 확인한다.

ls ~/robot_maps

다음 두 파일이 있어야 한다.

test_lab.pgm
test_lab.yaml

YAML 내용을 확인한다.

cat ~/robot_maps/test_lab.yaml

이미지 경로와 해상도를 본다.

image: test_lab.pgm
resolution: 0.05
origin: [-2.50, -3.00, 0.0]
occupied_thresh: 0.65
free_thresh: 0.25
mode: trinary

이미지를 열어 보고 싶다면 일반 이미지 뷰어를 사용할 수 있다.

eog ~/robot_maps/test_lab.pgm

서버나 WSL2 환경처럼 GUI 이미지 뷰어를 쓰기 어렵다면 파일 크기라도 확인하자.

file ~/robot_maps/test_lab.pgm

이제 SLAM을 종료하고, 저장된 지도를 사용하는 navigation launch를 실행한다. 실제 명령은 로봇 패키지에 따라 다르지만 구조는 다음과 같다.

ros2 launch <your_navigation_package> navigation.launch.py \
  map:=$HOME/robot_maps/test_lab.yaml

RViz에서 지도가 보이면 첫 번째 관문을 통과한 것이다. 다음으로 Fixed Frame이 map인지 확인하고, 로봇 모델과 LiDAR 스캔이 지도 위에 자연스럽게 놓이는지 본다. 이후 2D Pose Estimate로 초기 위치를 지정하고, AMCL이 로봇 위치를 안정적으로 잡는지 확인하면 된다.

이 실습에서 실패한다면 대부분은 지도 파일 경로, use_sim_time, TF, 토픽 이름 중 하나다. 이 네 가지는 앞으로 Nav2를 다룰 때도 계속 등장하는 단골 손님이다.

지도는 다음 부의 출발점이다

우리는 지금까지 SLAM으로 만든 지도를 저장하고, 그 파일을 다시 불러와 내비게이션의 입력으로 사용하는 방법을 살펴보았다. 이 장에서 가장 중요한 문장은 하나다.

지도는 한 번 만들고 끝나는 결과물이 아니라, 자율주행 시스템이 반복해서 사용하는 핵심 자산이다.

SLAM 중에는 로봇이 지도를 만든다. 내비게이션 중에는 로봇이 그 지도 위에서 자기 위치를 찾고, 목표까지의 경로를 계획한다. 같은 map이라는 이름 아래에서 만들기와 사용하기가 이어진다.

이제 5부의 SLAM 이야기는 마무리된다. 다음 장부터는 저장된 지도를 들고 본격적으로 자율주행의 세계로 들어간다. 로봇은 현재 위치를 알아야 하고, 목표 위치를 받아야 하며, 지도 위에서 길을 찾고, 눈앞의 장애물을 피해야 한다. 그 모든 기능을 묶어 주는 대표적인 ROS2 내비게이션 프레임워크가 바로 Nav2다.

다음 장에서는 자율주행 로봇 시스템의 전체 구조를 큰 그림으로 살펴본다. 지도, 위치 추정, 경로 계획, 제어, 장애물 회피가 어떻게 하나의 흐름으로 연결되는지 보게 될 것이다.

참고

1Navigating while Mapping (SLAM) — Nav2 Documentation

2navigation2/nav2_map_server README

3nav2_map_server — ROS2 Humble Documentation

4Generating a map — TurtleBot 4 User Manual

5Map Server — Nav2 Rolling Documentation

6Map Server / Saver — Nav2 Configuration Guide

7ROS2 SLAM Mapping Course — PuppyPi Documentation

8slam_toolbox 2.6.10 Documentation

책 목록

ROS2로 완성하는 자율주행 로봇: 노드·TF·SLAM·Nav2 실전 설계

전체 목차 보기

이 노트는 보도·비평·교육·연구 목적으로 작성되었습니다. 저작권 문의가 있으시면 에서 알려주세요.

틸노트는 사용자가 AI를 활용해 노트를 쉽게 작성하거나 편집할 수 있는 서비스입니다.

AI 스퀘어 단톡방에 함께해요

AI 활용과 틸노트 사용 팁을 나누는 오픈채팅방입니다. 입장 비밀번호는 tilnote입니다.

참여하기