퍼블리셔(Publisher)

퍼블리셔는 ROS2에서 데이터를 다른 노드로 보내는 역할을 한다. 이를 통해 한 노드가 데이터를 발행하면 다른 노드들이 그 데이터를 받아서 처리할 수 있다. 퍼블리셔의 기본 구조는 아래와 같은 과정을 따른다.

퍼블리셔 생성 과정

  1. 노드에서 퍼블리셔 객체 생성
    퍼블리셔는 노드 내에서 특정 메시지 타입을 발행할 수 있도록 정의된다. 퍼블리셔 객체를 생성할 때는 퍼블리싱할 메시지 타입과 토픽 이름을 설정한다.

  2. 메시지 전송
    퍼블리셔는 데이터를 발행할 때, 일정한 주기로 메시지를 생성하여 전송한다. 각 메시지는 특정 토픽을 통해 송신된다.
    이때 토픽은 송신자와 수신자를 연결하는 가상의 채널 역할을 하며, 퍼블리셔가 토픽에 메시지를 발행하면 해당 토픽에 구독하고 있는 서브스크라이버가 메시지를 수신하게 된다.

퍼블리셔의 데이터 흐름을 다이어그램으로 나타내면 아래와 같다.

graph TD; A[퍼블리셔] --> B[메시지 생성]; B --> C[토픽에 발행]; C --> D[서브스크라이버에게 전송];

퍼블리셔 생성 코드 예시 (Python)

import rclpy
from rclpy.node import Node
from std_msgs.msg import String

class MyPublisher(Node):

    def __init__(self):
        super().__init__('my_publisher')
        self.publisher_ = self.create_publisher(String, 'my_topic', 10)
        self.timer = self.create_timer(0.5, self.publish_message)

    def publish_message(self):
        msg = String()
        msg.data = 'Hello ROS2'
        self.publisher_.publish(msg)

def main(args=None):
    rclpy.init(args=args)
    node = MyPublisher()
    rclpy.spin(node)
    rclpy.shutdown()

if __name__ == '__main__':
    main()

이 코드는 기본적인 퍼블리셔 구현을 보여준다. self.publisher_std_msgs/msg/String 타입의 메시지를 my_topic이라는 토픽으로 발행한다.

서브스크라이버(Subscriber)

서브스크라이버는 특정 토픽을 구독하며, 퍼블리셔가 발행하는 메시지를 수신하는 역할을 한다. 서브스크라이버는 다음과 같은 과정을 거쳐 메시지를 처리한다.

서브스크라이버 생성 과정

  1. 노드에서 서브스크라이버 객체 생성
    서브스크라이버는 구독할 메시지 타입과 토픽을 지정하여 생성된다. 이때 구독한 토픽에서 메시지가 들어오면 이를 처리하기 위한 콜백 함수를 정의해야 한다.

  2. 메시지 수신 및 처리
    서브스크라이버는 퍼블리셔가 발행한 메시지를 수신한 후, 이를 콜백 함수에서 처리한다. 각 메시지는 실시간으로 들어오며, 서브스크라이버는 메시지가 들어올 때마다 콜백 함수를 실행한다.

서브스크라이버의 동작을 다이어그램으로 나타내면 아래와 같다.

graph TD; A[퍼블리셔] --> B[토픽]; B --> C[서브스크라이버]; C --> D[콜백 함수 실행];

서브스크라이버 생성 코드 예시 (Python)

import rclpy
from rclpy.node import Node
from std_msgs.msg import String

class MySubscriber(Node):

    def __init__(self):
        super().__init__('my_subscriber')
        self.subscription = self.create_subscription(
            String,
            'my_topic',
            self.listener_callback,
            10)
        self.subscription  # prevent unused variable warning

    def listener_callback(self, msg):
        self.get_logger().info('Received: "%s"' % msg.data)

def main(args=None):
    rclpy.init(args=args)
    node = MySubscriber()
    rclpy.spin(node)
    rclpy.shutdown()

if __name__ == '__main__':
    main()

서브스크라이버는 my_topic 토픽을 구독하며, 퍼블리셔로부터 메시지를 수신하면 listener_callback 함수에서 이를 처리한다.

퍼블리셔와 서브스크라이버의 관계

통신 구조

