[ROS2 Mastery 5편] ROS2 Topic 프로그래밍 (2) – Subscriber로 위치 정보 수신하기

안녕하세요! 자율주행 로봇과 시스템 제어를 연구하고 개발하시는 대학생 및 연구원 여러분. 쿼드(QUAD) 드론연구소의 자료를 바탕으로 진행되는 ‘ROS2 속성 마스터 과정’ 연재 블로그 5편에 오신 것을 환영합니다.

지난 4편에서는 ROS2 통신의 핵심인 발행자(Publisher) 노드를 작성하여, 거북이에게 이동 명령(cmd_vel)을 보내 원을 그리게 만드는 방법을 학습했습니다. 이번 5편에서는 그 반대의 개념인 구독자(Subscriber) 노드를 직접 프로그래밍해 보겠습니다.

로봇 제어에 있어 ‘현재 로봇의 상태(위치, 속도 등)’를 파악하는 것은 필수적입니다. 이번 실습에서는 Turtlesim 노드가 자체적으로 발행하고 있는 거북이의 실시간 위치 정보 토픽을 수신(Subscribe)하여, 터미널 화면에 좌표 데이터로 출력하는 파이썬 노드를 만들어 볼 것입니다.

원리를 깊이 있게 이해하고자 하는 연구원분들의 시각에 맞춰 상세히 설명해 드릴 테니, 차근차근 따라와 주시기 바랍니다!


구독(Subscribe) 대상 분석: 어떤 토픽을 받아야 할까?

ROS2 프로그래밍의 첫 단추는 언제나 CLI(Command Line Interface) 도구를 활용한 데이터 분석입니다. 코드를 짜기 전에 우리가 어떤 이름의 토픽을 구독해야 하고, 그 토픽이 어떤 형태의 데이터를 담고 있는지 알아내야 합니다.

터미널을 열고 거북이 시뮬레이터를 먼저 실행해 봅니다.

Bash
ros2 run turtlesim turtlesim_node

이제 새로운 터미널을 열고 다음 분석 명령어들을 순서대로 실행해 봅시다.

1.토픽 리스트 확인

현재 ROS2 네트워크에서 돌아다니고 있는 토픽의 목록을 조회합니다.

Bash
ros2 topic list

출력된 목록을 살펴보면 /turtle1/pose라는 토픽이 발행되고 있는 것을 확인할 수 있습니다. 이름에서 알 수 있듯, 이 토픽이 거북이의 자세(위치) 정보를 담고 있을 확률이 높습니다.

2. 토픽 데이터 타입 확인

해당 토픽이 어떤 자료형(Data Type)으로 통신하고 있는지 확인합니다.

Bash
ros2 topic info /turtle1/pose

결과를 보면 데이터 타입이 turtlesim/msg/Pose임을 알 수 있습니다.

3. 메시지 구조 파악

이제 이 Pose라는 메시지 객체 안에 구체적으로 어떤 변수들이 들어있는지 해부해 보겠습니다.

Bash
ros2 interface show turtlesim/msg/Pose

명령어를 실행하면 다음과 같은 구조가 출력됩니다.

Plaintext
float32 x
float32 y
float32 theta
float32 linear_velocity
float32 angular_velocity

분석 결과, 이 메시지는 2차원 공간에서의 위치 값인 x, y 좌표, 거북이가 바라보는 방향인 theta(라디안 값), 그리고 현재 직선 속도(linear_velocity)와 회전 속도(angular_velocity) 정보를 모두 포함하고 있다는 것을 알 수 있습니다.

💡[연구원 팁] 데이터 실시간 모니터링

코딩 전 센서 데이터가 정상적으로 들어오는지 확인하려면 echo 명령어를 사용합니다.

Bash
ros2 topic echo /turtle1/pose

이 명령어를 켜둔 상태로 다른 터미널에서 ros2 run turtlesim turtle_teleop_key를 실행해 거북이를 움직여 보세요. 거북이가 이동함에 따라 터미널에 출력되는 x, y 값이 실시간으로 변하는 것을 직접 눈으로 확인할 수 있습니다.

자, 이제 목표가 명확해졌습니다! 우리는 turtlesim/msg/Pose 타입의 데이터를 받아오는 콜백(Callback) 함수를 작성하면 됩니다.


Subscriber 노드 코드 작성: pose_subscriber.py

