패키지의 기본 개념

ROS2 패키지는 특정 기능을 제공하는 독립적인 단위로, 노드(Node), 메시지(Message), 서비스(Service), 액션(Action) 등의 ROS2 요소를 포함할 수 있다. ROS2 패키지는 소스 코드, 실행 파일, 설정 파일, 메시지 정의, 서비스 정의 등의 파일로 구성되며, 특정 기능을 실행하거나 구현하기 위해 함께 배포된다.

ROS2의 패키지 구조는 CMake를 사용한 빌드 시스템과 colcon이라는 빌드 도구를 기반으로 구성되며, 모든 ROS2 패키지는 특정 디렉토리 구조를 따라야 한다. 이러한 구조는 패키지의 가독성, 유지 관리, 배포를 용이하게 하며, 빌드 및 종속성 관리를 표준화한다.

패키지의 디렉토리 구조

ROS2 패키지는 다음과 같은 기본 디렉토리 및 파일 구조를 따른다.

my_package/
├── CMakeLists.txt
├── package.xml
├── include/
├── src/
├── launch/
├── msg/
├── srv/
├── action/
├── config/
└── scripts/

CMakeLists.txt

CMakeLists.txt 파일은 패키지의 빌드를 정의하는 핵심 파일이다. CMake는 ROS2 패키지의 빌드 시스템으로 사용되며, 이 파일에는 프로젝트 이름, 종속성, 빌드 타겟, 컴파일러 옵션 등이 포함된다. ROS2 패키지의 CMakeLists.txt는 여러 단계로 구성되며, 다음과 같은 주요 항목들을 포함한다.

  1. 프로젝트 이름 설정: cmake project(my_package)

  2. 필요한 ROS2 종속성 선언: cmake find_package(ament_cmake REQUIRED) find_package(rclcpp REQUIRED)

  3. 실행 파일 생성 및 빌드 규칙 설정: cmake add_executable(my_node src/my_node.cpp) target_link_libraries(my_node rclcpp)

package.xml

package.xml 파일은 ROS2 패키지의 메타데이터를 정의하는 XML 파일이다. 이 파일은 패키지의 이름, 버전, 라이선스, 종속성 등을 기술한다. ROS2에서 패키지 관리는 이 파일을 통해 이루어지며, 이를 통해 colcon과 같은 빌드 도구가 패키지의 빌드 순서 및 종속성을 결정한다.

<package format="3">
  <name>my_package</name>
  <version>0.1.0</version>
  <description>A simple ROS2 package</description>
  <maintainer email="maintainer@example.com">Maintainer Name</maintainer>
  <license>Apache-2.0</license>
  <buildtool_depend>ament_cmake</buildtool_depend>
  <exec_depend>rclcpp</exec_depend>
</package>

이 외에도 패키지의 실행에 필요한 종속성(exec_depend), 개발에 필요한 종속성(buildtool_depend), 테스트에 필요한 종속성 등을 정의할 수 있다.

include/ 디렉토리

include/ 디렉토리는 ROS2 패키지에서 사용할 헤더 파일을 저장하는 디렉토리이다. 일반적으로 패키지의 이름을 포함하는 하위 디렉토리를 만들어 그 안에 헤더 파일을 배치한다. 예를 들어, my_package라는 패키지가 있다면, 헤더 파일은 include/my_package/ 안에 저장된다.

이 구조는 C++에서 라이브러리나 클래스의 선언을 중앙에서 관리할 수 있도록 하며, 여러 소스 파일에서 일관된 헤더 파일을 사용할 수 있게 해준다.

src/ 디렉토리

src/ 디렉토리는 ROS2 패키지의 소스 코드 파일을 저장하는 디렉토리이다. 여기에는 C++ 또는 Python으로 작성된 노드 파일들이 위치하며, CMake를 사용해 빌드될 파일들이 포함된다.

launch/ 디렉토리

