AEGLOSLAB ROS 2 Jazzy 대전

제8부 항법

8.8 Nav2 튜닝·평가·현장 디버깅

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

8.1~8.7에서 Nav2의 각 요소를 봤다. 이제 그것들을 실제 환경에서 동작하게 만드는 일이 남았다.

이것이 어려운 이유는 명확하다. 파라미터가 수백 개이고, 층이 여러 개이며, 증상 하나에 원인 후보가 여럿이다. "로봇이 안 움직인다"의 원인이 tf일 수도, 측위일 수도, 코스트맵일 수도, 컨트롤러일 수도 있다.

이 장은 그 혼란을 순서와 절차로 다룬다. 무엇부터 튜닝하고, 증상에서 어떻게 원인을 좁히며, 성능을 어떻게 측정하는가.

배우는 것:

  • 튜닝 순서 — 왜 이 순서인가.
  • 진단 플레이북 — 증상에서 원인으로.
  • RViz2 활용 — 무엇을 봐야 하는가.
  • 성능 지표 — 무엇을 측정할 것인가.
  • 기록과 재현 — bag 기반 분석.
  • 시뮬레이션 평가 — 무엇을 검증할 수 있는가.
  • 현장 시험 절차 — 안전하게 검증하기.

선수 지식: 제8부 전체(8.1~8.7), 11.3(기록 — 함께 읽으면 좋다), 11.1(현장 디버깅 플레이북에서 일반화).

정직: 이 장의 절차와 지표는 실무 관행의 정리이며 표준화된 것이 아니다. 파라미터 이름은 배포판 의존이고(확인 필요), 튜닝 값은 로봇·환경마다 다르므로 제시하지 않는다(3.6.1 방침). 이 환경에서 검증하지 않았다(목차 3).(→ 191차 확인: TurtleBot3 + nav2_loopback_sim(Jazzy 공식 경량 시뮬레이터)으로 실제 Nav2 스택을 기동하고 실제 내비게이션 목표까지 보냈다. 8.8.3.1의 진단 명령들은 문서 그대로 동작함을 확인했고, 두 가지 정직한 보완점(헤드리스 기동 시 /initialpose 타이밍 문제, ros2 control list_controllers가 이 시뮬레이션 구성엔 안 맞음)을 찾아 반영했다. 아래 각 절의 실행 확인(191차) 참고.)(→ 196차 확인: 물리 엔진 없는 loopback 방식이 아닌, 실제 Gazebo 물리 + 실제 AMCL 경로(tb3_simulation_launch.py)로도 재현했다 — 같은 /initialpose 경고가 출력되지만 headless 부트업이 191차처럼 영구 실패하지 않고 훨씬 길게(35초 이상) 재시도하며 늦게 온 초기 포즈도 정상 수용함을 확인. 8.8.3.1의 실행 확인(196차) 참고.)

8.8.2 튜닝 순서

하위 계층부터 차례로 점검한다. 이것이 이 장에서 가장 중요한 원칙이다.

1. tf 트리 5.1 · 5.5 2. 오도메트리 7.6.7 3. 센서 데이터 7.1.9 · 7.5.9 4. 지도 8.2.8 5. 측위 8.3.9 6. 코스트맵 8.4.9 7. 플래너 8.5 8. 컨트롤러 8.5.8 9. BT · 복구 8.6 10. 안전 · 속도 8.7

각 단계의 통과 기준:

#단계통과 기준
1tfview_frames로 트리 완전, 지연 정상
2오도메트리폐루프 시험 통과(7.6.7)
3센서RViz2에서 방향·거리 정확(7.1.9)
4지도왜곡 없이 닫힘(8.2.8)
5측위주행 중 스캔이 지도와 정렬(8.3.9)
6코스트맵표시·지우기 정상(8.4.9)
7플래너/plan이 합리적
8컨트롤러경로를 부드럽게 추종
9BT실패 시 복구가 동작
10안전정지 영역·속도 제한 동작

