Skip to main content
Summary

지도는 완성되었지만, 로봇은 아직 길을 모른다

지난 장에서 우리는 로봇을 천천히 움직이며 실내 지도를 만들었다. indoor_lab_2026_05_14_v1.yaml과 indoor_lab_2026_05_14_v1.pgm 같은 파일이 생겼고, RViz에서는 검은 벽과 흰 복도가 제법 그럴듯하게 보였다. 여기까지 오면 마치 자율주행이 거의 끝난 것처럼 느껴진다.

하지만 로봇 입장에서는 아직 중요한 질문 하나가 남아 있다.

"좋아, 지도는 알겠어. 그런데 나는 지금 그 지도 위의 어디에 있지?"

사람은 건물 안내도를 보면 "나는 지금 엘리베이터 앞이니까 여기쯤이겠군" 하고 금방 짐작한다. 로봇에게도 같은 능력이 필요하다. 저장된 지도 위에서 현재 위치와 방향을 추정해야 한다. 이 과정을 위치 추정, 영어로는 localization이라고 부른다.

이번 장에서는 SLAM을 끄고, 이미 만들어진 지도를 다시 불러온다. 그리고 AMCL을 실행해 로봇이 지도 위에서 자기 위치를 찾아가게 만든다. SLAM이 "움직이며 지도를 그리는 일"이었다면, AMCL은 "이미 있는 지도 위에서 나를 찾는 일"이다.

이 단계가 안정되어야 다음 장에서 Nav2가 목표 지점까지 정확한 경로를 만들 수 있다. 현재 위치가 틀리면 아무리 훌륭한 planner와 controller가 있어도 엉뚱한 곳에서 출발한다고 믿는다. 지도 위 위치 추정은 자율주행 로봇의 출발점이다.

지도 위에서 위치 추정하기

이번 장의 실행 구조

이번 장에서 사용할 구성은 지난 장의 SLAM 구성과 비슷해 보이지만 역할이 다르다. SLAM 노드가 빠지고, 그 자리에 map_server와 AMCL이 들어온다.

저장된 지도 YAML/PGM
        │
        ▼
   map_server ───────► /map
        │
        ▼
      AMCL ◄───────── /scan
        ▲             /odom, /tf
        │
        └────────────► /amcl_pose, /particle_cloud, map → odom TF

map_server는 저장된 지도 파일을 읽어서 ROS2 토픽으로 발행한다. 이때 발행되는 대표 토픽이 /map이고, 메시지 타입은 nav_msgs/msg/OccupancyGrid다. 점유 격자 지도는 공간을 작은 칸으로 나누고, 각 칸이 비어 있는지, 막혀 있는지, 아직 알 수 없는지를 값으로 표현한다. 지도 YAML 파일의 resolution, origin, occupied_thresh, free_thresh 같은 값은 이 이미지를 실제 공간의 좌표로 해석하는 데 사용된다.1

AMCL은 /map을 받아 이미 알고 있는 환경의 기준으로 삼는다. 여기에 로봇의 바퀴 오도메트리와 LiDAR 스캔을 결합해 현재 위치를 추정한다. Nav2 문서에서도 nav2_amcl은 만들어진 지도 위에서 로봇의 위치와 방향을 추정하는 localization 패키지이며, 보통 sensor_msgs/msg/LaserScan을 발행하는 /scan 토픽을 사용한다고 설명한다.2

이제 중요한 변화가 하나 있다. SLAM을 실행할 때는 SLAM 노드가 map → odom 변환을 만들었다. 이번에는 AMCL이 그 역할을 맡는다. 즉, 로봇의 전체 좌표계는 다음처럼 이어져야 한다.

map → odom → base_link → laser

또는 로봇 모델에 따라 다음처럼 보일 수도 있다.

map → odom → base_footprint → base_link → base_scan

프레임 이름은 로봇마다 조금씩 다르지만 핵심은 같다. map에서 로봇 몸체까지 TF가 끊기지 않고 이어져야 Nav2가 "지도 위의 현재 위치"를 사용할 수 있다.

