제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 | view_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 | 컨트롤러 | 경로를 부드럽게 추종 |
| 9 | BT | 실패 시 복구가 동작 |
| 10 | 안전 | 정지 영역·속도 제한 동작 |
견해: "Nav2가 안 된다"의 80%는 1~5단계 문제다. 그런데 사람들은 대개 7~8단계(플래너·컨트롤러 파라미터)부터 조정한다 — 그것이 가장 눈에 보이는 층이기 때문이다. 하위 계층이 틀린 상태에서 상위 계층을 튜닝하면 해결되지 않고, 나중에 아래를 고치면 위 튜닝이 무효가 된다.
견해: 각 단계의 통과 기준을 명시적으로 확인하고 기록하라. "오도메트리 폐루프 오차 3 cm/10 m"처럼 숫자로 남기면, 나중에 성능이 악화됐을 때 무엇이 변했는지 비교할 수 있다(7.6.7의 정기 검증).
8.8.3 진단 플레이북 — 증상에서 원인으로
8.8.3.1 로봇이 전혀 움직이지 않는다
실행 확인(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→odomtf를 전혀 내보내지 않는데(소스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_time53.028초(191차 55.1초와 근접), 최종distance_remaining0.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~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 체인 미확인 | 어디서 막혔는지 모름 | 전후 비교 |
| 5 | RViz2 Fixed Frame 고정 | 관점 제한 | map·odom 전환 |
| 6 | 간헐 실패에 기록 없음 | 진단 불가 | 상시·이벤트 기록 |
| 7 | 코스트맵을 상시 기록 | 용량 폭증 | 세션에만 |
| 8 | bag 재생 시 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부가 반복한 것은 셋이다.
- 하위 계층이 먼저다. tf·오도메트리·센서·지도·측위가 정확해야 그 위가 동작한다. 항법 문제의 대부분이 항법 밖에 있다(8.8.2).
- 기구학이 선택을 정한다. 로봇이 어떻게 움직일 수 있는지가 플래너·컨트롤러 선택을 결정하며, 불일치하면 따라갈 수 없는 경로가 나온다(8.5.2).
- Nav2는 안전 장치가 아니다. 유용한 안전 기능을 제공하지만 전부 소프트웨어이며, 하드웨어 계층이 없으면 안전 논증을 세울 수 없다(8.7.7).
이제 로봇은 움직이고, 보고, 어디로 갈지 안다. 남은 것은 무언가를 집고 조작하는 일이다. 팔을 어떻게 계획하고, 충돌을 어떻게 피하고, 물체를 어떻게 파지하는가 — 제9부 매니퓰레이션(9.1~9.6) 의 주제다. MoveIt 2 아키텍처부터 시작한다.
8.8.12 제8부를 마치며 — 항법
이 장은 제8부(8.1~8.8)의 마지막이므로, 부 전체를 하나의 관점으로 묶는다.
제8부가 반복해 말한 세 가지:
- Nav2는 여러 서버의 합주다. 하나가 종료되면 전체가 비정상이 되므로 라이프사이클과 주기를 아는 것이 진단의 출발점이다(8.1).
- 기본값을 믿지 말라. Nav2와 컨트롤러의
cmd_vel기본 타입이 서로 다르고(8.1.14.1), BT 틱 주기는 이름과 단위가 혼동을 일으키게 되어 있다(8.1.14.2). - 튜닝은 측정 다음이다. 코스트맵·플래너·컨트롤러 파라미터를 조정하기 전에 무엇이 느린지 재라(8.8).
다음 부에서 무엇이 달라지나: 항법이 평면 위의 이동이었다면 제9부는 3차원 공간의 팔 — 매니퓰레이션이다. 자유도가 늘고 충돌 검사가 지배 비용이 되며, "경로가 있는가"보다 "그 경로가 안전한가"가 문제가 된다.
제9부는 매니퓰레이션(9.1~9.6)다.