견해: "Nav2가 안 된다"의 80%는 1~5단계 문제다. 그런데 사람들은 대개 7~8단계(플래너·컨트롤러 파라미터)부터 조정한다 — 그것이 가장 눈에 보이는 층이기 때문이다. 하위 계층이 틀린 상태에서 상위 계층을 튜닝하면 해결되지 않고, 나중에 아래를 고치면 위 튜닝이 무효가 된다.

견해: 각 단계의 통과 기준을 명시적으로 확인하고 기록하라. "오도메트리 폐루프 오차 3 cm/10 m"처럼 숫자로 남기면, 나중에 성능이 악화됐을 때 무엇이 변했는지 비교할 수 있다(7.6.7의 정기 검증).

8.8.3 진단 플레이북 — 증상에서 원인으로

8.8.3.1 로봇이 전혀 움직이지 않는다

예 예 예 예 아니오 아니오 아니오 아니오 움직이지 않음 목표가 수락됐나? BT · 라이프사이클 확인 8.1.4 /plan 이 나오나? 플래너 실패 코스트맵 · 목표 도달성 /cmd_vel 이 나오나? 컨트롤러 실패 진척 검사기 · 충돌 예측 값이 0인가? collision_monitor · 속도 평활기 8.7 하드웨어 · ros2_control 6.3.8

실행 확인(191차) — 이 흐름도의 "플래너 실패" 갈래를 실제로, 의도치 않게 재현했다. 검증 초반, 지도의 실제 자유 공간을 확인하지 않고 임의의 초기 위치(맵 좌표 (0,0))를 줬더니 근처 목표(1~2m 거리)조차 계획이 안 됐다 — 로그에 "GridBased plugin failed to plan from (0.00, 0.00) to (2.00, 0.50): Failed to create plan with tolerance of: 0.500000"가 반복 출력되며 /plan이 끝내 나오지 않았고, 액션은 15회 복구 시도 끝에 ABORTED(error_code: 208)로 끝났다 — 정확히 이 흐름도의 "D: /plan이 나오나? → 아니오 → E: 플래너 실패" 갈래였다. 원인은 시뮬레이션 패키지(nav2_minimal_tb3_sim)의 spawn_tb3.launch.py가 정의한 실제 기본 스폰 위치(x_pose=-2.00, y_pose=-0.50)를 확인하지 않고 (0,0)을 임의로 골랐던 것 — 그 위치가 지도상 벽·미지 영역에 있었던 것으로 보인다. 올바른 시작 위치로 다시 시도하니 계획이 즉시 성공했고, 실제로 로봇 위치가 시간에 따라 바뀌며 목표를 향해 이동했다 — 다만 도중에 컨트롤러가 두 차례 "Failed to make progress"로 멈춰 spin·backup 복구를 5회 거쳤고, 55.1초 만에 결국 SUCCEEDED(error_code: 0)로 도착했다. 정직한 정리: 실패든 성공이든 각 갈래가 로그에 남긴 신호(플래너 경고 메시지, number_of_recoveries 증가, "Failed to make progress")가 이 흐름도가 예측하는 것과 정확히 일치했다 — 다만 실제 성공까지 매끄럽지 않았고 5번의 복구가 필요했던 것도 가감 없이 남긴다. 원본: tools/gap_harness/gap190/nav2_test/logs/(nav_goal_result.log(실패)·nav_goal_result3.log(성공)).

# 단계별 확인
ros2 action list | grep navigate
ros2 topic echo /plan --once --no-arr
ros2 topic echo /cmd_vel
ros2 topic echo /cmd_vel_smoothed        # 평활기 전후 비교
ros2 topic echo /collision_monitor_state # 안전 개입 여부
ros2 control list_controllers            # 컨트롤러 활성(6.3.6)(→ 191차 확인: 표준 TurtleBot3 시뮬레이션(`nav2_loopback_sim`)에는 이 명령이 안 맞을 수 있다 — 아래 실행 확인(191차) 참고)

견해: /cmd_vel 전후를 비교하는 것이 결정적이다. 컨트롤러는 명령을 내는데 로봇이 안 움직이면 평활기·collision_monitor·ros2_control 중 하나가 막고 있는 것이다. 토픽 체인을 따라가면 어디서 끊기는지 즉시 보인다.

