
ROS2 기반의 자율주행 스택인 Nav2 (Navigation 2)의 개념정리
Nav2의 전체적인 아키텍처와 주요 컴포넌트들을 정리하려한다.

move_base는 ROS1에서 자율주행을 담당하는 핵심 패키지이다. 위의 그림과 같이 하나의 거대한 move_base 블록 안에 모든 기능이 들어 있다.
global_planner, local_planner, costmap, recovery_behaviors 등 핵심 기능들이 하나의 노드 안에서 강하게 결합되어 돌아간다.FSM(Finite State Machine) : 시스템이 가질 수 있는 상태(State)의 개수가 유한하게 정해져 있고, 특정 조건이 만족되면 다른 상태로 넘어가는 작동 방식

Nav2 그림을 보면, 기능들이 독립적인 캡슐처럼 쪼개져 있는 것을 볼 수 있다.
Lifecycle Manager가 시스템을 모니터링하며 각 서버의 상태를 체계적으로 관리한다.Navigation이란 로봇이 주어진 환경에서 장애물을 피해 목적지까지 최적의 경로를 찾아 이동하는 과정을 말한다.
이를 위해서는 다음과 같은 기능들이 필수적이다.
Nav2는 ROS2 환경에서 이 모든 기능을 수행할 수 있도록 여러 ROS 패키지들을 조합하여 만든 네비게이션 프레임워크이다.
Nav2 시스템을 이해하기 위해 반드시 알아야 할 세 가지 핵심 개념은 Action Server, Lifecycle Node, Behavior Tree 이다.
네비게이션은 목표 지점까지 이동하는 데 시간이 오래 걸리는 작업이다. 기본적인 ROS Service는 요청 후 응답이 올 때까지 시스템이 차단(Blocking)되지만, Action은 장시간 실행되는 작업을 비동기적으로 처리하는 데 설계되었다.
예를 들어, Nav2에서는 최상위 행동 트리(BT) 네비게이터가 MapsToPose.action이라는 액션 메시지를 통해 다른 하위 액션 서버들과 통신하며 작업을 수행한다.
MapsToPose.action 파일 구조
# goal definition
geometry_msgs/PoseStamped pose
string behavior_tree# feedback definition
geometry_msgs/PoseStamped current_pose
builtin_interfaces/Duration navigation_time
builtin_interfaces/Duration estimated_time_remaining
int16 number_of_recoveries
float32 distance_remaining
float32 position_tracking_error
float32 heading_tracking_error# result definition
uint16 NONE=0
uint16 UNKNOWN=9000
uint16 FAILED_TO_LOAD_BEHAVIOR_TREE=9001
uint16 TF_ERROR=9002
uint16 TIMEOUT=9003
uint16 error_code
string error_msgROS 2에서는 노드의 상태를 체계적으로 관리하기 위해 Lifecycle Node 개념을 도입했다. 일반적인 노드는 단순히 실행/종료되지만, Lifecycle Node는 여러 단계의 상태를 가지며 제어 가능하다.
Nav2의 모든 주요 서버는 Lifecycle Node로 구현되어 nav2_lifecycle_manager에 의해 관리된다.
상태 단계: Unconfigured → Inactive → Active → Finalized
장점: 시스템 시작 시 파라미터 로딩, 메모리 할당 등을 거쳐 필요한 노드만 정확한 시점에 Active 상태로 전환하여 실행 효율을 높이고 안정성을 보장한다. 충돌 시 시스템을 안전하게 다운시키는 역할도 한다.
아래는 파이썬으로 작성된 간단한 Lifecycle Node 예제이다.
from rclpy.lifecycle import LifecycleNode
from rclpy.lifecycle import State
from rclpy.lifecycle import TransitionCallbackReturn
import rclpy
class MyLifecycleNode(LifecycleNode):
def __init__(self):
super().__init__('my_lifecycle_node')
self.get_logger().info("Lifecycle Node created.")
def on_configure(self, state: State):
self.get_logger().info("Configuring node...")
return TransitionCallbackReturn.SUCCESS
def on_activate(self, state: State):
self.get_logger().info("Activating node...")
return TransitionCallbackReturn.SUCCESS
def on_deactivate(self, state: State):
self.get_logger().info("Deactivating node...")
return TransitionCallbackReturn.SUCCESS
def on_cleanup(self, state: State):
self.get_logger().info("Cleaning up node...")
return TransitionCallbackReturn.SUCCESS
def on_shutdown(self, state: State):
self.get_logger().info("Shutting down node...")
return TransitionCallbackReturn.SUCCESS
if __name__ == '__main__':
rclpy.init()
node = MyLifecycleNode()
rclpy.spin(node)
rclpy.shutdown()
예제 코드를 보면 LifecycleNode를 상속받아 on_configure, on_activate 등의 콜백 함수로 노드의 상태 전이를 세밀하게 제어한다. 이처럼 설정 단계와 실제 동작 단계를 엄격히 분리하게 되면, 무거운 리소스 할당이나 파라미터 로딩을 원하는 시점에 제어할 수 있어 시스템 초기화 시 노드 간의 실행 순서 꼬임을 원천적으로 방지할 수 있다. 뿐만 아니라, 특정 노드에 장애가 발생하더라도 시스템 전체를 강제 종료할 필요 없이 해당 노드만 안전하게 해제(on_cleanup)하고 재설정하여 복구할 수 있으므로, 복잡한 로봇 네비게이션 환경에서 메모리 관리의 효율성과 구동 안정성을 극대화할 수 있다.
Nav2의 의사결정 프로세스를 관리하는 가장 중요한 개념이다. Behavior Tree는 여러 동작(Node)을 나무가지처럼 계층적 자료 구조로 연결하여 로봇의 행동 흐름을 제어한다.
과거의 복잡한 FSM(Finite State Machine)을 대체하여, 수십 개의 상태 전환을 훨씬 단순하게 시각화하고 관리할 수 있다. 트리의 왼쪽 아래부터 오른쪽 순으로 탐색하며 의존성이 없는 노드들이 우선 실행되는 구조이다.
FSM : 상태 기반의 동작 제어(단방향제어 전달)로 단순 작업에는 유용하지만 복잡한 작업에는 문제 발생
Nav2는 BehaviorTree.CPP V3 라이브러리를 사용하여 기본 제공 노드 및 사용자가 직접 만든 Custom Node Plugin을 조합해 유연한 네비게이션 행동을 구축합니다. 이를 통해 장애물 회피, 복구 동작(Spin, Backup) 등을 조합하여 수행할 수 있습니다.
Nav2는 각 네비게이션 핵심 기능을 실행하는 독립적인 서버(Node)들로 분리되어 동작한다. 행동 트리(BT)가 상황에 맞는 서버를 호출하여 작업을 처리한다.
로봇의 현재 위치에서 목표 지점까지 갈 수 있는 전역 경로(Global Path)를 생성한다. 전역 비용 맵(Global Costmap)과 센서 데이터를 기반으로 최적의 경로를 계산한다.
nav2_navfn_planner/NavfnPlanner (GridBased) 등이 사용된다.# yaml 예시
planner_server:
ros__parameters:
planner_plugins: ["GridBased"]
GridBased:
plugin: "nav2_navfn_planner/NavfnPlanner"
전역 경로를 따라 이동할 수 있도록 로컬 경로(Local Path)를 제어하고 로봇의 속도 및 방향 명령(cmd_vel)을 생성한다. 실시간으로 장애물을 회피하며 로봇 속도를 조절한다.
dwb_core::DWBLocalPlanner (FollowPath) 등이 사용된다.# yaml 예시
controller_server:
ros__parameters:
controller_plugins: ["FollowPath"]
FollowPath:
plugin: "dwb_core::DWBLocalPlanner"
Planner가 생성한 전역 경로의 급격한 회전이나 각진 부분을 부드러운 곡선 경로로 조정하여 로봇이 더 자연스럽게 이동하도록 만든다.
네비게이션 실패 시, 로봇이 스스로 문제를 해결하기 위한 복구 동작(Recovery Behaviors)을 실행한다. 장애물로 막혔을 때 제자리에서 회전(Spin)하거나 후진(BackUp)하여 공간을 확보한다. 비용이 큰 자원(costmap 등)에 접근하여 활용한다.
Nav2는 로봇 주변 환경을 인식하고 표현하기 위해 Costmap(비용 지도)을 활용한다.
로봇이 이동할 수 있는 안전 영역과 장애물 위치를 나타내는 2D 그리드 맵이다. 각 셀은 특정 비용(cost)을 가지며, 로봇은 비용이 낮은 곳을 따라 최적의 경로를 설정한다.
Global Costmap : 전체적인 경로 계획(Planner)을 위해 사용된다. 주로 정적인 장애물(벽, 건물 등)을 반영하며 /map 기반으로 업데이트 빈도가 낮다.
Local Costmap : 실시간 장애물 회피 및 단기 경로 계획(Controller)을 위해 사용된다. 로봇 주변 장애물을 실시간 센서 데이터로 감지하여 업데이트하며 업데이트 빈도가 높다.
Costmap은 여러 개의 레이어를 조합하여 생성된다.
/map에서 알려진 장애물(벽) 반영장애물 주변에는 Inflation 레이어에 의해 진한 하늘색(Global) 또는 보라색-빨간색(Local)으로 표현되는 고비용 구역이 설정된다. 로봇 반경이 크거나 높은 비용이 설정된 지역은 이동에 어려움을 겪을 수 있으므로 적절한 파라미터 튜닝이 필요하다.
기존 Costmap에 특정 영역을 필터링하여 추가적인 제한을 적용할 수 있다.
네비게이션 시스템을 사용하기 위해서는 커뮤니티 표준(REP 105)을 준수하는 TF 트리 구성이 필수적이다.
커뮤니티 표준(REP 105)는 좌표게 이름 짓기 및 연결 순서에 대한 표준 규칙이다.
map➔odom➔base_link➔센서들
Nav2는 라이다뿐만 아니라 Vision이나 Depth 기반 위치 결정 시스템과도 함께 사용할 수 있다.
마지막으로 Nav2의 전체 아키텍처를 한눈에 정리해 보자.
Nav2를 사용하는 이유는 명확하다.