AEGLOSLAB ROS 2 Jazzy 대전

제8부 항법

8.3 위치추정 — AMCL 튜닝

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

지도가 있어도 로봇이 그 지도의 어디에 있는지 모르면 항법이 불가능하다. AMCL(Adaptive Monte Carlo Localization)이 그 답이며, ROS 생태계에서 가장 오래 쓰인 측위 알고리즘이다.

AMCL의 실무적 어려움은 잘 될 때는 아무 문제가 없다가, 안 될 때는 원인이 여러 층에 걸쳐 있다는 것이다. 오도메트리·지도·센서·환경 어느 것이든 원인이 될 수 있고, 파라미터가 20개가 넘는다.

이 장은 파티클 필터의 동작을 정성적 수준에서 세우고, 그 위에서 파라미터의 의미와 튜닝 순서를 다룬다. 필터 이론은 자매서 robotics 제9부(RB-195~RB-241)·kalmann이 다룬다(집필 상태는 A.5.9).

배우는 것:

  • 동작 원리 — 파티클이 무엇을 하는가.
  • map → odom 출력 — REP 105와의 연결(5.1.7).
  • 운동 모델 — 알파 파라미터의 의미.
  • 관측 모델 — likelihood_field vs beam.
  • 파티클 관리 — 개수·재표본화·KLD.
  • 초기화와 전역 측위 — 어디서 시작하는가.
  • 발산과 복구 — 잃었을 때.
  • 튜닝 순서와 검증.

선수 지식: 8.2(지도), 7.6(오도메트리), 7.5(라이다), 5.1.7(REP 105), 8.1(Nav2 구조).

정직: AMCL 파라미터 이름·기본값은 배포판에 따라 다를 수 있다(확인 필요). 아래는 널리 쓰이는 항목이며 정확한 목록은 Nav2 문서가 정본이다. 튜닝 수치는 환경·로봇 의존이므로 출발점으로만 제시한다(3.6.1 방침). 이 환경에서 검증하지 않았다(목차 3). 146차(125번째 회차)가 ros:jazzy-ros-base에 nav2_amcl·nav2_map_server·nav2_lifecycle_manager를 설치해 실제로 AMCL을 기동하고, 이 장의 핵심 주장 세 가지를 실행 확인했다 — map → odom 발행, 정지 시 갱신 없음, 전역 측위 서비스. 8.1·8.2·8.4~8.6·9.1·9.2는 이미 이전 회차들이 설치된 패키지의 파일·XML·이름을 실물과 대조하는 방식으로 실행 확인해 뒀다 — 이번 회차는 그와 달리 지도·합성 센서 데이터를 갖춘 노드를 실제로 lifecycle 기동해 런타임 동작(TF·토픽 발행 패턴)을 관찰한 첫 사례다. 아래 각 절을 보라.

8.3.2 동작 원리 — 정성적 이해

AMCL은 "로봇이 있을 법한 위치 후보 수천 개"를 유지하고, 관측으로 걸러낸다.

파티클 집합 (각각 하나의 자세 가설) ① 예측 오도메트리만큼 이동 + 노이즈 ② 갱신 각 파티클에서 본 스캔이 실제 스캔과 얼마나 맞는가 ③ 가중치 부여 ④ 재표본화 가중치 높은 파티클을 복제 추정 자세 = 가중 평균
단계하는 일관련 파라미터
① 예측오도메트리로 파티클 이동 + 불확실성 추가alpha1~5
② 갱신스캔과 지도 비교로 각 파티클 평가laser_model_type·z_*
③ 가중치맞을수록 높은 가중치—
④ 재표본화좋은 파티클을 남기고 부적합한 것 제거resample_interval·min/max_particles

핵심 통찰: 파티클이 퍼져 있으면 "모른다", 모여 있으면 "확신한다" 는 뜻이다. RViz2에서 파티클 구름을 보는 것이 AMCL 진단의 기본이다.