실행 확인(191차) — 이 진단 체인을 실제 Nav2 스택(TurtleBot3 + nav2_loopback_sim, Jazzy 공식 경량 시뮬레이터 — 물리 엔진 없이 명령을 그대로 되돌려주는 방식)으로 처음부터 끝까지 실행했다. ros2 action list | grep navigate는 문서 그대로 /navigate_through_poses·/navigate_to_pose를 정확히 냈다. /plan·/cmd_vel·/cmd_vel_smoothed도 실제로 존재하고 목표가 활성일 때 데이터가 흘렀다. ros2 control list_controllers는 이 환경에서 곧바로 실패했다 — ros2: error: ... invalid choice: 'control'. 이 서브커맨드는 별도 패키지(ros2controlcli)가 제공하는데 nav2-bringup·nav2-minimal-tb3-sim·navigation2 어느 것도 이를 끌어오지 않았고(apt-cache policy로 "Installed: (none)" 확인), ros2 node list에 /controller_manager 자체가 없었다 — 표준 시뮬레이션 TurtleBot3에는 애초에 ros2_control 스택이 없다는 뜻이다. 이 진단 단계는 실제 하드웨어나 ros2_control 기반 로봇에는 맞지만, 이 책이 예시로 쓰는 것과 같은 순수 시뮬레이션 TurtleBot3에는 그대로 적용되지 않는다. 더 근본적인 헤드리스 운용 함정도 발견했다 — nav2_loopback_sim은 RViz2의 "2D Pose Estimate"가 발행하는 것과 같은 /initialpose 메시지를 받기 전까지는 map→odom tf를 전혀 내보내지 않는데(소스 nav2_loopback_sim/loopback_simulator.py 직접 확인), RViz2 없이 헤드리스로 실행하면 이 메시지가 영원히 안 와서 global_costmap 활성화가 약 10초 만에 영구히 실패했다(재시도 없음 — 실패 20초 뒤에 뒤늦게 /initialpose를 보내도 이미 중단된 부트업은 복구되지 않았다). 해결책: 런치와 동시에 /initialpose를 5 Hz로 반복 발행하는 프로세스를 같이 기동해야 부트업 타임아웃 안에 확실히 도착시킬 수 있었다(1회성 발행은 디스커버리 지연 때문에 타이밍을 놓쳤다). 원본: tools/gap_harness/gap190/nav2_test/(loopback_simulator.py·logs/·findings_summary.txt).(→ 196차 확인: 위 "약 10초 만에 영구히 실패·재시도 없음"은 nav2_loopback_sim 고유의 거동이었다 — 실제 Gazebo 물리 + 실제 AMCL 경로(tb3_simulation_launch.py)로 재현하니 같은 경고는 출력되지만 실패하지 않고 무한히 재시도했다. 아래 실행 확인(196차) 참고.)