map_server로 저장한 지도 불러오기

먼저 지난 장에서 저장한 지도 파일이 있는지 확인하자.

ls ~/ros2_ws/src/my_robot_navigation/maps

예를 들어 다음 두 파일이 있어야 한다.

indoor_lab_2026_05_14_v1.yaml
indoor_lab_2026_05_14_v1.pgm

YAML 파일을 열어 본다.

cat ~/ros2_ws/src/my_robot_navigation/maps/indoor_lab_2026_05_14_v1.yaml

대략 다음과 비슷한 내용이 보일 것이다.

image: indoor_lab_2026_05_14_v1.pgm
mode: trinary
resolution: 0.05
origin: [-7.96, -6.60, 0]
negate: 0
occupied_thresh: 0.65
free_thresh: 0.25

여기서 resolution: 0.05는 픽셀 하나가 실제 5cm라는 뜻이다. origin은 지도 이미지의 원점이 map 좌표계에서 어디에 놓이는지를 뜻한다. 쉽게 말해 지도 그림을 실제 좌표 평면 위에 올려놓을 때, 왼쪽 아래 기준점을 어디에 둘 것인지 알려 주는 값이다.

이 값이 잘못되면 지도는 보이지만 실제 환경과 어긋난다. 해상도가 틀리면 1m 복도가 2m처럼 보이거나, 반대로 너무 좁게 해석된다. 원점이 틀리면 로봇의 위치는 맞는 듯 보이지만 목표 지점과 벽 위치가 계속 어긋난다. 지도 파일은 단순한 이미지가 아니라 좌표 정보가 붙은 로봇의 세계다.

가장 간단한 방법은 Nav2의 localization launch를 사용하는 것이다. TurtleBot3 시뮬레이션처럼 Gazebo를 쓰고 있다면 use_sim_time:=true를 준다.

source /opt/ros/$ROS_DISTRO/setup.bash
source ~/ros2_ws/install/setup.bash

ros2 launch nav2_bringup localization_launch.py \
  map:=~/ros2_ws/src/my_robot_navigation/maps/indoor_lab_2026_05_14_v1.yaml \
  use_sim_time:=true

실제 로봇이라면 시뮬레이션 시간이 아니라 실제 시간을 써야 하므로 use_sim_time:=false로 둔다.

ros2 launch nav2_bringup localization_launch.py \
  map:=~/ros2_ws/src/my_robot_navigation/maps/indoor_lab_2026_05_14_v1.yaml \
  use_sim_time:=false

직접 만든 파라미터 파일을 함께 쓰고 싶다면 다음처럼 params_file을 전달한다.

ros2 launch nav2_bringup localization_launch.py \
  map:=~/ros2_ws/src/my_robot_navigation/maps/indoor_lab_2026_05_14_v1.yaml \
  use_sim_time:=true \
  params_file:=~/ros2_ws/src/my_robot_navigation/config/nav2_params.yaml

실행 후 노드 목록을 확인한다.

ros2 node list

정상이라면 적어도 다음과 비슷한 노드가 보인다.

/amcl
/map_server
/lifecycle_manager_localization

map_server와 AMCL은 Nav2의 lifecycle node다. lifecycle node는 단순히 프로세스가 떠 있다고 바로 일하는 것이 아니라, unconfigured, inactive, active 같은 상태를 가진다. localization_launch.py를 사용하면 보통 lifecycle manager가 자동으로 configure와 activate를 수행한다.

상태를 확인해 보자.

ros2 lifecycle get /map_server
ros2 lifecycle get /amcl

둘 다 active라면 지도와 위치 추정 시스템이 일을 시작할 준비가 된 것이다.

RViz에서 지도 자체를 먼저 검사하기

AMCL을 보기 전에 지도부터 확인해야 한다. 로봇 위치 추정이 이상할 때 많은 사람이 곧바로 AMCL 파라미터를 고치지만, 실제 원인은 지도 파일인 경우가 적지 않다.

RViz를 실행한다.

rviz2

또는 Nav2 기본 RViz 설정을 사용할 수 있다.

