[ROS2 Mastery 3편] 첫 ROS2 패키지 생성 및 Python 노드(Node) 프로그래밍 가이드

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

지난 1편과 2편에서는 ROS2의 작업 공간(Workspace)을 구축하는 방법과 핵심 통신 개념, 그리고 이를 시각화해 주는 강력한 도구인 RQT의 활용법에 대해 알아보았습니다. 이번 3편에서는 드디어 여러분의 손으로 직접 코드를 작성하여 로봇 제어의 주체가 되는 ‘첫 번째 ROS2 패키지’를 생성하고, 파이썬(Python)을 이용해 ‘커스텀 노드(Node)’를 프로그래밍하는 전체 과정을 상세하게 안내해 드리겠습니다.

단순히 코드를 복사하고 붙여넣는 것을 넘어, 코드가 동작하는 원리와 구조를 깊이 있게 이해할 수 있도록 친절하게 설명해 드릴 테니 끝까지 함께해 주시기 바랍니다!


나만의 ROS2 패키지(Package) 만들기

ROS2 시스템에서 로봇을 구동하는 프로그램(노드)을 작성하려면, 가장 먼저 코드를 담을 그릇인 ‘프로젝트 패키지’를 생성해야 합니다. 패키지는 관련된 코드, 라이브러리, 설정 파일들을 하나로 묶어 관리하는 기본 단위입니다.

이번 실습에서 우리가 만들 패키지의 이름은 my_robot_controller로 정하겠습니다.

1) 패키지 생성 명령어 실행

터미널을 열고, 1편에서 만들었던 우리의 작업 공간(Workspace) 소스 디렉토리인 ~/ros2_ws/src로 이동합니다. 그리고 ROS2 환경을 소싱한 뒤 패키지 생성 명령어를 실행합니다.

Bash
# 기본 환경 소싱
source /opt/ros/jazzy/setup.bash

# 작업 공간의 src 디렉토리로 이동
cd ~/ros2_ws/src

# Python 기반의 ROS2 패키지 생성
ros2 pkg create --build-type ament_python my_robot_controller

명령어를 실행하면 터미널 화면에 my_robot_controller 패키지 디렉토리와 함께 프로그래밍에 필요한 필수 파일들이 자동으로 생성되었음을 알리는 메시지가 출력됩니다.

2) 생성된 파일 내용 확인 및 첫 번째 빌드

패키지가 성공적으로 생성되었다면, Visual Studio Code(VS Code)와 같은 에디터를 사용하여 만들어진 파일들의 구조를 살펴봅니다. 패키지 폴더 안에는 패키지의 메타 정보를 담고 있는 package.xml 파일과, 파이썬 패키지 설치 및 실행 진입점을 설정하는 setup.py 파일 등이 생성되어 있는 것을 확인할 수 있습니다.

이제 이 빈 패키지가 정상적으로 작동하는지 확인하기 위해 첫 번째 빌드를 수행해 보겠습니다. 작업 디렉토리의 최상위 경로(~/ros2_ws)로 이동하여 colcon build 명령을 실행합니다.

Bash
# 작업 공간 최상위 디렉토리로 이동
cd ~/ros2_ws

# 전체 작업 공간 빌드
colcon build --symlink-install

에러 없이 빌드가 완료되었다면, ROS2 패키지를 성공적으로 생성한 것입니다!


my_robot_controller Node 만들기

이제 실제 ROS2 프로그램에서 작업의 주체가 되는 노드(Node)를 파이썬으로 직접 프로그래밍해 보겠습니다. 우리가 작성할 이 샘플 노드는 실행 시 터미널 화면에 "Hello time: <넘버 증가>"라는 메시지를 주기적으로 출력하는 기능을 수행합니다.

1) 노드 파일 생성 및 실행 권한 부여

작성할 파이썬 소스 코드는 패키지 폴더 내부의 동명 디렉토리(my_robot_controller) 안에 위치해야 합니다. 해당 디렉토리로 이동하여 my_first_node.py라는 이름의 파이썬 파일을 생성합니다.

ROS2에서는 파이썬 스크립트를 독립적인 실행 파일로 취급하므로, 코드를 작성하기 전에 시스템이 이 파일을 실행할 수 있도록 chmod +x 명령어를 통해 실행 권한(x)을 부여해 주어야 합니다.

Bash
# 소스 코드가 위치할 디렉토리로 이동
cd ~/ros2_ws/src/my_robot_controller/my_robot_controller