실행 확인(196차) — 위 191차 발견의 "확인하지 못한 것"(물리 기반 Gazebo TurtleBot3에서도 같은 문제가 나오는지)에 답한다. nav2_bringup의 tb3_simulation_launch.py(loopback이 아닌, 실제 Gazebo 물리 엔진 + 실제 AMCL을 쓰는 공식 런치 파일 — 처음엔 마침표 앵커 정규식으로 놓쳤다가 앵커 없는 재조회로 찾아냄, ros-jazzy-turtlebot3-gazebo·ros-jazzy-turtlebot3-navigation2 추가 설치)로 헤드리스 기동하고, 191차와 똑같이 초기 위치(-2.0, -0.5)를 늦게(동시 발행이 아니라 지연 발행) 주는 실험을 했다. 경고 문구 자체는 191차와 동일하게 출력됐다 — amcl이 "AMCL cannot publish a pose or update the transform. Please set the initial pose..."를 0.5초 간격으로 반복(로그에서 64회 확인), global_costmap도 "Timed out waiting for transform from base_link to map..."를 같은 간격으로 반복했다. 그러나 결정적으로 다른 점은 — 부트업이 중단되지 않았다. 런치 시작 후 정확히 35.376초 뒤에 지연 발행한 /initialpose가 amcl에 "initialPoseReceived"로 반영됐고, 그로부터 3.35초 뒤(런치 후 총 38.728초) lifecycle_manager_navigation이 "Managed nodes are active"로 정상 전환했다 — "Aborting bringup" 문구는 로그 전체(74,000줄) 어디에도 없었다(파일 전체 검색으로 재확인; abort 검색에 걸리는 4건은 전부 훨씬 뒤 실제 주행 중 controller_server의 "[follow_path] [ActionServer] Aborting handle."로, 개별 경로 추종 실패에 대한 정상적 recovery 트리거이지 부트업 실패가 아니다). 이후 191차와 동일한 목표(x: 0.0, y: 0.5)를 보내 SUCCEEDED(error_code: 0)로 완주했고, number_of_recoveries가 0→1→3→4→5로 진행(191차의 loopback_sim 결과와 진행값이 완전히 같다)해 5회, navigation_time 53.028초(191차 55.1초와 근접), 최종 distance_remaining 0.250m(191차 0.246m과 근접)이었다. 정직한 결론: "/initialpose 없이는 headless 부트업이 절대 진행 안 된다"는 191차의 핵심 경고는 두 경로 모두에서 재확인되지만, "몇 초 만에 영구 실패하느냐"는 nav2_loopback_sim 고유의 구현 특성이지 Nav2/lifecycle_manager 일반의 특성이 아니다 — 실제 Gazebo+AMCL 경로는 훨씬 더 관대하게(적어도 35초 이상, 상한은 미확인) 재시도하며 늦게 온 초기 포즈도 정상적으로 받아들인다. 다만 recoveries 진행값이 두 배치에서 우연히 일치한 것인지, 이 특정 시작·목표 조합의 결정론적 결과인지는 각 1회 실행만으로는 단정할 수 없다(반복 시행 미시도). 원본: tools/gap_harness/gap195/nav2_gazebo_test/(logs/nav2_gz_launch.log·logs/nav_goal_gz.log·logs/apt_install{,2}.log·findings_summary.txt).

8.8.3.2 벽으로 경로를 계획한다

원인확인
측위 오차스캔과 지도 정렬(8.3.9)
지도가 현실과 다름환경 변화(8.2.9)
코스트맵에 벽이 없음StaticLayer 설정(8.4.3)
allow_unknown: true미지 통과(8.5.3)
팽창 부족inflation_radius(8.4.6)

8.8.3.3 좁은 통로를 못 지난다

원인확인
팽창 과대inflation_radius vs 통로 폭(8.4.6)
풋프린트 과대robot_radius·footprint(8.4.8)
측위 오차통로에서 특히 치명적
플래너 해상도코스트맵 resolution
컨트롤러 여유충돌 예측이 보수적

8.8.3.4 진동하거나 왕복한다

원인확인
컨트롤러 튜닝lookahead_dist·critic(8.5.8)
경로가 진동/plan 확인·평활기(8.5.6)
목표 허용오차 과소도착 판정 실패(8.5.6)
측위가 흔들림파티클 분산(8.3.9)
속도 한계 불일치세 곳 확인(8.7.4)

8.8.3.5 갇혀서 복구만 반복한다

원인확인
유령 장애물코스트맵 시각화(8.4.7)
실제로 막힘환경 확인
측위 상실신뢰도(8.3.8)
복구가 부적절BT 구성(8.6.5)
진척 검사기 과엄격정상 기동을 실패로(8.5.6)

8.8.3.6 가끔만 실패한다

가장 진단하기 어려운 부류다.

원인확인
특정 구역에서만위치를 기록해 패턴 확인
동적 장애물시간대·통행량
tf 타이밍외삽 오류 빈도(5.2)
센서 간헐 오류진단 오류율(6.9)
계산 부하 변동주기 미달(6.6.6)
측위 드리프트 누적장시간 주행 후

견해: 간헐적 실패는 bag 기록 없이 진단할 수 없다(8.8.6). 발생 시점의 상태를 남겨야 원인을 찾을 수 있고, 그래서 상시 기록이 중요하다.

8.8.4 RViz2 활용

Nav2 디버깅의 주 도구다. 무엇을 봐야 하는지가 핵심이다.

