[ROS2] Executor와 Callback Group

TaerinLog·2025년 6월 2일

기반지식

- ros2는 콜백 기반의 비동기 이벤트 처리 시스템

문제상황

콜백 A, B, C가 있을 때, 콜백 A가 n초 동안 스레드를 점유하면, 콜백 B와 C는 그동안 실행되지 않음
-> 특히, B와 C가 센서 데이터를 처리하는 콜백이라면, 데이터가 제때 업데이트되지 않아 지연이나 누락 문제가 발생할 수 있음
=> 스레드 병렬 처리로 해결 가능


Executor

  • 콜백들을 실제로 실행시키는 주체 (스케줄러)
    ㄴ ROS 2에서 콜백을 언제, 어떻게 실행할지를 결정하는 스케줄러
    ㄴ 노드에서는 콜백을 등록만 함

Executor 종류

1. [default] SingleThreadedExecutor

  • 하나의 스레드만 사용해서 콜백을 하나씩 순차적으로 실행
  • 즉, 병렬 실행이 되지 않음
  • 아래 그림의 모든 콜백이 순차적 실행

2. MultiThreadedExecutor

  • 여러 개의 스레드를 사용하여 콜백 병렬 실행이 가능

Executor 사용 예시

from rclpy.executors import SingleThreadedExecutor, MultiThreadedExecutor

def main(args=None):
    rclpy.init(args=args)
    node = ClassNode()

    # Create a single-threaded executor
    executor = SingleThreadedExecutor()
    executor.add_node(node)

    # Create a multi-threaded executor
    executor = MultiThreadedExecutor(num_threads=3)
    executor.add_node(node)
    
    try:
        executor.spin()
    finally:
        executor.shutdown()
        executor.destroy_node()
    rclpy.shutdown()

Callback Group

  • 콜백 간의 실행 동시성 정책을 결정하는 그룹
  • 스레드가 1개라면 어떠한 콜백 그룹을 설정하더라도 병렬 실행이 되지 않음

Callback Group 종류

1. [default] MutuallyExclusiveCallbackGroup

  • 그룹 내부 콜백은 한 번에 하나씩만 실행
  • 공유 변수, 자원 충돌에게서 안전함
  • 각 그룹의 콜백 1개씩만 접근 가능

2. ReentrantCallbackGroup

  • 그룹 내부의 콜백을 병렬 실행 허용 → 멀티스레드 환경에서 성능 향상

Callback Group 사용 예시

  • ReentrantCallbackGroup로 묶인 group1의 콜백들은 병렬 처리 가능
  • MutuallyExclusiveCallbackGroup의 group2 내부의 콜백은 병렬 처리 안됨
from rclpy.callback_groups import MutuallyExclusiveCallbackGroup

class Example(Node):
    def __init__(self):
        super().__init__('example')

        ### Callback group ### 
        # group1의 콜백들은 병렬 처리 가능
        self.group1 = ReentrantCallbackGroup()
        # group2 내부의 콜백은 하나씩만 처리 가능
        self.group2 = MutuallyExclusiveCallbackGroup()

        # Subscriber
        self.function1 = self.create_subscription(msg,'topic1',self.call_back1, 10,callback_group=self.group1)
        self.function2 = self.create_subscription(msg,'topic2',self.call_back2, 10,callback_group=self.group1)

        # Timer
        self.function2 = self.create_timer(0.3, self.call_back3, callback_group=self.group2)

    def call_back1(self, msg):
        self.get_logger().info("call_back1")
    def call_back2(self, msg):
        self.get_logger().info("call_back2")
    def call_back3(self, msg):
        self.get_logger().info("call_back3")

참고 : CPU의 논리 코어(Logical Core) 개수 확인

ㄴ ROS 2 노드를 실행할 때 사용할 수 있는 스레드/코어 수

  • Linux : cat /proc/cpuinfo | grep processor | wc -l
  • Python : psutil.cpu_count(logical=True)

실무 궁금증: 센서 콜백 여러 개는 싱글 스레드로 충분할까?

콜백이 가볍다면(read | logging 등) 싱글 스레드로 충분하지만, 고속 센서나 무거운 처리가 있다면 Reentrant + MultiThreadedExecutor 조합이 필요

병목이 생길 수 있는 경우

  • 센서 자체가 고주기(>100Hz) 인 경우
  • 이미지 처리, 딥러닝 모델 호출, 파일 저장, 경로 탐색 같은 무거운 작업 시
  • 콜백 간에 잠금(lock) 구조가 걸릴 경우

상황별 조합

상황Callback GroupExecutor
단순 제어 로직MutuallyExclusiveSingleThreadedExecutor
센서 다수 / 빠른 주기ReentrantMultiThreadedExecutor
이미지 처리 / 딥러닝ReentrantMultiThreadedExecutor
공유 자원 접근 있음MutuallyExclusive어떤 Executor든 OK
profile
taerin

0개의 댓글