중요한 성질:

성질함의
오도메트리가 있어야 예측이 된다오도메트리 품질이 낮으면 AMCL도 부정확하다
지도와 스캔이 맞아야 갱신이 된다지도가 틀리면 잘못된 곳으로 수렴
확률적실행마다 결과가 미세하게 다르다
한 번 잃으면 스스로 못 찾을 수 있다전역 재측위가 필요(8.3.7)

8.3.3 map → odom 출력

AMCL은 map → base_link를 직접 발행하지 않는다(5.1.7).

AMCL 추정: map에서 로봇 위치 오도메트리: odom에서 로봇 위치 차이를 계산 map → odom 발행 = 오도메트리 누적 오차 보정량

$$T_{\text{map} \leftarrow \text{odom}} = T_{\text{map} \leftarrow \text{base}}^{\text{AMCL}} \cdot \left(T_{\text{odom} \leftarrow \text{base}}\right)^{-1}$$

이유: base_link의 부모가 둘이 되면 트리 제약을 위반한다(5.1.4). odom → base_link는 오도메트리가 발행하므로, AMCL은 그 위쪽을 담당한다.

결과로 나타나는 현상:

현상설명
map → odom이 점프한다재측위 시 보정이 반영됨 — 정상
odom → base_link는 연속오도메트리는 점프하지 않음
제어는 odom 기준으로점프에 영향받지 않게(5.1.11의 13번)
# 보정량 관찰 — 크면 오도메트리 오차가 크다는 뜻
ros2 run tf2_ros tf2_echo map odom

실행 확인(146차, Jazzy). nav2_map_server+nav2_amcl을 4m×4m 정사각형 방 지도로 실제로 기동해(nav2_lifecycle_manager로 configure→activate) /tf 원시 메시지를 직접 열어 봤다 — AMCL이 발행하는 항목은 정확히 frame_id: map·child_frame_id: odom 이었다(map → base_link가 아니다). ros2 run tf2_ros tf2_echo map base_link도 별도로 성공했는데, 이는 직접 간선이 아니라 map → odom → base_link 합성으로 풀린 것이다(같은 시각 tf2_echo map odom은 성공, 별도 시도한 tf2_echo가 "map" 프레임을 처음엔 못 찾다가 /tf에 실제로 발행되고 나서야 풀리는 것도 확인). 목차 3, tools/gap_harness/gap146.

견해: map → odom의 변화량이 오도메트리 품질의 지표다. 이 값이 계속 크게 변하면 오도메트리가 드리프트하고 있고, 거의 변하지 않으면 오도메트리가 좋은 것이다. 이것을 진단으로 감시하면(6.9) 타이어 마모·설정 변화를 조기에 감지할 수 있다.

8.3.4 운동 모델과 알파 파라미터

예측 단계에서 파티클에 얼마나 노이즈를 줄 것인가를 정한다.

amcl:
  ros__parameters:
    robot_model_type: "nav2_amcl::DifferentialMotionModel"
    # 또는 "nav2_amcl::OmniMotionModel"

    alpha1: 0.2      # 회전으로 인한 회전 오차
    alpha2: 0.2      # 병진으로 인한 회전 오차
    alpha3: 0.2      # 병진으로 인한 병진 오차
    alpha4: 0.2      # 회전으로 인한 병진 오차
    alpha5: 0.2      # (전방향 모델 전용) 측면 이동
파라미터의미크게 하면
alpha1회전 → 회전 오차회전 시 파티클이 각도로 퍼짐
alpha2병진 → 회전 오차직진 시에도 각도 불확실
alpha3병진 → 병진 오차직진 시 거리로 퍼짐
alpha4회전 → 병진 오차회전 시 위치 불확실
alpha5측면 이동 오차전방향 로봇

설정 원칙:

오도메트리 상태알파 값
정확(폐루프 오차 작음)작게(0.05~0.1)
보통0.1~0.3
부정확(미끄러짐 많음)크게(0.3~0.5)