퍼블리셔와 서브스크라이버는 ROS2의 기본 통신 패턴을 형성하며, 퍼블리셔는 메시지를 발행하고, 서브스크라이버는 이를 받아서 처리한다. 이 관계는 퍼블리셔가 토픽을 통해 데이터를 발행하면, 해당 토픽을 구독하고 있는 서브스크라이버가 메시지를 수신하는 방식으로 이루어진다. 퍼블리셔와 서브스크라이버는 비동기 통신을 지원하며, 퍼블리셔가 메시지를 발행할 때 서브스크라이버는 즉시 메시지를 받을 수 있다.

이 비동기 통신을 수학적으로 표현하면, 퍼블리셔가 발행하는 메시지 \mathbf{m}(t)가 시간에 따라 변할 때, 서브스크라이버는 이 메시지를 구독하여 특정 시간 t에 해당하는 메시지를 수신한다.

\mathbf{m}(t) = \begin{cases} \text{메시지 데이터} & \text{퍼블리셔에서 발행} \\ \text{NULL} & \text{발행된 메시지 없음} \end{cases}

서브스크라이버는 메시지가 발행된 시점에서 t = t_0에 메시지 \mathbf{m}(t_0)를 수신한다.

메시지 손실과 QoS 정책

ROS2는 퍼블리셔와 서브스크라이버 간의 메시지 전달에 있어 QoS(품질 서비스, Quality of Service) 정책을 지원한다. QoS는 네트워크 환경과 메시지 전달 신뢰성을 조절하는 중요한 요소이다. 기본적으로 메시지가 손실되지 않도록 보장하거나, 네트워크 상태에 따라 메시지 손실을 허용하는 방식으로 설정할 수 있다.

QoS의 기본 매개변수는 다음과 같다.

QoS 정책을 설정한 퍼블리셔와 서브스크라이버 간의 통신 구조는 아래와 같다.

graph TD; A[퍼블리셔] -->|QoS 정책| B[토픽]; B --> C[서브스크라이버]; C --> D[QoS 정책 적용];

QoS 설정은 네트워크가 불안정하거나 다중 퍼블리셔/서브스크라이버 환경에서 중요한 역할을 한다.

퍼블리셔와 서브스크라이버 간의 메시지 타이밍

메시지가 발행되고 수신되는 주기와 타이밍은 시스템 성능에 영향을 미친다. 퍼블리셔는 메시지를 주기적으로 발행하는데, 이 주기를 발행 주기 T_{pub}라 하고, 서브스크라이버가 메시지를 수신하는 주기를 수신 주기 T_{sub}라 한다. 발행 주기와 수신 주기는 일반적으로 동일하지만, 네트워크 상태나 처리 속도에 따라 다를 수 있다.

발행 주기와 수신 주기 간의 관계를 수식으로 표현하면 다음과 같다.

T_{sub} = T_{pub} + \Delta T

여기서 \Delta T는 네트워크 지연 시간 또는 시스템 처리 시간으로 인한 시간 차이를 나타낸다. \Delta T가 클수록 메시지 수신이 늦어질 수 있으며, 이는 실시간 시스템에서 문제가 될 수 있다.

퍼블리셔와 서브스크라이버의 확장성

퍼블리셔와 서브스크라이버의 관계는 다수의 퍼블리셔와 서브스크라이버 간에도 적용될 수 있다. 여러 퍼블리셔가 동일한 토픽으로 메시지를 발행하고, 여러 서브스크라이버가 해당 토픽을 구독할 수 있다. 이를 통해 시스템의 확장성을 높일 수 있다. 예를 들어, 다중 퍼블리셔와 서브스크라이버가 있을 때, 각 퍼블리셔는 독립적으로 메시지를 발행하고, 서브스크라이버는 그 메시지를 수신하여 처리한다.

graph TD; A1[퍼블리셔 1] --> B[토픽]; A2[퍼블리셔 2] --> B; B --> C1[서브스크라이버 1]; B --> C2[서브스크라이버 2];

여기서 N개의 퍼블리셔가 각각의 메시지를 발행하고, M개의 서브스크라이버가 해당 메시지를 구독할 수 있는 구조가 된다. 발행 주기와 수신 주기의 조율이 중요한 요소이다.