ros2 run rviz2 rviz2 -d $(ros2 pkg prefix nav2_bringup)/share/nav2_bringup/rviz/nav2_default_view.rviz

RViz의 Fixed Frame은 map으로 설정한다. 그리고 Map display를 추가해 토픽을 /map으로 둔다. 지도가 화면에 보이면 첫 번째 관문은 통과한 것이다.

터미널에서도 지도 메타데이터를 확인할 수 있다.

ros2 topic echo /map_metadata --once

출력에는 지도 너비, 높이, 해상도, 원점 정보가 들어 있다. 해상도가 YAML의 resolution과 같은지 확인한다. 예를 들어 0.05로 저장했는데 /map_metadata에서 다른 값이 보인다면 다른 지도 파일을 불러왔거나, YAML을 잘못 수정했을 수 있다.

RViz에서 지도 스케일도 눈으로 확인하자. Gazebo 월드나 실제 공간의 대략적인 크기를 알고 있다면 복도 폭, 문 폭, 방 크기가 지도에서 지나치게 크거나 작게 보이지 않아야 한다. 예를 들어 실제 문 폭이 약 0.8m인데 지도에서는 로봇 두 대가 나란히 지나갈 만큼 넓어 보인다면 해상도나 지도 자체를 의심해야 한다.

지도 방향도 중요하다. 지난 장에서 만든 지도와 Gazebo 또는 실제 환경의 방향을 머릿속으로 비교해 보자. 지도 위쪽이 실제 북쪽일 필요는 없다. 그러나 로봇이 바라보는 방향과 RViz에서 지정하는 방향이 일관되어야 한다. AMCL은 지도 위에서 방향, 즉 yaw까지 추정한다. 초기 방향을 180도 반대로 주면 같은 위치에 있어도 LiDAR 스캔이 벽과 맞지 않아 수렴이 늦어질 수 있다.

AMCL은 여러 개의 "가능한 나"를 시험한다

AMCL은 Adaptive Monte Carlo Localization의 약자다. 말은 길지만 핵심 아이디어는 재미있다. 로봇이 자신의 위치를 하나로 단정하지 않고, 여러 개의 후보를 동시에 들고 간다.

이 후보 하나하나를 particle, 즉 입자라고 부른다. 각 입자는 "로봇이 이 위치와 방향에 있을 수도 있다"라는 가설이다. 어떤 입자는 복도 입구를 가리키고, 어떤 입자는 방 안쪽을 가리키고, 어떤 입자는 아예 엉뚱한 벽 근처를 가리킬 수 있다. 처음에는 여러 가설이 흩어져 있다가, 로봇이 움직이고 LiDAR가 주변을 관측할수록 그중 가능성이 높은 가설만 살아남는다.

공식 nav2_amcl 문서에서도 AMCL은 알려진 2D 지도 위에서 2D 레이저 스캐너를 사용해 로봇의 위치와 방향을 추정하는 확률적 localization 모듈이며, adaptive 또는 KLD-sampling 기반 Monte Carlo localization을 구현한다고 설명한다.3

동작 흐름은 크게 두 단계로 볼 수 있다.

먼저 오도메트리로 예측한다. 로봇 바퀴가 앞으로 20cm 굴렀다면 각 입자도 대략 앞으로 20cm 이동했다고 가정한다. 하지만 바퀴는 미끄러질 수 있고, 바닥은 완벽하지 않으며, 회전도 조금씩 틀어진다. 그래서 AMCL은 오도메트리 이동에 약간의 불확실성을 섞는다. "정확히 20cm"가 아니라 "대략 20cm 근처"로 여러 입자가 퍼지는 것이다.

다음으로 LiDAR 스캔으로 평가한다. 어떤 입자가 정말 로봇의 위치라면, 그 위치에서 본 LiDAR 스캔은 지도에 그려진 벽과 잘 맞아야 한다. 반대로 입자가 엉뚱한 곳에 있다면 LiDAR가 본 벽과 지도상의 벽이 맞지 않는다. AMCL은 이런 관측 일치도를 바탕으로 각 입자에 가중치를 부여한다. 잘 맞는 입자는 높은 가중치를 얻고, 맞지 않는 입자는 낮은 가중치를 얻는다.