견해: 알파를 크게 하면 "안전"해 보이지만 대가가 있다. 파티클이 넓게 퍼지면 관측이 그것을 좁혀야 하는데, 특징이 적은 환경에서는 못 좁힌다. 결과적으로 추정이 흔들린다. 7.6.7의 폐루프 시험으로 실제 오도메트리 오차를 측정하고, 그에 맞춰 설정하는 것이 표준적 방법이다.

함정: 알파를 너무 작게 하면 파티클이 실제 오차를 따라가지 못해 발산한다. 로봇이 미끄러졌는데 파티클은 그럴 리 없다고 믿으면, 모든 파티클이 틀린 곳에 있게 된다.

함정: robot_model_type이 로봇 기구학과 맞아야 한다. 차동 구동 로봇에 전방향 모델을 쓰면 불필요한 측면 불확실성이 생기고, 반대면 실제 측면 이동을 반영하지 못한다.

8.3.5 관측 모델

amcl:
  ros__parameters:
    laser_model_type: "likelihood_field"     # 또는 "beam"

    laser_min_range: -1.0        # -1이면 센서 값 사용
    laser_max_range: 100.0
    max_beams: 60                # ★ 스캔에서 사용할 광선 수

    # likelihood_field 모델
    z_hit: 0.5                   # 정상 측정 비중
    z_rand: 0.5                  # 무작위 노이즈 비중
    sigma_hit: 0.2               # 측정 노이즈 표준편차(m)
    laser_likelihood_max_dist: 2.0

    # beam 모델 추가 항
    z_short: 0.05                # 예상보다 짧은 측정(동적 장애물)
    z_max: 0.05                  # 최대 거리 반환
    lambda_short: 0.1

두 모델의 차이:

축likelihood_fieldbeam
계산빠름(사전 계산된 거리장)느림(광선 추적)
동적 장애물상대적으로 강건z_short로 모델링
정확도대체로 충분이론적으로 정밀
튜닝간단파라미터 많음
일반적 선택✅ 기본특수한 경우

견해: likelihood_field가 대부분의 경우 맞다. 빠르고 튜닝이 단순하며, 실내 환경에서 충분히 정확하다. beam 모델은 계산이 비용이 크고 파라미터가 많아 얻는 것에 비해 부담이 크다.

max_beams가 중요하다:

값효과
작다(30~60)빠름, 대개 충분
크다(180+)정확할 수 있으나 계산 증가

라이다가 720점을 주더라도 전부 쓸 필요가 없다. 60개 정도로 균등 샘플링해도 측위에는 충분한 경우가 많다.

함정: z_hit + z_rand(likelihood_field)나 네 항의 합이 1에 가까워야 한다(확인 필요: 정규화 여부). 임의로 정하면 가중치 계산이 왜곡된다.

함정: sigma_hit이 너무 작으면 조금만 어긋나도 가중치가 0에 가까워져 파티클이 모두 소멸한다. 지도가 완벽하지 않다는 것을 감안해 0.1~0.3 정도로 둔다.

8.3.6 파티클 관리

amcl:
  ros__parameters:
    min_particles: 500
    max_particles: 2000

    # KLD 표본화 — 불확실성에 따라 파티클 수 자동 조절
    pf_err: 0.05
    pf_z: 0.99

    # 재표본화
    resample_interval: 1         # 몇 번의 갱신마다 재표본화
    recovery_alpha_slow: 0.001   # 0이면 비활성
    recovery_alpha_fast: 0.1

    # 갱신 조건 — 이만큼 움직여야 필터 갱신
    update_min_d: 0.25           # m
    update_min_a: 0.2            # rad

    transform_tolerance: 1.0     # tf 발행 유효 기간

적응적(Adaptive) 파티클 수:

