# ROS2의 ROS1과의 차이점

#### DDS 기반 통신 구조

ROS1에서 사용되던 TCP 혹은 UDP 기반의 통신 방식과 달리, ROS2는 DDS(Data Distribution Service) 미들웨어를 활용한다. DDS는 퍼블리셔와 서브스크라이버 간의 통신을 좀 더 유연하게 구성할 수 있도록 하는 분산 시스템 표준으로, 메시지 전달 구조를 네트워크 위에서 직접 제어할 수 있는 강력한 기능을 제공한다.

이때 DDS를 선택함으로써 얻게 되는 핵심 이점은 다음과 같이 정리할 수 있다.

* **네트워크 토폴로지 제어**: 노드 간 연결이 다채로운 형태로 구성 가능하며, 자동으로 발견되는 노드(Discovery) 메커니즘을 통해 확장성을 높인다.
* **QoS(Quality of Service) 설정**: 메시지 전달 방식에 대한 다양한 정책(신뢰성, 지연, 우선순위 등)을 노드마다 독립적으로 설정할 수 있다.
* **프로토콜 호환성**: 다양한 DDS 벤더를 선택할 수 있으며, 네트워크 요건에 따라 맞춤형 설정이 가능하다.

ROS2에서 DDS를 기반으로 통신 지연 시간(예: $\mathbf{t}\_{\mathrm{latency}}$)을 모델링할 때, 노드와 노드 간의 거리(네트워크 홉 수), 메시지 크기 등을 고려해야 한다. 단순하게 표현하면,

$$
\mathbf{t}*{\mathrm{latency}} \approx \frac{\mathbf{M}}{\mathbf{B}*{\mathrm{net}}}
$$

여기서 $\mathbf{M}$은 메시지의 크기(단위: bytes), $\mathbf{B}\_{\mathrm{net}}$은 네트워크 대역폭(단위: bytes/s)이다. DDS를 쓸 경우, 위와 같은 단순 추정치 이상으로 QoS 정책에 의해 우선순위가 달라지거나, 신뢰성 재전송으로 인해 추가 지연이 발생하기도 한다.

#### 엄격한 실시간성 고려

ROS2는 내부적으로 실시간성을 고려할 수 있도록 설계되었다. 실시간 제어가 필요한 로보틱스 환경에서, 일정한 주기를 갖는 주기 작업(periodic task)과 트리거가 발생할 때마다 실행되는 스포라딕(sporadic) 작업 등을 안정적으로 처리해야 한다. ROS2는 다음과 같은 요소로 실시간성을 강화한다.

* **실시간 운영체제(RTOS) 호환**: 흔히 사용되는 RTOS(예: VxWorks, QNX 등) 상에서도 ROS2를 구동할 수 있으며, 이를 위해 DDS 계층에서 RT 운영체제 특성에 맞는 구현을 제공한다.
* **멀티스레드 처리를 위한 Executor**: ROS2 Executor는 콜백의 실행 흐름을 제어하며, 우선순위 기반 스케줄링 및 Lock-free 큐 접근 등을 통해 지연을 최소화하려 한다.

ROS1 대비 ROS2에서의 실시간성 보완 효과를 대략적으로 수식화하면, 임의의 시분할 스케줄링 상황에서 특정 노드의 응답 시간 $\mathbf{R}$이 다음을 만족하도록 설계하려 한다고 볼 수 있다.

$$
\mathbf{R} \leq \mathbf{D}
$$

여기서 $\mathbf{D}$는 해당 노드가 요구하는 마감 시간(deadline)이다. ROS2는 DDS의 QoS와 멀티스레드 Executor를 활용해 $\mathbf{R}$을 최소화하는 방향으로 여러 설정값을 제어하도록 지원한다.

#### 보안(Security) 모델

ROS2는 DDS에서 지원하는 보안 확장인 DDS Security를 활용하여 데이터 암호화, 인증, 접근 제어 등을 수행할 수 있게 되었다. 이를 통해 같은 물리 네트워크 상에 존재하더라도, 권한이 없는 노드가 메시지에 접근하거나 발행할 수 없도록 하는 체계를 구축할 수 있다.

아래 mermaid 예시는 ROS2에서 각 노드 사이의 통신 경로가 DDS 보안 계층으로 보호되고 있음을 개략적으로 보여준다.

