[ROS2 Mastery 4편] ROS2 Topic 프로그래밍 (1) – Publisher로 거북이 제어하기

안녕하세요! 자율주행과 로봇 공학의 세계를 탐구하시는 대학생 및 연구원 여러분.

지난 3편에서는 비어있는 ROS2 패키지를 생성하고, 파이썬(Python)으로 시간에 따라 로그를 출력하는 아주 기본적인 노드(Node)를 만들어 보았습니다. 이번 4편부터는 본격적으로 로봇 제어의 핵심인 ‘통신(Communication)’을 다루게 됩니다. 그 첫 번째 시간으로 토픽(Topic) 프로그래밍을 통해 Turtlesim의 거북이가 원을 그리며 스스로 움직이도록 제어하는 Publisher(발행자) 노드를 직접 구현해 보겠습니다.


제어 대상 분석: 토픽(Topic)과 메시지(Message) 파악하기

로봇을 제어하는 코드를 작성하기 전에 가장 먼저 해야 할 일은 ‘내가 어떤 토픽에 어떤 형태의 데이터를 보내야 로봇이 움직이는가?’를 파악하는 것입니다. ROS2의 강력한 CLI(Command Line Interface) 도구들을 활용하여 이를 먼저 조사해 보겠습니다.

터미널을 열고 거북이 시뮬레이터를 실행해 주세요.

Bash
ros2 run turtlesim turtlesim_node

이제 새로운 터미널을 열고 거북이를 움직이기 위한 토픽의 정보를 확인합니다. 거북이를 움직이는 토픽의 이름은 /turtle1/cmd_vel입니다. ros2 topic info 명령어를 사용하여 이 토픽의 데이터 타입을 조회해 봅니다.

Bash
ros2 topic info /turtle1/cmd_vel

출력 결과를 보면 /turtle1/cmd_vel 토픽은 geometry_msgs/msg/Twist라는 데이터 타입을 사용한다는 것을 알 수 있습니다. 그렇다면 이 Twist라는 메시지 안에는 구체적으로 어떤 변수들이 들어있을까요? 이를 확인하기 위해 ros2 interface show 명령어를 사용합니다.

Bash
ros2 interface show geometry_msgs/msg/Twist

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

Bash
Vector3  linear
        float64 x
        float64 y
        float64 z
Vector3  angular
        float64 x
        float64 y
        float64 z

분석해 보면 Twist 메시지는 3차원 벡터인 linear(직선 이동 속도)와 angular(회전 이동 속도)라는 두 가지 내부 구조체 데이터를 가지고 있습니다.

  • linear.x (Forward): 로봇이 앞으로 전진하는 속도입니다.
  • angular.z (Yaw): 로봇이 제자리에서 회전하는 속도(각속도)입니다.

따라서 우리가 작성할 파이썬 노드는 geometry_msgs/msg/Twist 타입의 객체를 생성한 뒤, linear.xangular.z에 적절한 값을 넣어 /turtle1/cmd_vel 토픽으로 주기적으로 발행(Publish)해주면 됩니다!


Publisher 노드 코드 작성: draw_circle.py

이제 본격적으로 코드를 작성해 봅시다. 지난 3편에서 만들었던 my_robot_controller 패키지의 소스 코드 디렉토리로 이동하여 새로운 파이썬 파일 draw_circle.py를 생성하고, 시스템이 이 스크립트를 실행할 수 있도록 실행 권한(chmod +x)을 부여합니다.

Python
# 노드 디렉토리로 이동
cd ~/ros2_ws/src/my_robot_controller/my_robot_controller

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

VS Code 등의 에디터로 draw_circle.py를 열고 아래의 코드를 작성합니다. 코드를 타이핑하면서 각 부분이 어떤 역할을 하는지 고민해 보세요.

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

