A.7 ROS 2 런치 시스템
1. ROS 2 런치 시스템의 학술적 정의 및 도입 목적
1.1 학술적 정의 (Academic Definition)
ROS 2 런치(Launch) 시스템은 분산 로봇 소프트웨어 환경에서 다수의 노드(Node)와 프로세스의 인스턴스화, 구성(Configuration), 그리고 실행 흐름을 자동화하고 제어하는 프로세스 오케스트레이션(Process Orchestration) 프레임워크입니다.
시스템적으로 이는 사용자가 선언한 LaunchDescription 객체를 해석하여 시스템의 초기 상태를 정의하고, 런타임 환경에서 요구되는 변수 할당, 통신 토폴로지 설정, 그리고 조건부 실행 논리를 평가하는 엔진 역할을 수행합니다. ROS 1의 정적(Static) 마크업 언어 기반 아키텍처에서 진화하여, 프로그래밍 언어(주로 Python)의 튜링 완전성(Turing-completeness)을 활용해 시스템의 실행 명세를 동적이고 절차적으로 모델링합니다.
1.2 도입 목적 (Purpose of Introduction)
로봇 시스템의 규모가 커지고 복잡도가 증가함에 따라, 단일 컴포넌트의 독립적 실행을 넘어 전체 시스템 차원의 통합 관리가 필수적으로 요구되었습니다. ROS 2 런치 시스템의 핵심 도입 목적은 다음과 같습니다.
- 다중 프로세스 오케스트레이션 및 자동화
현대의 로봇 애플리케이션은 센서 드라이버, 인지, 계획, 제어 등 수십에서 수백 개의 독립적인 노드로 구성됩니다. 런치 시스템은 개발자가 개별 노드를 수동으로 실행하는 비효율성을 제거하고, 의존성에 따른 실행 순서 보장 및 병렬 시작을 자동화하여 시스템 부팅 프로세스를 일관되게 관리합니다.
- 런타임 구성(Runtime Configuration)의 동적 할당
소스 코드의 재컴파일 없이 로봇의 동작 환경에 맞게 시스템을 동적으로 재구성하기 위함입니다. 런치 시스템은 실행 시점에 매개변수(Parameters)를 주입하고, 토픽 리매핑(Topic Remapping)을 통해 통신 그래프를 변경하며, 네임스페이스(Namespace)를 할당하여 다중 로봇 환경에서 시스템 간의 충돌을 논리적으로 격리합니다.
- 상태 기반 이벤트 처리 및 생명주기 제어 (Lifecycle Management)
단순한 프로세스 실행을 넘어, 실행 중인 프로세스의 상태 변화(예: 비정상 종료)를 감지하고 대응하는 메커니즘을 제공합니다. 특히 ROS 2에 도입된 라이프사이클 노드(Lifecycle Node)의 상태 천이(State Transition - 예: Unconfigured -> Inactive -> Active)를 이벤트 핸들러를 통해 프로그래밍적으로 제어하여, 시스템의 안정성과 결함 허용성(Fault Tolerance)을 높입니다.
- ROS 1
roslaunch의 한계 극복 및 아키텍처 유연성 확보
ROS 1의 XML 기반 roslaunch는 복잡한 조건문 처리나 외부 시스템 스크립트와의 통합에 한계가 있었습니다. ROS 2는 Python 기반 API를 기본 프론트엔드로 채택함으로써, 런타임 환경 변수 평가, 파일 시스템 접근, 복잡한 조건부 로직(IfCondition, UnlessCondition)을 런치 명세에 직접 포함할 수 있는 극대화된 유연성을 제공하기 위해 도입되었습니다.
2. 다중 노드 관리 및 시스템 자동화의 기술적 필요성
ROS 2(Robot Operating System 2) 환경에서 다중 노드 관리 및 시스템 자동화 기능을 제공하는 런치(Launch) 시스템의 도입은 단순한 편의성 증대를 넘어, 분산형 로봇 소프트웨어 아키텍처가 정상적으로 작동하기 위한 필수적인 기술적 요구사항입니다. 그 구체적인 기술적 필요성은 다음과 같습니다.
2.1 마이크로서비스 아키텍처(Microservice Architecture)의 스케일링 한계 극복
- 현상: ROS 2 기반의 현대 로봇 시스템은 센서 데이터 수집, 인지, SLAM(동시적 위치추정 및 지도작성), 경로 계획, 모터 제어 등 각 기능이 독립된 프로세스인 노드(Node) 단위로 분리되어 실행됩니다. 상용 로봇 시스템의 경우 동시에 구동되어야 하는 노드의 수가 수십에서 수백 개에 달합니다.
- 필요성: 관리자가 개별 터미널에서 CLI(Command Line Interface)를 통해 각 노드를 수동으로 실행하고 종료하는 것은 물리적으로 불가능하며, 인적 오류(Human Error)를 유발합니다. 따라서 단일 명령어로 전체 시스템 트리를 기동하고 해제할 수 있는 자동화된 진입점(Entry Point)이 요구됩니다.
2.2 런타임 종속성(Runtime Dependency) 및 실행 순서의 결정론적 제어
- 현상: 다중 노드 환경에서는 노드 간의 엄격한 실행 종속성이 존재합니다. 예를 들어, 자율주행 알고리즘 노드는 라이다(LiDAR) 드라이버 노드가 정상적으로 데이터를 발행(Publish)하기 전까지 대기해야 하거나, 특정 환경 설정 노드가 완료된 후 실행되어야 합니다.
- 필요성: 시스템은 프로세스의 시작, 종료, 또는 특정 시간 경과 등의 상태 변화를 감지하고, 이에 반응하여 다음 노드를 실행하는 이벤트 구동(Event-driven) 방식의 자동화 시퀀스를 보장해야 합니다.
2.3 통신 토폴로지(Topology) 및 구성(Configuration)의 동적 주입
- 현상: 동일한 로봇 소프트웨어 패키지라 하더라도, 실행 환경(실제 하드웨어, Gazebo 시뮬레이션, 테스트 환경 등)이나 로봇의 식별자(다중 로봇 환경에서의 Robot ID)에 따라 각 노드가 참조해야 하는 설정값이 달라집니다.
- 필요성: 소스 코드를 수정하지 않고 런타임에 네임스페이스(Namespace)를 부여하여 토픽 충돌을 방지하고, 토픽 리매핑(Topic Remapping) 규칙을 일괄 적용하며, 외부 YAML 파일을 통해 수천 개의 파라미터(Parameters)를 정확한 노드에 주입하는 중앙 집중화된 구성 자동화 시스템이 필요합니다.
2.4 장애 허용성(Fault Tolerance) 및 생명주기(Lifecycle) 관리
- 현상: 분산 시스템에서는 특정 노드가 메모리 누수나 하드웨어 통신 오류로 인해 비정상 종료(Crash)될 수 있습니다.
- 필요성: 주요 노드가 종료되었을 때 이를 즉각적으로 감지하여 해당 프로세스만 다시 실행(Respawn)하거나, 시스템의 안전을 위해 연관된 다른 노드들을 순차적으로 안전하게 종료시키는 상태 기반 대응이 필요합니다. 또한, ROS 2에 도입된 라이프사이클 노드(Lifecycle Node)의 상태 천이(Unconfigured \rightarrow Inactive \rightarrow Active)를 중앙에서 오케스트레이션(Orchestration)하기 위한 자동화 프레임워크가 필수적입니다.
2.5 컴포넌트(Component) 기반 자원 할당 최적화
- 현상: ROS 2는 IPC(Inter-Process Communication) 오버헤드를 줄이기 위해 여러 노드를 단일 프로세스의 메모리 공간에 적재하는 컴포넌트 컨테이너(Component Container) 기술을 지원합니다.
- 필요성: 시스템 설계 시점의 요구사항에 따라 특정 노드들을 독립된 프로세스로 실행할지, 아니면 공유 메모리 기반의 컨테이너에 동적으로 로드(Load)할지를 런타임에 결정하고 배치(Deployment)하는 스크립트 기반의 자동화 제어력이 요구됩니다.
3. ROS 1 roslaunch 대비 구조적 변화 및 Python 기반 아키텍처 특징
ROS 2 런치 시스템은 기존 ROS 1 roslaunch의 한계를 극복하고 복잡한 분산 로봇 소프트웨어의 요구사항을 충족하기 위해 아키텍처가 전면 개편되었습니다. 가장 핵심적인 기술적 변화는 XML 기반의 정적 선언 방식에서 Python 기반의 동적 프로그래밍 모델로의 전환입니다.
3.1 ROS 1과 ROS 2 런치 시스템의 구조적 비교
| 비교 항목 | ROS 1 roslaunch | ROS 2 launch 시스템 |
|---|---|---|
| 기본 작성 언어 | XML | Python (XML, YAML 추가 지원) |
| 논리 구조 | 정적(Static), 선언적(Declarative) | 동적(Dynamic), 절차적/객체지향적 |
| 상태 및 이벤트 제어 | 제한적 (순차 실행 등 복잡한 제어 불가) | 내장된 이벤트 기반 아키텍처 (Event-Driven) |
| 모듈화 및 확장성 | <include> 태그를 통한 단편적 파일 병합 | Python 모듈 임포트 및 시스템 레벨의 객체 재사용 |
| 디버깅 및 테스트 | roslaunch 전용 파서 및 콘솔 출력 의존 | 표준 Python 디버거(pdb) 및 단위/통합 테스트 프레임워크 활용 |
3.2 Python 기반 아키텍처의 기술적 특징
- 튜링 완전성(Turing Completeness)을 통한 동적 실행 제어: Python의 조건문, 반복문, 예외 처리 메커니즘을 런치 파일 내에서 완전하게 사용할 수 있습니다. 이를 통해 런타임 환경 변수, 네트워크 상태, 하드웨어 연결 유무에 따라 시스템 실행 시나리오를 동적으로 재구성할 수 있습니다.
- 객체 지향적 시스템 명세(Object-Oriented Specification): 시스템을 구성하는 모든 행위(노드 실행, 파라미터 로드, 스크립트 호출 등)가 Python 클래스 객체(Action)로 추상화됩니다. 생성된 인스턴스들은 최종적으로
LaunchDescription이라는 최상위 컨테이너 객체에 수집되어 런타임에 평가됩니다. - 이벤트 구동형(Event-Driven) 라이프사이클 관리: 프로세스의 시작, 종료, 지연(Delay) 등의 상태 변화를 시스템 이벤트로 감지합니다. 특정 이벤트가 발생했을 때 연쇄적으로 다른 프로세스를 실행하거나 종료하도록 이벤트 핸들러(Event Handler)를 구성하여 정밀한 타이밍 제어가 가능합니다.
- 프로세스 관리 프레임워크의 범용성: ROS 2의 핵심 런치 기능인
launch패키지는 ROS 프레임워크에 종속되지 않는 범용 운영체제 프로세스 관리 도구로 설계되었습니다. ROS 관련 기능은launch_ros패키지로 분리되어 있어, 일반 시스템 데몬이나 외부 바이너리 스크립트도 ROS 노드와 동일한 생명주기 하에서 통합 제어할 수 있습니다. - 검증 및 자동화 테스트 통합: 런치 파일 자체가 유효한 Python 모듈이므로,
pytest와 같은 표준 테스트 프레임워크와 결합하여 시스템 통합 테스트(Integration Test)를 자동화하기 용이합니다. 런치 시스템 내에서 프로세스의 표준 출력(stdout)을 캡처하여 검증하는 테스트 파이프라인 구축이 가능합니다.
4. 시스템에서 지원하는 프론트엔드 언어 규격 (Python, XML, YAML)
ROS 2 런치(Launch) 시스템은 시스템의 복잡도와 사용자의 목적에 따라 세 가지 프론트엔드(Frontend) 언어 형식을 지원합니다. 세 가지 규격 모두 내부적으로는 launch 패키지의 아키텍처(구체적으로 파이썬 기반의 LaunchDescription 객체)로 파싱되어 실행되므로, 최종적인 런타임 성능상의 차이는 없습니다. 각 규격의 학술적 및 구조적 특징은 다음과 같습니다.
4.0.1 ### 4.1 파이썬 (Python, .launch.py)
파이썬은 ROS 2 런치 시스템의 근간이 되는 언어이자 가장 높은 자유도를 제공하는 명령형(Imperative) 프론트엔드입니다.
- 구조적 특징: 프로그래밍 언어의 모든 제어문(if-else, for 루프 등)과 자료구조를 런치 파일 내에서 온전히 활용할 수 있습니다.
launch및launch_rosAPI에 직접 접근하여 노드와 액션 객체를 인스턴스화합니다. - 적용 환경: 외부 스크립트 실행 결과에 따른 조건부 노드 실행, 런타임 환경 변수에 기반한 동적 매개변수 생성, 복잡한 라이프사이클(Lifecycle) 이벤트 핸들링 등 고도화된 동적 시스템 구성에 필수적입니다.
- 설계적 한계: 선언적 방식에 비해 코드가 길어지고 직관적인 가독성이 저하될 수 있으며, 디버깅 시 파이썬 스크립트 수준의 문법 및 로직 검증이 요구됩니다.
4.1 XML (eXtensible Markup Language, .launch.xml)
XML은 ROS 1의 roslaunch 문법과 가장 유사한 형태를 띠는 선언적(Declarative) 프론트엔드입니다.
- 구조적 특징: 태그(Tag)와 속성(Attribute) 기반의 계층적 구조를 가집니다. ROS 1에서 ROS 2로 시스템을 마이그레이션하는 과정에서 기존 시스템 설계자들에게 높은 친숙함을 제공합니다.
- 적용 환경: 정적인 토픽 리매핑, 단순 다중 노드 병렬 실행, 고정된 매개변수 할당 등 런타임 조건 분기가 없는 직관적인 실행 명세에 적합합니다.
- 설계적 한계: 복잡한 런타임 연산이나 동적 이벤트 처리가 제한적이며, 파이썬 API에서 제공하는 일부 심화 기능을 XML 태그로 100% 매핑하여 지원하지는 않습니다.
4.2 YAML (YAML Ain’t Markup Language, .launch.yaml)
YAML은 데이터 직렬화(Data Serialization) 양식을 활용한 또 다른 선언적 프론트엔드입니다.
- 구조적 특징: 들여쓰기 기반의 키-값(Key-Value) 쌍으로 구성되어, XML 대비 마크업 오버헤드(태그 등)가 배제되므로 기계 판독성 및 인간 가독성이 가장 뛰어납니다.
- 적용 환경: 매개변수 설정 파일(
.yaml)과의 문법적 일관성을 유지할 수 있어, 구성 데이터(Configuration) 중심의 시스템 배포 및 정적 환경 구성에 유리합니다. - 설계적 한계: XML과 마찬가지로 프로그래밍 로직을 내장할 수 없으며, 노드와 액션이 매우 복잡하게 중첩되는 아키텍처에서는 들여쓰기 구조를 파악하기 어려워질 수 있습니다.
[프론트엔드 규격 요약 비교]
| 구분 | 지원 확장자 | 설계 패러다임 | 코드 가독성 | 로직/제어 유연성 | ROS 1 친숙도 |
|---|---|---|---|---|---|
| Python | .launch.py | 명령형 / 객체지향 | 상대적 낮음 | 매우 높음 (동적 제어) | 낮음 |
| XML | .launch.xml | 선언적 (마크업) | 보통 | 제한적 (정적 구성) | 높음 |
| YAML | .launch.yaml | 선언적 (데이터) | 뛰어남 | 제한적 (정적 구성) | 낮음 |
5. 코어 패키지 아키텍처: launch 패키지와 launch_ros 패키지의 분리 및 역할
ROS 2 런치 시스템은 범용성과 확장성을 확보하기 위해 코어 아키텍처를 launch 패키지와 launch_ros 패키지로 엄격하게 분리하여 설계되었습니다. 이러한 분리 아키텍처의 학술적 의미와 각 패키지의 기술적 역할을 아래와 같이 기술합니다.
5.1 아키텍처 분리의 목적
ROS 1의 roslaunch가 ROS 생태계에 강하게 결합되어 있던 것과 달리, ROS 2는 의존성을 역전시키고 모듈성을 높이는 방향으로 재설계되었습니다.
가장 핵심적인 설계 철학은 **“ROS 프레임워크에 종속되지 않는 범용 프로세스 관리 도구”**와 **“ROS 2 특화 기능 명세”**를 분리하는 것입니다. 이를 통해 시스템 프로세스 제어의 유연성을 극대화하고 소프트웨어 의존성을 최소화합니다.
5.2 launch 패키지의 역할 및 특징
launch 패키지는 ROS 2 미들웨어(DDS)나 ROS 개념(토픽, 서비스, 파라미터 등)을 전혀 알지 못하는 순수 Python 기반의 범용 프로세스 실행 및 관리 프레임워크입니다.
- 의존성: ROS 환경에 종속되지 않으며 독립적인 Python 라이브러리로 동작 가능합니다.
- 주요 기능:
- 프로세스 생명주기 관리: OS 레벨의 프로세스를 실행, 모니터링, 종료(
ExecuteProcess액션). - 이벤트 기반 아키텍처 (Event-Driven Architecture): 특정 프로세스가 시작되거나 종료될 때, 혹은 특정 타이머가 만료되었을 때 상태를 전이시키는 이벤트(
Event) 및 이벤트 핸들러(EventHandler) 시스템 제공. - 치환 (Substitutions) 평가: 런타임에 변수, 환경 변수, 경로 등을 동적으로 평가하여 명령어로 치환(
EnvironmentVariable,LaunchConfiguration). - 제어 흐름 추상화: 조건부 실행(
IfCondition,UnlessCondition), 그룹화(GroupAction) 등의 로직 처리.
5.3 launch_ros 패키지의 역할 및 특징
launch_ros 패키지는 launch 패키지의 기본 아키텍처(Actions, Events, Substitutions)를 상속받아 ROS 2 도메인에 특화된 기능을 제공하는 확장 레이어입니다.
- 의존성:
launch패키지와 ROS 2 코어 라이브러리(rclpy등)에 강하게 의존합니다. - 주요 기능:
- 노드(Node) 실행 추상화: 범용 프로세스 실행을 래핑하여 ROS 2 노드를 실행하기 위한
launch_ros.actions.Node액션 제공. - 통신 토폴로지 구성: 토픽 이름 재지정(Remapping) 및 네임스페이스(Namespace) 푸시(
PushRosNamespace) 기능. - 파라미터(Parameter) 주입: 노드 실행 시 인라인 파라미터 딕셔너리 및 외부 YAML 파일 연동 지원.
- 상태 머신 제어: ROS 2 라이프사이클 노드(Lifecycle Node)의 상태 천이(Configure, Activate 등)를 제어하는 특화된 이벤트 및 핸들러(
LifecycleTransition등) 제공.
5.4 패키지 간 구성요소 비교
다음은 두 패키지의 핵심 클래스 및 동작을 비교한 표입니다.
| 구분 | launch (범용 프로세스 관리) | launch_ros (ROS 2 특화 확장) |
|---|---|---|
| 핵심 액션 (Actions) | ExecuteProcess, LogInfo, TimerAction, IncludeLaunchDescription | Node, LifecycleNode, PushRosNamespace, SetParametersFromFile |
| 이벤트 (Events) | ProcessStarted, ProcessExited | StateTransition (라이프사이클 관련) |
| 치환 (Substitutions) | EnvironmentVariable, FindExecutable, Command | FindPackageShare (ROS 2 패키지 경로 탐색) |
결론적으로, 사용자가 작성하는 대부분의 ROS 2 런치 파일은 범용 논리 흐름을 구성하기 위해 launch 패키지를, 실제 로봇 노드 구동 및 ROS 통신망 설정을 위해 launch_ros 패키지를 동시에 임포트하여 사용하게 됩니다. 이 계층적 분리 덕분에 ROS 2 런치 시스템은 단순한 ROS 노드 실행기를 넘어 복잡한 시스템 통합 테스트 및 자동화 스크립트 도구로 활용될 수 있습니다.
6. 런치 시스템의 주요 동작(Actions) 및 프로세스 제어 메커니즘
ROS 2 런치 시스템에서 Action은 런치 프로세스 내에서 실행, 평가 또는 시스템 상태 변경을 유발하는 가장 기본적인 논리적 단위입니다. 런치 시스템은 이러한 Action 객체들의 집합을 순차적 혹은 비동기적으로 평가하여 전체 시스템의 생명주기를 관리합니다.
주요 동작(Actions)과 이를 기반으로 한 프로세스 제어 메커니즘은 다음과 같이 분류됩니다.
6.1 핵심 실행 동작 (Execution Actions)
이 동작들은 시스템 내에서 실제 프로세스를 생성하고 실행하는 역할을 담당합니다.
launch_ros.actions.Node: ROS 2 환경에 특화된 노드 실행 객체입니다. 패키지 이름(package), 실행 파일(executable), 노드 이름(name)을 필수적으로 명세하며, 네임스페이스(namespace), 파라미터(parameters), 토픽 리매핑(remappings) 등의 ROS 특화 구성을 캡슐화하여 프로세스에 주입합니다.launch.actions.ExecuteProcess: ROS 2 노드에 국한되지 않고, 운영체제(OS) 수준의 임의의 명령어(CLI)나 외부 스크립트를 실행하기 위한 동작입니다.cmd매개변수를 통해 실행할 명령어 배열을 전달하며, 시스템 환경 변수 설정 등을 지원합니다.
6.2 구조화 및 논리 제어 동작 (Structuring & Logic Actions)
다수의 노드와 프로세스를 체계적으로 관리하고, 실행 흐름을 제어하기 위한 동작입니다.
launch.actions.IncludeLaunchDescription: 다른 런치 파일(.launch.py,.launch.xml등)을 현재 런치 컨텍스트에 포함시킵니다. 이를 통해 모듈화된 런치 시스템 구축이 가능하며, 대규모 시스템에서 코드 재사용성을 확보합니다.launch.actions.GroupAction: 여러 개의Action객체를 단일 논리적 그룹으로 묶습니다. 그룹 내에 포함된 모든 하위 동작들에 대해 특정 네임스페이스를 일괄 적용하거나,IfCondition/UnlessCondition과 결합하여 조건부 렌더링(실행) 범위를 지정할 때 사용됩니다.launch.actions.TimerAction: 런치 시스템 평가 과정에서 시간 지연을 부여합니다. 지정된 시간(초)이 경과한 후 내부의 하위Action을 실행하도록 스케줄링하여, 프로세스 간의 단순한 순차적 초기화 지연이 필요할 때 활용됩니다.
6.3 상태 관리 및 로깅 동작 (State Management & Logging)
시스템의 런타임 상태를 모니터링하고 기록하는 동작입니다.
launch.actions.LogInfo: 런치 콘솔의 표준 출력(stdout)으로 문자열 데이터를 출력합니다. 런타임에 평가되는 치환(Substitutions) 변수 값을 확인하거나, 특정 분기점 도달을 기록하는 디버깅 목적으로 사용됩니다.launch.actions.DeclareLaunchArgument: 외부(CLI 등)로부터 런타임 매개변수를 입력받기 위한 인자를 선언합니다. 이 동작이 평가되어야만 시스템 내에서 해당 인자의 값을 참조할 수 있습니다.
6.4 프로세스 제어 메커니즘: 이벤트 처리 (Event-Driven Control)
ROS 2 런치 시스템은 단순한 순차 실행을 넘어, 프로세스의 상태 변화(시작, 종료 등)를 감지하고 이에 반응하는 비동기적 이벤트 드리븐(Event-Driven) 제어 메커니즘을 제공합니다.
launch.actions.RegisterEventHandler: 시스템 내에서 발생하는 특정 이벤트(Event)를 감지하고, 지정된 조건이 충족될 때 등록된 동작을 트리거하는 핵심 제어 객체입니다.- 주요 이벤트 핸들러 (
launch.event_handlers): OnProcessStart: 특정 프로세스(노드 등)가 성공적으로 시작되었을 때 다른Action을 실행합니다. (예: 데이터베이스 노드가 켜진 후 로직 노드 실행)OnProcessExit: 프로세스가 종료되었을 때 트리거됩니다. 프로세스의 종료 코드(Exit Code)를 평가하여 정상 종료 시 후속 작업을 진행하거나, 비정상 종료 시 시스템 전체를 셧다운(launch.actions.EmitEvent(launch.events.Shutdown))하는 등 의존성 관리에 사용됩니다.- 프로세스 재시작 (Respawn):
Node동작 내에respawn=True속성을 부여하면, 프로세스가 예기치 않게 종료되었을 때 런치 시스템이 이를 감지하고 자동으로 해당 프로세스를 다시 시작하여 시스템 가용성을 유지합니다.
7. 런타임 변수 평가를 위한 치환(Substitutions) 기능의 원리
7.1 치환(Substitution)의 학술적 정의 및 목적
ROS 2 런치 시스템에서 ’치환(Substitution)’은 런치 파일이 파싱 및 정의되는 시점(Definition time)이 아닌, 시스템이 실제로 실행되는 시점(Runtime)에 동적으로 값을 평가(Evaluate)하여 반환하는 객체입니다. 시스템의 유연성을 확보하기 위해 환경 변수, 런치 인자(Launch Arguments), 파일 시스템 경로 등을 런타임 문맥(Launch Context)에 맞게 해석하는 역할을 수행합니다.
7.2 지연 평가(Delayed Evaluation) 메커니즘
치환 기능의 핵심은 지연 평가 아키텍처에 기반합니다.
- 정의 단계 (Declaration):
LaunchDescription객체 생성 시, 치환 대상은 문자열(String)이 아닌launch.substitutions.Substitution를 상속받는 추상 객체 형태로 메모리에 적재됩니다. - 평가 단계 (Evaluation):
LaunchService가 노드를 실행하거나 액션을 수행할 때, 현재 실행 환경의 상태 정보를 담고 있는LaunchContext객체를 치환 객체의perform(context)메서드에 전달합니다. - 결과 반환: 치환 객체는 전달받은 문맥 정보를 바탕으로 최종 문자열(String)을 연산하여 반환합니다.
7.3 주요 치환 클래스(Substitution Classes) 및 기능 명세
런치 시스템에서 제공하는 표준 치환 객체들은 다음과 같습니다.
LaunchConfiguration:DeclareLaunchArgument를 통해 런치 시스템에 선언된 매개변수(Argument)의 값을 런타임에 호출합니다. 사용자의 CLI 입력이나 상위 런치 파일의 주입에 따라 노드의 동작 파라미터를 변경할 때 필수적으로 사용됩니다.EnvironmentVariable: 운영체제의 환경 변수(OS Environment Variables) 값을 읽어옵니다. (예:USER,ROS_DOMAIN_ID)PathJoinSubstitution: 운영체제에 독립적인(OS-agnostic) 파일 시스템 경로 병합을 수행합니다. 패키지 경로와 하위 디렉토리 경로를 운영체제의 파일 시스템 규격에 맞춰 안전하게 연결합니다.FindExecutable: 시스템의PATH환경 변수에 등록된 특정 실행 파일의 절대 경로를 탐색하여 반환합니다.Command: 서브프로세스(Subprocess)를 통해 셸 명령어(Shell Command)를 실행하고, 해당 프로세스의 표준 출력(stdout) 결과를 문자열로 반환합니다. 주로xacro파일을 파싱하여 URDF 로봇 모델을 동적으로 생성할 때 사용됩니다.FindPackageShare/FindPackagePrefix:launch_ros패키지에서 제공하는 도메인 특화 기능으로, 빌드된 ROS 2 패키지의share또는prefix디렉토리 절대 경로를 런타임 시스템 내에서 동적으로 탐색합니다.
7.4 치환 객체의 결합 (Composition of Substitutions)
복수의 치환 객체 또는 정적 문자열은 배열(List) 형태로 결합될 수 있습니다. 런치 시스템은 배열 내의 모든 요소를 순차적으로 평가한 후, 이를 단일 문자열로 병합(Concatenation)하여 최종 인자로 전달합니다. 이를 통해 패키지 경로 해석, 환경 변수 주입, 파일명 지정 등을 단일 구문 내에서 복합적이고 안전하게 수행할 수 있습니다.
8. 이벤트(Events) 모델 및 이벤트 핸들러(Event Handlers)의 처리 구조
ROS 2 런치 시스템은 비동기 이벤트 구동(Asynchronous Event-Driven) 아키텍처를 기반으로 런타임 상태 변화를 관리합니다. 시스템의 실행 흐름은 순차적인 스크립트 실행이 아니라, 프로세스의 시작, 종료, 타이머 만료와 같은 상태 변화를 시스템에 보고하고 이에 반응하여 후속 동작을 동적으로 스케줄링하는 방식으로 이루어집니다.
- 이벤트(Event) 모델의 정의: 런치 시스템 내에서 발생하는 모든 유의미한 상태 변화는
launch.events.Event클래스를 상속받는 객체로 인스턴스화됩니다. 시스템은 운영 체제 수준의 프로세스 상태 변화(예: PID 생성, 종료 코드 반환), 타이머(Timer) 만료, 또는 ROS 2 라이프사이클 노드의 상태 전환(State Transition) 정보를 이벤트 객체에 담아 시스템에 방출(Emit)합니다. - 런치 서비스(Launch Service)와 이벤트 루프: 시스템의 코어 엔진인
LaunchService는 내부적으로 Python의asyncio라이브러리를 활용하여 비동기 이벤트 루프를 구동합니다. 이 루프는 런치 시스템이 가동되는 동안 지속적으로 활성화되어, 시스템 내부 계층 및 자식 프로세스에서 발생하는 모든 이벤트를 수신(Listen)하고 전역 이벤트 대기열(Queue)에 등록합니다. - 이벤트 핸들러(Event Handler)의 구조: 발생한 이벤트에 대한 시스템의 반응 로직을 정의하는 엔티티입니다.
EventHandler객체는 특정 타입의 이벤트를 필터링하는 매칭 로직과, 해당 이벤트가 감지되었을 때 반환할 새로운 액션(Action) 목록으로 구성됩니다. 핸들러는 정적인 실행 계획을 런타임에 동적으로 수정할 수 있는 권한을 가집니다. - 이벤트 처리 파이프라인(Processing Pipeline): 특정 이벤트가 런치 서비스 대기열에서 꺼내어지면(Dequeued), 시스템은 런치 컨텍스트(Launch Context)에 등록된 모든 이벤트 핸들러를 순회하며 해당 이벤트를 처리할 적격성이 있는지 평가합니다. 조건이 일치하는 핸들러가 식별되면, 해당 핸들러가 품고 있는 후속 액션들이 런치 시스템의 실행 큐에 새롭게 주입(Inject)되어 즉각적으로 실행됩니다.
- 주요 내장 이벤트 핸들러: ROS 2
launch패키지는 빈번하게 사용되는 이벤트 처리 패턴을 내장 객체로 제공합니다. OnProcessStart: 특정 프로세스가 운영 체제로부터 PID를 할당받고 실행을 시작한 직후 트리거됩니다.OnProcessExit: 프로세스가 종료되었을 때 트리거되며, 정상 종료 코드 혹은 세그먼테이션 오류(Segmentation Fault)와 같은 비정상 종료 상태를 판별하여 후속 조치(예: 에러 로그 출력, 프로세스 재시작)를 수행하는 데 사용됩니다.OnExecutionComplete: 특정 액션 객체의 실행이 완전히 종료되었음을 알릴 때 사용됩니다.OnShutdown: 런치 시스템 전체의 종료 시퀀스가 시작될 때 발생하는 이벤트를 가로채어, 자원 해제나 안전한 종료 로직을 강제하는 데 활용됩니다.
9. LaunchDescription 객체를 활용한 런치 파일의 런타임 명세 구조
9.1 LaunchDescription 객체의 학술적 정의
ROS 2 런치 시스템에서 launch.LaunchDescription 클래스는 시스템 실행 시 평가되어야 할 모든 런치 엔티티(Launch Entities)를 포함하는 최상위(Root) 컨테이너이다. 이 객체는 런치 프로세스의 생명주기 동안 실행될 작업(Actions), 이벤트 핸들러(Event Handlers), 상태 전이 명세, 시스템 변수 등을 순차적 혹은 조건부로 기술하는 선언적 데이터 구조를 제공한다.
9.2 런타임 명세 구조 및 평가(Evaluation) 메커니즘
- 실행 큐(Execution Queue) 등록:
LaunchDescription에 추가된 엔티티들은 런치 서비스(launch.LaunchService)로 전달되어 실행 큐에 등록된다. - 엔티티 트리(Entity Tree) 확장: 초기에는 1차원 리스트 형태로 엔티티를 수용하지만, 특정 엔티티(예:
IncludeLaunchDescription,GroupAction)가 하위 엔티티를 포함할 수 있으므로 런타임 평가 단계에서는 다차원적인 트리 구조로 전개(Yield)된다. - 지연 평가(Lazy Evaluation):
LaunchDescription명세 내에 포함된 치환(Substitutions) 파라미터나 조건문(Conditions)은 객체 생성 시점이 아닌, 해당 엔티티가 런치 서비스에 의해 방문(Visited) 및 실행되는 런타임 시점에 최종적으로 평가된다. 이를 통해 런타임 환경 변수나 CLI 입력값에 기반한 동적 제어가 성립된다.
9.3 구성 요소 주입 및 조작 방식
LaunchDescription 객체는 인스턴스 생성자(Constructor)를 통해 초기 엔티티 리스트를 배열 형태로 주입받는 방식을 표준으로 한다. 또한, 인스턴스화 이후 객체의 add_action() 메서드를 호출하여 제어 흐름에 따라 런타임에 동적으로 엔티티를 추가하는 것도 허용된다.
9.4 명세 구조의 추상화 및 코드 기반 표현 (Python API 기준)
표준적인 ROS 2 런치 시스템 구동을 위해, Python 런치 파일은 파일 최상단에 generate_launch_description()이라는 전역 진입점(Entry-point) 함수를 정의해야 하며, 이 함수는 반드시 유효한 LaunchDescription 객체를 반환해야 한다.
from launch import LaunchDescription
from launch.actions import DeclareLaunchArgument, LogInfo
from launch.substitutions import LaunchConfiguration
from launch_ros.actions import Node
def generate_launch_description():
# 1. 런치 인자(Arguments) 선언 명세
arg_sim_time = DeclareLaunchArgument(
'use_sim_time',
default_value='false',
description='Use simulation (Gazebo) clock if true'
)
# 2. 실행할 프로세스 동작(Action) 명세
log_action = LogInfo(
msg="System is initializing..."
)
# 3. ROS 2 노드(Node) 실행 명세
sensor_node = Node(
package='sensor_package',
executable='sensor_node_exe',
name='primary_sensor',
parameters=[{'use_sim_time': LaunchConfiguration('use_sim_time')}]
)
# 4. LaunchDescription 객체 생성 및 엔티티 주입
# 리스트에 명시된 순서가 최상위 엔티티의 초기 평가 순서가 됨
return LaunchDescription([
arg_sim_time,
log_action,
sensor_node
])
9.5 구조적 특성 요약
- 결정론적 선언(Deterministic Declaration): 시스템 시작 시 실행해야 할 대상(What)과 환경(Context)을 명시적으로 제한하고 정의한다.
- 상태 비저장(Stateless) 아키텍처:
LaunchDescription객체 자체는 단순히 실행 계획(Plan)을 담고 있는 정적 명세서이며, 런타임의 동적 상태(프로세스 PID, 노드 생존 여부 등)를 직접 저장하거나 추적하지 않는다. 실제 상태 관리와 프로세스 모니터링은 이 명세서를 해석하는 코어LaunchService와 하위 이벤트 시스템으로 완전히 위임된다.
10. 단일 및 다중 노드(Node)의 패키지, 실행 파일, 노드명 지정 방식
ROS 2 런치 시스템에서 단일 및 다중 노드를 실행하기 위해서는 launch_ros.actions 모듈에서 제공하는 Node 클래스를 활용하여 각 노드의 실행 명세를 정의해야 합니다. 이 과정은 시스템이 올바른 바이너리를 찾고, 런타임 환경에서 고유한 식별자를 부여하도록 지정하는 핵심 절차입니다.
10.1 핵심 인자(Arguments) 명세
Node 객체를 생성할 때 필수적이고 가장 기본이 되는 세 가지 인자는 다음과 같습니다.
package(패키지): 실행하려는 노드가 포함된 ROS 2 패키지의 이름을 지정합니다. 시스템은 이 이름을 바탕으로 ROS 2 워크스페이스 내에서 해당 패키지의 설치 경로(예:install/<package_name>/lib/<package_name>)를 탐색합니다.executable(실행 파일): 패키지 내에서 실제로 실행할 컴파일된 바이너리 파일명(C++의 경우) 또는 스크립트 파일명(Python의 경우)을 지정합니다.CMakeLists.txt또는setup.py에 정의된 엔트리 포인트(Entry point) 이름과 정확히 일치해야 합니다.name(노드명): 런타임 시 ROS 2 네트워크 상에서 해당 노드를 식별하기 위한 고유한 이름을 지정합니다. 이 인자를 생략할 경우 소스 코드 내에서 초기화된 기본 노드 이름이 사용되지만, 런치 파일을 통한 명시적 지정을 통해 소스 코드 수정 없이 노드의 이름을 동적으로 재정의(Override)할 수 있습니다.
10.2 단일 노드(Single Node) 지정 방식
단일 노드를 실행할 때는 하나의 Node 객체를 생성하고, 이를 LaunchDescription에 포함하여 반환합니다.
from launch import LaunchDescription
from launch_ros.actions import Node
def generate_launch_description():
return LaunchDescription([
Node(
package='turtlesim',
executable='turtlesim_node',
name='my_turtle_simulator',
output='screen'
)
])
위 명세는 turtlesim 패키지의 turtlesim_node 실행 파일을 my_turtle_simulator라는 식별자로 실행하도록 시스템에 지시합니다.
10.3 다중 노드(Multiple Nodes) 지정 방식
복수의 노드를 동시에 실행하기 위해서는 필요한 만큼의 Node 객체를 생성하여 LaunchDescription의 리스트 내에 순차적으로 나열합니다. 다중 노드 구성 시 가장 중요한 기술적 고려사항은 이름 충돌(Name Collision) 방지입니다.
동일한 실행 파일을 여러 번 인스턴스화할 경우, 반드시 name 인자나 namespace 인자를 다르게 지정하여 ROS 2 그래프 상에서 각 노드가 고유하게 식별되도록 설계해야 합니다.
from launch import LaunchDescription
from launch_ros.actions import Node
def generate_launch_description():
# 첫 번째 노드 명세
talker_node = Node(
package='demo_nodes_cpp',
executable='talker',
name='sensor_data_publisher'
)
# 두 번째 노드 명세 (동일한 패키지와 실행 파일, 다른 노드명)
talker_node_alt = Node(
package='demo_nodes_cpp',
executable='talker',
name='diagnostics_publisher'
)
# 세 번째 노드 명세
listener_node = Node(
package='demo_nodes_py',
executable='listener',
name='data_subscriber'
)
return LaunchDescription([
talker_node,
talker_node_alt,
listener_node
])
학술적 의의:
이러한 객체 지향적 명세 방식은 기존 ROS 1의 XML 기반 정적 태그(pkg, type, name) 구조에서 탈피하여, 프로그래밍 언어의 변수 및 객체를 통한 동적이고 프로그래매틱한 토폴로지 구성을 가능하게 합니다. 다중 노드 실행 시 각 노드는 독립적인 프로세스로 포크(Fork)되어 운영체제 수준에서 병렬로 관리됩니다.
11. CLI 기반 실행 메커니즘 및 ros2 launch 명령어 체계
ROS 2의 ros2 launch 명령줄 인터페이스(CLI)는 다중 노드 및 프로세스 명세서인 런치 파일을 파싱하고, 비동기 이벤트 루프 기반으로 시스템을 구동하는 핵심 엔트리 포인트입니다. 해당 메커니즘은 단순히 스크립트를 순차적으로 실행하는 것을 넘어, 종속성 평가, 매개변수 주입, 생명주기 관리를 총괄합니다.
11.1 CLI 기반 실행 메커니즘 (Execution Mechanism)
ros2 launch 명령어가 호출될 때 내부적으로 수행되는 아키텍처 흐름은 다음과 같습니다.
- 명령어 파싱 (Command Parsing):
ros2cli프레임워크가 입력된 패키지 이름, 런치 파일 이름, 런치 인자(Launch Arguments), 그리고 CLI 옵션을 구문 분석합니다. - 파일 탐색 및 로드 (File Resolution and Loading):
- 패키지 이름이 제공된 경우,
ament_index_python라이브러리를 사용하여 현재 환경 변수(AMENT_PREFIX_PATH)에 등록된 워크스페이스 내에서 해당 패키지의share디렉토리를 탐색합니다. - 지정된 런치 파일(Python, XML, YAML)을 동적으로 로드합니다.
- 객체 인스턴스화 (Instantiation): 로드된 런치 파일 내의
generate_launch_description()함수(Python의 경우)를 호출하여 시스템 구성이 정의된LaunchDescription객체를 생성합니다. - 서비스 구동 (Service Execution): 생성된
LaunchDescription객체는launch.LaunchService인스턴스에 전달됩니다.LaunchService는 Python의asyncio기반 이벤트 루프를 생성하여, 정의된 모든 엔티티(Entities)와 동작(Actions)을 비동기적으로 평가하고 프로세스를 포크(Fork)하여 실행합니다.
11.2 ros2 launch 명령어 구문 (Command Syntax)
ros2 launch CLI는 패키지 기반 실행과 절대/상대 경로 기반 실행을 모두 지원합니다.
패키지 기반 실행 (표준 방식):
ros2 launch <package_name> <launch_file_name> [arguments]
<package_name>: 대상 런치 파일이 포함된 ROS 2 패키지명.<launch_file_name>: 실행할 런치 파일의 이름 (예:demo.launch.py).
경로 기반 직접 실행:
ros2 launch <path_to_launch_file> [arguments]
- 패키지 빌드 및 소싱(sourcing) 과정 없이 특정 경로의 런치 파일을 직접 실행할 때 사용됩니다. 독립적인 테스트에 주로 활용됩니다.
11.3 런치 인자 주입 (Launch Argument Injection)
런타임에 시스템 구성을 동적으로 변경하기 위해 CLI를 통해 런치 인자를 전달할 수 있습니다. 런치 인자는 런치 파일 내부에서 LaunchConfiguration 객체로 치환(Substitution)되어 노드의 매개변수나 조건부 실행의 플래그로 사용됩니다.
인자 전달 구문:
ros2 launch <package_name> <launch_file_name> key:=value
- 여러 개의 인자를 전달할 경우 공백으로 구분하여
key1:=value1 key2:=value2형태로 덧붙입니다.
11.4 주요 디버깅 및 인트로스펙션 옵션 (Introspection Options)
런치 시스템은 파일 실행 전후의 상태를 점검하고 디버깅하기 위한 다양한 CLI 옵션을 제공합니다.
-s,--show-args- 지정된 런치 파일에서 사용 가능한 모든 런치 인자(Launch Arguments)의 목록, 기본값, 그리고 설명을 출력합니다. 실제 프로세스는 실행하지 않습니다.
-p,--print- 실행될
LaunchDescription의 전체 계층 구조와 포함된 노드, 치환자, 이벤트 등을 콘솔에 계층적으로 출력합니다(Dry-run). 런치 파일의 논리적 오류를 사전에 검증하는 데 필수적인 옵션입니다. -a,--show-all-subentities--print옵션과 함께 사용되며, 축약된 하위 엔티티들까지 모두 강제로 확장하여 가장 상세한 수준의 런치 구조를 출력합니다.-d,--debug- 런치 시스템의 코어 엔진(LaunchService 등)에서 발생하는 디버그 수준의 로그 메시지를 활성화하여 프로세스 포크, 이벤트 처리 과정 등의 심층적인 디버깅 정보를 제공합니다.
12. 인라인(Inline) 및 외부 YAML 구성을 통한 매개변수(Parameters) 주입 기법
12. 인라인(Inline) 및 외부 YAML 구성을 통한 매개변수(Parameters) 주입 기법
ROS 2 시스템에서 매개변수(Parameter)는 노드(Node)의 런타임 동작을 제어하고 구성하는 핵심 요소입니다. 런치(Launch) 시스템은 노드를 실행하는 시점에 이러한 매개변수를 주입(Injection)하기 위해 launch_ros.actions.Node 클래스의 parameters 인자를 활용합니다. 매개변수를 주입하는 방식은 크게 런치 파일 내부에 직접 작성하는 ’인라인 방식’과 외부 파일에서 불러오는 ’YAML 구성 방식’으로 나뉩니다.
인라인(Inline) 매개변수 주입
인라인 주입 방식은 런치 파일의 Python 코드 내부에 매개변수의 키(Key)와 값(Value)을 딕셔너리(Dictionary) 형태로 직접 명시하는 방법입니다.
-
특징 및 활용: 소수의 매개변수를 설정하거나, 런치 실행 시점에 결정되는 동적 변수(
LaunchConfiguration등)를 매개변수 값으로 할당해야 할 때 주로 사용됩니다. -
자료형 지원: 정수, 실수, 문자열, 불리언(Boolean) 및 이들의 배열(List) 형태를 지원합니다.
-
구현 예시:
Python
from launch_ros.actions import Node
from launch.substitutions import LaunchConfiguration
def generate_launch_description():
return LaunchDescription([
Node(
package='sensor_driver',
executable='lidar_node',
name='front_lidar',
parameters=[
{'port_name': '/dev/ttyUSB0'}, # 문자열 매개변수
{'baud_rate': 115200}, # 정수 매개변수
{'publish_tf': True}, # 불리언 매개변수
{'frame_id': LaunchConfiguration('frame')} # 동적 치환 변수
]
)
])
외부 YAML 구성을 활용한 매개변수 주입
대규모 로봇 시스템에서는 하나의 노드가 수십 개의 매개변수를 요구할 수 있습니다. 이 경우 런치 파일에 모든 값을 명시하는 것은 유지보수성을 저하시키므로, 설정 값을 별도의 .yaml 파일로 분리하여 로드하는 방식을 채택합니다.
-
특징 및 활용: 코드(런치 파일)와 데이터(매개변수 설정)를 분리하는 소프트웨어 공학적 설계 원칙(Separation of Concerns)을 따릅니다. 동일한 노드를 다양한 환경(시뮬레이션, 실제 로봇 등)에서 실행할 때 설정 파일만 교체하여 재사용성을 극대화할 수 있습니다.
-
경로 해석: 런타임 오류를 방지하기 위해 절대 경로를 사용해야 하며, 일반적으로
ament_index_python패키지를 통해 설치된 패키지의 공유 디렉토리(Share directory) 경로를 동적으로 추적하여 결합합니다. -
구현 예시:
Python
import os
from ament_index_python.packages import get_package_share_directory
from launch_ros.actions import Node
def generate_launch_description():
# YAML 파일의 절대 경로 생성
config_file_path = os.path.join(
get_package_share_directory('navigation_pkg'),
'config',
'nav_params.yaml'
)
return LaunchDescription([
Node(
package='navigation_pkg',
executable='planner_node',
name='global_planner',
parameters=[config_file_path] # YAML 파일 경로를 리스트 형태로 주입
)
])
혼합 주입 및 덮어쓰기 (Overriding) 메커니즘
parameters 인자는 리스트(List) 구조를 취하므로, 여러 개의 YAML 파일과 인라인 딕셔너리를 동시에 전달할 수 있습니다. 런치 시스템은 리스트에 명시된 순서대로 매개변수를 평가합니다.
-
우선순위 원칙: 리스트의 뒤쪽에 위치한 매개변수 설정이 앞쪽에 위치한 설정을 덮어씁니다(Override).
-
응용 전략: 먼저 뼈대가 되는 대규모 기본 설정 파일(YAML)을 로드한 뒤, 현재 런치 실행 컨텍스트에 맞게 변경해야 하는 특정 변수만 인라인 딕셔너리로 추가하여 값을 재할당하는 패턴이 널리 사용됩니다.
Python
parameters=[
config_file_path, # 1. 수십 개의 기본값이 담긴 YAML 로드
{'use_sim_time': True}, # 2. 인라인으로 특정 값 덮어쓰기
{'robot_id': LaunchConfiguration('id')} # 3. 런타임 인자로 동적 할당
]
13. 통신 토폴로지 재구성을 위한 토픽 리매핑(Topic Remapping) 적용
13.1 통신 토폴로지 재구성을 위한 토픽 리매핑(Topic Remapping) 적용
13.1. 개념 및 학술적 목적
토픽 리매핑(Topic Remapping)은 ROS 2 시스템에서 노드(Node)의 컴파일된 소스 코드를 수정하지 않고, 런타임(Runtime) 환경에서 해당 노드가 발행(Publish)하거나 구독(Subscribe)하는 토픽의 식별자(이름)를 동적으로 변경하는 메커니즘입니다.
이 기능은 소프트웨어 컴포넌트의 재사용성을 보장하기 위한 핵심 설계 패턴입니다. 다중 로봇 시스템이나 복잡한 센서 네트워크를 구성할 때 발생하는 토픽 이름의 충돌(Namespace Collision)을 방지하고, 시스템 설계자의 의도에 맞춰 노드 간의 데이터 흐름(통신 토폴로지)을 유연하게 재배선(Rewiring)하는 데 목적이 있습니다.
13.2. 동작 원리
ROS 2 노드가 실행 및 초기화될 때, 소스 코드 내부에 하드코딩된 기본(Default) 토픽 이름은 런치 시스템으로부터 주입된 리매핑 규칙에 의해 인터셉트(Intercept)되어 새로운 문자열로 치환됩니다. 이 치환 과정은 ROS 2의 클라이언트 라이브러리(RCL) 계층에서 처리되므로, 비즈니스 로직을 담당하는 상위 계층은 통신 대상의 변경 사실을 알 필요 없이 투명하게 동작합니다.
13.3. Python 런치 아키텍처에서의 명세 방법
Python 기반의 ROS 2 런치 파일에서는 launch_ros.actions.Node 엔티티를 인스턴스화할 때 remappings 매개변수를 활용합니다. 리매핑 규칙은 ('기존_토픽명', '대상_토픽명') 형태의 튜플(Tuple)을 포함하는 리스트(List) 구조로 전달됩니다.
13.4. 구현 코드 예시
다음은 범용 카메라 드라이버 노드를 실행하면서, 기본 출력 토픽인 /image_raw를 특정 목적을 가진 /left_camera/image_color로 재구성하는 런치 파일의 명세입니다.
Python
from launch import LaunchDescription
from launch_ros.actions import Node
def generate_launch_description():
return LaunchDescription([
Node(
package='generic_camera_driver',
executable='camera_node',
name='left_camera_sensor',
output='screen',
# remappings 인자를 통한 통신 토폴로지 재구성
remappings=[
('/image_raw', '/left_camera/image_color'),
('/camera_info', '/left_camera/camera_info_calibrated')
]
)
])
13.5. 시스템 설계 시의 기술적 이점
- 모듈 간 의존성 제거 (Decoupling): 데이터 처리 알고리즘(예: 객체 인식 노드)과 하드웨어 제어(예: 카메라 드라이버) 노드가 특정 토픽 이름에 종속되는 것을 방지하여, 독립적인 모듈 개발 및 테스트를 가능하게 합니다.
- 동일 노드의 다중 인스턴스화 (Multi-instantiation): 2개 이상의 동일한 센서(예: 스테레오 카메라의 좌/우 렌즈)나 액추에이터를 구동할 때, 동일한 실행 파일을 사용하되 리매핑을 통해 각 인스턴스의 데이터 스트림을 물리적으로 격리할 수 있습니다.
14. 시스템 격리를 위한 네임스페이스(Namespace) 설정 방법
ROS 2 환경에서 네임스페이스(Namespace)는 노드, 토픽, 서비스, 액션 등의 ROS 자원 식별자(Identifier)에 계층적인 접두사(Prefix)를 부여하여 독립적인 통신 영역을 구축하는 논리적 격리 기법입니다. 주로 다중 로봇 시스템이나 동일한 알고리즘의 다중 인스턴스 실행 시 발생하는 식별자 충돌(Name Collision)을 원천적으로 방지하기 위해 사용됩니다.
런치 시스템에서 네임스페이스를 설정하고 시스템을 격리하는 방법은 적용 범위에 따라 크게 세 가지 범주로 분류됩니다.
14.1 개별 노드 수준의 네임스페이스 설정 (Node-Level Namespace)
가장 기초적인 격리 방식으로, 특정 노드 단일 개체에만 네임스페이스를 할당합니다. launch_ros.actions.Node 클래스의 인스턴스를 생성할 때 namespace 매개변수를 명시적으로 전달합니다. 이 방식이 적용된 노드에서 생성되는 모든 통신 인터페이스는 해당 네임스페이스를 상속받습니다.
Python
from launch_ros.actions import Node
def generate_launch_description():
# 개별 노드에 'robot1' 네임스페이스 부여
sensor_node = Node(
package='sensor_package',
executable='sensor_node',
name='camera_sensor',
namespace='robot1' # 결과 토픽: /robot1/camera_sensor/...
)
return LaunchDescription([sensor_node])
14.2 그룹 기반의 일괄 네임스페이스 격리 (Group-Level Namespace)
다수의 노드나 컴포넌트를 논리적인 단일 시스템으로 묶어 동일한 네임스페이스를 부여할 때 사용되는 방식입니다. launch.actions.GroupAction을 사용하여 실행 컨텍스트(Execution Context)의 범위를 제한하고, launch_ros.actions.PushRosNamespace를 통해 해당 그룹 내부에 네임스페이스 상태를 주입(Push)합니다.
Python
from launch import LaunchDescription
from launch.actions import GroupAction
from launch_ros.actions import Node, PushRosNamespace
def generate_launch_description():
# 'robot2' 네임스페이스를 공유하는 노드 그룹 생성
robot2_group = GroupAction(
actions=[
PushRosNamespace('robot2'),
Node(
package='navigation_pkg',
executable='nav_node',
name='navigator'
),
Node(
package='control_pkg',
executable='controller_node',
name='controller'
)
]
)
return LaunchDescription([robot2_group])
14.3 외부 서브 런치 파일의 네임스페이스 격리 (Include-Level Namespace)
모듈화된 대규모 시스템 아키텍처에서 하위 런치 파일을 가져올 때(IncludeLaunchDescription), 해당 파일에 정의된 모든 노드와 통신 체계를 특정 네임스페이스 하위로 종속시키는 기법입니다. 기존 컴포넌트의 소스 코드나 런치 파일을 수정하지 않고도 다중 인스턴스화를 가능하게 합니다.
Python
from launch import LaunchDescription
from launch.actions import GroupAction, IncludeLaunchDescription
from launch.launch_description_sources import PythonLaunchDescriptionSource
from launch_ros.actions import PushRosNamespace
import os
from ament_index_python.packages import get_package_share_directory
def generate_launch_description():
sub_launch_file = os.path.join(
get_package_share_directory('complex_robot_system'),
'launch',
'core_system.launch.py'
)
# 포함되는 전체 런치 시스템을 'swarm_agent_1' 하위로 격리
agent_1_system = GroupAction(
actions=[
PushRosNamespace('swarm_agent_1'),
IncludeLaunchDescription(
PythonLaunchDescriptionSource(sub_launch_file)
)
]
)
return LaunchDescription([agent_1_system])
네임스페이스 적용 시 주의사항:
- 절대 경로(‘/’)와 상대 경로의 차이: 네임스페이스 설정 시 접두사에 슬래시(
/)를 붙이면 절대 네임스페이스로 평가되어 상위PushRosNamespace의 영향을 무시하게 됩니다. 동적 격리를 위해서는 슬래시 없는 상대 경로를 사용하는 것이 원칙입니다. - 글로벌 매개변수와의 충돌: 네임스페이스가 격리되더라도
/rosout이나/parameter_events와 같은 ROS 2 시스템의 전역 토픽은 기본적으로 네임스페이스의 영향을 받지 않고 시스템 전체에서 공유됩니다.
15. 런타임 동적 구성을 위한 런치 인자(Launch Arguments) 정의 및 활용
15. 런타임 동적 구성을 위한 런치 인자(Launch Arguments) 정의 및 활용
15.1. 런치 인자(Launch Arguments)의 학술적 정의 및 시스템적 목적
ROS 2 런치 시스템에서 런치 인자는 시스템 실행 시점(Runtime)에 외부로부터 변수 값을 주입받아 런치 기술(Launch Description) 내의 동작(Action) 및 노드 구성을 동적으로 제어하기 위한 매개변수화(Parameterization) 메커니즘입니다. 동일한 런치 파일을 다양한 로봇 환경, 네임스페이스, 시뮬레이션 및 하드웨어 조건에서 수정 없이 재사용할 수 있도록 시스템의 모듈화(Modularity)와 유연성(Flexibility)을 보장하는 핵심 요소입니다.
15.2. 인자 선언: DeclareLaunchArgument 액션
런치 시스템 내에서 특정 인자를 활용하기 위해서는 Python API의 launch.actions.DeclareLaunchArgument 클래스를 사용하여 명시적인 선언 과정을 거쳐야 합니다. 이 선언은 런치 프로세스에게 해당 인자의 존재와 특성을 알리는 역할을 합니다.
name(필수): 런치 인자를 식별하는 고유한 문자열 키(Key)입니다.default_value(선택): 외부에서 값이 주입되지 않았을 때 시스템이 평가할 예비 값(Fallback value)입니다. 이 값이 생략될 경우, 해당 인자는 필수 주입 인자로 간주되어 런타임 에러를 발생시킵니다.description(선택): 인자의 목적과 유효한 입력 형식 등을 기술하는 메타데이터입니다. 터미널에서ros2 launch ... --show-args명령어 입력 시 시스템 사용자를 위한 도움말로 출력됩니다.choices(선택): 입력 가능한 문자열 값의 집합을 제한하는 제약 조건(Constraints)으로, 유효하지 않은 값이 주입되는 것을 런타임 초기에 차단합니다.
15.3. 런타임 변수 평가: LaunchConfiguration 치환(Substitution)
선언된 런치 인자의 실제 값은 launch.substitutions.LaunchConfiguration 클래스를 통해 참조됩니다. 이는 정적 변수가 아닌 치환(Substitution) 객체로 작동하며, 런치 기술(Launch Description)이 생성되는 파싱(Parsing) 시점이 아니라 개별 액션이 실행되는 런타임(Runtime) 시점에 외부 주입 값 또는 기본값 문자열로 지연 평가(Lazy Evaluation)됩니다. 평가된 값은 노드의 이름, 매개변수(Node Parameters), 파일 경로 조립, 혹은 IfCondition과 같은 조건부 실행 논리에 주입됩니다.
15.4. 외부 값 주입 메커니즘
정의된 런치 인자에 값을 할당하는 방식은 크게 두 가지 아키텍처로 구분됩니다.
- CLI(Command Line Interface) 기반 주입:
운영체제 셸(Shell) 환경에서 시스템을 구동할 때, := 연산자를 사용하여 직접 값을 할당합니다.
구문: ros2 launch <package_name> <launch_file> <argument_name>:=<value>
- 계층적 런치 시스템(IncludeLaunchDescription)을 통한 주입:
상위 런치 파일이 하위 런치 파일을 포함(Include)할 때, launch_arguments 매개변수에 딕셔너리(Dictionary) 또는 튜플 리스트 형태의 키-값 쌍을 전달하여 프로그래밍 방식으로 값을 주입합니다. 이를 통해 복잡한 대규모 시스템의 계층적 변수 전파가 가능해집니다.
15.5. 구조적 구현 명세 (Python API)
다음은 런치 인자의 선언, 치환, 그리고 노드 구성에 적용되는 일련의 논리적 흐름을 나타내는 표준 명세입니다.
Python
from launch import LaunchDescription
from launch.actions import DeclareLaunchArgument
from launch.substitutions import LaunchConfiguration
from launch_ros.actions import Node
def generate_launch_description():
# 1. LaunchConfiguration 객체 생성 (런타임 평가용 참조 변수)
use_sim_time_arg = LaunchConfiguration('use_sim_time')
# 2. DeclareLaunchArgument를 통한 런치 인자 시스템 선언
declare_use_sim_time = DeclareLaunchArgument(
name='use_sim_time',
default_value='false',
choices=['true', 'false'],
description='Boolean flag to indicate whether to use simulation time'
)
# 3. 평가된 런치 인자를 대상 노드의 파라미터로 주입
example_node = Node(
package='my_robot_package',
executable='my_robot_node',
name='robot_controller',
parameters=[{
'use_sim_time': use_sim_time_arg
}]
)
# 4. LaunchDescription 객체에 액션 적재 및 반환
return LaunchDescription([
declare_use_sim_time,
example_node
])
16. IfCondition 및 UnlessCondition을 활용한 조건부 실행(Conditional Execution)
ROS 2 런치 시스템에서 IfCondition과 UnlessCondition은 런타임에 결정되는 변수나 런치 인자(Launch Arguments)의 값에 따라 특정 액션(Action)이나 노드(Node)의 실행 여부를 동적으로 제어하는 조건부 실행 메커니즘입니다. 이 기능은 단일 런치 파일 내에서 시뮬레이션과 실제 하드웨어 환경의 분리, 디버깅 모드 전환, 선택적 시각화 도구 실행 등 다양한 런타임 구성을 구현할 때 사용됩니다.
작동 원리 및 문자열 평가 기준
해당 조건문들은 launch.conditions 모듈에 정의되어 있으며, 평가 대상이 되는 문자열 표현식이나 치환(Substitution) 객체를 입력받아 논리적 참(True) 또는 거짓(False)을 판별합니다.
- IfCondition: 전달된 평가식이 참에 해당하는 문자열일 경우 객체에 연결된 액션을 실행합니다. 판별 과정에서 대소문자를 구분하지 않으며,
'true','1','t','y','yes','on'문자열을 참으로 간주합니다. - UnlessCondition:
IfCondition의 역논리로 동작합니다. 전달된 평가식이 거짓에 해당하는 문자열일 경우에만 연결된 액션을 실행합니다. 대소문자 구분 없이'false','0','f','n','no','off'문자열을 입력받았을 때 조건이 성립된 것으로 간주하여 액션을 실행시킵니다.
구현 구조 및 코드 명세
조건부 실행 객체는 Node 또는 IncludeLaunchDescription과 같은 런치 액션 인스턴스를 생성할 때 condition 매개변수에 할당됩니다. 주로 외부 입력을 참조하는 LaunchConfiguration 객체와 결합하여 구성됩니다.
Python
from launch import LaunchDescription
from launch.actions import DeclareLaunchArgument
from launch.conditions import IfCondition, UnlessCondition
from launch.substitutions import LaunchConfiguration
from launch_ros.actions import Node
def generate_launch_description():
# 런치 인자 선언 (기본값: 'true')
gui_arg = DeclareLaunchArgument(
'use_gui',
default_value='true',
description='GUI 시각화 도구 실행 여부 지정'
)
use_gui_config = LaunchConfiguration('use_gui')
# IfCondition: use_gui_config가 참('true', '1' 등)으로 평가될 때만 노드 실행
rviz_node = Node(
package='rviz2',
executable='rviz2',
name='rviz2',
condition=IfCondition(use_gui_config)
)
# UnlessCondition: use_gui_config가 거짓('false', '0' 등)으로 평가될 때만 노드 실행
headless_logger_node = Node(
package='data_logger_pkg',
executable='headless_logger',
name='headless_logger',
condition=UnlessCondition(use_gui_config)
)
return LaunchDescription([
gui_arg,
rviz_node,
headless_logger_node
])
위의 런치 명세에 따라, CLI에서 ros2 launch <패키지명> <런치파일명> use_gui:=false 명령을 통해 인자를 주입할 경우 IfCondition이 부여된 rviz2 노드의 프로세스 생성은 차단되며, UnlessCondition이 부여된 headless_logger 노드의 프로세스만 시스템 메모리에 로드되어 실행됩니다.
17. 프로세스 생명주기 제어: OnProcessExit, Respawn 등 상태 기반 이벤트 처리
17. 프로세스 생명주기 제어: OnProcessExit, Respawn 등 상태 기반 이벤트 처리
ROS 2 런치(Launch) 시스템은 단일 스크립트의 순차적 실행을 넘어, 실행 중인 프로세스(노드)의 상태 변화를 실시간으로 감지하고 대응하는 비동기식 이벤트 구동(Event-Driven) 아키텍처를 제공합니다. 이를 통해 다중 노드로 구성된 복잡한 로봇 시스템의 실행 순서를 동적으로 제어하고, 예기치 않은 프로세스 종료에 대한 복원력을 확보할 수 있습니다.
17.1 이벤트 구동 아키텍처 개요 (Event-Driven Architecture Overview)
런치 시스템 내의 모든 프로세스 상태 변화(시작, 종료, 표준 출력 발생 등)는 ‘이벤트(Event)’ 객체로 시스템 내부에 발행(Emit)됩니다.
시스템 설계자는 RegisterEventHandler 액션을 사용하여 특정 이벤트가 발생했을 때 지정된 콜백(Callback)이나 후속 액션(Action)을 실행하도록 ’이벤트 핸들러(Event Handler)’를 등록할 수 있습니다. 이 메커니즘은 노드 간의 엄격한 실행 의존성(Execution Dependency)을 부여할 때 핵심적으로 사용됩니다.
17.2 OnProcessExit 핸들러의 원리 및 적용
OnProcessExit는 특정 대상 프로세스(Target Process)가 종료(Exit)되었을 때 발생하는 ProcessExited 이벤트를 감지하여 사전에 정의된 액션 리스트를 실행하는 이벤트 핸들러입니다.
주요 활용 목적:
- 순차적 실행 보장: 초기화, 파라미터 로드, 맵 서버 등 선행되어야 하는 일회성 노드가 정상 종료된 후 메인 제어 노드를 실행해야 할 때 사용됩니다.
- 캐스케이딩 종료 (Cascading Shutdown): 핵심 프로세스(예: 로봇 하드웨어 드라이버)가 종료되었을 때, 런치 시스템 전체에
Shutdown이벤트를 발생시켜 모든 관련 노드를 안전하게 동시 종료시킵니다.
구현 명세 (Python API 기준):
Python
from launch.actions import RegisterEventHandler, EmitEvent, LogInfo
from launch.event_handlers import OnProcessExit
from launch.events import Shutdown
from launch_ros.actions import Node
# 대상 프로세스 정의
primary_node = Node(
package='my_package',
executable='primary_executable',
name='primary_node'
)
# 이벤트 핸들러 등록
shutdown_on_exit_handler = RegisterEventHandler(
event_handler=OnProcessExit(
target_action=primary_node,
on_exit=[
LogInfo(msg="Primary node exited. Shutting down the launch system."),
EmitEvent(event=Shutdown()) # 런치 시스템 전체 종료 이벤트 발행
]
)
)
17.3 Respawn 메커니즘을 통한 자가 복구 (Self-Healing via Respawn)
로봇 운영 중 일시적인 네트워크 오류나 메모리 누수 등으로 인해 특정 노드가 비정상 종료(Crash)될 수 있습니다. Respawn 기능은 프로세스가 종료되었을 때 이를 즉시 또는 지정된 지연 시간(Delay) 이후에 자동으로 재시작하는 기능입니다.
ROS 2 런치 시스템에서는 Node 액션 객체의 매개변수로 이를 간단히 통합하여 지원합니다. 내부적으로는 OnProcessExit 이벤트와 유사하게 프로세스 종료 이벤트를 감시하고, 지정된 조건에 따라 재실행 액션을 시스템 큐에 삽입하는 방식으로 작동합니다.
구현 명세 (Python API 기준):
Python
from launch_ros.actions import Node
resilient_node = Node(
package='sensor_driver',
executable='lidar_node',
name='lidar_sensor',
respawn=True, # 프로세스 종료 시 자동 재시작 활성화
respawn_delay=2.0 # 재시작 전 2.0초 대기 (CPU 과점유 및 무한 재시작 루프 방지)
)
17.4 기타 연계 이벤트 핸들러 (Other Related Event Handlers)
상태 기반 제어를 고도화하기 위해 OnProcessExit 외에도 다음과 같은 핸들러들이 제공됩니다.
OnProcessStart: 특정 프로세스가 운영체제 레벨에서 성공적으로 시작되었을 때 액션을 트리거합니다. 노드 실행 시점 직후에 특정 진단 메시지를 출력하거나 지연 타이머를 시작할 때 유용합니다.OnProcessIO: 타겟 프로세스의 표준 출력(stdout)이나 표준 에러(stderr) 스트림에 특정 문자열 패턴이 감지되었을 때 액션을 트리거합니다. 노드가 특정 초기화 완료 메시지(예: “Server is ready”)를 출력했을 때 후속 노드를 실행하는 등 정밀한 동기화에 활용됩니다.
18. ROS 2 라이프사이클 노드(Lifecycle Node)의 상태 천이(State Transition) 제어
ROS 2의 라이프사이클 노드(Managed Node)는 시스템의 초기화, 실행, 종료 과정을 결정론적(Deterministic)으로 제어하기 위해 고안된 유한 상태 기계(Finite State Machine) 기반의 아키텍처를 가집니다. ROS 2 런치(Launch) 시스템은 이러한 라이프사이클 노드의 상태를 런타임에 동적으로 제어하고, 노드 간의 상태 의존성을 관리하는 핵심적인 역할을 수행합니다.
18.1 라이프사이클 노드의 주요 상태 및 천이 명령
라이프사이클 노드는 4개의 기본 상태(Primary States)와 각 상태를 이동하기 위한 천이 명령(Transition Commands)으로 구성됩니다. 런치 시스템은 이 명령들을 이벤트 형태로 발생시켜 노드의 상태를 제어합니다.
- 기본 상태 (Primary States):
Unconfigured: 노드가 인스턴스화되었으나 통신 인프라나 메모리가 할당되지 않은 초기 상태.Inactive: 설정이 완료되어 퍼블리셔, 서브스크라이버 등이 생성되었으나, 실제 데이터 퍼블리싱이나 콜백 처리는 차단된 대기 상태.Active: 데이터 송수신 및 콜백 처리가 정상적으로 수행되는 실행 상태.Finalized: 노드가 소멸되기 직전의 최종 상태.- 천이 명령 (Transitions):
configure:Unconfigured\rightarrowInactiveactivate:Inactive\rightarrowActivedeactivate:Active\rightarrowInactivecleanup:Inactive\rightarrowUnconfiguredshutdown: 임의의 기본 상태 \rightarrowFinalized
18.2 런치 시스템의 상태 제어 핵심 컴포넌트
launch_ros 패키지는 일반 노드와 라이프사이클 노드를 구분하여 처리하며, 상태 제어를 위해 다음과 같은 특화된 클래스와 이벤트를 제공합니다.
launch_ros.actions.LifecycleNode:
일반 Node 액션 클래스를 상속받아 구현되며, 런치 시스템이 해당 노드를 라이프사이클 노드로 인식하고 고유의 서비스(/node_name/change_state, /node_name/get_state)를 통해 통신할 수 있도록 합니다.
launch_ros.events.lifecycle.ChangeState:
특정 라이프사이클 노드에 상태 천이를 요청하는 이벤트 객체입니다. 천이 명령(예: lifecycle_msgs.msg.Transition.TRANSITION_CONFIGURE)을 인자로 포함합니다.
launch.actions.EmitEvent:
런치 시스템 내에서 ChangeState와 같은 이벤트를 발생시켜 실제 상태 천이를 트리거하는 액션입니다.
launch_ros.event_handlers.OnStateTransition:
특정 노드의 상태가 변경되었을 때 발생하는 이벤트를 감지하는 핸들러입니다. 목표 상태(Target State)에 도달했을 때 후속 액션을 실행하도록 콜백을 정의할 수 있습니다.
18.3 상태 천이 제어 및 의존성 관리 구현 메커니즘
런치 시스템에서 라이프사이클 노드를 제어하는 가장 일반적인 패턴은 이벤트 기반의 순차적 상태 천이입니다.
- 초기 실행 및 자동 천이: 노드가 실행되면 기본적으로
Unconfigured상태에 놓입니다. 런치 파일 내에서EmitEvent를 사용하여 노드 실행 직후configure이벤트를 발생시키고, 이후 연속적으로activate이벤트를 발생시켜 노드를 가동 상태로 만듭니다. - 노드 간 상태 의존성 체인(Dependency Chain): 대규모 로봇 시스템에서는 센서 드라이버가 완전히 활성화된 후 알고리즘 노드가 켜져야 하는 등의 실행 순서 보장이 필수적입니다. 이를 위해
OnStateTransition핸들러를 사용합니다.
- 예시: 센서 노드(Node A)가
Unconfigured에서Inactive로 천이 완료됨을 감지 \rightarrow 알고리즘 노드(Node B)의configure이벤트 발생. - 센서 노드(Node A)가
Inactive에서Active로 천이 완료됨을 감지 \rightarrow 알고리즘 노드(Node B)의activate이벤트 발생.
이러한 이벤트 기반 제어 아키텍처를 통해 ROS 2 런치 시스템은 시작 순서 충돌이나 데이터 손실 없이 다수의 노드를 안정적으로 초기화하고 종료할 수 있는 환경을 제공합니다.
19. IncludeLaunchDescription을 활용한 런치 파일의 모듈화 및 재사용성 확보
19. IncludeLaunchDescription을 활용한 런치 파일의 모듈화 및 재사용성 확보
1. 개념 및 기술적 목적
ROS 2 런치 시스템에서 IncludeLaunchDescription 액션(Action)은 독립적으로 작성된 하위 런치 파일을 상위 런치 파일 내로 병합하여 실행할 수 있도록 지원하는 핵심 API이다. 대규모 로봇 소프트웨어 시스템을 단일 런치 파일로 구성할 경우 발생하는 코드 중복 및 유지보수성 저하 문제를 해결하기 위해 도입되었다. 이를 통해 센서 드라이버, 네비게이션 스택, 로봇 모델 상태 발행(Robot State Publisher) 등 특정 도메인의 기능을 독립된 런치 파일로 모듈화(Modularity)하고, 다양한 상위 시스템이나 시나리오에서 이를 호출하여 재사용(Reusability)할 수 있다.
2. 핵심 구성 요소 및 API 구조
런치 파일을 포함하는 메커니즘은 주로 다음 세 가지 요소의 결합으로 구현된다.
IncludeLaunchDescription: 다른 런치 파일을 실행 스케줄에 추가하는 주체 액션 클래스이다. (launch.actions모듈 포함)LaunchDescriptionSource: 포함할 런치 파일의 출처와 구문 형식을 지정하는 객체이다. Python 기반 런치 파일을 호출할 경우 서브클래스인PythonLaunchDescriptionSource가 사용된다. (launch.launch_description_sources모듈 포함)get_package_share_directory: ROS 2 빌드 시스템(ament)에서 특정 패키지의 설치 경로(Share directory)를 런타임에 동적으로 반환하는 함수이다. 하드코딩된 절대 경로 대신 이 함수를 사용하여 시스템 이식성을 확보한다. (ament_index_python.packages모듈 포함)
3. 데이터 전달 메커니즘 (Launch Arguments Injection)
모듈화된 하위 런치 파일은 상위 시스템의 문맥에 따라 동적으로 구성되어야 한다. IncludeLaunchDescription을 호출할 때 launch_arguments 매개변수를 활용하면 상위 런치 파일에서 하위 런치 파일로 인자(Arguments)를 주입할 수 있다. 전달되는 데이터는 문자열 형태의 키-값(Key-Value) 쌍으로 구성된다.
4. 구현 명세 (코드 예제)
아래는 상위 런치 파일에서 하위 패키지의 런치 파일을 포함하고, 실행 인자를 전달하는 표준적인 Python 런치 API 구현 구조이다.
Python
import os
from ament_index_python.packages import get_package_share_directory
from launch import LaunchDescription
from launch.actions import IncludeLaunchDescription
from launch.launch_description_sources import PythonLaunchDescriptionSource
def generate_launch_description():
# 1. 하위 런치 파일의 절대 경로를 동적으로 탐색 및 조합
target_package_dir = get_package_share_directory('target_sub_package')
target_launch_file = os.path.join(target_package_dir, 'launch', 'sensor_bringup.launch.py')
# 2. IncludeLaunchDescription 객체 생성 및 인자 주입
included_launch = IncludeLaunchDescription(
PythonLaunchDescriptionSource(target_launch_file),
launch_arguments={
'use_sim_time': 'true', # 시뮬레이션 시간 사용 여부 전달
'namespace': 'robot_1', # 네임스페이스 전달
'resolution': 'high'
}.items()
)
# 3. LaunchDescription 반환
return LaunchDescription([
included_launch
])
5. 시스템 설계 시 고려사항
- 경로 조합의 안정성: 운영체제 간의 경로 구분자 차이를 방지하기 위해 반드시
os.path.join또는 런치 시스템의PathJoinSubstitution을 사용하여 경로를 결합해야 한다. - 인자 충돌 방지: 하위 런치 파일에서
DeclareLaunchArgument를 통해 선언되지 않은 인자를 상위에서 주입하려 할 경우 런타임 경고 또는 오류가 발생할 수 있으므로, 각 모듈 간의 인터페이스(입출력 인자)가 명확히 정의되어야 한다.
20. 패키지 내 launch 디렉토리 표준 구조 및 구성 방법
20. 패키지 내 launch 디렉토리 표준 구조 및 구성 방법
ROS 2 패키지에서 런치(Launch) 시스템을 원활하게 운용하고 타 패키지와의 종속성을 유지하기 위해서는 launch 디렉토리의 표준화된 계층 구조와 빌드 시스템 상의 명시적인 설치(Install) 구성이 요구됩니다.
20.1 표준 디렉토리 계층 구조 (Standard Directory Architecture)
런치 파일은 패키지의 최상단 루트 디렉토리 하위에 위치한 launch 디렉토리에 배치하는 것이 표준 규약입니다. 단일 런치 파일로 관리가 어려운 대규모 시스템의 경우, 목적과 실행 환경에 따라 하위 디렉토리를 분리하여 모듈화를 수행합니다.
Plaintext
my_ros2_package/
├── CMakeLists.txt (C++ 패키지) 또는 setup.py (Python 패키지)
├── package.xml
├── src/
├── include/
└── launch/ # 런치 파일 표준 디렉토리
├── bringup.launch.py # 하위 노드를 통합 실행하는 메인 기동 스크립트
├── perception_module.launch.py # 특정 기능(인지) 컴포넌트 실행 스크립트
├── simulation/ # 시뮬레이션 전용 하위 디렉토리 (모듈화)
│ └── gazebo_env.launch.py
└── config/ # (선택) 런치 파일 내부에서 로드할 YAML 매개변수 파일
└── node_params.yaml
20.2 파일 명명 및 작성 규칙 (Naming Conventions)
- 확장자 명시: ROS 2 시스템이 런치 파일의 파서를 식별할 수 있도록 프론트엔드 언어 규격에 맞춰
.launch.py,.launch.xml,.launch.yaml확장자를 정확히 사용해야 합니다. - 엔트리 포인트 명명: 다수의 노드를 포함하여 시스템 전체를 기동하는 최상위 런치 파일은 관례적으로
bringup.launch.py로 명명하여 실행 진입점(Entry Point)을 명확히 합니다.
20.3 빌드 시스템 구성 및 설치 명세 (Build System Configuration)
작성된 런치 파일이 ros2 launch <패키지명> <런치파일명> 명령어를 통해 CLI에서 정상적으로 호출되기 위해서는, 소스 공간(Source Space)의 파일들이 빌드 과정을 거쳐 설치 공간(Install Space, install/<package_name>/share/<package_name>/launch/)으로 복사되어야 합니다. 이는 패키지의 빌드 유형에 따라 다르게 설정됩니다.
20.3.1 C++ 패키지 (ament_cmake 기반)
CMakeLists.txt 파일 내에 install(DIRECTORY ...) 함수를 호출하여 launch 디렉토리 전체를 대상 경로로 복사하도록 지시합니다.
CMake
# CMakeLists.txt 내부
# 생략 (프로젝트 정의 및 의존성 선언) ...
install(DIRECTORY
launch
DESTINATION share/${PROJECT_NAME}/
)
# 생략 (ament_package 호출) ...
20.3.2 Python 패키지 (ament_python 기반)
setup.py 파일 내의 data_files 튜플 리스트에 런치 파일들의 원본 경로와 대상 설치 경로를 매핑합니다. 파이썬의 glob 모듈을 활용하여 launch 디렉토리 내의 유효한 확장자를 가진 모든 파일을 동적으로 포함하는 패턴이 권장됩니다.
Python
# setup.py 내부
import os
from glob import glob
from setuptools import setup
package_name = 'my_python_package'
setup(
name=package_name,
version='0.0.0',
packages=[package_name],
data_files=[
('share/ament_index/resource_index/packages', ['resource/' + package_name]),
('share/' + package_name, ['package.xml']),
# launch 디렉토리 내부의 모든 런치 파일(*.launch.py, *.launch.xml 등)을 설치 공간으로 복사
(os.path.join('share', package_name, 'launch'), glob(os.path.join('launch', '*launch.[pxy][yma]*'))),
],
install_requires=['setuptools'],
zip_safe=True,
# 생략 ...
)
이러한 구성 방법을 준수하면 워크스페이스 빌드(colcon build) 시 런치 파일이 시스템 경로에 올바르게 인덱싱되어, 실행 시 ModuleNotFoundError 또는 런치 파일 검색 실패(Launch file not found) 오류를 방지할 수 있습니다.
21. 하드웨어 추상화 계층 및 시뮬레이션 환경에 따른 런치 파일 분리 전략
21.1 하드웨어 추상화 계층 및 시뮬레이션 환경에 따른 런치 파일 분리 전략
로봇 소프트웨어 개발 시, 시뮬레이션 환경(Gazebo, Ignition 등)과 실제 물리적 하드웨어 간의 전환을 매끄럽게 수행하는 것은 시스템의 확장성과 유지보수성에 직결됩니다. ROS 2 런치 시스템은 인자(Arguments)와 조건부 실행(Conditions)을 통해 소스 코드 수정 없이 환경을 전환할 수 있는 강력한 분리 전략을 제공합니다.
21.1.1 분리 전략의 핵심 목적
- 코드 재사용성 극대화: 인지, 판단, 제어(Navigation, SLAM 등)와 같은 상위 수준의 알고리즘 런치 파일을 실제 로봇과 시뮬레이션 양쪽에서 동일하게 사용합니다.
- 의존성 격리: 하드웨어 드라이버(센서, 모터 제어기)와 시뮬레이션 플러그인의 실행을 물리적으로 분리하여 충돌을 방지합니다.
- 안전성 확보: 가상 환경에서 검증된 시스템 로직을 형태의 변형 없이 실제 하드웨어에 배포하여 예기치 않은 오류를 최소화합니다.
21.1.2 3계층 런치 아키텍처 (3-Tier Launch Architecture)
효율적인 분리를 위해 런치 파일을 일반적으로 세 가지 계층으로 모듈화합니다.
- 최상위 브링업 계층 (Top-level Bringup Layer):
- 시스템의 진입점입니다. 사용자가 CLI를 통해
use_sim(시뮬레이션 사용 여부)과 같은 전역 인자를 전달하는 계층입니다.
- 환경 특화 계층 (Environment-Specific Layer):
- 하드웨어 런치 (
hardware.launch.py): 실제 센서(LiDAR, Camera) 드라이버 및ros2_control하드웨어 인터페이스를 로드합니다. - 시뮬레이션 런치 (
simulation.launch.py): 시뮬레이터(Gazebo)를 실행하고, 로봇 모델(URDF/SDF)을 스폰(Spawn)하며, 시뮬레이션용 센서 플러그인을 로드합니다.
- 공통 애플리케이션 계층 (Common Application Layer):
- 상태 발행기(
robot_state_publisher), Navigation2, MoveIt2 등 환경에 구애받지 않는 핵심 노드들을 포함합니다.
21.1.3 핵심 구현 기법
3.1. use_sim_time 매개변수 적용
시뮬레이션 환경과 실제 환경을 분리할 때 가장 중요한 ROS 2 파라미터는 use_sim_time입니다. 시뮬레이터를 사용할 때는 노드들이 시스템 클럭이 아닌 시뮬레이터가 발행하는 /clock 토픽을 참조해야 동기화 문제가 발생하지 않습니다. 최상위 런치 파일은 이 값을 모든 하위 노드에 일괄적으로 주입해야 합니다.
3.2. Launch 인자와 조건부 실행 (IfCondition / UnlessCondition)
Python 런치 API를 사용하여 런타임에 전달된 인자 값에 따라 하위 런치 파일을 선택적으로 포함(IncludeLaunchDescription)합니다.
IfCondition(use_sim): 시뮬레이션 환경일 때 평가되어 참이 됩니다.UnlessCondition(use_sim): 실제 하드웨어 환경일 때 평가되어 참이 됩니다.
21.1.4 구조화된 Python 런치 파일 예시
아래는 시뮬레이션과 실제 하드웨어를 분리하여 실행하는 최상위 런치 파일(bringup.launch.py)의 학술적 예시입니다.
Python
from launch import LaunchDescription
from launch.actions import DeclareLaunchArgument, IncludeLaunchDescription
from launch.conditions import IfCondition, UnlessCondition
from launch.launch_description_sources import PythonLaunchDescriptionSource
from launch.substitutions import LaunchConfiguration, PathJoinSubstitution
from launch_ros.substitutions import FindPackageShare
def generate_launch_description():
# 1. 런치 인자 정의: 기본값은 실제 하드웨어(False)로 설정
use_sim_arg = DeclareLaunchArgument(
'use_sim',
default_value='false',
description='시뮬레이션 환경 실행 여부 (true: Gazebo, false: Real Hardware)'
)
# 런치 런타임 설정값 가져오기
use_sim = LaunchConfiguration('use_sim')
# 패키지 경로 탐색
bringup_pkg = FindPackageShare('my_robot_bringup')
# 2. 시뮬레이션 전용 런치 파일 포함 (use_sim == true 일 때만 실행)
sim_launch = IncludeLaunchDescription(
PythonLaunchDescriptionSource(
PathJoinSubstitution([bringup_pkg, 'launch', 'simulation.launch.py'])
),
condition=IfCondition(use_sim),
launch_arguments={'use_sim_time': 'true'}.items()
)
# 3. 실제 하드웨어 전용 런치 파일 포함 (use_sim == false 일 때만 실행)
hw_launch = IncludeLaunchDescription(
PythonLaunchDescriptionSource(
PathJoinSubstitution([bringup_pkg, 'launch', 'hardware.launch.py'])
),
condition=UnlessCondition(use_sim),
launch_arguments={'use_sim_time': 'false'}.items()
)
# 4. 공통 애플리케이션 런치 파일 포함 (환경 무관하게 항상 실행)
# use_sim 상태에 따라 use_sim_time 매개변수를 동적으로 전달
common_app_launch = IncludeLaunchDescription(
PythonLaunchDescriptionSource(
PathJoinSubstitution([bringup_pkg, 'launch', 'common_apps.launch.py'])
),
launch_arguments={'use_sim_time': use_sim}.items()
)
return LaunchDescription([
use_sim_arg,
sim_launch,
hw_launch,
common_app_launch
])
위와 같은 설계 패턴을 적용하면, 시스템 통합 단계에서 단일 런치 명령어(ros2 launch my_robot_bringup bringup.launch.py use_sim:=true)만으로 수십 개의 노드로 구성된 복잡한 로봇 소프트웨어 스택의 실행 환경을 결정론적이고 안전하게 제어할 수 있습니다.
22. 시스템 실행 로그 분석을 통한 문제 해결 및 디버깅 기법
22.1 시스템 실행 로그 분석을 통한 문제 해결 및 디버깅 기법
ROS 2 런치 시스템 환경에서 시스템 실행 로그는 다중 노드의 생명주기, 통신 상태, 그리고 런타임 구성 오류를 식별하는 핵심 지표이다. 실행 로그 분석 및 디버깅 기법은 다음과 같이 분류된다.
22.1. 로깅 아키텍처 및 로그 데이터 저장소
- 로그 디렉토리 아키텍처: ROS 2 런치 시스템 및 노드 실행 시 생성되는 모든 로그는 기본적으로
~/.ros/log/디렉토리 하위에 세션 고유 식별자(타임스탬프 등)를 기반으로 한 폴더에 저장된다. 해당 폴더 내에는 런치 시스템 자체의 프로세스 로그(launch.log)와 실행된 각 개별 노드의 독립적인 표준 출력/에러 로그 파일이 분리되어 기록된다. - 출력 스트림(stdout/stderr) 제어: 런치 파일(
LaunchDescription) 내에서 노드를 명세할 때output='screen'속성을 부여하여 런타임 로그를 호스트 콘솔에 직접 출력하도록 구성할 수 있으며,output='log'설정 시 콘솔 출력을 억제하고 파일로만 기록하여 I/O 오버헤드를 줄일 수 있다.
22.2. 로그 레벨(Log Level) 동적 제어
- 문제 상황 발생 시 시스템의 내부 동작을 추적하기 위해 로깅 심각도(Severity)를 조절해야 한다. 로그 수준은
DEBUG,INFO,WARN,ERROR,FATAL5단계로 구분된다. - 런타임 주입 기법: 런치 파일 내
Node객체 선언 시arguments매개변수를 활용하여 특정 노드의 로거(Logger) 레벨을 강제 지정할 수 있다. - 작성 예시:
arguments=['--ros-args', '--log-level', 'debug']
22.3. 런치 시스템 디버깅 특화 CLI 분석 도구
--debug플래그: CLI 명령어ros2 launch <package_name> <launch_file> --debug를 실행하면, 런치 시스템이 프론트엔드 파일을 파싱하고 런타임 치환(Substitution) 변수를 평가하는 전체 과정을 콘솔에 상세히 출력한다. 이는 조건문(IfCondition,UnlessCondition)의 논리적 평가 오류나 환경 변수 바인딩 실패를 추적하는 데 필수적으로 사용된다.--print(또는-p) 플래그: 런치 파일이 실행될 때 생성되는 최종LaunchDescription객체의 계층적 구조를 실제 프로세스 실행 없이 터미널에 출력한다. 이를 통해 복잡하게 중첩된 런치 파일(IncludeLaunchDescription)의 런타임 구성을 정적으로 사전 검증할 수 있다.
22.4. 주요 에러 로그 유형 및 인과 관계 추적
- 실행 가능 객체 탐색 실패 (Executable not found): 로그에 특정 패키지 내 노드 실행 파일을 찾을 수 없다는 에러(
launch.substitutions.substitution_failure.SubstitutionFailure)가 발생할 경우, 빌드 시스템 설정 오류로 간주한다.CMakeLists.txt의install(TARGETS...)지시어 누락 또는 Python 패키지의setup.py내entry_points명세가 정상적으로 처리되었는지 점검한다. - 프로세스 비정상 종료 (Process Exit Code Analysis): 하위 노드 프로세스가 비정상 종료될 경우 런치 로그에 플랫폼 종속적인 종료 코드(Exit Code)와 함께
OnProcessExit이벤트가 발생 기록으로 남는다. 세그멘테이션 결함(Segmentation fault, 코드 139)이나 메모리 누수에 의한 강제 종료는 해당 노드의 개별.log파일 역추적 및 운영체제 수준의 코어 덤프(Core Dump) 분석과gdb연동을 병행하여 원인을 규명한다. - 매개변수 주입 및 타입 검증 오류 (Parameter Type Mismatch): 외부 YAML 파일 파싱 에러나, 런치 파일에서 주입한 치환 변수 데이터 타입이 타겟 노드가 요구하는 타입 명세와 불일치할 경우 런타임 에러가 발생한다. 이 경우 런치 프로세스는 파싱 단계 또는 노드 초기화 단계에서 예외를 발생시키며, 콜스택(Call Stack) 로그를 통해 오류가 발생한 프론트엔드 파일의 라인 번호 및 치환식을 특정할 수 있다.
23. 대규모 로봇 소프트웨어 시스템을 위한 런치 파일 작성 권장 사항 (Best Practices)
대규모 로봇 소프트웨어 시스템에서 ROS 2 런치 파일을 작성할 때 시스템의 확장성, 유지보수성 및 구동 안정성을 확보하기 위한 학술적이고 사실적인 권장 사항(Best Practices)은 다음과 같습니다.
1. 모듈화 및 계층적 구성 (Modularity and Hierarchical Composition)
- 기능별 분리: 모든 노드를 단일 런치 파일에 선언하는 것을 지양하고, 하위 시스템 단위(예: 하드웨어 드라이버, 내비게이션, 로컬라이제이션, 매니퓰레이션)로 독립적인 런치 파일을 작성해야 합니다.
- 트리 아키텍처 구축:
IncludeLaunchDescription클래스를 사용하여 최상위(Top-level) 런치 파일이 하위 시스템의 런치 파일들을 계층적으로 호출하도록 구성하여 코드의 재사용성을 극대화합니다.
2. 설정 데이터의 분리 및 매개변수화 (Configuration Separation and Parameterization)
- 하드코딩 배제: 노드의 실행 매개변수(Parameters)를 런치 파일 내부에 직접 작성하지 않고, 패키지 내
config/디렉토리에 위치한 외부 YAML 파일로 완전히 분리해야 합니다. - 런치 인자 활용: 런타임에 시스템 구성을 동적으로 제어할 수 있도록
DeclareLaunchArgument를 정의하고, 내부 요소에서는LaunchConfiguration객체를 통해 이를 참조하도록 설계합니다.
3. 통신 토폴로지 격리 (Topology Isolation via Namespace and Remapping)
- 네임스페이스 적용: 다중 로봇 시스템이나 동일한 하위 시스템이 여러 개 인스턴스화되는 환경에서는 토픽(Topic) 충돌을 방지하기 위해
PushRosNamespace액션을 사용하여 실행되는 노드들을 특정 네임스페이스 영역으로 강제 격리합니다. - 동적 리매핑: 소스 코드를 수정하지 않고도 데이터 흐름을 재구성할 수 있도록 노드 선언 시
remappings인수를 적극 활용하여 퍼블리셔와 서브스크라이버 간의 의존성을 런치 단계에서 해결합니다.
4. 제어 로직을 위한 Python 런치 API의 표준화 (Standardization on Python Launch API)
- 대규모 시스템에서는 정적인 명세만 가능한 XML이나 YAML 규격 대신, 시스템 환경 변수 접근, OS 모듈 활용, 그리고 프로그래밍적 제어 구조(루프, 조건문) 적용이 가능한 Python 기반 런치 시스템을 1차 표준으로 채택하는 것이 권장됩니다.
5. 시스템 상태 동기화 및 라이프사이클 관리 (State Synchronization and Lifecycle)
- 결정론적 실행 보장: 의존성이 얽혀 있는 복잡한 시스템에서는 노드의 무작위 실행 순서에 따른 오류를 막기 위해 일반 노드 대신 ROS 2
LifecycleNode를 도입합니다. - 이벤트 기반 제어:
RegisterEventHandler와OnStateTransition등의 이벤트 핸들러를 사용하여, 선행 하위 시스템의 상태 천이(예: ’Inactive’에서 ’Active’로 변경)가 확인된 후 종속된 후행 노드가 실행되도록 동기화 파이프라인을 구축합니다.
6. 프로세스 복원력 및 예외 처리 (Process Resilience and Exception Handling)
- 자동 복구: 일시적인 네트워크 지연이나 연산 오류로 인해 종료될 수 있는 시스템 크리티컬 노드에는
respawn=True및respawn_delay속성을 부여하여 가용성을 유지합니다. - 안전 종료(Clean Shutdown): 핵심 컨트롤러나 하드웨어 드라이버 프로세스가 비정상 종료될 경우,
OnProcessExit이벤트를 포착하여 전체 런치 시스템에EmitEvent(event=Shutdown())을 발생시켜 안전하게 전체 시스템을 종료시켜야 합니다.
7. 자원 최적화를 위한 조건부 실행 (Conditional Execution for Resource Optimization)
- 시뮬레이션 환경 토글(
use_sim_time), 무거운 시각화 도구(RViz) 실행 여부, 또는 특정 센서 파이프라인의 활성화 여부를 런치 인자로 받고, 이를IfCondition및UnlessCondition과 결합하여 불필요한 컴퓨팅 자원의 낭비를 원천적으로 차단합니다.