우리가 작업 중인 my_robot_controller 패키지 디렉토리로 이동하여 새로운 파이썬 소스 파일을 생성하고 실행 권한을 부여합니다.

Bash
# 노드 소스 코드가 있는 디렉토리로 이동
cd ~/ros2_ws/src/my_robot_controller/my_robot_controller

# 파일 생성 및 실행 권한(x) 부여
touch pose_subscriber.py
chmod +x pose_subscriber.py

VS Code와 같은 텍스트 에디터를 열어 pose_subscriber.py 파일에 아래의 코드를 작성합니다.

Python
#!/usr/bin/env python3
import rclpy
from rclpy.node import Node
from turtlesim.msg import Pose  # 분석했던 Pose 메시지 타입을 import 합니다.

class PoseSubscriberNode(Node):
    def __init__(self):
        # 1. 노드 이름 초기화
        super().__init__('pose_subscriber')
        
        # 2. Subscriber(구독자) 생성
        # 타입: Pose, 토픽명: '/turtle1/pose', 콜백 함수: self.pose_callback, 큐 사이즈: 10
        self.pose_subscriber_ = self.create_subscription(
            Pose, 
            '/turtle1/pose', 
            self.pose_callback, 
            10
        )
        self.get_logger().info("거북이 위치 구독 노드가 시작되었습니다!")

    # 3. 콜백(Callback) 함수 정의
    def pose_callback(self, msg: Pose):
        # 수신된 메시지(msg)의 x, y 좌표를 터미널 로그로 출력합니다.
        self.get_logger().info(f"현재 거북이 위치 - X: {msg.x:.2f}, Y: {msg.y:.2f}")

def main(args=None):
    rclpy.init(args=args)
    node = PoseSubscriberNode()
    
    try:
        # 노드를 실행 상태로 유지하며 콜백 함수를 대기시킵니다.
        rclpy.spin(node)
    except KeyboardInterrupt:
        pass
    finally:
        node.destroy_node()
        rclpy.shutdown()

if __name__ == '__main__':
    main()

💡[코드 심층 분석]

단순한 코드 같지만, 로봇 소프트웨어의 이벤트 구동(Event-driven) 방식을 이해하는 매우 중요한 구조입니다.

  1. 메시지 패키지 Import: 앞서 CLI로 확인했던 데이터 타입인 turtlesim.msg 패키지에서 Pose 클래스를 불러옵니다.
  2. Subscriber 생성: self.create_subscription() 메쏘드를 사용하여 구독자를 만듭니다.
    • 첫 번째 매개변수(Pose): 수신할 데이터의 타입입니다.
    • 두 번째 매개변수('/turtle1/pose'): 통신할 토픽의 정확한 이름입니다.
    • 세 번째 매개변수(self.pose_callback): 데이터가 도착했을 때 실행될 콜백 함수를 지정합니다.
    • 네 번째 매개변수(10): 데이터 수신 큐(버퍼)의 크기입니다.
  3. 콜백 함수 (Callback Function): 지난번 Publisher 예제에서는 타이머에 의해 주기적으로 콜백이 실행되었지만, Subscriber의 콜백 함수인 pose_callback네트워크를 통해 토픽 데이터가 도착할 때마다 수동적(피동적)으로 자동 실행됩니다. 매개변수로 전달된 msg 객체 안에는 x, y, theta 값이 담겨 있으며, 우리는 msg.xmsg.y를 읽어들여 화면에 출력(get_logger().info)하게 됩니다.


설정 파일(setup.py) 업데이트 및 진입점 등록

노드 코드를 완성했다면, ROS2 환경(colcon 빌드 시스템)이 터미널 명령어를 통해 이 파이썬 스크립트를 찾아 실행할 수 있도록 setup.py에 진입점(Entry point)을 등록해야 합니다.

패키지 최상단 폴더(~/ros2_ws/src/my_robot_controller)에 위치한 setup.py 파일을 열고, entry_points 영역의 console_scripts 배열에 우리가 만든 노드를 추가해 줍니다.