{% @mermaid/diagram content="flowchart LR
A\[노드 A] -- 암호화된 DDS 통신 --> B\[노드 B]
B -- 암호화된 DDS 통신 --> C\[노드 C]
A -- 제한된 권한 --> C" %}

위처럼 DDS 보안 계층을 적용하면, 네트워크 내부에서도 통신 자체가 안전하게 보호된다. 이로 인해 ROS1에서 별도로 튜닝하던 보안 설정보다 일관되고 체계적으로 노드 간 데이터를 보호할 수 있다.

#### 라이프사이클(Lifecycle) 매니지먼트

ROS2는 노드가 특정 상태(State)로 전이(transition)되는 과정을 세밀하게 제어할 수 있는 라이프사이클 매니지먼트를 지원한다. 이는 **Managed Node**라고 불리는 개념을 통해, 노드가 **Unconfigured → Inactive → Active → Finalized** 등의 상태를 거치도록 설계하여 시스템 신뢰도를 높인다. ROS1에서는 이러한 라이프사이클 개념이 없거나 제한적으로만 적용되었기에, 노드가 시작부터 종료까지 한정된(대체로 항상 실행 중인) 상태로 머무는 경우가 일반적이었다.

ROS2 라이프사이클 매니지먼트를 간단히 모델링하면, 노드 상태 $\mathbf{S}$가 시간에 따라 변하며, 가능한 상태 집합을 ${\text{Unconfigured}, \text{Inactive}, \text{Active}, \text{Finalized}}$라 할 때,

$$
\mathbf{S}(t) \in { \text{Unconfigured}, \text{Inactive}, \text{Active}, \text{Finalized} }
$$

상태 전이는 사용자의 명시적 호출이나 시스템 이벤트에 의해 일어나므로, 상태 변화 함수를 $\mathbf{f}$라고 하면,

$$
\mathbf{S}(t + \Delta t) = \mathbf{f}(\mathbf{S}(t), \mathbf{event})
$$

와 같이 표현할 수 있다. 여기서 $\mathbf{event}$는 특정 서비스 호출(예: $\texttt{configure}, \texttt{activate}, \texttt{deactivate}, \texttt{shutdown}$) 등이 될 수 있다.

라이프사이클 매니지먼트를 통해 다음과 같은 장점을 누릴 수 있다.

* **정교한 초기화**: 노드가 $\text{Unconfigured}$ 상태에서 초기화를 수행하고, $\text{Inactive}$ 상태에서 일부 자원을 할당한 뒤, $\text{Active}$ 상태로 들어가서 실제 로직을 수행할 수 있다.
* **안정적 재시작**: 특정 노드의 문제가 생겼을 때, 전체 시스템을 중단하지 않고 해당 노드만 재구성하거나 $\text{Finalized}$ 상태로 종료시킬 수 있다.
* **모듈식 운영**: 동적 환경에서 노드별 라이프사이클을 개별적으로 제어하기 쉽다.

#### 파라미터(Parameters) 시스템 변화

ROS2의 파라미터 시스템은 ROS1 대비 크게 개선되어, 노드별 파라미터 설정과 동적 업데이트가 더 체계적으로 이뤄진다. ROS1에서 파라미터 서버(parameter server)는 전역적으로 동작하여, 어떤 노드가 특정 네임스페이스(namespace)의 파라미터를 요청하면 해당 값을 가져오는 방식이었다. 반면 ROS2에서는 노드마다 독립된 파라미터 집합이 존재하고, 이를 $\texttt{rclcpp}$나 $\texttt{rclpy}$ API로 핸들링한다.

간단히 표현하면, 각 노드 $\mathbf{n}\_i$가 보유한 파라미터 벡터를 $\mathbf{p}\_i$라고 할 때,

$$
\mathbf{p}*i = \begin{bmatrix} p*{i,1} \ p\_{i,2} \ \vdots \ p\_{i,k} \end{bmatrix}
$$

이 $\mathbf{p}\_i$는 다른 노드 $\mathbf{n}\_j$의 파라미터 벡터 $\mathbf{p}\_j$와 물리적으로나 논리적으로 분리되어 있으므로, 서로 영향 없이 개별적으로 관리할 수 있다. 또한 *파라미터 이벤트*가 발생할 때(예: 사용자가 $\mathbf{p}\_i$의 일부 요소를 업데이트), 이를 감지하는 콜백(callback) 함수를 통해 노드 로직이 자동으로 반응한다.

ROS2 파라미터 시스템의 대표적인 특징은 다음과 같다.

* **노드 스코프**: 파라미터가 노드 스코프 단위로 유효하다.
* **파라미터 이벤트**: 파라미터 변경 시 콜백을 통해 노드에서 즉시 처리 가능하다.
* **통합 도구**: ROS2 CLI(Command Line Interface)나 rqt 등의 GUI 툴을 통해 런타임에 파라미터를 조회·변경할 수 있다.

#### 컴포저블 노드(Composable Nodes)

ROS2는 하나의 프로세스 안에서 여러 노드를 동시에 구동할 수 있는 컴포저블 노드(Composable Nodes) 기능을 지원한다. ROS1에서도 비슷한 방식으로 멀티스레드 스피너를 사용하거나 특정 형태의 nodelet 등을 활용했지만, ROS2는 아키텍처 레벨에서 이를 좀 더 체계적으로 제공한다. 이를 통해 노드 간 통신 오버헤드(예: 프로세스 간 IPC 비용)를 줄이고, 자원을 효율적으로 사용할 수 있다.

예를 들어, 같은 주소 공간에서 노드 $\mathbf{n}\_1$과 노드 $\mathbf{n}*2$가 실행될 경우, 메시지를 전달할 때 굳이 커널을 거치지 않고 직접 버퍼를 주고받을 수 있어 지연($\mathbf{t}*{\mathrm{latency}}$)과 CPU 부하가 감소한다. 이를 간략히 수식으로 표현하면,

$$
\mathbf{t}*{\mathrm{latency}}^{\mathrm{inproc}} < \mathbf{t}*{\mathrm{latency}}^{\mathrm{ipc}}
$$

여기서 $\mathbf{t}*{\mathrm{latency}}^{\mathrm{inproc}}$는 인프로세스(in-process) 통신 지연이며, $\mathbf{t}*{\mathrm{latency}}^{\mathrm{ipc}}$는 프로세스 간(IPC) 통신 지연이다. ROS2는 컴포저블 노드를 통해 인프로세스 통신이 가능하도록 하여 성능을 높일 수 있다.

#### 빌드 시스템 및 패키지 구조 변화

ROS2는 빌드 툴로 **colcon**을 도입하여, ROS1에서 사용하던 **catkin**이나 **ament** 기반 구조를 한층 통합하고 확장성 있게 관리할 수 있도록 했다. colcon은 멀티 패키지 빌드와 의존성 해석, 설치 과정을 유연하게 지원하며, 이를 통해 다음과 같은 이점을 얻을 수 있다.

* **패키지 병렬 빌드**: 여러 패키지를 동시 빌드하여 전체 빌드 시간을 단축할 수 있다.
* **탐지 및 의존성 관리**: 빌드 과정에서 자동으로 패키지를 탐지하고, 필요한 순서에 맞춰 빌드해준다.
* **확장성**: 플러그인 방식으로 다양한 빌드 스크립트나 테스트 프레임워크를 추가로 끼워 넣을 수 있다.

빌드 시스템을 수학적으로 간단히 모델링해 보면, 어떤 프로젝트가 $\mathbf{P}$개의 패키지를 가진다고 할 때, 패키지 의존성 그래프를 $\mathbf{G} = (\mathbf{V}, \mathbf{E})$로 표현할 수 있다. 여기서

$$
\mathbf{V} = { p\_1, p\_2, \ldots, p\_{\mathbf{P}} }
$$

$$
\mathbf{E} = { (p\_i, p\_j) \mid p\_j \text{는 } p\_i \text{에 의존한다} }
$$

이제 colcon 빌드 과정을 통해 $\mathbf{G}$를 토대로 위상 정렬(topological sort)을 수행하여, 의존성이 충돌하지 않도록 패키지를 빌드한다.

#### 멀티 플랫폼 및 윈도우 지원 강화

ROS2는 ROS1 대비 훨씬 폭넓은 OS를 지원한다. 특히 기존 ROS1에서 부분적으로나마 지원하던 윈도우 환경이 ROS2부터 공식적으로 강화되었다. 이는 다음과 같은 기술적 변화로 인해 가능해졌다.

* **CMake 확장**: 순수 C++ 코드를 활용해 윈도우에서도 문제없이 빌드가 가능하도록 설계
* **POSIX 의존성 감소**: DDS 기반 통신이 OS 독립적 구조를 가지도록 설계
* **윈도우 네이티브 빌드 체인**: Visual Studio 환경에서의 호환성 고려

이로써 ROS2는 리눅스(특히 Ubuntu LTS 시리즈), 윈도우, macOS, 그리고 각종 임베디드 RTOS 등 다양한 플랫폼에 대한 호환성을 확보했다. 이를 $\mathbf{OS}\_i$를 특정 운영체제 집합, $\mathbf{arch}\_j$를 특정 CPU 아키텍처 집합이라 할 때,

$$
(\mathbf{OS}\_i, \mathbf{arch}\_j) \in {(\text{Linux}, \text{x86\_64}), (\text{Windows}, \text{x86\_64}), (\text{Linux}, \text{ARM}), \ldots }
$$

와 같이 여러 쌍으로 ROS2를 포팅할 수 있다.

#### 다양한 프로그래밍 언어 지원

ROS2는 C++(rclcpp)와 파이썬(rclpy)뿐 아니라, Java, Rust, Go 등 여러 언어 바인딩이 활발히 개발되고 있다. 이는 ROS2가 핵심 레이어인 \*\*rcl(rcl layer)\*\*을 통해 언어 중립적인 인터페이스를 제공하기 때문이다. rclcpp, rclpy 등은 rcl layer를 기반으로 확장된 형태이며, 필요한 언어권마다 rcl에 대한 래퍼(Wrapper)를 구현할 수 있다.

이를 수식적으로 표현해 보자면, 특정 언어 LL에 대해 ROS2 호환 래퍼를 wL\mathbf{w}\_L라 할 때,

$$
\mathbf{w}\_L : \text{rcl} \mapsto \text{언어별 API}
$$

즉, rcl 레이어의 함수를 적절한 언어 호출 규약(ABI)에 맞추어 연결해주는 일종의 사상(mapping) 역할을 한다. 이 구조를 통해 ROS2는 여러 개발 언어를 수용할 수 있다.

#### Launch 시스템 변화

ROS1에서는 XML 혹은 YAML 기반의 .launch.launch 파일을 사용하여 노드 실행, 매개변수 설정 등을 선언했다. ROS2는 이를 한 단계 발전시켜 **Python 스크립트 기반**의 .launch.py.launch.py 방식을 도입했다. 따라서 유연하고 프로그래밍적인 구성이 가능해졌으며, Launch 파일 내에서 조건부 분기나 변수 연산 등을 쉽게 구현할 수 있다.

이를 수식적으로 단순 비유해 보면, 기존 ROS1 .launch.launch 파일은 상태 없는 선언형(Declarative) 구문이었다면, ROS2 .launch.py.launch.py는 아래와 같은 의사 코드 형태로 표현 가능한 프로시저적(Imperative) 실행 흐름을 포함한다:

```python
def generate_launch_description():
    node_1 = Node(...)
    node_2 = Node(...)
    ...
    return LaunchDescription([...])
```

여기서 `node_1`, `node_2` 등은 실제로는 실행 시점에 LaunchDescription 객체에 들어가며, 필요하다면 조건부 실행(If-Else), 반복문, 환경변수 확인 등을 수행할 수 있다.

ROS2 Launch는 내부적으로 그래프 형태의 실행 흐름을 구성하고, 시점에 따른 이벤트(예: 노드 시작/종료 시점 등)와 연동되는 콜백을 처리할 수 있다. 이를 표기하자면, 노드 집합 $\mathbf{N} = {n\_1, n\_2, \ldots, n\_k}$와 이벤트 집합 $\mathbf{E} = {e\_1, e\_2, \ldots, e\_m}$가 있을 때, Launch는 다음과 같은 매핑 $\mathbf{L}$을 수행한다:

$$
\mathbf{L}: \mathbf{E} \mapsto {\text{콜백, 상태 전이, 조건 분기}}
$$

즉, 특정 이벤트 $e\_i$가 발생했을 때 어떤 콜백 함수가 호출되고, 그에 따른 노드 상태나 실행 흐름이 어떻게 달라지는지를 Launch 레벨에서 제어한다.

#### IDL 기반 메시지 정의

ROS1에서 메시지 정의는 .msg 파일, 서비스 정의는 .srv 파일, 액션 정의는 .action 파일 등으로 분리되어 있었다. ROS2는 **Interface Definition Language(IDL)** 표준을 활용하여, DDS를 비롯한 다양한 미들웨어에서 호환성을 확보할 수 있게 되었다. 사실상 기존 .msg, .srv, .action 포맷은 ROS2에서도 유효하지만, 내부적으로는 IDL 파싱 과정을 거쳐 미들웨어에 맞게 변환된다.

이를 아주 간단히 수식으로 설명하면, ROS2 인터페이스(메시지, 서비스, 액션)를 하나의 **IDL 스펙** $\mathbf{I}$로 통일해두고, DDS를 비롯한 다양한 미들웨어 혹은 언어별 시리얼라이저는

$$
\mathbf{ser}*{\mathrm{DDS}}(\mathbf{I}), \quad \mathbf{ser}*{\mathrm{C++}}(\mathbf{I}), \quad \mathbf{ser}\_{\mathrm{Python}}(\mathbf{I}), \ldots
$$

등의 함수를 통해 IDL 정의를 실제 사용 가능한 바이너리 혹은 언어 구조로 변환한다. 이렇게 표준화된 언어로 메시지가 정의되므로, 다른 DDS 구현체나 다른 언어 환경에서도 호환성을 높일 수 있다.

#### ROS1 Bridge

ROS1과 ROS2 사이에서 메시지를 상호 변환해 주는 **ROS1 Bridge** 또한 ROS2가 제공하는 기능적 차별점 중 하나다. 이는 ROS1 환경을 완전히 폐기하기 이전에, 점진적으로 ROS2로 마이그레이션할 수 있도록 돕는다.

ROS1 Bridge의 내부 동작을 단순하게 표현하면, ROS1 노드와 ROS2 노드 사이에서 메시지를 **양방향**으로 중계한다. 예를 들어, ROS1에서 발행된 토픽 $\mathbf{topic}*{\mathrm{ros1}}$의 메시지 $\mathbf{m}*{\mathrm{ros1}}$가 Bridge를 거쳐 ROS2의 $\mathbf{topic}\_{\mathrm{ros2}}$로 전달될 때,

$$
\mathbf{m}*{\mathrm{ros2}} = \mathbf{conv}(\mathbf{m}*{\mathrm{ros1}})
$$

와 같은 변환 함수를 수행한다. conv\mathbf{conv}는 각 메시지 타입별로 정의된 직렬화-역직렬화, 필드 맵핑 등을 처리한다. 이를 반대 방향(ROS2 → ROS1)으로도 동일하게 지원한다.

이처럼 ROS1 Bridge를 통해 기존에 ROS1으로 구성된 로봇 시스템과 새롭게 ROS2로 개발된 시스템을 동시에 구동하거나, 순차적으로 ROS2로 이전하는 전략을 사용할 수 있다.

#### 테스트 및 디버깅(Testing & Debugging) 개선

ROS2는 자체적으로 **ament\_cmake**, **colcon test** 등 다양한 테스트 프레임워크와 연동이 수월해졌으며, Launch 시스템과도 유기적으로 결합하여 통합 테스트를 진행하기 편리해졌다. 또한 ros2 CLI에서 제공하는 다음과 같은 명령어들(예: `ros2 topic echo`, `ros2 service call`, `ros2 node list`, `ros2 param get` 등)을 활용해 런타임에 시스템 동작 상태를 검사하고, 문제를 디버깅할 수 있다.

테스트 자동화를 수학적으로 단순화하면, 각 노드 $\mathbf{n}\_i$가 수행해야 할 테스트 케이스 집합 $\mathbf{T}*i$가 있고, 각 테스트 케이스 $\mathbf{t}j$를 통과했을 때와 통과하지 못했을 때의 시스템 상태를 각각 $\mathbf{S}{\mathrm{pass}}(i, j)$와 $\mathbf{S}*{\mathrm{fail}}(i, j)$라 하면,

$$
\mathbf{S}(i, j) = \begin{cases} \mathbf{S}\_{\mathrm{pass}}(i, j) & \text{if }\mathbf{t}*j\text{ passed}, \ \mathbf{S}*{\mathrm{fail}}(i, j) & \text{otherwise}. \end{cases}
$$

각 노드는 독립적으로 여러 테스트 케이스를 거쳐 인증을 받고, 이후 통합 테스트 단계에서 전체 시스템 레벨의 검증을 수행한다. Launch 파일을 통해 노드를 일괄 실행 후, 자동화된 테스트 스크립트로 결과를 확인하는 식이다.

#### 액션(Action) 인터페이스 개선

ROS2에서 액션(Action)은 \*\*피드백(Feedback), 결과(Result), 목표(Goal)\*\*를 다루는 비동기 통신 패턴을 보다 명확하게 정립해 놓은 인터페이스다. ROS1에서도 actionlib을 사용해 유사한 구조를 제공했지만, ROS2는 기본 구조 자체가 DDS를 활용하기 때문에 더욱 일관성 있는 방식으로 액션을 처리할 수 있다.

기본적으로 액션 서버(서버 노드)와 액션 클라이언트(클라이언트 노드)는 다음과 같은 흐름으로 동작한다.

1. 클라이언트 노드가 \*\*목표(Goal)\*\*를 서버 노드에 전송한다.
2. 서버 노드는 목표를 수락하거나 거부(Reject)한다.
3. 목표가 수락됐다면, 서버 노드는 작업을 수행하며 \*\*피드백(Feedback)\*\*을 중간중간 클라이언트에 보낸다.
4. 최종 작업이 끝나면 \*\*결과(Result)\*\*를 반환한다.

ROS2 액션 인터페이스를 간단히 수식화하면, 목표(Goal)를 $\mathbf{g}$, 피드백(Feedback)을 $\mathbf{f}(t)$ (시간 $t$에 대한 부분 결과), 결과(Result)를 $\mathbf{r}$라 할 때,

$$
\mathbf{client}\xrightarrow{\mathbf{g}} \mathbf{server}, \quad \mathbf{server} \xrightarrow{\mathbf{f}(t)} \mathbf{client}, \quad \mathbf{server}\xrightarrow{\mathbf{r}} \mathbf{client}.
$$

여기서 $\mathbf{f}(t)$는 실제로 주기적으로(또는 불규칙적으로) 전송되는 중간 메시지이며, $\mathbf{r}$는 작업이 끝난 시점에서 한 번 전송되는 최종 결과다. ROS2 액션은 이러한 메시지들의 정의를 IDL 기반 형식으로 제공하며, 기존의 토픽, 서비스, 파라미터 인터페이스와 유기적으로 함께 사용할 수 있도록 설계됐다.

#### 로깅(Logging) 기능 확장

ROS2는 $\texttt{rcutils}$와 $\texttt{rcl}$ 레이어를 통해 로깅 인터페이스를 정립했고, 이를 각 언어 클라이언트 라이브러리(C++, Python 등)가 공통적으로 활용하도록 구조화했다. 따라서 ROS1 대비 다음과 같은 특징을 갖는다.

* **Logger 이름(Name) 분리**: 노드별(또는 라이브러리별)로 Logger 이름을 체계적으로 부여할 수 있고, 이를 통해 로그 필터링이 수월하다.
* **로깅 레벨 동적 변경**: $\texttt{DEBUG}, \texttt{INFO}, \texttt{WARN}, \texttt{ERROR}, \texttt{FATAL}$ 등의 로깅 레벨을 런타임에 동적으로 조정 가능하다.
* **백엔드 선택**: 파일 출력, 콘솔 출력 등 여러 로깅 백엔드를 쉽게 교체할 수 있다.

이를 단순하게 표현하면, 각 노드 $\mathbf{n}\_i$가 출력하는 로그를 $\mathbf{log}\_i(t)$라 정의했을 때, 사용자가 $\texttt{ros2 param set}$ 등을 통해 동적으로 해당 노드의 로깅 레벨 $\mathbf{L}\_i$를 변경하면,

$$
\mathbf{log}\_i(t) =  \begin{cases} \mathbf{msg}\_i(t) & \text{if }\text{level}(\mathbf{msg}\_i(t)) \ge \mathbf{L}\_i,\ \varnothing & \text{otherwise}. \end{cases}
$$

즉, 설정된 로깅 레벨 $\mathbf{L}\_i$보다 우선순위가 낮은 메시지는 버려지고, 높은 메시지만 출력된다. 이를 통해 로봇 시스템 전체 로그 량을 제어하고, 필요한 시점에만 상세 로그를 보도록 설정할 수 있다.

#### tf2(좌표 변환 라이브러리)

ROS1에서 사용하던 tf 라이브러리를 ROS2에서는 **tf2**라는 명칭으로 개선하여 제공한다. ROS2의 tf2는 DDS 기반의 통신 모델을 반영한 구조로, 여러 좌표계(Frame) 사이의 변환 정보를 퍼블리싱하고, 이를 각 노드가 서브스크라이빙하여 필요한 시점에 변환을 수행한다.

간단히 말해, 하나의 좌표계 $\mathbf{A}$에서 다른 좌표계 $\mathbf{B}$로 변환할 때, tf2는 다음과 같은 변환 행렬 $\mathbf{T}\_{A \rightarrow B}$를 관리한다.

$$
\mathbf{T}\_{A \rightarrow B} =  \begin{bmatrix} 1 & 0 & 0 & x\ 0 & 1 & 0 & y\ 0 & 0 & 1 & z\ 0 & 0 & 0 & 1 \end{bmatrix}
$$

여기에 회전(쿼터니언 등)까지 포함하면 3차원 좌표계 변환을 전부 표현할 수 있다. 여러 좌표계가 얽혀 있을 때는 그래프 구조로 표현하며, tf2는 필요한 시점에 해당 그래프를 탐색하여 변환을 계산한다. ROS2의 tf2는 ROS1의 tf와 개념적으로 동일하지만, 노드의 라이프사이클, QoS 설정 등을 DDS 표준 하에서 좀 더 유연하게 제어할 수 있다는 점이 다르다.

#### Executor와 콜백 그룹(Callback Groups)

ROS2에서 **Executor**는 노드의 콜백(callback)들을 어떤 순서와 방식으로 실행할지를 결정하는 핵심 구성 요소다. ROS1에서도 스피너(spinner) 개념을 통해 콜백 처리를 했지만, ROS2는 \*\*콜백 그룹(Callback Groups)\*\*이라는 메커니즘을 추가로 제공하여, 콜백 간 독립성을 정교하게 제어한다.

* **Reentrant Callback Group**: 동일한 콜백 그룹 내 콜백들이 동시에(병렬로) 실행될 수 있음
* **Mutually Exclusive Callback Group**: 해당 콜백 그룹 내 콜백은 절대 동시에 실행되지 않음

또한 Executor가 단일 스레드로 동작할지 멀티스레드(스레드 풀)로 동작할지를 설정할 수 있으므로, 노드마다 원하는 방식으로 동시성을 제어할 수 있다. 이를 아주 간단하게 수식으로 나타내면,

$$
\mathbf{E}: \mathbf{C} \mapsto \text{스케줄링}
$$

여기서 $\mathbf{C}$는 콜백(콜백 그룹 포함)의 집합이고, $\mathbf{E}$는 Executor가 $\mathbf{C}$ 내 각 콜백을 어떤 시점(또는 어떤 스레드)에서 실행할지를 정하는 매핑이다. Reentrant 그룹에 속한 콜백 $\mathbf{c}\_r$와 Mutually Exclusive 그룹에 속한 콜백 $\mathbf{c}\_m$이 동시에 도착한 경우, 멀티스레드 Executor라면 $\mathbf{c}\_r$는 병렬로, $\mathbf{c}\_m$은 독점적으로 스케줄될 수 있다.

#### 네임스페이스(Name Space) 규칙 변화

ROS2는 각 노드, 토픽, 서비스에 대해 좀 더 엄격하고 유연한 **네임스페이스 규칙**을 제공한다. ROS1에서도 네임스페이스는 존재했지만, 런타임 리매핑(remap) 과정이나 $\texttt{roslaunch}$ XML에서의 네임스페이스 설정 등이 복잡하게 얽혀 있었다. ROS2에서는 네임스페이스가 **노드 단위**로 명시적으로 설정되며, 런타임 시에도 CLI나 Launch 파일에서 일관적으로 제어할 수 있다.

예를 들어, ROS2에서 어떤 노드 $\mathbf{n}$가 $\texttt{/robot1}$ 네임스페이스에 할당되고, 그 노드가 퍼블리시하는 토픽 이름이 $\texttt{cmd\_vel}$이라면, 최종 Fully Qualified Name(FQN)은 아래와 같이 결정된다.

$$
\texttt{/robot1/cmd\_vel}
$$

이는 ROS1처럼 `~`(틸드) 기호 등을 복잡하게 사용하지 않고, 명시적으로 Launch 파일 혹은 CLI 옵션에서 $\texttt{--ros-args --remap cmd\_vel:=/robot1/cmd\_vel}$ 형태로 지정 가능하다. 이러한 규칙 통일로 인해, 대규모 시스템에서 토픽 충돌이나 노드 명령 혼선을 줄일 수 있다.

#### QoS 프로파일 설정

ROS2는 DDS를 기반으로 하는 만큼, 토픽(Topic)의 통신 특성을 **QoS(품질 속성) 프로파일**로 다양하게 설정할 수 있다. 대표적인 QoS 정책은 다음과 같다.

* Reliability:
  * Reliable: 메시지 손실 없이 반드시 전달
  * Best Effort: 네트워크 상황에 따라 메시지 손실 가능
* Durability:
  * Volatile: 새로 구독을 시작하면 그 시점부터 메시지 수신
  * Transient Local: 구독자가 늦게 붙어도, 퍼블리셔가 보유한 기존 메시지를 전달
* History:
  * Keep Last: 특정 버퍼 크기만큼 메시지를 보관
  * Keep All: 가능한 모든 메시지를 보관

ROS2 코드는 이러한 QoS 프로파일을 인자로 받아 노드나 토픽별로 세밀하게 지정할 수 있다. 어떤 퍼블리셔 $\mathbf{pub}$의 QoS를 $\mathbf{Q}*{\mathrm{pub}}$이라 하고, 서브스크라이버 $\mathbf{sub}$의 QoS를 $\mathbf{Q}*{\mathrm{sub}}$라 할 때, DDS 레벨에서 $\mathbf{Q}*{\mathrm{pub}}$와 $\mathbf{Q}*{\mathrm{sub}}$가 서로 호환(compatible)되는지 여부에 따라 실제 통신이 성공적으로 이뤄질지 결정된다. 즉,

$$
\mathbf{connect}(\mathbf{pub}, \mathbf{sub}) =  \begin{cases} \text{SUCCESS} & \text{if } \mathbf{Q}*{\mathrm{pub}} \simeq \mathbf{Q}*{\mathrm{sub}}, \ \text{FAIL} & \text{otherwise}. \end{cases}
$$

이를 통해 로봇 시스템 설계자는 신뢰성, 지연, 메모리 사용 등을 균형감 있게 고려할 수 있다.

#### 마이크로 ROS(micro-ROS)

ROS1 시절에도 임베디드 환경을 위한 시도가 여러 차례 있었지만, ROS2는 \*\*마이크로 ROS(micro-ROS)\*\*라는 공식 프로젝트를 통해 보다 체계적으로 **마이크로컨트롤러(MCU) 환경**까지 확장하고 있다. 예컨대 FreeRTOS, Zephyr 등의 경량 RTOS 위에서 돌아가는 마이크로 ROS 에이전트(Agent)와 클라이언트(Client)를 구성하여, 저사양 장치에서도 ROS2의 일부 기능을 활용할 수 있다.

이를 간단히 표현하면, 아래와 같은 구조를 갖는다.

* **micro-ROS Client**: MCU 상에서 동작하며, 센서 데이터 취득이나 간단한 제어 수행
* **micro-ROS Agent**: 리소스가 더 풍부한 보드 혹은 PC 상에서 동작하며, DDS 네트워크로 연결

$$
\text{MCU}(\mathbf{client}) \longleftrightarrow \text{Agent} \longleftrightarrow \text{ROS2 Network}
$$

이 방식으로 MCU가 DDS 기반 ROS2 네트워크에 간접적으로 참여한다. ROS1에서는 이러한 구조가 공식적으로는 없었기에, 임베디드 보드를 ROS 네트워크에 편입하기 위해서는 커스텀 브리지나 별도 경량 프로토콜을 구현하는 수고가 더 컸다.

#### rclcpp/rclpy 초기화와 종료

ROS2는 언어별 클라이언트 라이브러리에 상관없이, 동일한 로직 흐름으로 **초기화**와 **종료**를 수행한다. 예를 들어, C++에서는 `rclcpp::init(argc, argv)`로 ROS2 관련 자원들을 초기화하고, 모든 노드가 종료된 뒤에는 `rclcpp::shutdown()`을 호출하여 할당 자원을 해제한다. Python의 `rclpy`에서도 `rclpy.init()` 및 `rclpy.shutdown()`이 동일한 맥락으로 동작한다.

이 과정을 수식으로 단순화하면,

$$
\text{init} \rightarrow \mathbf{rcl}\text{ 레이어 준비} \rightarrow \text{노드 실행} \rightarrow \text{shutdown}
$$

정도로 표현할 수 있다. $\mathbf{rcl}$ 레이어가 DDS 엔진과 시스템 리소스를 초기화하고, 이후 노드들이 통신을 수행하다가 모든 작업이 끝나면 종료(Shutdown) 처리로 넘어가는 순서다. ROS1에서도 유사한 흐름(`ros::init`, `ros::spin`, `ros::shutdown`)이 존재했지만, ROS2는 노드 라이프사이클, Executor, DDS 연결 등 훨씬 더 많은 내부 요소를 고려하여 초기화·종료 단계를 관리한다.