# 파이썬 노드 파일 생성
touch my_first_node.py

# 실행 권한 부여
chmod +x my_first_node.py

2) VS Code를 이용한 코드 작성

이제 작업 공간 최상위 경로에서 VS Code를 실행하여 방금 만든 my_first_node.py 파일을 열고 코드를 작성해 봅시다. (현재 디렉토리를 의미하는 .을 덧붙여 VS Code를 엽니다.)

아래는 우리가 작성할 첫 번째 ROS2 파이썬 노드의 전체 소스 코드입니다. 복사하여 붙여넣기보다는, 코드가 어떤 역할을 하는지 한 줄씩 타이핑하며 익혀보시기를 권장합니다.

Python
#!/usr/bin/env python3
import rclpy
from rclpy.node import Node

class MyFirstNode(Node):
    def __init__(self):
        # 부모 클래스인 Node를 초기화하며 노드 이름을 지정합니다.
        super().__init__('my_first_node')
        self.counter_ = 0
        # 1초마다 timer_callback 함수를 실행하는 타이머를 생성합니다.
        self.create_timer(1.0, self.timer_callback)

    def timer_callback(self):
        # 화면에 로그를 출력합니다.
        self.get_logger().info('Hello time: ' + str(self.counter_))
        self.counter_ += 1

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

if __name__ == '__main__':
    main()


코드 심층 분석

연구원과 대학생 여러분은 단순히 코드가 동작하는 것을 넘어 “왜 이렇게 설계되었는지” 원리를 이해하는 것이 중요합니다. 위에서 작성한 코드를 세 부분으로 나누어 깊이 있게 분석해 보겠습니다.

1) 필수 패키지 Import

Python
import rclpy<br>from rclpy.node import Node<br>

가장 상단에서는 ROS2 Python 프로그래밍을 위한 핵심 클라이언트 라이브러리인 rclpy 패키지와, 노드 생성을 위한 Node 클래스를 import 합니다. 이 두 가지는 ROS2 파이썬 프로그래밍에 있어 필수적인 뼈대입니다.

2) main() 함수의 역할

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

main() 함수는 프로그램의 시작점입니다. 먼저 rclpy.init() 메쏘드를 호출하여 ROS2 통신 환경을 초기화하고, 우리가 정의한 MyFirstNode 객체를 생성합니다.

그다음 가장 중요한 rclpy.spin(node) 함수가 등장합니다. ROS2의 노드는 한 번 실행되고 끝나는 것이 아니라, 외부에서 종료 시그널(예: Ctrl+C)을 받을 때까지 계속해서 대기하며 작업을 수행해야 합니다. spin() 메쏘드는 노드를 실행 상태로 유지하며 주기적으로 콜백 함수들을 처리하는 역할을 수행합니다. 종료 시그널을 받으면 루프를 빠져나오고 shutdown() 메쏘드가 호출되어 노드를 메모리에서 안전하게 해제합니다.

프로그램 최하단의 if __name__ == '__main__': 구문은 이 스크립트 파일이 모듈로 불려간 것이 아니라, 터미널에서 독립적인 실행 파일로 직접 실행될 때에만 main() 함수가 작동하도록 보장해 줍니다.

3) 노드 클래스(Node Class) 설계

Python
class MyFirstNode(Node):
    def __init__(self):
        super().__init__('my_first_node')
        self.counter_ = 0
        self.create_timer(1.0, self.timer_callback)

일반적인 ROS2 노드는 객체 지향 프로그래밍(OOP) 방식을 따라 Node 클래스를 상속받아 작성합니다. __init__ 생성자에서 부모 클래스를 초기화하며 이 노드의 이름을 'my_first_node'로 부여합니다.

로봇 제어기 노드 클래스는 일반적으로 ‘콜백(Callback) 함수’라는 수동적(Passive)인 구조를 활용합니다. 즉, 코드가 순차적으로 계속 실행되는 무한 루프 방식이 아니라, 특정 이벤트(타이머 만료, 메시지 수신 등)가 발생했을 때 시스템에 의해 피동적으로 실행되는 구조입니다. 위 코드에서는 create_timer()를 이용해 1.0초 주기의 타이머를 설정하고, 매 주기마다 timer_callback 함수가 동작하도록 설계했습니다.

Python
    def timer_callback(self):
        self.get_logger().info('Hello time: ' + str(self.counter_))
        self.counter_ += 1