class DrawCircleNode(Node):
    def __init__(self):
        super().__init__('draw_circle') # 노드의 이름을 'draw_circle'로 지정합니다.
        
        # 1. Publisher 생성
        # 타입: Twist, 토픽명: '/turtle1/cmd_vel', 큐 사이즈: 10
        self.cmd_vel_pub_ = self.create_publisher(Twist, '/turtle1/cmd_vel', 10)
        
        # 2. 타이머 생성
        # 0.5초마다 send_velocity_command 콜백 함수를 실행합니다.
        self.timer_ = self.create_timer(0.5, self.send_velocity_command)
        
        self.get_logger().info("거북이 원 그리기 노드가 시작되었습니다!")

    def send_velocity_command(self):
        # 3. 메시지 객체 생성 및 데이터 할당
        msg = Twist()
        msg.linear.x = 2.0   # 초당 2.0의 속도로 전진
        msg.angular.z = 1.0  # 초당 1.0 라디안의 속도로 회전
        
        # 4. 토픽 발행(Publish)
        self.cmd_vel_pub_.publish(msg)

def main(args=None):
    rclpy.init(args=args)
    node = DrawCircleNode()
    
    try:
        rclpy.spin(node)
    except KeyboardInterrupt:
        pass
    finally:
        node.destroy_node()
        rclpy.shutdown()

if __name__ == '__main__':
    main()

[코드 심층 분석]

  1. 모듈 Import: ROS2의 파이썬 핵심 패키지인 rclpy와 더불어, 앞서 우리가 CLI로 확인했던 데이터 타입인 geometry_msgs.msg에서 Twist 패키지를 import 합니다.
  2. Publisher 생성: self.create_publisher 메쏘드를 사용하여 발행자를 만듭니다. 첫 번째 인자는 데이터 타입(Twist), 두 번째 인자는 발행할 토픽의 이름(/turtle1/cmd_vel), 세 번째 인자(10)는 통신 지연 시 메시지를 보관할 큐(Queue)의 사이즈(버퍼 크기)를 의미합니다.
  3. 타이머(Timer) 설정: ROS2 프로그래밍은 무한 루프(while True:)를 지양하고 타이머 콜백을 권장합니다. create_timer(0.5, ...)를 통해 0.5초마다 등록된 콜백 함수가 실행되도록 설정합니다.
  4. 콜백 함수 및 데이터 발행: 0.5초마다 호출되는 send_velocity_command 함수 내부에서 Twist 메시지 인스턴스를 생성합니다. linear.x에 2.0, angular.z에 1.0을 입력하여 앞으로 전진하면서 회전하도록 데이터를 채워준 뒤, publish() 메쏘드로 토픽을 ROS2 네트워크에 쏘아 올립니다.


설정 파일 수정: package.xml 및 setup.py

파이썬 코드 작성을 마쳤다고 해서 끝이 아닙니다! ROS2의 빌드 시스템(Colcon)이 이 패키지를 올바르게 빌드하고 실행할 수 있도록 두 가지 설정 파일을 수정해야 합니다.

1) package.xml 의존성(Dependency) 추가

우리의 파이썬 코드 안에서 geometry_msgs라는 외부 메시지 패키지를 새롭게 import 하여 사용했습니다. 시스템이 빌드될 때 해당 패키지가 필요하다는 것을 명시해 주어야 에러가 발생하지 않습니다. 패키지의 루트 디렉토리(~/ros2_ws/src/my_robot_controller)에 있는 package.xml 파일을 열고 아래 코드를 추가합니다.

또한 거북이 시뮬레이터를 제어할 것이므로 기존의 turtlesim 패키지에 대한 의존성도 함께 추가해 주는 것이 좋습니다.

Python
  <!-- 기타 기존 의존성 태그들 아래에 추가 -->
  <depend>geometry_msgs</depend>
  <depend>turtlesim</depend>

2) setup.py 진입점(Entry Point) 추가