launch/ 디렉토리는 ROS2 시스템을 실행하는 데 필요한 런치 파일을 저장하는 곳이다. 런치 파일은 여러 노드를 한 번에 실행하거나, 노드 간의 통신을 설정하고 다양한 파라미터를 지정하는 데 사용된다. ROS2에서는 XML 또는 Python 형식의 런치 파일을 사용할 수 있으며, 이 파일을 통해 복잡한 시스템을 한 번에 실행할 수 있다.

예시로 Python 기반의 런치 파일은 다음과 같이 작성된다:

from launch import LaunchDescription
from launch_ros.actions import Node

def generate_launch_description():
    return LaunchDescription([
        Node(
            package='my_package',
            executable='my_node',
            name='my_node',
            output='screen',
            parameters=[{'use_sim_time': False}]
        )
    ])

msg/ 디렉토리

msg/ 디렉토리는 사용자 정의 메시지 타입을 정의하는 디렉토리이다. ROS2에서는 노드 간에 교환되는 데이터를 메시지(Message)라는 형태로 주고받으며, 기본 제공되는 메시지 타입 외에 사용자가 필요에 따라 새로운 메시지를 정의할 수 있다.

예를 들어, 위치 정보를 포함하는 메시지를 정의하려면 Position.msg 파일을 다음과 같이 작성할 수 있다:

float64 x
float64 y
float64 z

이렇게 정의된 메시지는 ROS2 빌드 시스템에서 자동으로 메시지 타입으로 변환되어, 노드 간 통신에서 사용할 수 있게 된다.

srv/ 디렉토리

srv/ 디렉토리는 사용자 정의 서비스(Service) 타입을 정의하는 디렉토리이다. ROS2 서비스는 요청(Request)과 응답(Response) 패턴을 통해 통신하며, 서비스 정의 파일은 이 두 가지 정보를 모두 포함해야 한다.

예시로 AddTwoInts.srv라는 파일을 다음과 같이 작성할 수 있다:

int64 a
int64 b
---
int64 sum

이 서비스는 두 개의 정수를 입력으로 받아 그 합을 응답으로 반환하는 서비스이다.

action/ 디렉토리

action/ 디렉토리는 사용자 정의 액션(Action) 타입을 정의하는 디렉토리이다. 액션은 서비스와 비슷하지만, 장시간에 걸친 작업을 처리할 수 있으며, 중간에 피드백을 제공할 수 있는 특징을 갖는다.

액션 파일은 목표(Goal), 피드백(Feedback), 결과(Result) 세 가지 섹션으로 나누어진다. 예를 들어, 로봇이 지정된 거리만큼 이동하는 액션을 정의할 수 있다.

# 목표
float64 distance
---
# 피드백
float64 current_distance
---
# 결과
bool success

config/ 디렉토리

config/ 디렉토리는 ROS2 노드에서 사용할 설정 파일을 저장하는 곳이다. 주로 YAML 형식으로 작성된 파일들이 이 디렉토리에 포함되며, 노드 실행 시 파라미터 값을 지정할 수 있다. 이 디렉토리를 사용하면 동일한 코드를 다양한 환경에서 재사용할 수 있으며, 파라미터 변경을 통해 유연한 시스템 구성이 가능한다.

예시 YAML 파일:

my_node:
  ros__parameters:
    param_name: param_value

scripts/ 디렉토리

scripts/ 디렉토리는 ROS2 패키지에서 사용할 스크립트 파일들을 저장하는 곳이다. 주로 Python으로 작성된 스크립트 파일이 여기에 위치하며, CMake 빌드 과정에서 별도로 컴파일되지 않고 바로 실행할 수 있다.

Python 스크립트 예시:

#!/usr/bin/env python3

import rclpy
from rclpy.node import Node

class MyNode(Node):
    def __init__(self):
        super().__init__('my_node')
        self.get_logger().info('Hello ROS2')

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

if __name__ == '__main__':
    main()

이와 같이 작성된 스크립트는 ROS2 환경에서 실행할 수 있으며, Python 기반 노드를 쉽게 실행할 수 있게 해준다.