타이머에 의해 호출되는 콜백 함수 내부에서는 get_logger().info() 메쏘드를 사용합니다. 이는 일반적인 파이썬의 print() 문과 유사하게 터미널 화면에 문자열과 데이터를 출력하는 ROS2 노드 클래스의 전용 메쏘드입니다. 이를 통해 “Hello time:” 문자열과 함께 계속해서 증가하는 카운터 값을 출력하게 됩니다.


setup.py 수정 및 진입점(Entry Point) 등록

파이썬 스크립트 작성을 마쳤다고 해서 바로 ros2 run 명령어로 노드를 실행할 수 있는 것은 아닙니다. 터미널 명령어가 우리가 작성한 파이썬 파일을 찾아서 실행할 수 있도록, 패키지 설정 파일인 setup.py 파일의 entry_points 필드에 실행 스크립트를 등록해 주어야 합니다.

VS Code 탐색기에서 패키지 루트 디렉토리에 있는 setup.py 파일을 열고, 하단의 entry_points 영역을 찾아 다음과 같이 수정합니다.

Python
    entry_points={
        'console_scripts': [
            'test_node = my_robot_controller.my_first_node:main'
        ],
    },

여기서 console_scripts 필드 항목에 작성된 규칙의 의미를 정확히 이해하는 것이 매우 중요합니다.

  • test_node: 터미널에서 우리가 ros2 run 명령어로 호출할 실행 노드 이름을 지정합니다.
  • my_robot_controller.my_first_node:main: 실제 실행될 파이썬 패키지 이름(my_robot_controller)과 파이썬 파일명(my_first_node.py), 그리고 실행 진입점이 되는 함수(main())를 매핑합니다.

일반적으로는 개발자의 혼란을 방지하기 위하여 실행 노드 이름과 파이썬 파일명을 동일하게 일치시키는 것이 모범 사례입니다. 하지만 이번 실습에서는 각 항목이 의미하는 바를 여러분이 명확하게 파악할 수 있도록, 의도적으로 실행 노드 이름을 test_node로 다르게 지정하여 등록해 보았습니다.


최종 빌드 및 노드 실행 테스트

모든 코드 작성과 설정이 완료되었습니다. 이제 코드를 빌드하고 우리의 첫 번째 노드가 정상적으로 숨 쉬는지 확인해 볼 차례입니다.

작업 공간의 최상위 디렉토리로 이동하여 colcon build 명령어로 패키지를 다시 빌드합니다.

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

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

빌드가 성공적으로 완료되었다면, 환경 설정 파일(오버레이)을 소싱하여 시스템에 새로 빌드된 노드의 존재를 알려줍니다. 그 후 ros2 run 명령어를 통해 노드를 실행합니다.

Bash
# 환경 설정 소싱
source install/local_setup.bash

# 패키지명(my_robot_controller)과 등록한 실행 노드명(test_node)을 입력하여 실행
ros2 run my_robot_controller test_node

성공입니다! 화면에 “Hello time:” 이라는 문자열과 함께 타이머 콜백에 의해 카운터 숫자가 1초마다 1씩 지속적으로 증가하며 출력되는 것을 확인할 수 있습니다. 이로써 여러분은 ROS2 시스템의 기초를 이해하고, 스스로 파이썬 노드를 설계하여 실행할 수 있는 능력을 갖추게 되었습니다. 실행 중인 노드는 터미널 창에서 Ctrl + C를 눌러 안전하게 종료할 수 있습니다.


마무리하며

이번 3편 블로그에서는 빈 ROS2 패키지를 직접 생성해 보고, Node 클래스를 상속받아 객체 지향적으로 파이썬 기반의 노드를 프로그래밍하는 방법에 대해 깊이 있게 학습했습니다. 또한 setup.py 파일의 진입점(Entry Point) 설정 원리를 통해 ROS2 빌드 시스템이 코드를 어떻게 찾아 실행하는지도 파악할 수 있었습니다.

단순한 카운터 출력 노드였지만, 오늘 배운 타이머 생성과 콜백(Callback) 구조는 앞으로 수많은 로봇 센서 데이터를 처리하고 모터를 제어하는 가장 기본적이고 핵심적인 뼈대가 됩니다.

다음 [ROS2 Mastery 4편]에서는 오늘 만든 my_robot_controller 패키지에 살을 붙여, 드디어 Turtlesim의 거북이를 자유자재로 제어하는 “ROS2 Topic 프로그래밍 (Publisher)” 실습을 진행해 보겠습니다.


YouTube Tutorial

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

기고일: 2026.05.07

Similar Posts

답글 남기기