이 과정을 반복하면 입자 구름이 점점 실제 위치 주변으로 모인다. 마치 안개가 걷히며 로봇의 위치가 드러나는 것처럼 보인다.

AMCL 파라미터의 최소 골격

AMCL에는 많은 파라미터가 있지만, 처음부터 모두 외울 필요는 없다. 우선 프레임 이름, 토픽 이름, 입자 수, 오도메트리 모델 정도를 정확히 맞추는 것이 중요하다. Nav2 AMCL 설정 문서에는 base_frame_id, odom_frame_id, global_frame_id, scan_topic, map_topic, min_particles, max_particles, robot_model_type, tf_broadcast 같은 주요 파라미터가 정리되어 있다.4

예를 들어 차동 구동 TurtleBot 계열 로봇이라면 다음과 같은 구성이 출발점이 될 수 있다.

amcl:
  ros__parameters:
    use_sim_time: true

    global_frame_id: "map"
    odom_frame_id: "odom"
    base_frame_id: "base_footprint"

    scan_topic: "scan"
    map_topic: "map"
    tf_broadcast: true

    robot_model_type: "nav2_amcl::DifferentialMotionModel"

    min_particles: 500
    max_particles: 2000

    update_min_d: 0.25
    update_min_a: 0.2

    laser_model_type: "likelihood_field"
    max_beams: 60
    laser_min_range: -1.0
    laser_max_range: 100.0

    set_initial_pose: false

여기서 가장 자주 틀리는 값은 base_frame_id다. 어떤 로봇은 base_link를 기준 프레임으로 쓰고, 어떤 로봇은 바닥 투영 프레임인 base_footprint를 쓴다. 실제 TF 트리에 없는 이름을 넣으면 AMCL은 로봇의 오도메트리 자세를 제대로 얻지 못한다.

먼저 실제 프레임을 확인하자.

ros2 run tf2_tools view_frames

또는 다음 명령으로 직접 변환을 확인한다.

ros2 run tf2_ros tf2_echo odom base_link

만약 실패하고 base_footprint는 된다면 AMCL 설정도 그에 맞춰야 한다.

ros2 run tf2_ros tf2_echo odom base_footprint

scan_topic도 마찬가지다. 기본값은 보통 scan 또는 /scan이지만, 네임스페이스를 쓰는 로봇에서는 /robot1/scan처럼 달라질 수 있다. AMCL이 구독하는 토픽과 실제 LiDAR가 발행하는 토픽이 다르면 위치 추정은 시작조차 할 수 없다.

ros2 topic list | grep scan
ros2 topic hz /scan

AMCL 노드가 무엇을 구독하고 발행하는지도 확인한다.

ros2 node info /amcl

일반적으로 AMCL은 /map, /scan, /initialpose, /tf 관련 데이터를 받아 위치 추정을 수행하고, /amcl_pose, /particle_cloud, /tf 등을 내보낸다. /tf에는 특히 map → odom 변환이 포함되며, 이 변환이 Nav2 전체 좌표계의 중요한 연결부가 된다.5

2D Pose Estimate로 로봇에게 힌트 주기

AMCL은 확률적으로 위치를 찾지만, 무작정 아무 데서나 시작하게 두면 시간이 오래 걸린다. 그래서 보통 RViz의 2D Pose Estimate 도구로 초기 위치를 알려 준다.

RViz 상단 도구 모음에서 2D Pose Estimate를 클릭한다. 지도 위에서 로봇이 실제로 있는 위치를 클릭하고, 마우스를 드래그해 로봇이 바라보는 방향을 지정한다. 이 조작은 /initialpose 토픽으로 geometry_msgs/msg/PoseWithCovarianceStamped 메시지를 발행한다.

