[ROS2] Launch 프로그래밍

HY K·2024년 9월 21일
0

ROS2

목록 보기
17/18

이번 포스팅에서는 ROS2의 Launch 프로그래밍에 대해서 다뤄보자.
참고한 링크는 다음과 같다.
https://cafe.naver.com/openrt/24735


ROS2 Launch

ROS2에서 단일 노드를 실행하기 위해서는

$ ros2 run <pkg_name> <node_name>

명령어를 사용한다.

하지만 만약, 복수의 노드를 실행시키고 싶으면 Launch 시스템을 활용해야한다.
뿐만 아니라, Launch 시스템은 다음과 같은 기능을 제공한다.

💡 Launch 시스템의 기능
1. 복수의 노드를 한번에 실행
2. 노드 실행 시 매개변수, 노드 이름, 노드 네임스페이스, 환경변수 등 다양한 설정

ROS2의 Launch 시스템은 다음과 같은 파일 양식을 지원한다.

💡 ROS2 Launch 시스템 파일 양식
1. XML 파일 → ROS1에서 계승
2. YAML 파일 → 많이 사용되진 않음
3. 파이썬 파일(launch.py) → 쉽고 강력함


Launch 파일 작성하기

launch 파일은 복수개의 노드와, 노드에서 사용할 파라미터 폴더 등을 불러오는 강력한 기능을 지원한다. 그리고 ROS2에서는 파이썬에 기반한 launch 파일(launch.py) 작성을 지원하기 때문에 ROS1 대비 작성이 쉬워졌다.

예시 launch 파일 코드는 다음과 같다.

#!/usr/bin/env python3

import os

from ament_index_python.packages import get_package_share_directory
from launch import LaunchDescription
from launch.actions import DeclareLaunchArgument
from launch.substitutions import LaunchConfiguration
from launch_ros.actions import Node


def generate_launch_description():
    param_dir = LaunchConfiguration(
        'param_dir',
        default=os.path.join(
            get_package_share_directory('topic_service_action_rclpy_example'),
            'param',
            'arithmetic_config.yaml'))

    return LaunchDescription([
        DeclareLaunchArgument(
            'param_dir',
            default_value=param_dir,
            description='Full path of parameter file'),

        Node(
            package='topic_service_action_rclpy_example',
            executable='argument',
            name='argument',
            parameters=[param_dir],
            output='screen'),

        Node(
            package='topic_service_action_rclpy_example',
            executable='calculator',
            name='calculator',
            parameters=[param_dir],
            output='screen'),
    ])

하나하나 차근차근 살펴보자.

generate_launch_description

def generate_launch_description():

    xxx = LaunchConfiguration(yyy)

    return LaunchDescription([
        DeclareLaunchArgument(aaa),
        Node(bbb),
        Node(ccc),
    ])

기본으로 사용하는 메서드이다. 이 메서드는 LaunchConfiguration 클래스를 이용하여 노드 실행 파일 관련 설정들을 초기화 하고, 리턴값으로 LaunchDescription 클래스를 반환하게 된다.

그리고 LaunchConfiguration 클래스의 생성자를 통해 파라미터 파일이 저장된 경로를 설정해줄 수 있다.

def generate_launch_description():
    param_dir = LaunchConfiguration(
        'param_dir',
        default=os.path.join(
            get_package_share_directory('topic_service_action_rclpy_example'),
            'param',
            'arithmetic_config.yaml'))

LaunchDescription