표시확인 사항
TF프레임 관계·지연(5.1.10)
Map(/map)지도 자체
Map(/global_costmap/costmap)전역 비용·팽창
Map(/local_costmap/costmap)지역 장애물·유령(8.4.9)
LaserScan지도와 정렬되는가(측위 검증 — 8.3.9)
Path(/plan)전역 경로가 합리적인가
Path(/local_plan)지역 궤적
PoseArray(/particle_cloud)AMCL 파티클 분산(8.3.2)(→ 191차 확인: AMCL이 실제로 실행되는 배치에 한정된다 — nav2_loopback_sim(경량 시뮬레이터)은 측위 자체를 대체해 /particle_cloud·/amcl_pose가 전혀 존재하지 않았다. 아래 실행 확인(191차) 참고)
Polygon(/polygon_stop 등)collision_monitor 영역(8.7.3)
Footprint로봇 형상이 맞는가
MarkerArray플러그인별 시각화

실행 확인(191차) — 위 표는 AMCL이 실제로 실행되는 배치를 가정한다. TurtleBot3 + nav2_loopback_sim(8.8.3.1 참고)으로 실측하니, ros2 topic list에 /particle_cloud도 /amcl_pose도 없었다 — 이 경량 시뮬레이터가 측위(로컬라이제이션) 자체를 대체해 참값 위치를 바로 map→odom으로 내보내기 때문이다. 버그가 아니라 범위 차이다: 실물 로봇이나 Gazebo 물리 기반 시뮬레이션(AMCL이 실제로 실행되는 배치)에서는 이 표 그대로지만, 경량 시뮬레이터로 빠르게 스택만 검증할 때는 이 항목이 해당하지 않는다는 것을 밝혀 둔다.

Fixed Frame 선택이 중요하다:

Fixed Frame보이는 것
map전역 관점 — 측위·지도 정렬 확인
odom지역 관점 — 점프 없이 부드러운가
base_link로봇 관점 — 센서 방향 확인

견해: odom을 Fixed Frame으로 두고 관찰하는 것이 과소평가된 기법이다. map 기준에서는 재측위 점프 때문에 모든 것이 흔들려 보이지만, odom 기준에서는 로봇의 실제 움직임과 센서 데이터의 일관성을 볼 수 있다. 컨트롤러 진동을 진단할 때 특히 유용하다.

저장한 RViz2 설정을 배포하라:

Node(package='rviz2', executable='rviz2',
     arguments=['-d', os.path.join(pkg, 'rviz', 'nav2_debug.rviz')])

견해: 팀 표준 RViz2 설정을 두면 진단이 빨라진다. 매번 디스플레이를 추가하는 대신 열면 바로 필요한 것이 보인다. "기본 관찰용"과 "심층 디버깅용" 두 가지를 두는 것도 유용하다.

8.8.5 성능 지표

"잘 되고 있다"를 숫자로 표현한다.

지표의미측정
성공률목표 도달 비율액션 결과 집계
소요 시간목표까지navigation_time
경로 길이실제 주행 거리오도메트리 적분
경로 효율실제/직선 거리 비계산
복구 횟수어려움 정도number_of_recoveries
평균·최소 장애물 거리안전 여유코스트맵·스캔
속도 프로파일부드러움/cmd_vel 기록
가속도 지터승차감·안정성/cmd_vel 미분
계획 실패율플래너 성능액션 결과
주기 준수계산 여유6.6.6
# 개념: 항법 성능 수집 노드
class NavMetrics(Node):
    def on_goal_result(self, result, start_time, start_pose, goal_pose):
        self.total += 1
        if result.status == GoalStatus.STATUS_SUCCEEDED:
            self.success += 1
        straight = distance(start_pose, goal_pose)
        actual = self.odom_distance_since(start_time)
        self.record(efficiency=actual / straight if straight > 0 else None,
                    duration=now() - start_time,
                    recoveries=result.result.number_of_recoveries)

