본 포스트는 ROS1에 대한 전반적인 지식이 있다는 가정 하에 작성되었으며, The Construct Youtube 영상을 참고하였습니다.
1 패키지란?
ROS2도 프로그램을 다루기 위해 ROS1처럼 패키지를 사용합니다. 패키지는 특정한 ROS2 프로그램을 포함하는 모든 파일들(cpp, python, configuration, compliation, launch, parameters 파일들)이라고 생각하면 되겠습니다.
ROS2에서는 2가지 종류의 패키지를 만들 수 있는데... 하나는 파이썬 패키지, 다른 하나는 CMake(C++) 패키지 입니다.
모든 파이썬 패키지는 다음과 같은 파일 및 디렉터리 구조를 갖습니다.
- package.xml - 패키지에 대한 meta-information(패키지 관리자, 의존성 등등...)에 대한 파일
- setup.py - 패키지를 컴파일하는 방법에 대한 설명 파일
- setup.cfg - 해당 스크립트가 설치될 위치를 정의하는 파일
- /<package_name> - ROS2에서는 패키지를 만들면 해당 패키지와 똑같은 이름의 디렉토리가 패키지 내부에 생성됩니다. 우리는 파이썬 스크립트를 이 폴더 안에다가 넣을 것이고, 처음 생성하면 기본적으로 __init__.py 파일이 생성됩니다.
일부 패키지들은 예를 들어 launch 같은 추가 디렉토리를 갖고 있기도 합니다.
2 패키지 만들기
이제 패키지를 직접 만들어보겠습니다. 패키지를 만들 때는 ROS1과 비슷하게, ROS2 workspace 에서 작업을 할 것입니다. ROS1에서는 일반적으로 catkin_ws 라는 workspace를 만들었지만, ros2에서는 일반적으로 ros2_ws를 사용합니다.
예제 2.1.
(이전 포스트의 내용을 진행했다는 가정 하에 설명합니다)
먼저, 터미널 창에서 ROS2를 source 합니다.
$ source /opt/ros/humble/setup.bash
ros2_ws 디렉토리에서 패키지를 생성합니다.
$ cd ~/ros2_ws/ $ cd src $ ros2 pkg create --build-type ament_python my_package --dependencies rclpy
실행 결과

즉, ros2에서 python을 사용하는 사용자 정의 패키지를 만드는 명령어는 다음과 같습니다(물론 src 디렉토리에서 실행해야 합니다)
$ ros2 pkg create --build-type ament_python <package_name> --dependencies <package_dependencies>
다음으로 패키지를 빌드하고, source하면 패키지를 사용할 수 있습니다(ros1의 catkin_make에 대응됩니다)
$ cd ~/ros2_ws && colcon build --symlink-install $ source install/setup.bash
만약 이전 포스트에서 설명한 대로 bashrc 파일을 수정하셨으면, 아래와 같이 명령어를 입력합니다
$ cb $ sb
패키지가 잘 만들어졌는지 확인해봅니다
$ ros2 pkg list | grep my_package

만약 특정한 패키지만 빌드하고 싶다면, 다음 명령을 실행합니다
colcon build --packages-select <package_name> --symlink-install
3. ROS2 프로그래밍 해보기
1. my_package 패키지 내부의 my_directory에 simple.py 를 하나 만들고, (편집기는 vs code를 사용했습니다)
$ cd ~/ros2_ws/src/my_package/my_package $ code simple.py
다음과 같이 코드를 작성합니다.
simple.py
import rclpy # ROS1의 rospy에 대응 from rclpy.node import Node def main(args=None): rclpy.init(args=args) print("ROS2 is working! ") rclpy.shutdown() if __name__ == '__main__': main()
2. my_package 패키지 내부에 launch 디렉토리를 만듭니다.
$ cd ~/ros2_ws/src/my_package $ mkdir launch
3. launch 파일을 하나 만들고,
$ cd ~/ros2_ws/src/my_package/launch $ code my_package_launch_file.launch.py
4. 다음과 같이 작성하고,
my_pacakge_launch_file.launch.py
from launch import LaunchDescription from launch_ros.actions import Node def generate_launch_description(): return LaunchDescription([ Node( package='my_package', executable='simple_node', output='screen'), ])
실행 권한을 부여해줍니다
$ chmod +x my_package_launch_file.launch.py
5. my_package 패키지 디렉토리에 있는 setup.py 파일을 다음과 같이 수정합니다.

위 파일을 아래와 같이 수정합니다
setup.py
from setuptools import setup import os from glob import glob package_name = 'my_package' setup( name=package_name, version='0.0.0', packages=[package_name], data_files=[ ('share/ament_index/resource_index/packages', ['resource/' + package_name]), ('share/' + package_name, ['package.xml']), (os.path.join('share', package_name), glob('launch/*.launch.py')), ], install_requires=['setuptools'], zip_safe=True, maintainer='justin', maintainer_email='justin@todo.todo', description='TODO: Package description', license='TODO: License declaration', tests_require=['pytest'], entry_points={ 'console_scripts': [ 'simple_node = my_package.simple:main' ], }, )
6. 빌드합니다.
$ cb $ sb
7. 실행해봅니다.
$ ros2 launch my_package my_package_launch_file.launch.py