터미널에서 ros2 run my_robot_controller draw_circle 명령어를 입력했을 때, 시스템이 방금 작성한 파이썬 파일의 main 함수를 찾아 실행하도록 매핑해주어야 합니다. 패키지 루트 디렉토리의 setup.py 파일을 열고 console_scripts 배열에 다음 줄을 추가합니다.

Python
    entry_points={
        'console_scripts': [
            'test_node = my_robot_controller.my_first_node:main',
            # 방금 만든 노드의 진입점을 추가합니다.
            'draw_circle = my_robot_controller.draw_circle:main'
        ],
    },
  • draw_circle: 터미널에서 실행할 노드의 이름입니다.
  • my_robot_controller.draw_circle:main: my_robot_controller 디렉토리 안의 draw_circle.py 스크립트 내부에 있는 main 함수를 가리킵니다.


빌드 및 실행 테스트

모든 코딩과 환경 설정이 완벽하게 마무리되었습니다! 이제 작업 공간의 최상위 루트 디렉토리로 이동하여 작성한 패키지를 새롭게 빌드해 봅시다.

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

# 전체 패키지 빌드
colcon build --symlink-install

에러 없이 정상적으로 빌드가 완료되었다면, 노드를 실행할 준비가 된 것입니다.

[실행 단계]

  1. 첫 번째 터미널: 거북이 시뮬레이터를 실행합니다.
  2. 두 번째 터미널: 새 터미널을 열고 우리가 방금 만든 오버레이 환경을 소싱한 후 draw_circle 노드를 실행합니다.

성공입니다! 키보드로 조종하지 않아도 우리가 만든 Publisher 노드가 0.5초마다 이동 속도 명령(Topic)을 보내기 때문에, 거북이가 아름다운 원을 그리며 무한히 회전하는 것을 볼 수 있습니다.

💡연구원 팁: RQT Graph로 통신 구조 확인하기

로봇 시스템이 거대해지면 데이터가 어디서 어디로 흐르는지 시각적으로 파악하는 것이 디버깅의 핵심입니다. 거북이가 돌고 있는 상태에서 완전히 새로운 터미널을 열고 rqt_graph 명령어를 실행해 보세요.

Bash
rqt_graph

화면을 보면 /draw_circle 노드(우리가 만든 발행자)에서 /turtlesim 노드(구독자)를 향해 /turtle1/cmd_vel 이라는 이름의 토픽 데이터가 실시간으로 흘러가고 있는 명확한 통신 구조를 시각적으로 확인할 수 있습니다.


마무리하며

이번 4편에서는 ROS2 프로그래밍의 꽃이라고 할 수 있는 ‘토픽 발행(Topic Publish)’의 전체 과정을 직접 코딩하며 익혀보았습니다.

  1. 명령어(ros2 topic info, ros2 interface show)를 통해 제어하고자 하는 데이터 형식을 분석하고,
  2. rclpy.node.Node를 상속받아 Publisher 객체와 주기적인 Timer를 생성한 뒤,
  3. 올바른 데이터(Twist)를 할당하여 지속적으로 송신하는 구조를 만들었습니다.

여러분은 이제 어떤 로봇 플랫폼이 주어지더라도, 로봇이 구독(Subscribe)하고 있는 토픽의 이름과 데이터 구조만 알아낸다면 여러분만의 알고리즘으로 자율주행 명령을 내릴 수 있는 기본적인 능력을 갖추게 되었습니다!

다음 [ROS2 Mastery 5편]에서는 이번과 반대되는 개념으로, 거북이의 현재 위치(x, y 좌표) 센서 데이터를 실시간으로 수신받아 터미널에 출력하는 “Subscriber 노드 만들기”에 대해 알아보도록 하겠습니다.

여러분의 성공적인 로봇 공학 연구를 응원하며, 다음 연재에서 뵙겠습니다. 감사합니다!

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

기고일: 2026.05.10

Similar Posts

답글 남기기

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