견해: 성공률과 복구 횟수가 가장 실용적인 두 지표다. 성공률은 시스템이 쓸 만한지를, 복구 횟수는 어디가 어려운지를 알려준다. 복구가 자주 일어나는 위치를 지도에 표시하면 문제 구역이 시각적으로 드러난다 — 그곳의 센서 사각, 좁은 통로, 유령 장애물을 찾을 단서가 된다.

견해: 변경 전후로 같은 경로를 반복 주행해 지표를 비교하라. "주관적으로 나아졌다"는 근거가 되지 못하고, 특히 여러 파라미터를 조정한 뒤에는 무엇이 효과였는지 알 수 없다. 한 번에 하나씩 바꾸고 측정하는 것이 유일하게 확실한 방법이다.

8.8.6 기록과 재현

현장 문제는 재현이 어렵다. 기록이 유일한 단서인 경우가 많다.

# 항법 디버깅용 기록 (11.3)
ros2 bag record \
  /scan /tf /tf_static /odom /map \
  /amcl_pose /particle_cloud \
  /plan /local_plan \
  /cmd_vel /cmd_vel_smoothed \
  /local_costmap/costmap /global_costmap/costmap \
  /diagnostics /rosout \
  -o nav_debug_$(date +%Y%m%d_%H%M%S)
토픽왜
/scan·/tf·/odom재현의 기반
/amcl_pose·/particle_cloud측위 상태
/plan·/local_plan계획·추종
/cmd_vel 계열명령 체인(8.8.3)
코스트맵무엇을 보고 있었나
/diagnostics·/rosout시스템 상태·로그(1.11.9)

함정: 코스트맵은 크다. 전체 기록은 용량이 빠르게 는다. 문제 재현 세션에만 포함하고, 상시 기록에서는 뺀다.

견해: 상시 기록은 작은 토픽만, 문제 발생 시 전체 기록이 실용적 절충이다. /tf·/odom·/cmd_vel·/diagnostics는 작아서 며칠도 기록할 수 있다. 7.8.6의 "순환 버퍼 + 이벤트 저장" 패턴이 여기에도 적용된다.

재생으로 재현:

# 시계 주의 — sim time 사용(1.10.3)
ros2 bag play nav_debug_20260814 --clock

# Nav2를 재생 데이터로 다시 실행한다
# (센서·tf를 bag에서, 나머지는 실행)

함정: bag 재생으로 Nav2를 완전히 재현하기는 어렵다. 로봇이 실제로 움직이지 않으므로 제어 루프가 닫히지 않는다. 측위·코스트맵·계획까지는 재현 가능하지만, 컨트롤러 동작은 개방 루프다. 그럼에도 원인 파악에 충분한 경우가 많다.

견해: /tf를 재생할 때 실행 중인 노드가 같은 변환을 발행하면 충돌한다(5.1.4). 재생 시에는 그 노드들을 끄거나, /tf를 다른 이름으로 리매핑해 비교용으로만 쓴다.

8.8.7 시뮬레이션 평가

무엇을 검증할 수 있고 무엇을 못 하는가.

검증 가능검증 불가
로직·상태 전이실제 센서 노이즈 특성
파라미터의 대략적 효과실제 바닥 마찰·미끄러짐
회귀(기능이 깨졌나)실제 측위 난이도
경로·계획 품질무선·통신 문제
복구 동작 흐름실제 사람 행동
대량 반복 시험하드웨어 열화

견해: 시뮬레이션의 가장 큰 가치는 "반복"이다. 같은 시나리오를 100번 실행해 성공률을 측정하는 것은 실기로는 불가능하다. 회귀 검증과 통계적 비교에 쓰고, 절대 성능은 실기로 확인하는 분업이 맞다(10.6).

자동 평가 구성:

예 아니오 시나리오 정의 시작 · 목표 · 장애물 시뮬 실행 목표 전송 결과·지표 수집 모든 시나리오? 리포트 성공률 · 시간 · 복구

견해: CI에 항법 시나리오 시험을 넣는 것을 검토하라(4.10.5). 시뮬 부하가 커서 매 커밋은 어렵지만, 야간 빌드로 실행하면 파라미터·코드 변경의 회귀를 검출한다. 다만 시뮬 테스트는 불안정하기 쉬우므로(4.10.5), 통과 기준을 통계적으로 설정해야 한다.