이때 위치보다 방향이 더 중요할 때도 많다. 복도 중앙에서 180도 반대 방향을 지정하면 LiDAR 스캔이 지도와 잘 맞지 않는다. 로봇은 "내가 여기 있는 것 같긴 한데, 벽이 왜 반대로 보이지?" 하고 혼란스러워한다. 실제 로봇 앞쪽이 지도에서 어느 방향인지 천천히 확인한 뒤 화살표 방향을 지정하자.

초기 위치를 지정하면 RViz에 입자 구름이 나타난다. PoseArray display를 추가하고 토픽을 /particle_cloud로 설정한다. Nav2 기본 RViz 설정에서는 이미 표시 항목이 준비되어 있을 수도 있다.

처음에는 입자들이 로봇 주변에 넓게 퍼져 보인다. 이것은 실패가 아니라 정상적인 불확실성이다. 로봇을 아주 조금 움직이거나 제자리에서 천천히 회전하면 AMCL이 새로운 LiDAR 관측을 얻는다. 관측이 지도와 잘 맞으면 입자 구름이 점점 좁아진다.

터미널에서는 추정 pose를 확인할 수 있다.

ros2 topic echo /amcl_pose --once

이 메시지에는 위치와 방향뿐 아니라 covariance, 즉 불확실성도 함께 들어 있다. covariance를 직관적으로 말하면 "AMCL이 자기 추정을 얼마나 불안해하는가"에 가깝다. 수렴이 잘되면 추정 위치가 안정되고, RViz에서 입자 구름도 좁아진다.

수렴이 잘되었는지 판단하는 법

AMCL이 정말 잘 수렴했는지는 숫자 하나만 보고 판단하기 어렵다. RViz에서 여러 표시를 함께 봐야 한다.

먼저 /particle_cloud를 본다. 입자들이 로봇 주변에 적당히 모여 있어야 한다. 완전히 점 하나처럼 사라질 필요는 없다. 실제 센서와 오도메트리에는 항상 오차가 있으므로 약간의 퍼짐은 자연스럽다. 하지만 입자들이 지도 곳곳에 흩어져 있거나, 로봇이 움직일 때마다 갑자기 다른 복도로 순간이동한다면 아직 안정되지 않은 것이다.

다음으로 /scan을 지도 위에 겹쳐 본다. LaserScan display를 추가하고 토픽을 /scan으로 설정한다. 스캔 점들이 지도 벽 위에 잘 얹히면 좋은 신호다. 로봇 앞의 벽이 지도에서도 앞에 있고, 오른쪽 벽이 지도에서도 오른쪽에 있어야 한다. 스캔이 벽과 평행하지만 전체적으로 밀려 있다면 초기 위치나 map → odom 추정이 어긋났을 수 있다. 스캔이 회전되어 보이면 초기 방향이나 센서 TF를 의심해야 한다.

또 하나는 /amcl_pose의 움직임이다. 로봇이 천천히 움직일 때 추정 pose도 부드럽게 따라와야 한다. 갑자기 1m 이상 튀거나 방향이 순간적으로 크게 바뀌면 AMCL이 서로 다른 위치 가설 사이에서 흔들리고 있을 가능성이 있다.

마지막으로 실제 위치와 RViz 위치를 비교한다. 시뮬레이션이라면 Gazebo 화면과 RViz를 나란히 본다. 실제 로봇이라면 바닥의 모서리, 문 입구, 책상 위치와 지도상의 위치를 비교한다. 로봇이 실제로 복도 중앙에 있는데 RViz에서는 벽 가까이에 붙어 있다면 아직 신뢰하면 안 된다.

수렴을 돕는 가장 좋은 방법은 로봇을 조금 움직여 더 많은 관측을 주는 것이다. 제자리에서 천천히 회전하거나, 30cm 정도 전진한 뒤 다시 멈추고, 주변 벽과 모서리를 충분히 보게 한다. 단, 빠르게 돌거나 긴 거리를 급하게 이동하면 오히려 오도메트리 오차와 스캔 불일치가 커질 수 있다.

map → odom 변환 확인하기