AMCL의 "A"가 이것이다. 불확실할 때는 파티클을 많이, 확신할 때는 적게 써서 계산을 절약한다.

상황파티클 수
초기·전역 측위max_particles에 가까움
안정적 추적min_particles에 가까움
재측위 중증가

update_min_d·update_min_a가 갱신 빈도를 정한다:

값효과
작다자주 갱신 — 정확하지만 부하가 크다
크다드물게 갱신 — 경량이지만 반응 느림

함정: 로봇이 정지해 있으면 AMCL이 갱신하지 않는다. update_min_d를 넘지 않으므로 필터가 실행되지 않고, 이것은 의도된 동작이다(정지 중에는 새 정보가 없다). 다만 정지 상태에서 다른 물체가 움직여도 반응하지 않는다는 뜻이므로, "왜 안 움직이지" 하고 시행착오를 겪지 않도록 알아 둔다.

실행 확인(146차, Jazzy). odom → base_link를 고정한 채(static_transform_publisher, 이동 없음) /scan을 5 Hz로 8초간(약 40개) 계속 입력했지만 /amcl_pose는 활성화 시점의 초기 자세 1건만 발행되고 이후 새 메시지가 0건이었다 — 스캔은 계속 들어오는데도 필터가 조용히 갱신을 건너뛴 것이다. 이어서 static_transform_publisher를 update_min_d(0.25 m)보다 큰 0.3 m 오프셋으로 교체하니(=순간 이동을 흉내), 정확히 새 /amcl_pose 1건이 곧바로 발행됐다 — 그 뒤 다시 정지 상태가 되자 갱신도 다시 멈췄다. "정지 시 갱신 없음·이동 시 갱신 재개"가 둘 다 실측으로 확인됐다. 목차 3, tools/gap_harness/gap146.

recovery_alpha_slow·_fast — 무작위 파티클 주입:

측위가 악화되면 무작위 파티클을 분산 배치해 복구를 시도한다.

설정효과
0(기본)비활성
활성화발산 시 자동 복구 시도, 다만 안정 시에도 노이즈

정직: 이 기능의 효과는 환경에 크게 의존한다(확인 필요). 켜면 발산에서 회복할 가능성이 생기지만, 정상 상태에서도 파티클이 흩어져 추정이 덜 안정될 수 있다. 기본(비활성)으로 시작하고, 발산이 반복되면 검토하라.

8.3.7 초기화와 전역 측위

AMCL은 시작 위치를 알아야 한다.

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

세 가지 초기화 방법:

방법설명적합
설정 파일고정 시작 위치도킹 스테이션에서 출발
RViz2 2D Pose Estimate사람이 지정개발·시험
전역 측위(/reinitialize_global_localization)지도 전체에 파티클 분산위치를 모를 때
# 전역 측위 요청
ros2 service call /reinitialize_global_localization std_srvs/srv/Empty

# 특정 위치로 설정 (토픽)
ros2 topic pub --once /initialpose geometry_msgs/msg/PoseWithCovarianceStamped "{...}"

실행 확인(146차, Jazzy). /reinitialize_global_localization이 정확히 이 이름·타입(std_srvs/srv/Empty)으로 존재하고 호출에 정상 응답했다. 껍데기뿐인 호출이 아니라는 것도 로그로 확인했다 — 호출 직후 AMCL 로그에 Initializing with uniform distribution → Global initialisation done!이 출력돼, 실제로 파티클을 지도 전체에 균등 재분산시킨다는 것을 보여 준다. 목차 3, tools/gap_harness/gap146.

전역 측위의 성공 조건:

조건설명
환경에 구별되는 특징대칭 환경에서는 실패
충분한 파티클max_particles가 커야
로봇이 움직여야정지 상태로는 후보를 못 줄인다
지도가 정확스캔과 맞아야

견해: 전역 측위 후에는 로봇을 움직여야 수렴한다. 제자리 회전이 효과적이다 — 다양한 방향의 스캔이 후보를 빠르게 걸러낸다. 자동 복구 절차에 "제자리 회전"을 넣는 것이 실무적으로 유용하다(8.7).