4. setup.py 파일 설명
setup.py 파일은 패키지를 적절하게 컴파일하기 위한 내용을 담고있습니다.
이전에 사용했던 코드는 다음과 같았습니다.
from setuptools import setup import os from glob import glob package_name = 'my_package' setup( name=package_name, version='0.0.0', packages=[package_name], data_files=[ ('share/ament_index/resource_index/packages', ['resource/' + package_name]), ('share/' + package_name, ['package.xml']), (os.path.join('share', package_name), glob('launch/*.launch.py')) ], install_requires=['setuptools'], zip_safe=True, maintainer='somebody very awesome', maintainer_email='user@user.com', description='TODO: Package description', license='TODO: License declaration', tests_require=['pytest'], entry_points={ 'console_scripts': [ 'simple_node = my_package.simple:main' ], }, )
이 setup.py의 주요 목표는 위 코드로부터 실행 파일을 만드는 것입니다. 이를 위해서는 entry_points 라는 이름의 딕셔너리 타입 변수에다 console_scripts에다 다음과 같은 형식으로 입력해줍니다.
'<executable_name> = <package_name>.<script_name>:main'
우리가 이전에 사용했던 코드와 비교해보면...
import os from glob import glob from setuptools import setup package_name = 'my_package' setup( #code ... #code entry_points={ 'console_scripts': [ 'simple_node = my_package.simple:main' ], }, #code ... )
즉, 위 코드에서는 my_package라는 패키지의 simple.py라는 파일로 simple_node라는 이름의 실행 노드를 만들어줍니다.
또한, 컴파일 과정 중에 setup.py의 파라미터 중 data_files라는 파라미터를 사용하는 launch 파일들의 파이썬 setup tools에 다음과 같이 정보를 건네줄 수 있습니다.
import os from glob import glob from setuptools import setup package_name = 'my_package' setup( #code ... #code data_files=[ ('share/ament_index/resource_index/packages', ['resource/' + package_name]), ('share/' + package_name, ['package.xml']), (os.path.join('share', package_name), glob('launch/*.launch.py')) ], #code ... #code )
위 코드의 목적은 launch files를 설치하기 위함입니다. 위의 예시에서는 my_package라는 패키지 내부의 launch 디렉토리에 있는 모든 launch 파일들(launch/*.launch.py)을 ~/ros2_ws/install/my_package/share/my_package/ 디렉토리에다 설치한다는 것입니다.
5. ROS2 Nodes
ROS2에서 각의 노드는 하나의 모듈만 다뤄야 합니다(예를 들어, 모터만 제어하는 노드, 라이다만 제어하는 노드 등...). 각 노드는 다른 방식을 사용하여 다른 노드와 통신할 수 있습니다.

예제 2.2
아까 위에서 사용했던 my_package의 simple.py를 수정합니다.
import rclpy from rclpy.node import Node class MyNode(Node): def __init__(self): # super()를 호출하여 Node object를 초기화 # 매개변수로 노드의 이름 전달 super().__init__('Test_node') # 2개의 파라미터를 받는 타이머 생성 # - 콜백 사이의 시간 (0.2초) # - 콜백때마다 호출할 함수 (timer_callback) self.create_timer(0.2, self.timer_callback) def timer_callback(self): # 터미널에 메시지 띄우기 self.get_logger().info("This message is from Test_node") def main(args=None): # ROS2 communication 초기화 rclpy.init(args=args) # MyNode object 생성 node = MyNode() # ctrl+c 누를 때까지 노드 실행 rclpy.spin(node) # ROS2 communication 종료 rclpy.shutdown() if __name__ == '__main__': main()
빌드 및 source 후, 해당 노드를 실행해봅니다.
$ cb $ sb $ ros2 launch my_package my_package_launch_file.launch.py

다음 명령으로 node 목록을 확인할 수 있습니다.
$ ros2 node list

또한, 다음 명령으로 node의 정보를 확인할 수 있습니다.
$ ros2 node info /Byakugan

6. Client Libraries
지금까지 위의 예제들에서 Client Libraries를 사용했었습니다. ROS1의 rospy, roscpp에 대응되는 것이라고 보시면 됩니다. ROS2에서는 ROS Client Library(RCL)이라고 하며, 다양한 ROS API가 필요로 하는 기본적인 기능들이 구현되어있습니다.
- rclcpp = C++ client library
- rclpy = Python client library
'Study > [ROS2] ROS2 Basic' 카테고리의 다른 글
ROS2 with C++ - Topic (0) | 2023.07.28 |
---|---|
ROS2 with python - Actions (0) | 2023.07.28 |
ROS2 with python - Executors and Callback Groups (0) | 2023.07.28 |
ROS2 with python - Understanding ROS2 Services (0) | 2023.07.27 |
ROS2 with python - Understanding ROS2 Topics (0) | 2023.07.26 |