AMCL이 안정적으로 위치를 추정하면 map → odom 변환을 발행한다. 이 변환은 매우 중요하다. 오도메트리는 짧은 시간 동안 로봇의 움직임을 부드럽게 추정하지만, 시간이 지날수록 오차가 누적된다. AMCL은 지도와 LiDAR를 이용해 그 누적 오차를 보정하고, 그 결과를 map → odom 변환으로 표현한다.

다음 명령으로 확인한다.

ros2 run tf2_ros tf2_echo map odom

값이 계속 출력되면 map과 odom이 연결되어 있다는 뜻이다. 이제 odom → base_link 또는 odom → base_footprint도 확인한다.

ros2 run tf2_ros tf2_echo odom base_link

로봇 모델에 따라 다음이 맞을 수도 있다.

ros2 run tf2_ros tf2_echo odom base_footprint

LiDAR 프레임도 확인한다.

ros2 run tf2_ros tf2_echo base_link base_scan

또는 다음처럼 로봇에 맞는 센서 프레임 이름을 사용한다.

ros2 run tf2_ros tf2_echo base_link lidar_link

전체 TF 트리를 파일로 보고 싶다면 다시 view_frames를 사용한다.

ros2 run tf2_tools view_frames

생성된 PDF에서 다음 연결이 끊기지 않아야 한다.

map → odom → base_link → laser

여기서 map → odom은 AMCL이 담당한다. odom → base_link는 로봇의 오도메트리 또는 시뮬레이션 구동계가 담당한다. base_link → laser는 URDF, xacro, robot_state_publisher, 또는 static transform이 담당한다.

이 셋 중 하나라도 끊기면 Nav2는 로봇의 현재 위치를 사용할 수 없다. 다음 장에서 목표 지점을 찍었을 때 경로가 나오지 않거나, 로봇이 이상한 방향으로 회전하거나, costmap이 로봇을 따라오지 않는 문제는 대부분 이 TF 연결에서 시작된다.

초기 위치를 잘못 찍었을 때

초기 위치를 잘못 찍는 것은 흔한 일이다. 특히 지도와 실제 환경이 비슷하게 생긴 복도에서는 한 칸 옆 복도를 찍어도 처음에는 그럴듯해 보일 수 있다. 문제는 로봇이 움직이면서 나타난다. LiDAR 스캔이 지도 벽과 맞지 않고, 입자 구름이 넓게 퍼지거나, /amcl_pose가 갑자기 다른 곳으로 이동한다.

이럴 때는 당황하지 말고 다시 2D Pose Estimate를 지정한다. 로봇을 멈춘 상태에서 실제 위치를 확인하고, 지도에서 같은 위치를 클릭한다. 방향 화살표를 가능한 한 정확히 맞춘다. 그리고 로봇을 천천히 회전시켜 LiDAR가 주변 구조를 충분히 보게 한다.

초기 위치를 자동으로 주고 싶다면 AMCL의 set_initial_pose와 initial_pose 파라미터를 사용할 수도 있다. 예를 들어 로봇이 항상 같은 충전 스테이션에서 시작한다면 다음과 같이 시작 pose를 설정할 수 있다.

amcl:
  ros__parameters:
    set_initial_pose: true
    initial_pose:
      x: 0.0
      y: 0.0
      z: 0.0
      yaw: 0.0

다만 처음 실습할 때는 RViz의 2D Pose Estimate를 권한다. 손으로 찍어 보아야 지도 좌표, 로봇 방향, particle cloud의 의미가 눈에 들어온다.

반복되는 복도는 로봇도 헷갈린다

AMCL은 마법이 아니다. 지도가 아무리 있어도 환경이 너무 비슷하게 반복되면 위치 추정이 어려워진다. 긴 복도에 똑같은 문이 일정 간격으로 반복된다면, 로봇은 "내가 세 번째 문 앞인지 네 번째 문 앞인지" 헷갈릴 수 있다.

이런 환경에서는 particle cloud가 한 곳으로 확실히 모이지 않고, 복도 여러 지점에 희미하게 나타날 수 있다. 또는 한동안 맞게 보이다가 특정 구간에서 pose가 튀기도 한다. AMCL 입장에서는 여러 위치 가설이 모두 그럴듯하기 때문이다.