DeclareLaunchArgument 클래스를 통해서 위에서 설정한 파라미터 변수 param_dir을 기본적인 launch argument로 설정할 수 있다.

    return LaunchDescription([
        DeclareLaunchArgument(
            'param_dir',
            default_value=param_dir,
            description='Full path of parameter file'),

그리고 Node 클래스로 실행할 노드를 설정하면 된다.
기본적으로 다음 5가지를 기재한다.


1. package : 실행할 패키지 이름 기재
2. executable : 실행 가능한 노드의 이름을 기재
3. name : 지정한 노드를 실행할 때 실제로 사용할 이름을 기재
4. parameter : 특정 파라미터 값을 넣어도 되고, DeclareLaunchArgument에서 지정한 변수를 사용해도 된다.
5. output : 로깅 설정, 기본적으로 특정 파일 이름으로 된 로그 파일에 로깅 정보가 기록된다. screen으로 output을 지정하면 스크린에도 출력된다.

예시 코드를 살펴보자.

    return LaunchDescription([
        DeclareLaunchArgument(
            'param_dir',
            default_value=param_dir,
            description='Full path of parameter file'),

        Node(
            package='topic_service_action_rclpy_example',
            executable='argument',
            name='argument',
            parameters=[param_dir],
            output='screen'),

        Node(
            package='topic_service_action_rclpy_example',
            executable='calculator',
            name='calculator',
            parameters=[param_dir],
            output='screen'),
    ])

또 다른 launch 파일의 유용한 기능으로는 remapping 기능이 있다. 이는 고유 이름을 변경하는 것으로, 내부 코드 변경 없이 토픽, 서비스, 액션 등의 고유 이름을 변경할 수 있는 기능을 제공한다.

예시 코드를 보면 다음과 같다.

        Node(
            package='topic_service_action_rclpy_example',
            executable='argument',
            name='argument',
            remappings=[
                ('/arithmetic_argument', '/argument'),
            ]

이 코드를 보면 remapping을 통해서 이름을 바꾸고 있음을 알 수 있다.

그리고 또 다른 유용한 기능으로 namespace 가 있다.

💡 namespace

  • 노드, 토픽, 서비스, 액션, 파라미터 등과 같은 고유 이름을 그룹화 하기 위해서 사용
  • 자신만의 네트워크 그룹화를 하고, 다른 namespace와의 충돌 등을 방지
  • ROS2에서 namespace를 사용하려면 --ros-args-r__ns:= 라는 CLI 옵션을 이용하거나
  • launch 파일을 실행시킬 때 namespace 항목을 변경하면 된다.
  • 예시를 들면 다음과 같다.
def generate_launch_description():
    ros_namespace = LaunchConfiguration('ros_namespace')

    return LaunchDescription([
        DeclareLaunchArgument(
            'ros_namespace',
            default_value=os.environ['ROS_NAMESPACE'],
            description='Namespace for the robot'),

        Node(
            package='topic_service_action_rclpy_example',
            namespace=ros_namespace,
            executable='argument',
            name='argument',
            output='screen'),

ros_namespace 변수를 통해 namespace를 선언하고, 환경변수인 ROS_NAMESPACE를 읽어오도록 지시한 코드이다. 이 변수를 활용하기 위해서는 .bashrc 파일에 환경변수를 설정하거나 아니면 터미널마다 명령어를 입력해줘야 한다.

또한 launch 파일은 generate_launch_description 함수의 return 값이 너무 많아진다고 판단되면 LaunchDescription의 add_action 함수를 통해 간결하게 표현할 수 있다. 예시 코드를 보면 다음과 같다.

#!/usr/bin/env python3

import os

from ament_index_python.packages import get_package_share_directory
from launch import LaunchDescription
from launch.actions import DeclareLaunchArgument
from launch.substitutions import LaunchConfiguration
from launch_ros.actions import Node


def generate_launch_description():
    param_dir = LaunchConfiguration(
        'param_dir',
        default=os.path.join(
            get_package_share_directory('topic_service_action_rclpy_example'),
            'param',
            'arithmetic_config.yaml'))

    launch_description = LaunchDescription()

    launch_description.add_action(launch.actions.DeclareLaunchArgument(
        'param_dir',
        default_value=param_dir,
        description='Full path of parameter file')

    argument_node = Node(
        package='topic_service_action_rclpy_example',
        executable='argument',
        name='argument',
        parameters=[param_dir],
        output='screen')

    calculator_node = Node(
        package='topic_service_action_rclpy_example',
        executable='calculator',
        name='calculator',
        parameters=[param_dir],
        output='screen')

    launch_description.add_action(argument_node)
    launch_description.add_action(calculator_node)

    return launch_description

다른 launch 파일 불러오기

get_package_share_directory 함수를 이용하면 특정 다른 패키지의 launch 파일을 불러와서 같이 실행이 가능하다. 예시 코드는 다음과 같다.

from ament_index_python.packages import get_package_share_directory
from launch import LaunchDescription
from launch.actions import IncludeLaunchDescription
from launch.actions import LogInfo
from launch.launch_description_sources import PythonLaunchDescriptionSource
from launch.substitutions import ThisLaunchFileDir


def generate_launch_description():
    return LaunchDescription([
        LogInfo(msg=['Execute three launch files!']),

        IncludeLaunchDescription(
            PythonLaunchDescriptionSource(
                [ThisLaunchFileDir(), '/xxxxx.launch.py']),
        ),

        IncludeLaunchDescription(
            PythonLaunchDescriptionSource(
                [ThisLaunchFileDir(), '/yyyyy.launch.py']),
        ),

        IncludeLaunchDescription(
            PythonLaunchDescriptionSource(
                [get_package_share_directory('bbbbb'), '/launch/zzzzz.launch.py']),
                // 여기서 다른 패키지의 launch 파일 불러오기
        ),
    ])

패키지 빌드

rclcpp 계열

CMakeLists.txt 파일에 다음 내용을 기재해야한다.

install(DIRECTORY
  launch
  DESTINATION share/${PROJECT_NAME}/
)

rclpy 계열

setup.py 내용에 launch 파일, 그리고 필요하다면 파라미터 파일 폴더를 추가해줘야 한다.

setup(
    name=package_name,
    version='0.1.0',
    packages=find_packages(exclude=['test']),
    data_files=[
        ('share/ament_index/resource_index/packages', ['resource/' + package_name]),
        (share_dir, ['package.xml']),
        (share_dir + '/launch', glob.glob(os.path.join('launch', '*.launch.py'))),
        (share_dir + '/param', glob.glob(os.path.join('param', '*.yaml'))),
    ],

패키지 빌드 및 실행

빌드의 경우

$ colcon build
$ colcon build --symlink-install
$ colcon build --symlink-install --packages-select
$ colcon build --symlink-install --packages-up-to

중에 하나 골라서 하면 되고,

실행의 경우

$ ros2 launch <pkg_name> <launch_file_name>

위 명령어를 사용하면 된다.

profile
로봇, 드론, SLAM, 제어 공학 초보

0개의 댓글