이번에는 ROS의 빌드 시스템에 대해서 이해하고, 실제로 패키지를 생성 및 빌드해보도록 하자.
작성에 사용한 링크는 다음과 같다.
https://cafe.naver.com/openrt/2585
https://wiki.ros.org/ROS/Tutorials/CreatingPackage
https://wiki.ros.org/ROS/Tutorials/BuildingPackages
https://wiki.ros.org/catkin/conceptual_overview
빌드 시스템에 대해서 좀 더 자세히 알아보자. ROS에서 사용하는 빌드 시스템인 catkin은 멀티 플랫폼 빌드 시스템인 CMake를 ROS에 최적화 시켜 탄생한 빌드 시스템이다. 기존에 사용하던 rosbuild를 대체하게 되며, 파이썬 스크립트와의 결합 능력, 패키지 배포 능력, cross compile 지원 및 이식성도 훨씬 우수해졌다.
catkin 시스템을 통해서 빌드하는 방법으로는 catkin_make와 catkin build 명령어가 있으며, 간단한 시스템에 대한 빌드는 전자, 복잡한 시스템에 대한 상세한 빌드 정보를 원하면 후자를 사용하는 것을 추천한다. 전반적으로 catkin build가 ROS의 표준으로 자리잡고 있다.
💡 빌드 시스템은 소스 코드로부터 목적 파일(object file)을 생성한 후 최종적으로 실행 가능한 파일을 생성하는 역할을 수행한다. 이때 빌드 시스템이 생성하는 목적 파일은 라이브러리, 실행 가능한 프로그램, 스크립트, 인터페이스(헤더파일 등) 등 다양한 형태를 가질 수 있다. ROS에서는 소스 코드는 패키지로 구성되며, 각 패키지는 빌드되면서 하나 이상의 목적 파일을 구성한다.
💡 특정 타겟(혹은 소스코드)을 빌드하려면 빌드 시스템에 컴파일러, 소스 코드의 위치, 코드의 종속성, 외부 종속성(의존성), 의존성 패키지/라이브러리들의 위치, 빌드 타겟, 빌드해야 하는 위치, 설치해야 하는 위치 등이 필요하다. 예를 들어 CMake의 경우 CMakeLists.txt 파일에 이들을 모두 지정한다.
ROS에서는 패키지 간 종속성을 관리하기 위해 CMake를 ROS에 최적화 해서 만든 catkin 빌드 시스템을 사용한다.
간단한 소프트웨어 개발 프로젝트를 수행하는 경우 CMake, IDE 등의 빌드 시스템으로도 충분히 가능하였으나, 크고 복잡하고, 다종다양한 코드 생태계에서는 단일로 사용하기 어려웠기 때문에 ROS에 100% 적합하다고 보기 어려웠다.
특히 ROS는 숫자가 매우 많고, 서로 독립적이고, 또 동시에 서로 강하게 의존하는 패키지들의 덩어리나 맟나가지이므로, 대규모 패키지 의존성을 처리하는 것에 특화된 커스텀 빌드 시스템 개발 필요성이 제기되었다.
rosbuild는 ORS 초기에 만들어졌기 때문에 미래 발전상을 완벽히 추종할 수 없었기에 새로운 빌드 시스템은 catkin이 개발되었다. catkin은 rosbuild와 다르게 윈도우에 이식이 가능하며, 파이썬 코드 또한 CMake 매크로를 통해서 쉽게 구현할 수 있다.
ROS 패키지를 생성하는 것은 매우 간단하다.
다음 단계를 따르기만 하면 된다.
$ cd ~/catkin_ws/src
$ catkin_create_pkg <package_name> <dependencies1> <dependencies2> <dependencies3> ..
catkin_create_pkg 명령어를 사용하면 catkin 빌드 시스템에 맞춰 CMakeLists.txt 파일과 package.xml 파일을 자동으로 생성한다.
생성된 패키지들은 catkin_ws/src 폴더 내에서 다음과 같은 구조를 가진다.
workspace_folder/ -- WORKSPACE
src/ -- SOURCE SPACE
CMakeLists.txt -- 'Toplevel' CMake file, provided by catkin
package_1/
CMakeLists.txt -- CMakeLists.txt file for package_1
package.xml -- Package manifest for package_1
...
package_n/
CMakeLists.txt -- CMakeLists.txt file for package_n
package.xml -- Package manifest for package_n
패키지를 생성하고 나면, 앞서 다루었던 rospack, rosls, roscd 등의 명령어를 통해서 패키지를편하게 다룰 수 있다.
특히, rospack depends1 <package_name>을 입력하면 패키지들의 1차 의존성 패키지를 확인할 수 있다.
$ catkin_create_pkg my_first_ros_pkg std_msgs roscpp
이러면 my_first_ros_pkg가 생성되며, 내부적으로 다음 폴더들을 담고 있다.
- include: 헤더 파일, 라이브러리 등
- src: 소스코드 폴더
- CMakeLists.txt: 빌드 설정 파일
- package.xml: 패키지 설정 파일
<?xml version="1.0"?>
<package format="2">
<name>my_first_ros_pkg</name>
<version>0.0.0</version>
<description>The my_first_ros_pkg package</description>
<!-- One maintainer tag required, multiple allowed, one person per tag -->
<!-- Example: -->
<!-- <maintainer email="jane.doe@example.com">Jane Doe</maintainer> -->
<maintainer email="kimhoyun@todo.todo">kimhoyun</maintainer>
<!-- One license tag required, multiple allowed, one license per tag -->
<!-- Commonly used license strings: -->
<!-- BSD, MIT, Boost Software License, GPLv2, GPLv3, LGPLv2.1, LGPLv3 -->
<license>TODO</license>
<!-- Url tags are optional, but multiple are allowed, one per tag -->
<!-- Optional attribute type can be: website, bugtracker, or repository -->
<!-- Example: -->
<!-- <url type="website">http://wiki.ros.org/my_first_ros_pkg</url> -->
<!-- Author tags are optional, multiple are allowed, one per tag -->
<!-- Authors do not have to be maintainers, but could be -->
<!-- Example: -->
<!-- <author email="jane.doe@example.com">Jane Doe</author> -->
<!-- The *depend tags are used to specify dependencies -->
<!-- Dependencies can be catkin packages or system dependencies -->
<!-- Examples: -->
<!-- Use depend as a shortcut for packages that are both build and exec dependencies -->
<!-- <depend>roscpp</depend> -->
<!-- Note that this is equivalent to the following: -->
<!-- <build_depend>roscpp</build_depend> -->
<!-- <exec_depend>roscpp</exec_depend> -->
<!-- Use build_depend for packages you need at compile time: -->
<!-- <build_depend>message_generation</build_depend> -->
<!-- Use build_export_depend for packages you need in order to build against this package: -->
<!-- <build_export_depend>message_generation</build_export_depend> -->
<!-- Use buildtool_depend for build tool packages: -->
<!-- <buildtool_depend>catkin</buildtool_depend> -->
<!-- Use exec_depend for packages you need at runtime: -->
<!-- <exec_depend>message_runtime</exec_depend> -->
<!-- Use test_depend for packages you need only for testing: -->
<!-- <test_depend>gtest</test_depend> -->
<!-- Use doc_depend for packages you need only for building documentation: -->
<!-- <doc_depend>doxygen</doc_depend> -->
<buildtool_depend>catkin</buildtool_depend>
<build_depend>roscpp</build_depend>
<build_depend>std_msgs</build_depend>
<build_export_depend>roscpp</build_export_depend>
<build_export_depend>std_msgs</build_export_depend>
<exec_depend>roscpp</exec_depend>
<exec_depend>std_msgs</exec_depend>
<!-- The export tag contains other, unspecified, tags -->
<export>
<!-- Other tools can request additional information be placed here -->
</export>
</package>
package.xml 파일의 원문을 보면 상당히 길다. 이를 현실적으로 다 볼 필요도 없고, 자세히 보면 하나하나 바로 이해가 가능하고 사용이 가능하다. 각 태그들을 대체적으로 정리하면 다음과 같다.
<?xml>: 문서 문법 정의
<package>: ROS 패키지 설정 부분, 가장 맨 밑으로 이어진다.
<name>: 패키지의 이름
<version>: 패키지의 버젼
<description>: 패키지의 간단한 설명
<maintainer>: 패키지 관리자의 연락처
<license>: 라이선스 기재,BSD, MIT 등등
<url>: 패키지 설명 웹사이트 주소
<author>: 패키지 개발자
<buildtool_depend>: 빌드 시스템의 의존성, catkin 사용
<build_depend>: 빌드 할 때의 의존성 패키지들
<run_depend>: 실행할 때 의존성 패키지들
<test_depend>: 테스트할 때의 패키지들
<export>: ROS에서 명시하지 않은 태그명을 사용할 때 사용
예를 들어 수정한 package.xml 파일은 다음과 같다.
<?xml version="1.0"?>
<package format="2">
<name>my_first_ros_pkg</name>
<version>0.1.0</version>
<description>The my_first_ros_pkg package</description>
<maintainer email="alpha12334@naver.com">kimhoyun</maintainer>
<license>MIT</license>
<buildtool_depend>catkin</buildtool_depend>
<build_depend>roscpp</build_depend>
<build_depend>std_msgs</build_depend>
<build_export_depend>roscpp</build_export_depend>
<build_export_depend>std_msgs</build_export_depend>
<exec_depend>roscpp</exec_depend>
<exec_depend>std_msgs</exec_depend>
<export>
</export>
</package>
더 자세한 내용은
https://wiki.ros.org/catkin/package.xml
여기 링크를 참조하도록 하자.
이번에는 빌드 설정 파일인 CMakeLists.txt에 대해서 알아보자. 이 파일은 원문이 굉장히 길다.
cmake_minimum_required(VERSION 3.0.2)
project(my_first_ros_pkg)
## Compile as C++11, supported in ROS Kinetic and newer
# add_compile_options(-std=c++11)
## Find catkin macros and libraries
## if COMPONENTS list like find_package(catkin REQUIRED COMPONENTS xyz)
## is used, also find other catkin packages
find_package(catkin REQUIRED COMPONENTS
roscpp
std_msgs
)
## System dependencies are found with CMake's conventions
# find_package(Boost REQUIRED COMPONENTS system)
## Uncomment this if the package has a setup.py. This macro ensures
## modules and global scripts declared therein get installed
## See http://ros.org/doc/api/catkin/html/user_guide/setup_dot_py.html
# catkin_python_setup()
################################################
## Declare ROS messages, services and actions ##
################################################
## To declare and build messages, services or actions from within this
## package, follow these steps:
## * Let MSG_DEP_SET be the set of packages whose message types you use in
## your messages/services/actions (e.g. std_msgs, actionlib_msgs, ...).
## * In the file package.xml:
## * add a build_depend tag for "message_generation"
## * add a build_depend and a exec_depend tag for each package in MSG_DEP_SET
## * If MSG_DEP_SET isn't empty the following dependency has been pulled in
## but can be declared for certainty nonetheless:
## * add a exec_depend tag for "message_runtime"
## * In this file (CMakeLists.txt):
## * add "message_generation" and every package in MSG_DEP_SET to
## find_package(catkin REQUIRED COMPONENTS ...)
## * add "message_runtime" and every package in MSG_DEP_SET to
## catkin_package(CATKIN_DEPENDS ...)
## * uncomment the add_*_files sections below as needed
## and list every .msg/.srv/.action file to be processed
## * uncomment the generate_messages entry below
## * add every package in MSG_DEP_SET to generate_messages(DEPENDENCIES ...)
## Generate messages in the 'msg' folder
# add_message_files(
# FILES
# Message1.msg
# Message2.msg
# )
## Generate services in the 'srv' folder
# add_service_files(
# FILES
# Service1.srv
# Service2.srv
# )
## Generate actions in the 'action' folder
# add_action_files(
# FILES
# Action1.action
# Action2.action
# )
## Generate added messages and services with any dependencies listed here
# generate_messages(
# DEPENDENCIES
# std_msgs
# )
################################################
## Declare ROS dynamic reconfigure parameters ##
################################################
## To declare and build dynamic reconfigure parameters within this
## package, follow these steps:
## * In the file package.xml:
## * add a build_depend and a exec_depend tag for "dynamic_reconfigure"
## * In this file (CMakeLists.txt):
## * add "dynamic_reconfigure" to
## find_package(catkin REQUIRED COMPONENTS ...)
## * uncomment the "generate_dynamic_reconfigure_options" section below
## and list every .cfg file to be processed
## Generate dynamic reconfigure parameters in the 'cfg' folder
# generate_dynamic_reconfigure_options(
# cfg/DynReconf1.cfg
# cfg/DynReconf2.cfg
# )
###################################
## catkin specific configuration ##
###################################
## The catkin_package macro generates cmake config files for your package
## Declare things to be passed to dependent projects
## INCLUDE_DIRS: uncomment this if your package contains header files
## LIBRARIES: libraries you create in this project that dependent projects also need
## CATKIN_DEPENDS: catkin_packages dependent projects also need
## DEPENDS: system dependencies of this project that dependent projects also need
catkin_package(
# INCLUDE_DIRS include
# LIBRARIES my_first_ros_pkg
# CATKIN_DEPENDS roscpp std_msgs
# DEPENDS system_lib
)
###########
## Build ##
###########
## Specify additional locations of header files
## Your package locations should be listed before other locations
include_directories(
# include
${catkin_INCLUDE_DIRS}
)
## Declare a C++ library
# add_library(${PROJECT_NAME}
# src/${PROJECT_NAME}/my_first_ros_pkg.cpp
# )
## Add cmake target dependencies of the library
## as an example, code may need to be generated before libraries
## either from message generation or dynamic reconfigure
# add_dependencies(${PROJECT_NAME} ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})
## Declare a C++ executable
## With catkin_make all packages are built within a single CMake context
## The recommended prefix ensures that target names across packages don't collide
# add_executable(${PROJECT_NAME}_node src/my_first_ros_pkg_node.cpp)
## Rename C++ executable without prefix
## The above recommended prefix causes long target names, the following renames the
## target back to the shorter version for ease of user use
## e.g. "rosrun someones_pkg node" instead of "rosrun someones_pkg someones_pkg_node"
# set_target_properties(${PROJECT_NAME}_node PROPERTIES OUTPUT_NAME node PREFIX "")
## Add cmake target dependencies of the executable
## same as for the library above
# add_dependencies(${PROJECT_NAME}_node ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})
## Specify libraries to link a library or executable target against
# target_link_libraries(${PROJECT_NAME}_node
# ${catkin_LIBRARIES}
# )
#############
## Install ##
#############
# all install targets should use catkin DESTINATION variables
# See http://ros.org/doc/api/catkin/html/adv_user_guide/variables.html
## Mark executable scripts (Python etc.) for installation
## in contrast to setup.py, you can choose the destination
# catkin_install_python(PROGRAMS
# scripts/my_python_script
# DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
# )
## Mark executables for installation
## See http://docs.ros.org/melodic/api/catkin/html/howto/format1/building_executables.html
# install(TARGETS ${PROJECT_NAME}_node
# RUNTIME DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
# )
## Mark libraries for installation
## See http://docs.ros.org/melodic/api/catkin/html/howto/format1/building_libraries.html
# install(TARGETS ${PROJECT_NAME}
# ARCHIVE DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
# LIBRARY DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
# RUNTIME DESTINATION ${CATKIN_GLOBAL_BIN_DESTINATION}
# )
## Mark cpp header files for installation
# install(DIRECTORY include/${PROJECT_NAME}/
# DESTINATION ${CATKIN_PACKAGE_INCLUDE_DESTINATION}
# FILES_MATCHING PATTERN "*.h"
# PATTERN ".svn" EXCLUDE
# )
## Mark other files for installation (e.g. launch and bag files, etc.)
# install(FILES
# # myfile1
# # myfile2
# DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}
# )
#############
## Testing ##
#############
## Add gtest based cpp test target and link libraries
# catkin_add_gtest(${PROJECT_NAME}-test test/test_my_first_ros_pkg.cpp)
# if(TARGET ${PROJECT_NAME}-test)
# target_link_libraries(${PROJECT_NAME}-test ${PROJECT_NAME})
# endif()
## Add folders to be run by python nosetests
# catkin_add_nosetests(test)
이 중에서 중요한 태그들만 살펴보자.
나머지는 직접 만들면서 익혀나가는게 빠르다.
cmkae_minimum_required cmake 최소 버젼 명시
project 패키지 이름
find_package(catkin REQUIRED COMPONENTS
roscpp
std_msgs
) catkin 빌드를 할 때 요구되는 구성 요소들이다.
add_message_files(~) 사용하는 메시지파일을 추가하는 옵션이다.
add_service_files(~) 사용하는 서비스 파일을 추가하는 옵션이다.
generate_messages(~) 의존하는 메시지를 사용하겠다는 옵션이다.
catkin_package(~) catkin 빌드 옵션이다.
INCLUDE_DIRS는 뒤에 설정한 include 폴더의 헤더 파일을 사용하겠다는 뜻이고,
LIBRARIES는 뒤에 설정한 패키지의 라이브러리를 사용하겠다는 의미고,
CATKIN_DEPENDS는 빌드할 때 의존하는 패키지들이다.
DEPENDS는 시스템 의존 패키지들을 서술한다.
include_directories include 폴더를 지정할 수 있는 옵션이다.
add_library C++ 라이브러리를 선언한다.
add_executable C++ 실행 파일을 선언한다.
더 자세한 내용은
https://wiki.ros.org/catkin/CMakeLists.txt
여기 링크를 참조하도록 하자.
package.xml 파일과 CMakeLists.txt 파일 수정이 끝났다면, src 폴더 하위에 C++ 혹은 파이썬을 사용해서 소스코드를 작성하고, catkin_ws 폴더로 와서 catkin_make 혹은 catkin build 명령어를 통해서 빌드한다.
빌드한 이후에는 roscore를 켜고, rosrun 혹은 roslaunch를 통해서 노드를 실행해보도록 하자.