8.8.8 현장 시험 절차

안전하게 검증하는 순서다.

1. 정지 상태 검증 tf · 센서 · 측위 2. 수동 조작 코스트맵 반응 확인 3. 짧은 목표 1~2 m, 저속 4. 장애물 반응 사람이 앞을 막아본다 5. 긴 경로 실제 운용 경로 6. 반복 주행 지표 수집 7. 예외 상황 막힘 · 센서 가림 8. 장시간 운용

각 단계의 안전 조치:

단계조치
1~2로봇을 들어 올리거나 바퀴를 띄움
3~4비상 정지 손에 들고·저속
5~6감시자 동행
7통제된 조건에서
8구역 제한·모니터링

견해: 4단계(사람이 앞을 막아본다)를 반드시 하라. 로봇이 사람을 감지해 멈추는지, 얼마나 여유를 두는지, 옆으로 피하려 하는지를 직접 확인한다. 이것을 시뮬로 대체할 수 없다 — 실제 사람의 다리는 라이다에 잘 안 보이고, 옷·자세에 따라 반사가 다르다.

견해: 7단계 예외 상황 시험이 자주 생략된다. 센서를 가리고, 좁은 곳에 가두고, 통신을 끊어 보는 시험이다. 정상 동작만 확인하고 배포하면 예외 상황에서 처음 문제를 만난다 — 그리고 그때는 대개 불리한 상황이다.

변경 관리:

규칙이유
한 번에 하나씩무엇이 효과였는지 알 수 있다
변경 전후 지표 비교근거 있는 판단
설정을 버전 관리되돌릴 수 있게
변경 이유 기록나중에 이해
재검증파라미터 하나가 행동을 바꾼다

견해: Nav2 파라미터 파일을 git으로 관리하고 변경마다 커밋 메시지에 이유를 적으라. 6개월 뒤 "왜 이 값이지"를 답할 수 있고, 문제가 생겼을 때 어느 변경이 원인인지 이분 탐색할 수 있다. 4.7.6의 PATCHES.md와 같은 논리다.

8.8.9 진단 상시화

튜닝이 끝나도 감시는 계속되어야 한다(6.9).

항목임계 예
측위 신뢰도공분산 임계(8.3.8)
복구 횟수목표당 N회 초과
성공률최근 N회 중 비율
계획 실패율비율
소요 시간예상 대비 배수
tf 지연임계 초과(5.1.10)
센서 주파수기대치 미달(6.9.4)
collision_monitor 개입빈도

견해: collision_monitor 개입 빈도가 좋은 조기 경보다. 정상 운용에서는 드물어야 하는데, 늘어난다면 환경이 변했거나(사람 증가·물건 적재) 측위·코스트맵이 악화된 것이다. 사고 전에 알 수 있는 신호다.

견해: 성능 지표를 시간에 따라 기록하면 열화가 보인다. 성공률이 서서히 떨어지거나 평균 소요 시간이 늘면, 타이어 마모·센서 오염·환경 변화를 의심할 수 있다(7.6.7의 정기 검증과 같은 논리).

8.8.10 함정 카탈로그

#함정증상대처
1상위 계층부터 튜닝해결 안 됨·나중에 무효하위 계층부터(8.8.2)
2여러 파라미터 동시 변경원인 불명하나씩
3지표 없이 "주관적 인상"으로 판단개선 여부 불명측정·비교
4/cmd_vel 체인 미확인어디서 막혔는지 모름전후 비교
5RViz2 Fixed Frame 고정관점 제한map·odom 전환
6간헐 실패에 기록 없음진단 불가상시·이벤트 기록
7코스트맵을 상시 기록용량 폭증세션에만
8bag 재생 시 tf 충돌비정상적인 결과노드 끄기·리매핑
9시뮬 결과를 실기로 오인현장 실패분업 이해(8.8.7)
10예외 상황 시험 생략현장에서 첫 경험7단계 수행
11사람 반응 시험 생략안전 위험4단계 필수
12설정 버전 관리 안 함되돌리기·추적 불가git·이유 기록
13튜닝 후 감시 중단열화를 못 알아챔진단 상시화