해결 방법은 관측의 구분력을 높이는 것이다. 로봇을 복도 중앙에서 직진만 시키지 말고, 모서리나 교차로, 방 입구처럼 특징이 많은 곳을 보게 한다. 문 안쪽 벽 일부를 관측하거나, 복도 끝에서 천천히 회전해 좌우 구조를 확인하게 하는 것도 도움이 된다.

지도 작성 단계에서도 반복 구조는 주의해야 한다. SLAM 지도가 흐릿하거나 벽이 이중으로 그려져 있으면 AMCL이 더 헷갈린다. 위치 추정이 계속 불안정하다면 AMCL 파라미터를 무리하게 바꾸기 전에 지도 품질부터 다시 확인하자.

LiDAR 데이터가 부족하거나 왜곡될 때

AMCL은 LiDAR 스캔을 중요한 관측으로 사용한다. 따라서 /scan이 부정확하면 위치 추정도 흔들린다. 유리벽, 거울, 검은색 흡광 재질, 낮은 장애물, 사람의 다리처럼 지도에 없거나 센서가 잘못 보는 물체는 AMCL을 혼란스럽게 만든다.

먼저 LiDAR 토픽이 정상인지 확인한다.

ros2 topic hz /scan
ros2 topic echo /scan --once

RViz에서 LaserScan을 켜고 스캔 점들이 실제 벽 모양과 비슷한지 본다. 점들이 특정 방향에서 계속 사라지거나, 지나치게 짧은 거리로 튀거나, 센서 앞에 아무것도 없는데 장애물처럼 찍힌다면 센서 자체나 시뮬레이션 bridge, 필터 설정을 확인해야 한다.

동적 장애물도 문제다. 사람이 로봇 앞을 계속 지나가면 AMCL은 지도에 없는 벽이 생겼다고 느낄 수 있다. 잠깐 지나가는 사람 한두 명은 큰 문제가 아닐 수 있지만, LiDAR 스캔의 상당 부분이 움직이는 물체로 채워지면 입자의 가중치 계산이 흔들린다.

실습할 때는 가능한 한 환경을 고정하자. 지도 작성 때 없던 박스를 치우거나, 의자를 옮기거나, 문을 열고 닫는 것도 영향을 준다. AMCL은 "저장된 지도와 현재 관측이 비슷하다"는 가정 위에서 작동한다. 세상이 바뀌면 로봇의 믿음도 흔들린다.

지도 원점, 해상도, TF가 틀렸을 때의 증상

AMCL 문제는 겉으로 비슷해 보여도 원인이 다르다. 증상을 보고 원인을 좁혀 가야 한다.

지도 해상도가 틀리면 LiDAR 스캔이 지도 벽과 비율부터 맞지 않는다. 스캔 모양은 비슷하지만 지도보다 크거나 작게 겹친다. 이 경우 AMCL 파라미터를 바꿔도 해결되지 않는다. YAML의 resolution과 실제 저장 당시 해상도를 확인해야 한다.

지도 원점이 틀리면 위치 전체가 일정하게 밀려 보일 수 있다. 특히 지도 이미지를 편집한 뒤 YAML의 origin을 그대로 두었거나, 이미지 파일만 다른 것으로 교체했을 때 이런 문제가 생긴다. 지도 이미지를 잘라냈다면 원점도 함께 바뀌어야 한다.

센서 TF가 틀리면 LaserScan이 로봇 기준에서 잘못 배치된다. 예를 들어 LiDAR가 실제로는 로봇 중심보다 10cm 앞에 있는데 URDF에서는 중심에 있다고 되어 있으면, 스캔과 벽이 계속 조금씩 어긋난다. LiDAR가 좌우 반대로 설정되었거나 yaw가 틀리면 스캔 전체가 회전되어 보인다.

오도메트리 TF가 불안정하면 로봇을 조금 움직였을 때 particle cloud가 과하게 퍼지거나 pose가 흔들린다. 바퀴 반지름, wheel separation, diff drive controller 설정이 틀린 경우 실제 이동과 /odom 이동이 달라진다. 시뮬레이션에서는 물리 파라미터나 플러그인 설정도 원인이 될 수 있다.

