ROS에서 명령을 실행하는 방법은 두 가지로 run과 launch가 있다. run은 단일한 명령이고 launch는 run의 집합이다. 만약 두 개의 ros 명령을 수행하려면 터미널 두 개를 띄우거나 실행하려는 두 명령을 다 입력해주어야 한다. ROS를 다루다보면 여러 ROS 명령을 수행해야 하므로 효율성을 위해 사실상 launch를 실질적으로 더 많이 사용한다. 파이썬을 이용해 launch 파일을 만들고 gazebo와 rviz를 띄우는 일종의 hello world를 수행해보자.

 

이를 위해 가장 먼저 작업 디렉터리를 생성하고 작업 디렉터리로 들어간다.

mkdir -p test_ws/src
cd test_ws/src

 

ROS는 패키지 단위로 프로그래밍이 이뤄지므로 아래 ROS 명령을 통해 패키지를 생성한다.

ros2 pkg create gazebo_pkg --build-type ament_python

 

“gazebo_pkg”는 생성할 패키지 명이다. --buile-type은 사용할 빌드 시스템을 의미한다. ROS1에서는 catkin이 사용되었으나 ROS2에서는 catkin의 업그레이드 버전인 ament를 사용한다. 여기서 ament_python은 파이썬 전용 ament 빌드 시스템이다. 위 명령어를 수행하면 다음과 같이 패키지 폴더가 생성되며 폴더로 들어가면 아래와 같은 파일이 자동으로 생성된다.

 

 

목표는 시뮬레이션을 위한 gazebo와 시각화를 위한 rviz를 같이 띄우는 것이다. 이를 위해 launch 파일을 만들어야 하며 이러한 launch 파일이 담길 폴더를 ‘launch’로 생성해준다. 이는 ROS 개발에 있어 컨벤션으로 가급적 지켜주면 좋다. 이후 test.launch.py를 만들어 준다.

 

 

이후 gazebo와 rviz2를 실행하는 아래 코드를 test.launch.py 파일에 작성해준다

from launch import LaunchDescription
from launch.actions import ExecuteProcess

def generate_launch_description():
    
    return LaunchDescription([
        ExecuteProcess(
            cmd=["gazebo"], output="screen"
        ),

        ExecuteProcess(
            cmd=["ros2", "run", "rviz2", "rviz2"], output="screen"
        ),
    ])


코드를 살펴보자면 launch 파일이 실행되기 위해 반드시 generate_launch_description 함수를 만들어야 한다. 이후 LaunchDescription에 수행할 명령을 리스트로 담아주면 된다. LaunchDescription은 해당 launch 파일이 실행해야 할 목록을 기술하는 클래스다. 안을 살펴보면 ExecuteProcess 클래스가 두 개 있다. 사용법이 매우 간단한 형태로 수행할 명령을 cmd 인자에 입력해주고 output 인자를 통해 로그를 터미널에 출력해줄 수 있도록 screen을 입력한다.

 

cd ~/test_ws

 

source /opt/ros/humble/local_setup.bash

  • ROS2 환경을 현재 셸 세션에 로드하여 ROS2 실행에 필요한 실행파일, 라이브러리, 환경변수 등에 접근할 수 있도록 하는 명령어다.
  • 터미널이 새로 열릴 때 마다 새로운 세션이 생성되므로 매번 입력해주어야 한다. 귀찮다면 .bashrc에 해당 명령어를 입력해두면 터미널이 생성될 때 마다 자동으로 실행된다.
  • 참고로 setup.bash가 있고 local_setup.bash가 있다. 둘 간의 차이점은 전자는 전역적으로 설치된 패키지 설정이고 후자는 지역적으로 설치된 패키지에 대한 설정을 수행한다. setup.bash를 수행하면 전역적으로 수행되어 간편하지만 다른 패키지와 충돌(?)이 발생할 수 있으므로 가급적(?) local_setup.bash를 사용한다.

만약 위 명령을 수행하지 않고 launch 파일을 실행하면 다음과 같이 만들어주었던 패키지 폴더를 찾지 못했다는 에러를 확인할 수 있다.

 

 

위 명령을 수행하고 다시 실행해보자. 이번엔 다른 에러가 발생했다. gazebo_pkg 폴더 안에 test.launch.py가 없다고 한다.

 

이렇게 에러가 발생하는 이유는 빌드 당시 setup.py를 제대로 설정해주지 않아서 발생한다.

 

~/test_ws/src/gazebo_pkg/setup.py에 들어가서 아래 15번째 줄에 있는 os.path.join(’share’, package_name, ‘launch’), glob(’launch/*.launch.py’))를 추가해주자. (import os, from glob import glob도 함께)

 

 

 

이후 다시 colcon build --symlink-install --packages-select gazebo_pkg 명령을 통해 빌드를 다시 수행한 뒤, launch 파일 실행을 위해 ros2 launch gazebo_pkg test.launch.py를 수행해주면 아래와 같이 gazebo와 rviz2가 함께 실행되는 것을 확인할 수 있다.

 

 

만약 gazebo를 실행할 때 시뮬레이션 환경이 구성되어 있는 world 파일을 넣고자 한다면 test.launch.py와 setup.py를 아래와 같이 살짝만 코드를 바꿔 world 파일 경로 인자를 추가해주는 방식으로 gazebo 실행 시 이미 만들어진 시뮬레이션 환경을 구성할 수 있다. (world 파일이 없다면 이전 포스팅 참고)

 

import os
from glob import glob
from setuptools import setup

package_name = 'gazebo_pkg'

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, 'launch'), glob('launch/*.launch.py')),
        (os.path.join('share/', package_name, 'worlds'), glob('worlds/*.world'))
    ],
    install_requires=['setuptools'],
    zip_safe=True,
    maintainer='roytravel',
    maintainer_email='roytravel@todo.todo',
    description='TODO: Package description',
    license='TODO: License declaration',
    tests_require=['pytest'],
    entry_points={
        'console_scripts': [
        ],
    },
)

참고로 리스트 data_files에 담긴 튜플 인자 두 개를 사용해 폴더와 파일을 복사한다. 두 번째 인자의 폴더와 파일을 복사해 첫 번째 인자에 있는 경로로 복사한다.

 

setup.py

import os
from launch import LaunchDescription
from launch.actions import ExecuteProcess
from ament_index_python.packages import get_package_share_directory

def generate_launch_description():
    package_name = 'gazebo_pkg'
    world_filename = 'test.world'
    packge_path = os.path.join(get_package_share_directory(package_name))
    world_path = os.path.join(packge_path, "worlds", world_filename)
    
    return LaunchDescription([
        ExecuteProcess(
            cmd=["gazebo", world_path], output="screen"
        ),

        ExecuteProcess(
            cmd=["ros2", "run", "rviz2", "rviz2"], output="screen"
        ),
    ])

 

위와 같이 작성해주고 재빌드를 해준 뒤 launch 파일을 실행하면 gazebo를 실행할 때 직접 만들어 두었던 world 파일을 적용하여 gazebo를 실행시킬 수 있다.

 

 

 

+ Recent posts