8.8.11 체크포인트

  • 튜닝 순서 열 단계와 각각의 통과 기준을 말할 수 있는가. "80%는 1~5단계 문제"라는 관찰의 함의는?
  • "로봇이 안 움직인다"를 진단하는 흐름과, /cmd_vel 체인 비교가 왜 결정적인가.
  • "좁은 통로를 못 지난다"의 원인 후보 다섯 가지를 들 수 있는가.
  • 간헐적 실패를 진단하려면 무엇이 필요한가.
  • RViz2에서 봐야 할 표시 열 가지와, Fixed Frame 선택이 무엇을 바꾸는가. odom 기준 관찰의 이점은?
  • 항법 성능 지표 열 가지 중 가장 실용적인 둘은 무엇이며 왜인가. 복구 위치를 지도에 표시하면 무엇을 알 수 있는가.
  • 항법 디버깅용 bag에 포함할 토픽과, 상시 기록에서 빼야 할 것은?
  • bag 재생으로 Nav2를 재현할 때의 한계와 주의점은?
  • 시뮬레이션으로 검증 가능한 것과 불가능한 것을 각각 세 가지 들 수 있는가. 시뮬의 가장 큰 가치는?
  • 현장 시험 여덟 단계와 각 단계의 안전 조치를 말할 수 있는가. 4·7단계가 왜 중요한가.
  • 변경 관리 규칙 다섯 가지와, 설정을 버전 관리해야 하는 이유는?
  • 상시 감시 항목 여덟 가지 중 좋은 조기 경보가 되는 것은?

여기까지가 제8부 항법이다. Nav2 아키텍처(8.1), 지도(8.2)와 측위(8.3), 코스트맵(8.4), 플래너·컨트롤러(8.5), 행동 트리(8.6), 안전(8.7), 그리고 튜닝·디버깅(8.8).

제8부가 반복한 것은 셋이다.

  1. 하위 계층이 먼저다. tf·오도메트리·센서·지도·측위가 정확해야 그 위가 동작한다. 항법 문제의 대부분이 항법 밖에 있다(8.8.2).
  2. 기구학이 선택을 정한다. 로봇이 어떻게 움직일 수 있는지가 플래너·컨트롤러 선택을 결정하며, 불일치하면 따라갈 수 없는 경로가 나온다(8.5.2).
  3. Nav2는 안전 장치가 아니다. 유용한 안전 기능을 제공하지만 전부 소프트웨어이며, 하드웨어 계층이 없으면 안전 논증을 세울 수 없다(8.7.7).

이제 로봇은 움직이고, 보고, 어디로 갈지 안다. 남은 것은 무언가를 집고 조작하는 일이다. 팔을 어떻게 계획하고, 충돌을 어떻게 피하고, 물체를 어떻게 파지하는가 — 제9부 매니퓰레이션(9.1~9.6) 의 주제다. MoveIt 2 아키텍처부터 시작한다.

8.8.12 제8부를 마치며 — 항법

이 장은 제8부(8.1~8.8)의 마지막이므로, 부 전체를 하나의 관점으로 묶는다.

제8부가 반복해 말한 세 가지:

  1. Nav2는 여러 서버의 합주다. 하나가 종료되면 전체가 비정상이 되므로 라이프사이클과 주기를 아는 것이 진단의 출발점이다(8.1).
  2. 기본값을 믿지 말라. Nav2와 컨트롤러의 cmd_vel 기본 타입이 서로 다르고(8.1.14.1), BT 틱 주기는 이름과 단위가 혼동을 일으키게 되어 있다(8.1.14.2).
  3. 튜닝은 측정 다음이다. 코스트맵·플래너·컨트롤러 파라미터를 조정하기 전에 무엇이 느린지 재라(8.8).

다음 부에서 무엇이 달라지나: 항법이 평면 위의 이동이었다면 제9부는 3차원 공간의 팔 — 매니퓰레이션이다. 자유도가 늘고 충돌 검사가 지배 비용이 되며, "경로가 있는가"보다 "그 경로가 안전한가"가 문제가 된다.

제9부는 매니퓰레이션(9.1~9.6)다.