제8부 항법
8.1 Nav2 아키텍처 전경
8.1.1 이 장에서 배우는 것 / 선수 지식
Nav2는 ROS 2에서 가장 큰 응용 스택이다. 20개가 넘는 패키지, 수십 개의 플러그인, 라이프사이클 관리, 행동 트리 — 처음 보면 압도적이고, 어디서부터 봐야 할지 알기 어렵다.
그런데 구조를 알면 단순하다. 몇 개의 서버가 각자 한 가지 일을 하고, 행동 트리가 그들을 조율한다. 그리고 거의 모든 것이 플러그인이라 교체 가능하다.
이 장은 그 전경을 세운다. 개별 요소는 8.2~8.8에서 깊이 다루고, 여기서는 무엇이 무엇과 어떻게 연결되는가와 Nav2가 요구하는 것·하지 않는 것을 명확히 한다.
배우는 것:
- 구성 요소 — 각 서버가 무엇을 담당하는가.
- 데이터 흐름 — 목표에서
/cmd_vel까지. - 라이프사이클 관리 — 왜 이 구조인가(1.12 적용).
- 액션 인터페이스 — 외부에서 Nav2를 쓰는 방법.
- 필수 입력 — tf·지도·센서·오도메트리.
- 플러그인 구조 — 어디를 바꿀 수 있는가.
- Nav2가 하지 않는 것 — 경계를 명확히.
- 1.3.12 실물 대조(8.1.14) — 기본 주기·상수·API, 그리고 컨트롤러와 어긋나는 기본값.
nav2_route실물(8.1.15) — 이 책이 안 다루는 그래프 기반 계획의 구성.
선수 지식: 1.12(라이프사이클), 1.8(액션), 5.1.7(REP 105 프레임), 4.2.6(pluginlib), 6.5.6(diff_drive_controller·/cmd_vel), 7.5(라이다).
정직: Nav2는 이 책에서 다루는 것 중 변화가 빠른 축에 속한다. 서버 구성·플러그인 목록·파라미터 이름이 배포판마다 추가·변경된다. 아래는 구조와 개념에 집중하며, 정확한 파라미터·플러그인 목록은 Nav2 문서의 Jazzy 버전이 정본이다
(확인 필요). 이 환경에서 실행 검증하지 않았다(목차 3).검증 상태(추가): 이 장의 서술 가운데 1곳은 이후 회차에서 Jazzy 실물과 대조해 확인했다 — 본문의 (실행 확인) 표기가 그것이다. 표기가 없는 서술은 위 고지가 그대로 적용된다(목차 3의 검증 범위 표).
8.1.2 구성 요소
| 서버 | 담당 | 관련 장 |
|---|---|---|
bt_navigator | 행동 트리 실행·전체 조율 | 8.6 |
planner_server | 전역 경로 계획 | 8.5 |
controller_server | 지역 제어·장애물 회피 | 8.5 |
behavior_server | 복구 동작(회전·후진·대기) | 8.7 |
smoother_server | 경로 평활화 | 8.5 |
velocity_smoother | 속도 명령 평활화 | 8.7 |
collision_monitor | 최종 안전 계층 | 8.7 |
waypoint_follower | 경유점 순회 | 8.6 |
map_server | 지도 제공 | 8.2 |
amcl | 지도 기반 측위 | 8.3 |
lifecycle_manager | 서버 수명 관리 | 1.12.6 |
두 개의 코스트맵이 있다는 점이 중요하다:
| 코스트맵 | 크기 | 갱신 | 용도 |
|---|---|---|---|
| 전역(global) | 지도 전체 | 느림 | 전역 경로 계획 |
| 지역(local) | 로봇 주변(예: 5×5 m) | 빠름 | 즉각 회피 |
전역은 정적 지도 + 큰 그림, 지역은 실시간 센서 + 즉각 반응이다(8.4).
8.1.3 데이터 흐름
주기가 다르다:
| 단계 | 전형적 주기 |
|---|---|
| 전역 계획 | 1 Hz(실행 확인 — BT XML의 RateController hz="1.0", 8.1.14.2) |
| 지역 제어 | 20 Hz |
| 지역 코스트맵 갱신 | 5~20 Hz |
| 전역 코스트맵 갱신 | 1~5 Hz |
| BT 틱 | 약 100 Hz(실행 확인 — bt_loop_duration: 10은 밀리초다, 8.1.14.2) |
견해: "전역은 가끔, 지역은 자주"가 핵심 구조다. 전역 경로를 매 주기 다시 계산할 필요는 없고(비용이 크다), 지역 제어는 장애물에 즉각 반응해야 한다. 이 분리가 계산 비용과 반응성을 동시에 만족시킨다.
/cmd_vel이 최종 출력이다. 6.5.6의 diff_drive_controller가 이것을 받아 바퀴 명령으로 바꾼다 — 제6부와 제8부가 여기서 만난다.
함정: Jazzy에서는 Nav2와
diff_drive_controller의 기본값이 서로 맞지 않는다(실행 확인, 8.1.14.1). Nav2는 기본이Twist, Jazzy의diff_drive_controller는TwistStamped만 받는다(6.5.12.1). 그대로 실행하면 구독이 매칭되지 않아 로봇이 안 움직이고, 오류도 경고도 없다. Nav2 쪽에enable_stamped_cmd_vel: true를 주는 것이 대처다.
8.1.4 라이프사이클 관리
Nav2의 모든 서버는 라이프사이클 노드다(1.12).
lifecycle_manager_navigation:
ros__parameters:
autostart: true
node_names:
- controller_server
- smoother_server
- planner_server
- behavior_server
- velocity_smoother
- bt_navigator
- waypoint_follower
bond_timeout: 4.0
node_names의 순서가 활성화 순서다. 의존 관계에 맞춰야 한다 — 지도가 먼저, 측위가 그 다음, 계획·제어가 마지막.
1.12.6에서 본 관리자 패턴의 참조 구현이 이것이다:
| 기능 | 설명 |
|---|---|
| 순서 있는 활성화 | 의존 순서대로 |
| 실패 시 롤백 | 하나가 실패하면 중단 |
| bond 감시 | 노드가 종료되면 감지(1.12.6) |
| 일괄 제어 | 전체 정지·재시작 |
# 수동 제어
ros2 service call /lifecycle_manager_navigation/manage_nodes \
nav2_msgs/srv/ManageLifecycleNodes "{command: 0}" # STARTUP
# command: 0=STARTUP, 1=PAUSE, 2=RESUME, 3=RESET, 4=SHUTDOWN (실행 확인)
# 5=CONFIGURE, 6=CLEANUP ← 본문에 없던 둘도 있다 (8.1.14.3)
견해: Nav2를 일시 정지시키는 기능이 실무에서 유용하다. 수동 조작으로 전환하거나 다른 작업을 할 때, 노드를 강제 종료하는 대신
PAUSE하면 상태를 유지한 채 멈춘다. 1.12.2에서Inactive가 주는 이점의 실제 사례다.
함정:
autostart: true인데 지도나 tf가 준비되지 않으면 활성화가 실패한다. 특히map_server가 지도 파일을 못 찾거나,amcl이 초기 자세를 못 받으면 그렇다. 부팅 순서와 의존을 launch에서 명확히 하라(14.4).
8.1.5 액션 인터페이스
외부에서 Nav2를 쓰는 주된 방법이다(1.8).
| 액션 | 서버 | 용도 |
|---|---|---|
NavigateToPose | bt_navigator | 단일 목표로 주행 |
NavigateThroughPoses | bt_navigator | 여러 경유점을 거쳐 |
FollowWaypoints | waypoint_follower | 경유점마다 작업 수행 |
ComputePathToPose | planner_server | 경로만 계산(주행 안 함) |
FollowPath | controller_server | 주어진 경로 추종 |
Spin·BackUp·Wait | behavior_server | 개별 복구 동작 |
# 목표 전송
ros2 action send_goal /navigate_to_pose nav2_msgs/action/NavigateToPose "{
pose: {
header: {frame_id: map},
pose: {position: {x: 2.0, y: 1.0, z: 0.0},
orientation: {w: 1.0}}
}
}" --feedback
# 코드에서 (1.8.5의 콜백 체인)
from nav2_msgs.action import NavigateToPose
self.nav_client = ActionClient(self, NavigateToPose, 'navigate_to_pose')
goal = NavigateToPose.Goal()
goal.pose.header.frame_id = 'map'
goal.pose.pose.position.x = 2.0
goal.pose.pose.orientation.w = 1.0
future = self.nav_client.send_goal_async(goal, feedback_callback=self.on_feedback)
future.add_done_callback(self.on_goal_response)
nav2_simple_commander가 편의 API를 제공한다(실행 확인 — 패키지·클래스·메서드 모두 실재, 8.1.14.4):
from nav2_simple_commander.robot_navigator import BasicNavigator
nav = BasicNavigator()
nav.waitUntilNav2Active()
nav.goToPose(goal_pose)
while not nav.isTaskComplete():
feedback = nav.getFeedback()
견해:
NavigateToPose의 다중 목표 정책은 선점(preempt)이다(1.8.7). 새 목표를 보내면 이전 주행이 대체된다. 이것이 원격 조작·동적 목표 변경에 자연스럽지만, "이전 목표가 취소됐다"는 결과를 받게 되므로 클라이언트가 이를 실패로 오해하지 않게 처리해야 한다.
함정: 목표의
frame_id를 확인하라.map기준이 일반적이지만odom이나base_link기준 상대 이동을 원할 수도 있다. Nav2가 변환하지만 tf가 있어야 하고,base_link기준 목표는 전송 시점에 고정된다는 점에 유의한다.
8.1.6 필수 입력
Nav2가 동작하려면 이것들이 갖춰져야 한다.
| 입력 | 형태 | 출처 | 관련 장 |
|---|---|---|---|
tf: map → odom | tf | AMCL·SLAM | 8.3·8.2 |
tf: odom → base_link | tf | 오도메트리·융합 | 7.6 |
tf: base_link → 센서 | tf(static) | URDF·RSP | 5.5 |
| 지도 | OccupancyGrid | map_server·SLAM | 8.2 |
| 센서 | LaserScan·PointCloud2 | 라이다·깊이 | 7.5·7.4 |
| 오도메트리 | Odometry | 컨트롤러·융합 | 7.6 |
| 로봇 형상 | footprint·radius | 설정 | 8.4 |
| 속도 한계 | 설정 | 로봇 사양 | 8.5 |
tf 트리가 완전해야 한다(5.1.4):
# 확인 (5.1.10)
ros2 run tf2_tools view_frames
ros2 run tf2_ros tf2_echo map base_link
함정: Nav2 문제의 상당수가 tf 문제다.
map → odom이 없으면 측위가 안 된 것이고,odom → base_link가 끊기면 오도메트리 문제이며, 센서 프레임이 없으면 코스트맵이 데이터를 못 쓴다. "Nav2가 안 된다"를 만나면 tf부터 확인하라(8.8).
함정:
use_sim_time을 모든 Nav2 노드에 일관되게 설정해야 한다(1.10.3). 일부만 다르면 tf 외삽 오류가 대량 발생한다(5.2.4).
/**:
ros__parameters:
use_sim_time: true # 1.9.7
8.1.7 플러그인 구조
Nav2의 거의 모든 알고리즘이 플러그인이다(4.2.6).
| 확장 지점 | 기반 클래스 | 예 |
|---|---|---|
| 전역 플래너 | nav2_core::GlobalPlanner | NavFn·Smac(8.5) |
| 지역 컨트롤러 | nav2_core::Controller | DWB·RPP·MPPI(8.5) |
| 코스트맵 레이어 | nav2_costmap_2d::Layer | static·obstacle·inflation(8.4) |
| 복구 동작 | nav2_core::Behavior | spin·backup(8.7) |
| BT 노드 | BT::ActionNodeBase 등 | 커스텀 조건·동작(8.6) |
| 목표 검사기 | nav2_core::GoalChecker | 도달 판정 |
| 진척 검사기 | nav2_core::ProgressChecker | 정체 감지 |
| 평활기 | nav2_core::Smoother | 경로 다듬기 |
| 코스트맵 필터 | — | 속도 제한·금지 구역(8.7) |
planner_server:
ros__parameters:
planner_plugins: ["GridBased"]
GridBased:
# 표기는 pkg::Class — 8.5.3의 함정
plugin: "nav2_navfn_planner::NavfnPlanner" # ← 교체 가능
tolerance: 0.5
use_astar: false
플러그인 이름과 설정 키가 대응한다 — planner_plugins에 나열한 이름이 하위 설정의 키가 된다. 이 구조가 혼동하기 쉬우니 주의한다.
견해: "Nav2를 쓴다"는 곧 "플러그인을 고른다"는 뜻이다. 로봇의 기구학·환경·요구에 따라 플래너와 컨트롤러를 선택하는 것이 Nav2 도입의 핵심 작업이며, 기본값이 모든 로봇에 맞지는 않는다(8.5).
8.1.8 실행 구성
# 기본: nav2_bringup 활용
from launch.actions import IncludeLaunchDescription
from launch.launch_description_sources import PythonLaunchDescriptionSource
nav2 = IncludeLaunchDescription(
PythonLaunchDescriptionSource([
os.path.join(get_package_share_directory('nav2_bringup'),
'launch', 'navigation_launch.py')]),
launch_arguments={
'use_sim_time': use_sim_time,
'params_file': my_nav2_params,
'autostart': 'true',
}.items(),
)
견해:
nav2_bringup을 그대로 include하는 것으로 시작하고, 필요해지면 자체 launch로 옮기라. 처음부터 자체 구성을 만들면 무엇이 필요한지 모르는 채로 시행착오를 겪는다. 파라미터 파일만 자기 것으로 바꾸는 것이 첫 단계다.
컴포지션 옵션(1.13):
Nav2 서버들은 컴포넌트로 제공되며, 한 컨테이너에 담을 수 있다(확인 필요: Jazzy에서의 기본값과 옵션).
| 구성 | 장점 | 단점 |
|---|---|---|
| 분리 프로세스 | 크래시 격리·디버깅 용이 | 통신 비용 |
| 한 컨테이너 | 코스트맵 등 대용량 공유 시 유리 | 크래시 전파(1.13.7) |
견해: 개발 중에는 분리, 성능이 문제되면 컴포지션을 검토한다. 코스트맵은 크지만 대개 프로세스 간 전송량이 이미지·포인트클라우드만큼 크지는 않으므로(7.8.2), 컴포지션의 이득이 인지 파이프라인만큼 극적이지는 않다.
8.1.9 Nav2가 하지 않는 것
경계를 명확히 하는 것이 중요하다.
| 하지 않음 | 누가 하는가 |
|---|---|
| 지도 작성 | SLAM(8.2) — Nav2는 소비만 |
| 오도메트리 생성 | 컨트롤러·융합(7.6) |
| 바퀴 제어 | ros2_control(6.5) |
| 임무 계획·작업 순서 | 상위 시스템(14.2) |
| 물체 인식·의미 이해 | 인지 파이프라인(제7부) |
| 3D 항법(드론·비행) | 별도 스택(확인 필요: 3D 지원 범위) |
| 다중 로봇 조정 | 상위 플릿 시스템(14.9) |
| 안전 인증 | 8.7·14.10 |
두 가지를 강조한다:
① Nav2는 지도를 만들지 않는다. SLAM(8.2)이 만들고, Nav2는 그것을 쓴다. 다만 SLAM을 켠 채로 Nav2를 실행할 수 있다(동시 SLAM·항법).
② Nav2의 안전 기능은 안전 인증이 아니다. collision_monitor(8.7)가 유용하지만, 기능 안전(functional safety) 요구를 만족시키지 않는다. 인증이 필요한 시스템은 별도의 안전 계층이 필요하다(14.10).
견해: "Nav2를 쓰면 안전하다"는 오해가 위험하다. Nav2는 센서가 보는 것을 피하려 노력하지만, 센서가 못 보는 것(유리·낮은 장애물·계단), 소프트웨어 실패, 통신 두절에는 무력하다. 하드웨어 비상 정지·범퍼·안전 라이다가 별개로 있어야 한다(8.7.8).
8.1.10 첫 실행 절차
처음 Nav2를 실행할 때의 순서다.
각 단계에서 다음으로 넘어가기 전 확인:
| 단계 | 확인 |
|---|---|
| 1 | view_frames로 트리 완전(5.1.10) |
| 2 | 폐루프 시험 통과(7.6.7) |
| 3 | RViz2에서 스캔이 올바른 방향(7.1.9) |
| 4 | 지도가 왜곡 없이 닫히는가(8.2) |
| 5 | 로봇 이동 시 스캔이 지도와 정렬(8.3) |
| 6 | 코스트맵에 유령 장애물이 없는가(8.4) |
| 7 | 목표까지 경로가 생기고 주행하는가 |
견해: 이 순서를 건너뛰면 반드시 되돌아온다. "Nav2가 안 된다"의 원인 대부분이 1~3단계에 있고, 그 상태에서 Nav2 파라미터를 아무리 튜닝해도 해결되지 않는다. 하위 계층이 먼저다 — 이것이 제5·6·7부를 먼저 다룬 이유이기도 하다.
8.1.13 이 책이 다루지 않는 Nav2 구성요소
번호에 대하여: 이 절은 나중에 추가되었다. 규약(CLD-03)에 따라 번호는 끝에 붙이고, 읽는 흐름을 위해 함정 카탈로그 앞에 놓았다.
실행 확인(2026-08-16): Nav2 1.3.12를 설치해 등록된 플러그인을 전수 대조하다가, 이 책의 제8부가 전혀 언급하지 않는 서브시스템이 있음을 확인했다.
| 구성요소 | 무엇인가 | 이 책의 취급 |
|---|---|---|
nav2_route | 그래프(노드·간선) 기반 경로 계획 — 사전에 정의한 도로망 위에서 경로를 고르고, 간선마다 비용 함수 8종을, 구간마다 동작 5종을 지정한다. GeoJSON으로 그래프를 싣고 내린다(전수 목록은 8.1.15) | 다루지 않음 |
nav2_graceful_controller | 부드러운 자세 수렴 컨트롤러 | 이름만(8.5.4) |
nav2_costmap_2d의 DenoiseLayer·PluginContainerLayer | 잡음 제거·레이어 묶음 | 이름만(8.4.3) |
nav2_behaviors::AssistedTeleop | 보조 원격 조작 동작 | 다루지 않음 |
nav2_smoother::SavitzkyGolaySmoother | 대안 평활기 | 이름만 |
도킹(opennav_docking) | 자율 충전 — Nav2 1.3에 본체 기능으로 들어왔다 | 실재만 확인(8.1.14.4) |
nav2_route가 특히 의미 있는 누락이다. 이 책의 제8부는 자유 공간 계획(코스트맵 위 A*·Hybrid-A*)을 전제로 서술되어 있는데, 창고·공장처럼 통로가 정해진 환경에서는 그래프 기반 경로 계획이 더 맞는 경우가 많다. 14.9.5가 "일방통행 경로·구역별 한 대만 같은 단순 규칙이 협상 알고리즘보다 예측 가능하다"고 한 판단과 같은 방향이며, 그 규칙을 경로 계획 층에서 표현하는 수단이 이것이다.
정직: 확인한 것은 이 패키지들이 실재한다는 사실과 등록된 플러그인 이름까지다. 성숙도·API·설정 방법·Jazzy에서의 지원 범위는 확인하지 않았다
(확인 필요). 이 책이 이들을 다루지 않는 이유는 "불필요해서"가 아니라 집필 시점의 커버리지 한계 때문이며, 필요하다면 Nav2 문서를 정본으로 삼으라. 제8부의 서술은 자유 공간 계획을 전제로 한다는 점을 염두에 두고 읽기 바란다.
8.1.14 Nav2 1.3.12를 설치해 대조했다 — 실측
ros-jazzy-navigation2 1.3.12를 설치해 이 장이 (확인 필요)로 남긴 항목들을 기본 설정 파일·메시지 정의·헤더와 대조했다.
8.1.14.1 가장 중요한 발견 — Nav2와 컨트롤러의 기본값이 서로 안 맞는다
nav2_util::TwistPublisher 헤더의 주석이 그대로 답이다.
"A simple wrapper on a Twist publisher that provides either Twist or TwistStamped … The default is to publish Twist to preserve backwards compatibility, but it can be overridden using the
enable_stamped_cmd_velparameter to publish TwistStamped."
한편 Jazzy의 diff_drive_controller는 TwistStamped만 받는다(6.5.12.1에서 확인).
| 기본 타입 | |
|---|---|
| Nav2 1.3.12(Jazzy) | Twist |
diff_drive_controller(Jazzy) | TwistStamped |
두 기본값을 그대로 두면 연결되지 않는다. 구독이 매칭되지 않으므로 로봇이 움직이지 않고, 오류도 경고도 나지 않는다. 같은 배포판 안에서 두 대표 스택의 기본값이 어긋나 있는 것이다.
대처: Nav2 쪽 파라미터에 enable_stamped_cmd_vel: true를 준다.
controller_server:
ros__parameters:
enable_stamped_cmd_vel: true
velocity_smoother:
ros__parameters:
enable_stamped_cmd_vel: true
함정: 이 파라미터는 노드마다 따로 선언된다.
declare_parameter_if_not_declared로 각 노드가 개별 선언하므로,cmd_vel을 다루는 모든 노드에 줘야 한다. 실제로 이 문자열을 가진 라이브러리가 아홉 개다 —controller_server·velocity_smoother·collision_monitor·docking, 그리고 거동 넷(spin·back_up·drive_on_heading·wait·assisted_teleop). 하나라도 빠뜨리면 그 동작에서만 로봇이 멈춘다 — 평소엔 잘 가다가 회복 거동에서만 안 움직이는, 진단하기 까다로운 증상이 된다.
/**:로 한 번에 주는 방법이 안전하다(14.3.12).
8.1.14.2 주기 표를 고쳤다 — BT 틱이 열 배 틀렸다
nav2_bringup의 기본 파라미터와 기본 BT XML에서 확인했다.
| 단계 | 책의 값 | 실제 기본값 | 출처 |
|---|---|---|---|
| 전역 계획 | 1 Hz 또는 이벤트 | 1 Hz ✅ | BT XML의 RateController hz="1.0" |
| 지역 제어 | 20 Hz | 20 Hz ✅ | controller_frequency: 20.0 |
| 지역 코스트맵 | 5~20 Hz | 5 Hz(발행 2 Hz) | local_costmap.update_frequency: 5.0 |
| 전역 코스트맵 | 1~5 Hz | 1 Hz(발행 1 Hz) | global_costmap.update_frequency: 1.0 |
| BT 틱 | 10~20 Hz ❌ | 약 100 Hz | bt_loop_duration: 10 |
bt_loop_duration은 헤르츠가 아니라 밀리초다. 10 ms 주기이므로 BT는 초당 약 100번 틱한다 — 책에 적었던 10~20 Hz는 한 자릿수 틀렸다. 같은 블록의 default_server_timeout: 20·wait_for_service_timeout: 1000이 밀리초인 것과 단위가 일관된다.
전역 계획 주기가 파라미터가 아니라 BT XML에 있다는 점도 실무적으로 중요하다.
nav2_params.yaml을 아무리 뒤져도 "전역 계획을 몇 초마다"에 해당하는 항목이 없다 — 행동 트리의RateController노드를 고쳐야 한다.expected_planner_frequency: 20.0은 계획 주기가 아니라 그보다 느리면 경고를 내는 기준값이다(이름이 혼동을 일으키게 지어져 있다).
8.1.14.3 라이프사이클 명령 상수 — 둘이 더 있다
uint8 STARTUP = 0 uint8 PAUSE = 1 uint8 RESUME = 2
uint8 RESET = 3 uint8 SHUTDOWN = 4
uint8 CONFIGURE = 5 uint8 CLEANUP = 6 ← 본문에 없던 둘
책이 적은 0~4는 모두 맞다. CONFIGURE·CLEANUP이 추가로 있어 Unconfigured ↔ Inactive 전이를 직접 지시할 수 있다(1.12.2의 상태도를 그대로 노출한다).
8.1.14.4 nav2_simple_commander — 실재하고, 책이 적은 것보다 훨씬 넓다
BasicNavigator와 waitUntilNav2Active()가 그대로 있다. 결과 판별용 열거형도 있다.
TaskResult.UNKNOWN=0 SUCCEEDED=1 CANCELED=2 FAILED=3
책이 언급하지 않은 기능이 여럿이다.
| 묶음 | 메서드 |
|---|---|
| 도킹(Nav2 1.3의 새 기능) | dockRobotByID·dockRobotByPose·undockRobot |
| 경로 | getPath·getPathThroughPoses·smoothPath·followPath |
| 웨이포인트 | followWaypoints·followGpsWaypoints |
| 코스트맵 | clearGlobalCostmap·clearLocalCostmap·clearAllCostmaps·clearCostmapAroundPose·clearCostmapExceptRegion |
| 거동 | spin·backup·driveOnHeading·assistedTeleop |
| 기타 | changeMap·setInitialPose·lifecycleStartup·lifecycleShutdown |
도킹이 기본 파라미터 파일에도 들어와 있다(docking_server, controller_frequency: 50.0). 자율 충전이 Nav2 본체 기능이 된 것 — 이 책은 다루지 않으므로 8.1.13의 목록에 추가한다.
견해: Python으로 Nav2를 쓸 것이라면 액션 클라이언트를 직접 구현하기 전에 이 목록을 먼저 보라. 코스트맵 부분 삭제(
clearCostmapAroundPose)처럼 직접 구현하면 꽤 성가신 것들이 이미 들어 있다.
8.1.15 nav2_route의 실물 — 실측
8.1.13은 "확인한 것은 이 패키지들이 실재한다는 사실과 등록된 플러그인 이름까지"라고 적고 나머지를 (확인 필요)로 남겼다. Jazzy에 설치해 등록 플러그인을 전수 확인했다(실행 확인 — ros-jazzy-nav2-route 1.3.12).
8.1.15.1 등록된 플러그인 전수
간선 비용 함수(EdgeCostFunction) — 8종:
| 플러그인 | 무엇을 기준으로 |
|---|---|
DistanceScorer | 간선 길이 |
TimeScorer | 예상 주행 시간 |
PenaltyScorer | 간선에 부여한 벌점 |
CostmapScorer | 코스트맵 값 |
SemanticScorer | 의미 태그(구역·통로 종류 등) |
DynamicEdgesScorer | 실행 중 변하는 간선 상태 |
GoalOrientationScorer | 목표에서의 자세 |
StartPoseOrientationScorer | 출발 자세 |
구간 동작(RouteOperation) — 5종: AdjustSpeedLimit(속도 제한 변경) · CollisionMonitor · ReroutingService(주행 중 재경로) · TimeMarker · TriggerEvent.
그래프 입출력: GeoJsonGraphFileLoader · GeoJsonGraphFileSaver — 읽기와 쓰기가 모두 있다.
8.1.13이 적은 이름은 전부 정확했고, 비용 함수 3종(
DynamicEdges·GoalOrientation·StartPoseOrientation)과 동작 1종(TriggerEvent)이 더 있었다.
8.1.15.2 이 목록이 말해 주는 설계
자유 공간 계획과 무엇이 다른가가 플러그인 이름에 드러난다.
- 비용이 간선 단위다. 코스트맵 격자가 아니라 미리 정의한 간선에 벌점·의미·시간을 붙인다 — "이 통로는 지게차 전용"·"이 구간은 야간에만" 같은 운영 규칙을 경로 계획에 직접 표현할 수 있다.
- 자세를 비용에 넣는다(
GoalOrientationScorer·StartPoseOrientationScorer) — 도킹·좁은 통로 진입처럼 어느 방향으로 들어가느냐가 중요한 경우를 다룬다. - 구간마다 동작을 지정한다 — 특정 간선에 들어서면 속도를 낮추거나(
AdjustSpeedLimit) 이벤트를 발행한다(TriggerEvent). 자유 공간 계획에는 이런 개념 자체가 없다.
*14.9.5가 "일방통행 경로·구역별 한 대만 같은 단순 규칙이 협상 알고리즘보다 예측 가능하다"고 한 판단을 경로 계획 층에서 표현하는 수단이 이것이다. 8.1.13이 이 누락을 "의미 있다"*고 적은 이유가 목록으로 확인된다.
8.1.15.3 확인하지 못한 것
- 실제로 그래프를 싣고 경로를 생성해 보지는 않았다 — 등록 목록까지다
(확인 필요: 동작·성숙도). - 각 스코어러의 파라미터와 기본값은 확인하지 않았다.
nav2_route와 기존 planner_server의 연계 방식(어느 쪽이 언제 쓰이는가)은 측정하지 않았다(확인 필요).
8.1.11 함정 카탈로그
| # | 함정 | 증상 | 대처 |
|---|---|---|---|
| 1 | tf 트리 불완전 | Nav2 전반 실패 | view_frames 먼저(5.1.10) |
| 2 | use_sim_time 불일치 | 대량 외삽 오류 | /**:로 전체(1.9.7) |
| 3 | /cmd_vel 타입 불일치 | 명령이 조용히 전달 안 됨 | Jazzy의 diff_drive_controller는 TwistStamped 고정(실행 확인, 6.5.12.1) — ros2 topic info -v로 양쪽 타입 확인 |
| 4 | 라이프사이클 활성화 순서 오류 | 서버 활성화 실패 | node_names 순서 |
| 5 | 지도·초기 자세 없이 autostart | 활성화 실패 | 의존 확인 |
| 6 | 플러그인 이름과 설정 키 불일치 | 플러그인 로드 실패 | 이름 대응 확인 |
| 7 | 하위 계층 미검증 상태로 튜닝 | 해결 안 됨 | 순서 준수(8.1.10) |
| 8 | "Nav2 = 안전"으로 오해 | 안전 논증 불가 | 별도 안전 계층(8.7) |
| 9 | Nav2가 지도를 만든다고 오해 | 구성 혼란 | SLAM 별도(8.2) |
| 10 | 선점 결과를 실패로 처리 | 잘못된 재시도 | 결과 코드 구분(1.8) |
| 11 | Nav2 기본값 그대로 컨트롤러에 연결 | Twist↔TwistStamped 불일치로 로봇이 안 움직임 | enable_stamped_cmd_vel: true(8.1.14.1) |
| 12 | 일부 노드에만 enable_stamped_cmd_vel | 회복 거동에서만 멈춤 | /**:로 일괄(8.1.14.1) |
| 13 | bt_loop_duration을 Hz로 오해 | 주기를 100배로 잘못 잡음 | 밀리초다(8.1.14.2) |
| 14 | expected_planner_frequency로 계획 주기 조절 시도 | 경고 기준일 뿐 | BT XML의 RateController(8.1.14.2) |
8.1.12 체크포인트
- Nav2의 주요 서버 여덟 가지와 각각의 담당을 말할 수 있는가.
- 전역·지역 코스트맵의 차이와, "전역은 가끔 지역은 자주"라는 구조의 근거는?
- 목표에서
/cmd_vel까지의 데이터 흐름을 그릴 수 있는가. 제6부와 만나는 지점은? - Nav2의 라이프사이클 관리가 1.12.6의 어떤 패턴인가.
PAUSE가 실무에서 유용한 이유는? - 주요 액션 인터페이스 여섯 가지와 각각의 용도를 말할 수 있는가.
NavigateToPose의 다중 목표 정책은? - Nav2의 필수 입력 여덟 가지를 들 수 있는가. "Nav2가 안 된다"를 만나면 무엇부터 확인하는가.
- 플러그인 확장 지점 여덟 가지를 말할 수 있는가. "Nav2를 쓴다 = 플러그인을 고른다"의 의미는?
- Nav2가 하지 않는 것 여덟 가지를 들 수 있는가. 안전에 관한 오해가 왜 위험한가.
- 첫 실행 절차 여덟 단계와, 각 단계의 확인 항목을 말할 수 있는가. 순서를 건너뛰면 왜 되돌아오는가.
- Jazzy에서 Nav2와
diff_drive_controller를 기본값으로 연결하면 무슨 일이 생기는가. 왜 진단이 어려운가(8.1.14.1). bt_loop_duration의 단위는? 전역 계획 주기는 어디서 바꾸는가(8.1.14.2).nav2_route의 비용 함수는 무엇을 단위로 매기며, 자유 공간 계획과 어떻게 다른가(8.1.15.2).- 구간 동작(
RouteOperation)이 자유 공간 계획에 없는 개념인 이유는(8.1.15.2).
다음 장 8.2은 Nav2의 필수 입력 중 하나 — 지도를 다룬다. 표현 형식, SLAM Toolbox로 만드는 법, 그리고 지도 품질 문제의 원인.