함정: 대칭적이거나 반복적인 환경(긴 복도·창고 통로)에서 전역 측위는 실패하거나 틀린 곳으로 수렴한다. 이런 환경에서는 초기 위치를 알려주는 다른 수단(도킹 스테이션·QR 마커·RFID)이 필요하다.

8.3.8 발산과 복구

"측위를 잃었다"는 상황을 감지하고 대응해야 한다.

발산 원인:

원인설명
미끄러짐·충돌오도메트리가 크게 틀림
로봇을 들어 옮김(납치)오도메트리와 무관한 이동
환경 변화지도와 스캔 불일치(8.2.9)
동적 장애물 다수사람이 둘러쌈
특징 부족긴 복도·빈 공간
잘못된 알파파티클이 실제 오차를 못 따라감
지도 오류애초에 틀린 지도

감지 방법:

지표해석
파티클 분산 증가확신 감소
pose.covariance 증가동일
스캔과 지도 불일치(RViz2)시각적으로 명확
map → odom 급변큰 보정 발생
# 개념: 공분산으로 발산 감지 → 진단(6.9)
def on_amcl_pose(msg):
    var_x = msg.pose.covariance[0]
    var_y = msg.pose.covariance[7]
    var_yaw = msg.pose.covariance[35]
    if var_x > THRESH or var_y > THRESH or var_yaw > YAW_THRESH:
        # 측위 불확실 — 경보·복구 시도
        ...

견해: 측위 신뢰도를 진단으로 내보내라(6.9). "로봇이 이상한 곳으로 간다"의 원인이 측위 실패인 경우가 많은데, 그것을 감지할 수단이 없으면 항법 문제로 오해한다. 공분산 임계를 넘으면 WARN, 크게 넘으면 ERROR로 두면 유용하다.

복구 전략:

아니오 아니오 측위 불확실 감지 1. 제자리 회전 (스캔 다양성 확보) 회복? 2. 전역 재측위 3. 이동하며 수렴 대기 회복? 4. 정지 · 사람 개입 요청

견해: 자동 복구에 한계를 두라. 무한정 재측위를 시도하며 이동하면 위험하다. N번 실패하면 정지하고 알리는 것이 안전하며, 특히 사람이 있는 환경에서 그렇다(8.7).

8.3.9 튜닝 순서

1. 지도 검증 8.2.8 2. 오도메트리 검증 7.6.7 3. 스캔 검증 7.5.9 4. 알파 설정 (폐루프 오차 기반) 5. 관측 모델 기본값으로 시험 6. 파티클 수 조정 7. 갱신 빈도 조정 8. 장시간·다양한 경로 검증

견해: 1~3단계가 튜닝의 대부분이다. AMCL 파라미터로 해결되는 문제는 생각보다 적고, 대개 입력이 문제다. 8.2.8과 같은 결론 — "원인은 대개 알고리즘 밖에 있다."

검증 방법:

시험확인
RViz2에서 스캔·지도 정렬이동 중에도 겹치는가
파티클 구름 관찰수렴하는가·퍼지는가
알려진 지점 왕복같은 위치로 돌아오는가
장시간 주행서서히 드리프트하는가
다양한 경로특정 구역에서만 부정확한가
납치 시험로봇을 옮기면 회복하는가

견해: "RViz2에서 스캔이 지도와 정렬되는가"가 가장 직관적이고 확실한 검증이다. 로봇을 주행시키며 스캔 점들이 지도의 벽 위에 계속 겹쳐 있으면 측위가 잘 되는 것이고, 어긋나거나 미끄러지면 문제가 있다. 이것을 보지 않고 파라미터를 조정하는 것은 눈 감고 튜닝하는 것과 같다.

8.3.10 대안

AMCL이 유일한 선택은 아니다.