결국 순서는 같다. 먼저 지도, 그다음 센서, 그다음 TF, 마지막으로 AMCL 파라미터다. 파라미터 튜닝은 진단의 출발점이 아니라 마지막 단계다.

위치 추정 문제 해결 체크리스트

AMCL이 잘 수렴하지 않을 때는 다음 순서로 확인하자.

확인 항목명령 또는 관찰정상일 때
지도 발행ros2 topic echo /map_metadata --once해상도와 크기가 예상과 일치한다
LiDAR 입력ros2 topic hz /scan일정한 주기로 LaserScan이 들어온다
오도메트리ros2 topic echo /odom --once로봇 이동에 따라 pose가 변한다
AMCL 상태ros2 lifecycle get /amclactive 상태다
입자 구름RViz /particle_cloud초기에는 퍼지고 관측 후 좁아진다
추정 poseros2 topic echo /amcl_pose --oncepose가 부드럽게 변한다
TF 연결tf2_echo map odomAMCL 실행 후 변환이 출력된다
센서 TFRViz /scan 겹침LaserScan이 지도 벽과 잘 맞는다

시뮬레이션에서는 시간 설정도 반드시 확인한다.

ros2 topic echo /clock --once
ros2 param get /amcl use_sim_time
ros2 param get /map_server use_sim_time

Gazebo를 쓰고 있다면 /clock이 발행되어야 하고, localization 관련 노드의 use_sim_time이 true여야 한다. 실제 로봇에서는 반대로 false가 일반적이다.

네임스페이스도 자주 놓치는 부분이다. 다중 로봇이나 TurtleBot4 환경에서는 /scan이 아니라 /robot1/scan, /map이 아니라 /robot1/map일 수 있다. 이때는 AMCL의 scan_topic, RViz display의 topic, TF frame prefix를 모두 실제 이름에 맞춰야 한다.

위치 추정이 안정되면 Nav2의 출발선에 선 것이다

이번 장에서 우리는 저장된 지도를 다시 불러오고, AMCL을 이용해 로봇의 현재 위치를 추정했다. map_server는 YAML과 PGM 또는 PNG 파일을 읽어 /map 점유 격자 지도로 발행했다. 우리는 RViz에서 해상도, 원점, 방향, 스케일이 실제 환경과 맞는지 확인했다.

AMCL은 지도 위에서 여러 위치 가설을 particle로 유지했다. 오도메트리로 입자들을 예측하고, LiDAR 스캔이 지도 벽과 얼마나 잘 맞는지에 따라 가중치를 갱신했다. 관측이 쌓이자 입자 구름은 실제 위치 주변으로 좁아졌고, /amcl_pose는 로봇의 추정 pose를 보여 주었다.

또한 map → odom → base_link → laser로 이어지는 TF 트리를 검증했다. 이 연결은 다음 장에서 결정적인 역할을 한다. Nav2의 planner는 지도 위 현재 위치에서 목표 위치까지 경로를 만들고, controller는 그 경로를 따라 로봇을 움직인다. 그런데 현재 위치가 흔들리면 경로도 흔들리고, 경로가 흔들리면 로봇 행동도 불안정해진다.

이제 로봇은 지도 위에서 자신이 어디에 있는지 안다. 다음 장에서는 RViz에서 목표 지점을 찍고, Nav2가 실제로 경로를 만들고 로봇을 움직이는 과정을 진행한다. 지금까지는 로봇에게 세계와 자기 위치를 알려 주었다. 이제 드디어 로봇에게 목적지를 말할 차례다.

참고

1시뮬레이터 활용 및 실습 (심화)

2Mapping and Localization — Nav2 documentation

3nav2_amcl — ROS 2 package documentation

4AMCL — Nav2 configuration guide

5[Nav2][amcl] AMCL패키지에서 어떻게 localization되는지 살펴보기

책 목록

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

전체 목차 보기

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

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

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

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

참여하기