Python
    entry_points={
        'console_scripts': [
            'test_node = my_robot_controller.my_first_node:main',
            'draw_circle = my_robot_controller.draw_circle:main',
            # 방금 작성한 구독자 노드의 진입점을 추가합니다.
            'pose_subscriber = my_robot_controller.pose_subscriber:main'
        ],
    },
  • pose_subscriber =: 터미널에서 ros2 run 명령어 뒤에 입력할 실행 노드의 이름입니다.
  • my_robot_controller.pose_subscriber:main: 실제 실행될 패키지 폴더 안의 pose_subscriber.py 파일 내에 있는 main 함수를 매핑한다는 의미입니다.


패키지 빌드 및 실행 테스트

모든 준비가 완료되었습니다! 이제 수정한 패키지를 빌드하고 실제로 거북이의 위치를 잘 수신하는지 테스트해 보겠습니다.

작업 공간의 루트 디렉토리로 이동하여 빌드를 수행합니다.

Bash
# 작업 공간 루트로 이동
cd ~/ros2_ws

# 전체 패키지 빌드 (--symlink-install을 사용하면 파이썬 파일 수정 시 재빌드를 최소화할 수 있습니다)
colcon build --symlink-install

에러 없이 빌드가 완료되었다면, 터미널 3개를 열어 전체 시스템을 구동해 보겠습니다.

💡[실행 단계]

  1. 첫 번째 터미널 (시뮬레이터 실행):
  2. 두 번째 터미널 (구독자 노드 실행): 새 터미널을 열고 우리가 수정한 오버레이 환경을 소싱한 뒤 노드를 실행합니다.
  3. 이 시점에서 터미널 화면에는 초기 위치인 X: 5.54, Y: 5.54가 빠르게 출력되기 시작할 것입니다.
  4. 세 번째 터미널 (키보드 조종기 실행): 거북이를 움직여서 위치가 변하는지 확인하기 위해 조종 노드를 실행합니다.

키보드 방향키를 눌러 거북이를 이리저리 움직여 보세요. 거북이가 이동함에 따라 pose_subscriber가 실행 중인 터미널 창의 좌표 값(X, Y)이 실시간으로 변하는 것을 확인할 수 있습니다! 센서 데이터를 성공적으로 수신하는 데 성공하셨습니다.


RQT Graph로 통신망 시각화 점검

연구 과정에서 로봇 노드가 여러 개로 늘어나면 시스템 통신망을 한눈에 파악하는 것이 디버깅의 핵심이 됩니다. 앞서 2편에서 배웠던 RQT 유틸리티를 활용해 현재 우리가 만든 통신 구조를 시각화해 보겠습니다.

현재 노드들이 실행 중인 상태에서 네 번째 터미널을 열고 다음 명령어를 입력합니다.

Bash
rqt_graph

화면을 살펴보면, /turtlesim 노드(센서 발행자)에서 출발한 화살표가 /turtle1/pose라는 토픽을 매개로 하여, 방금 우리가 직접 코딩한 /pose_subscriber 노드(센서 구독자)로 데이터가 실시간으로 흘러들어가는 완벽한 통신 다이어그램을 확인할 수 있습니다.


YouTube Tutorial

재생

마무리하며

이번 5편에서는 로봇 제어의 핵심인 ‘센서 데이터 수신(Subscriber)’ 방법에 대해 깊이 있게 학습했습니다. 데이터 타입을 분석하고(Pose), 콜백 함수(pose_callback)를 통해 비동기적으로 들어오는 토픽 데이터를 처리하여 로봇의 현재 상태(x, y 좌표)를 파악하는 구조를 직접 구현해 보았습니다.

로봇 공학에서 자율주행 알고리즘을 설계할 때 가장 기본이 되는 논리는 다음과 같습니다.

  1. 현재 상태 파악 (Subscriber)
  2. 목표 지점과의 오차 계산 (Algorithm)
  3. 제어 명령 전송 (Publisher)

우리는 이미 4편에서 Publisher를, 이번 5편에서 Subscriber를 마스터했습니다! 다음 [ROS2 Mastery 6편]에서는 이 두 가지 핵심 노드를 하나의 파이썬 파일로 결합하여, 거북이가 화면의 벽(펜스) 좌표를 스스로 인식하고 부딪히기 전에 방향을 틀어 회피하는 “Topic 통신을 활용한 Turtlesim 자율주행 구현” 데모를 진행해 보겠습니다.

작성자: 에이든(Aiden), 쿼드(QUAD) 드론연구소 마케팅팀

기고일: 2026.05.16

Similar Posts

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다