방법특징적합
AMCL검증됨·경량·2D일반적 실내
SLAM Toolbox localization포즈 그래프 기반·정확정밀 요구(8.2.6)
3D 측위(NDT·ICP 계열)3D 라이다실외·복잡 환경
시각 측위카메라 기반특징 풍부 환경
마커 기반(AprilTag 등)절대 위치·간단구조화된 환경
GNSS·RTK실외 절대실외(7.6.6)

견해: 실내 2D 환경에서 AMCL은 여전히 좋은 기본값이다. 경량이고 검증됐고 튜닝 지식이 축적되어 있다. SLAM Toolbox의 localization 모드는 더 정확할 수 있지만 부하가 크고, 필요가 명확할 때 검토한다.

견해: 마커 기반 보조가 실무에서 매우 유용하다. 도킹 스테이션·작업 위치에 마커를 두면 그 지점에서 절대 위치를 얻어 누적 오차를 리셋할 수 있다. AMCL과 배타적이지 않으며, 초기화·복구 수단으로 결합하면 강건해진다.

8.3.11 함정 카탈로그

#함정증상대처
1오도메트리 미검증 상태로 튜닝해결 안 됨하위 검증 먼저(8.3.9)
2알파를 근거 없이 크게추정 흔들림폐루프 오차 기반
3알파를 너무 작게발산실제 오차 반영
4robot_model_type 불일치부적절한 불확실성기구학에 맞게
5sigma_hit이 너무 작음파티클 소멸0.1~0.3
6max_beams를 과도하게계산 낭비60 내외로 충분
7초기 자세 미설정측위 안 됨세 방법 중 하나
8대칭 환경에서 전역 측위 기대실패·오수렴마커·도킹 활용
9전역 측위 후 정지수렴 안 됨회전·이동 필요
10정지 중 갱신 없음을 버그로 오해시간 낭비의도된 동작
11측위 신뢰도 미감시항법 문제로 오해공분산 진단(6.9)
12무한 자동 복구위험한 배회시도 횟수 제한
13map 기준으로 지역 제어재측위 시 로봇이 튐odom 기준(5.1.11)
14RViz2 시각 검증 생략눈 감고 튜닝스캔·지도 정렬 확인

8.3.12 체크포인트

  • 파티클 필터의 네 단계를 말할 수 있는가. 파티클이 퍼져 있다·모여 있다는 각각 무슨 뜻인가.
  • AMCL이 map → base_link가 아니라 map → odom을 발행하는 이유와 계산식은?
  • map → odom의 변화량이 무엇의 지표가 되는가.
  • 알파 파라미터 다섯 개의 의미와, 설정 기준을 말할 수 있는가. 크게·작게 했을 때의 각 위험은?
  • 두 관측 모델의 차이와, 일반적으로 무엇을 쓰는가. max_beams를 크게 할 필요가 없는 이유는?
  • sigma_hit이 너무 작으면 무슨 일이 생기는가.
  • 적응적 파티클 수가 무엇을 절약하는가. update_min_d가 정지 상태에서 만드는 동작은?
  • 초기화 세 가지 방법과 각각의 적합 상황은? 전역 측위의 성공 조건 네 가지는?
  • 전역 측위 후 로봇을 움직여야 하는 이유와, 효과적인 움직임은?
  • 발산 원인 일곱 가지와 감지 지표 네 가지를 말할 수 있는가.
  • 자동 복구에 한계를 두어야 하는 이유는?
  • 튜닝 순서 여덟 단계에서 대부분을 차지하는 것은 무엇인가. 가장 직관적인 검증 방법은?
  • AMCL의 대안 여섯 가지와, 마커 기반 보조가 유용한 이유는?

다음 장 8.4은 이 측위 위에서 장애물을 표현하는 코스트맵을 다룬다. 레이어 구조, 팽창, 센서 통합, 그리고 유령 장애물 문제.