Overview
ROS 2에서 실행 관리는 Executor가 담당한다. Executor는 OS 스레드 하나 이상을 사용해, 들어오는 메시지·이벤트에 대해 구독·타이머·서비스 서버·액션 서버 등의 콜백을 호출한다. ROS 1의 spin보다 실행 제어가 세밀한 명시적 Executor 클래스가 제공된다(rclcpp의 executor.hpp, rclpy의 executors.py, rclc의 executor.h). 기본 API는 ROS 1과 비슷하다.
아래는 C++ 클라이언트 라이브러리 rclcpp를 기준으로 설명한다.
Basic use (기본 사용)
가장 단순한 경우, 메인 스레드에서 노드의 메시지·이벤트를 처리하려면 rclcpp::spin(node)를 호출한다.
rclcpp::init(argc, argv);
rclcpp::Node::SharedPtr node = ...
rclcpp::spin(node); // 여기서 블로킹, 콜백 처리
spin(node)는 내부적으로 Single-Threaded Executor를 생성·실행하는 것과 같다.
rclcpp::executors::SingleThreadedExecutor executor;
executor.add_node(node);
executor.spin();
Executor의 spin()이 호출되면, 현재 스레드가 rcl·미들웨어 계층에 들어온 메시지·이벤트를 묻고, 해당 콜백을 호출하는 것을 노드가 종료될 때까지 반복한다. 미들웨어 QoS와 맞추기 위해, 메시지는 클라이언트 라이브러리 계층에 큐로 쌓지 않고 미들웨어에 두었다가 콜백이 처리할 때 가져온다(ROS 1과의 중요한 차이). Wait set이 “큐별로 하나의 이진 플래그” 형태로 Executor에게 “메시지 있음”을 알려주며, 타이머 만료 감지에도 쓰인다.
Single-Threaded Executor는 컴포넌트를 실행하는 컨테이너 프로세스에서도 사용된다(명시적 main 없이 노드가 생성·실행되는 경우).
Types of Executors (Executor 종류)
rclcpp는 공통 부모 클래스에서 파생된 세 가지 Executor를 제공한다.
| Executor | 설명 |
|---|---|
| SingleThreadedExecutor | 스레드 하나로 모든 콜백 처리. spin(node)의 실제 구현. |
| MultiThreadedExecutor | 설정 가능한 개수의 스레드로 여러 메시지·이벤트를 병렬 처리. |
| StaticSingleThreadedExecutor | 노드의 구독·타이머·서비스·액션 구조를 노드가 추가될 때 한 번만 스캔. 나머지 두 Executor는 이런 변경을 주기적으로 스캔하므로, Static은 초기화 시점에 모든 구독·타이머 등을 만드는 노드에만 사용하는 것이 좋다. 런타임 비용을 줄인다. |
세 Executor 모두 add_node(..)로 여러 노드를 넣어 한 Executor가 돌릴 수 있다. Multi-Threaded Executor에서 실제 병렬도는 콜백 그룹 설정에 따라 달라진다.
Callback groups (콜백 그룹)
ROS 2에서는 노드의 콜백을 그룹으로 묶을 수 있다. rclcpp에서는 Node::create_callback_group()으로, rclpy에서는 해당 콜백 그룹 타입의 생성자로 만든다. 이 콜백 그룹은 노드가 살아 있는 동안 유지되어야 하며(예: 클래스 멤버), 구독·타이머 등을 만들 때 옵션으로 지정한다. 지정하지 않은 구독·타이머 등은 기본 콜백 그룹에 들어간다.
콜백 그룹 타입(생성 시 결정):
- Mutually exclusive: 이 그룹 안의 콜백은 동시에 실행되지 않음.
- Reentrant: 이 그룹 안의 콜백은 동시에 실행될 수 있음.
서로 다른 그룹의 콜백은 항상 병렬 실행 가능하다. Multi-Threaded Executor는 스레드 풀을 써서 위 규칙에 맞게 가능한 만큼 콜백을 병렬 처리한다. 콜백 그룹을 여러 Executor에 나눠 줄당 add_callback_group(..)을 쓰면, OS 스케줄러로 특정 콜백(예: 제어 루프용 구독·타이머)을 다른 콜백보다 우선시할 수 있다.
Scheduling semantics (스케줄링 의미)
콜백 처리 시간이 메시지·이벤트가 도착하는 주기보다 짧으면 Executor는 사실상 FIFO 순서로 처리한다. 어떤 콜백이 길어지면 메시지·이벤트는 스택 하단 계층의 큐에 쌓이고, wait set은 “해당 토픽에 메시지가 있는지 여부”만 Executor에 알려준다. Executor는 이 정보만으로 서비스·액션 포함 메시지를 라운드 로빈으로 처리하며, FIFO 순서는 보장하지 않는다.
(타이머 이벤트를 다른 메시지보다 우선하던 동작은 Eloquent에서 제거되었다.)
Outlook (한계와 대안)
위 세 가지 rclcpp Executor는 많은 애플리케이션에 적합하지만, 실시간성(잘 정의된 실행 시간, 결정성, 실행 순서 제어)이 필요한 경우에는 다음 같은 한계가 있다.
- 스케줄링 의미가 복잡·혼합되어 있어 정형적인 타이밍 분석이 어렵다.
- 우선순위 역전: 높은 우선순위 콜백이 낮은 우선순위 콜백에 막힐 수 있다.
- 콜백 실행 순서를 명시적으로 제어할 수 없다.
- 특정 토픽에 대한 트리거를 내장으로 제어할 수 없다.
Executor 자체의 CPU·메모리 오버헤드도 무시할 수 없다. Static Single-Threaded Executor가 이를 크게 줄이지만 부족한 경우가 있다.
이를 일부 보완하는 방향으로:
- rclcpp WaitSet: Executor 대신 구독·타이머·서비스·액션 등에 직접 대기할 수 있다. 결정적이고 사용자 정의 처리 순서를 구현할 수 있으며, 여러 구독의 메시지를 묶어 처리하는 것도 가능하다. (
examples_rclcpp_wait_set패키지 참고.) - rclc Executor: micro-ROS용 C 클라이언트 라이브러리 rclc의 Executor. 콜백 실행 순서와 트리거 조건을 세밀하게 제어할 수 있으며, LET(Logical Execution Time) 시맨틱 아이디어를 반영한다.