ROS msg publisher & subscriber 작성 및 실행

1. 목적

ROS 메시지 통신에서 사용되는 발행자(Publisher) 와 구독자(Subscriber) 라는 용어는 쉽게 우리말로 따지면 송신과 수신역할을 담당하게 된다. ROS에서는 송신측을 Publisher, 수신측을 Subscriber 라고 부르고 있다. 이 강좌에서는 간단한 메시지 파일을 작성해보고, 발행자(Publisher) 노드 와 구독자(Subscriber) 노드를 작성 및 실행하는 것을 목적으로 한다.

2. 패키지 생성

1) 작업 폴더로 이동

$ cd ~/catkin_ws/src

2) 패키지 생성

: 아래의 명령어는 "oroca_ros_tutorials" 라는 패키지를 생성하는 명령어이다. 이 패키지는 의존하는 패키지로 "std_msgs"와 "roscpp"를 옵션으로 달아주었다. 로스의 표준 메시지 패키지인 std_msgs 와 로스에서 c/c++을 사용하기 위하여 클라이언트라이브러인 roscpp를 사용하겠다는 것으로 패키지 생성에 앞어서 미리 설치해야한다는 의미이다. 이러한 의존하는 패키지의 설정은 패키지 생성할 때 지정할 수도 있지만, 생성 후 package.xml 에서 직접 입력하여도 된다.

$ catkin_create_pkg oroca_ros_tutorials std_msgs roscpp

위와 같이 패키지를 생성하였으면 "~/catkin_ws/src"에 "oroca_ros_tutorials" 라는 패키지 폴더 및 ROS 패키지가 갖추어야할 기본 내부 폴더 및 CMakeLists.txt 와 package.xml가 생성된다. 다음은 아래와 같이 ls 명령어를 입력하여 내용을 보던가 윈도우의 탐색기와 같은 역할을 하는 GUI기반의 Nautilus를 이용하여 패키지 내부를 살펴보도록 하자.

$ ls

include ...................... 인클루드 폴더
src ............................. 소스코드 폴더
CMakeLists.txt .......... 빌드 설정 파일
package.xml .............. 패키지 설정 파일

3. 패키지 설정 파일 (package.xml) 수정

ROS의 필수 설정 파일 중 하나인 package.xml 은 패키지 정보를 담은 XML 파일로써 패키지의 이름, 저작자, 라이선스, 의존성 패키지 등을 기술하고 있다. 아래의 명령어로 gedit 툴을 이용하여 파일을 열고 현재의 노드에 맞도록 수정해보자.

$ gedit package.xml 

아래의 코드는 package.xml 를 이번 노드에 맞도록 수정한 내용이다. 내용중에 필자의 개인 정보가 포함되어 있으니, 이를 자신에 맞게 수정해주길 바란다. 각 옵션의 세부 설명은 "ROS 빌드 시스템" 을 참고하길 바란다.

<?xml version="1.0"?>
<package format="2">
  <name>oroca_ros_tutorials</name>
  <version>0.1.0</version>
  <description>The oroca_ros_tutorials package</description>

  <!-- One maintainer tag required, multiple allowed, one person per tag -->
  <!-- Example:  -->
  <!-- <maintainer email="[email protected]">Jane Doe</maintainer> -->
  <maintainer email="[email protected]">Youngwook Lee</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>MIT</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://oroca.org</url>
  <url type="repository">https://github.com/oroca/oroca_ros_tutorials.git</url>

  <!-- Author tags are optional, multiple are allowed, one per tag -->
  <!-- Authors do not have to be maintainers, but could be -->
  <!-- Example: -->
  <!-- <author email="[email protected]">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>

4. 빌드 설정 파일 (CMakeLists.txt) 수정

ROS의 빌드 시스템인 캐킨(cakin)은 기본적으로 CMake를 이용하고 있어서 패키지 폴더에 CMakeLists.txt 라는 파일에 빌드 환경을 기술하고 있다. 이는 실행 파일 생성, 의존성 패키지 우선 빌드, 링크 생성 등을 설정하게 되어 있다.

$ gedit CMakeLists.txt 

아래의 코드는 CMakeLists.txt 를 이번 노드에 맞도록 수정한 내용이다. 각 옵션의 세부 설명은 "ROS 빌드 시스템"을 참고하길 바란다.

cmake_minimum_required(VERSION 3.0.2)
project(oroca_ros_tutorials)

## 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
  message_generation
)

## 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 msgTutorial.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 oroca_ros_tutorials
 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}/oroca_ros_tutorials.cpp
# )

# 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(ros_tutorial_msg_publisher src/ros_tutorial_msg_publisher.cpp)

# Specify libraries to link a library or executable target against
target_link_libraries(ros_tutorial_msg_publisher ${catkin_LIBRARIES})

# 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(ros_tutorial_msg_publisher oroca_ros_tutorials_generate_messages_cpp)

# 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(ros_tutorial_msg_subscriber src/ros_tutorial_msg_subscriber.cpp)

# Specify libraries to link a library or executable target against
target_link_libraries(ros_tutorial_msg_subscriber ${catkin_LIBRARIES})

# Add cmake target dependencies of the executable
# same as for the library above
add_dependencies(ros_tutorial_msg_subscriber oroca_ros_tutorials_generate_messages_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 "")



#############
## 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_oroca_ros_tutorials.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)

5. 메시지 파일 작성

CMakeLists.txt 에 라는 파일에 "add_message_files(FILES msgTutorial.msg)" 라는 옵션을 넣었다. 이는 이번 노드에서 사용할 메시지인 msgTutorial.msg 를 빌드할때 포함하라는 이야기이다. 현재, msgTutorial.msg 는 생성하지 않았기에 아래와 같은 순서로 생성해주도록 하자.

$ cd ~/catkin_ws/src/oroca_ros_tutorials/     (패키지 폴더로 이동한다.)
$ mkdir msg     (패키지에 msg 라는 메시지 폴더를 신규 작성한다.)
$ cd msg     (작성한 msg 폴더로 이동)
$ gedit msgTutorial.msg    (msgTutorial.msg 파일 신규 작성 및 내용 수정)

내용으로는 아래와 같이 int32 메시지 형식에 data라는 이름의 메시지를 만들어주자.

int32 data

6. 발행자 노드 작성

add_executable(ros_tutorial_msg_publisher src/ros_tutorial_msg_publisher.cpp)

CMakeLists.txt 에 위와 같은 실행 파일을 생성하는 옵션을 주었다. 즉, "ros_tutorial_msg_publisher.cpp"라는 파일을 빌드하여 "ros_tutorial_msg_publisher"라는 실행 파일을 만들라는 이야기이다. 아래의 순서대로 발행자 노드 기능을 수행하는 "ros_tutorial_msg_publisher.cpp" 소스를 작성해 보자.

$ cd ~/catkin_ws/src/oroca_ros_tutorials/     (패키지 폴더로 이동한다.)
$ cd src     (노드의 소스코드 폴더인 src 폴더로 이동)
$ gedit ros_tutorial_msg_publisher.cpp    (ros_tutorial_msg_publisher.cpp 파일 신규 작성 및 내용 수정)
#include "ros/ros.h"                                    // ROS 기본 헤더파일
#include "oroca_ros_tutorials/msgTutorial.h"            // msgTutorial 메시지 파일 헤더 (빌드후 자동 생성됨)

int main(int argc, char **argv)                         // 노드 메인 함수
{
  ros::init(argc, argv, "ros_tutorial_msg_publisher");  // 노드명 초기화
  ros::NodeHandle nh;                                   // ROS 시스템과 통신을 위한 노드 핸들 선언

  // 발행자 선언, oroca_ros_tutorials 패키지의 msgTutorial 메시지 파일을 이용한
  // 발행자 ros_tutorial_pub 를 작성한다. 토픽명은 "ros_tutorial_msg" 이며,
  // 발행자 큐(queue) 사이즈를 100개로 설정한다는 것이다
  ros::Publisher ros_tutorial_pub = nh.advertise<oroca_ros_tutorials::msgTutorial>("ros_tutorial_msg", 100);

  // 루프 주기를 설정한다. "10" 이라는 것은 10Hz를 말하는 것으로 0.1초 간격으로 반복된다
  ros::Rate loop_rate(10);

  int count = 0;    // 메시지에 사용될 변수 선언

  while (ros::ok())
  {
    oroca_ros_tutorials::msgTutorial msg;      // msgTutorial 메시지 파일 형식으로 msg 라는 메시지를 선언
    msg.data = count;                   // count 라는 변수를 이용하여 메시지 값을 정한다

    ROS_INFO("send msg = %d", count);   // ROS_INFO 라는 ROS 함수를 이용하여 count 변수를 표시한다

    ros_tutorial_pub.publish(msg);      // 메시지를 발행한다. 약 0.1초 간격으로 발행된다

    loop_rate.sleep();                  // 위에서 정한 루프 주기에 따라 슬립에 들어간다

    ++count;                            // count 변수 1씩 증가
  }

  return 0;
}

7. 구독자 노드 작성

add_executable(ros_tutorial_msg_subscriber src/ros_tutorial_msg_subscriber.cpp)

CMakeLists.txt 에 위와 같은 실행 파일을 생성하는 옵션을 주었다. 즉, "ros_tutorial_msg_subscriber.cpp"라는 파일을 빌드하여 "ros_tutorial_msg_subscriber"라는 실행 파일을 만들라는 이야기이다. 아래의 순서대로 구독자 노드 기능을 수행하는 "ros_tutorial_msg_subscriber.cpp" 소스를 작성해 보자.

$ cd ~/catkin_ws/src/oroca_ros_tutorials/     (패키지 폴더로 이동한다.)
$ cd src     (노드의 소스코드 폴더인 src 폴더로 이동)
$ gedit ros_tutorial_msg_subscriber.cpp    (ros_tutorial_msg_subscriber.cpp 파일 신규 작성 및 내용 수정)
#include "ros/ros.h"                                    // ROS 기본 헤더파일
#include "oroca_ros_tutorials/msgTutorial.h"            // msgTutorial 메시지 파일 헤더 (빌드후 자동 생성됨)

// 메시지 콜백함수로써, 밑에서 설정한 ros_tutorial_sub 구독자에 해당되는 메시지를
// 수신하였을때 동작하는 함수이다
// 입력 메시지로는 oroca_ros_tutorial 패키지의 msgTutorial 메시지를 받도록 되어있다
void msgCallback(const oroca_ros_tutorials::msgTutorial::ConstPtr& msg)
{
  ROS_INFO("recieve msg: %d", msg->data);   // 수신된 메시지를 표시하는 함수
}

int main(int argc, char **argv)                         // 노드 메인 함수
{
  ros::init(argc, argv, "ros_tutorial_msg_subscriber"); // 노드명 초기화

  ros::NodeHandle nh;                                   // ROS 시스템과 통신을 위한 노드 핸들 선언

  // 구독자 선언, oroca_ros_tutorials 패키지의 msgTutorial 메시지 파일을 이용한
  // 구독자 ros_tutorial_sub 를 작성한다. 토픽명은 "ros_tutorial_msg" 이며,
  // 구독자 큐(queue) 사이즈를 100개로 설정한다는 것이다
  ros::Subscriber ros_tutorial_sub = nh.subscribe("ros_tutorial_msg", 100, msgCallback);

  // 콜백함수 호출을 위한 함수로써, 메시지가 수신되기를 대기, 수신되었을 경우 콜백함수를 실행한다
  ros::spin();

  return 0;
}
[출처] 로봇 운영체제 강좌 : 메시지 발행자 노드와 구독자 노드 작성 및 실행 (오픈소스 소프트웨어 & 하드웨어: 로봇 기술 공유 카페 (오로카)) | 작성자 표윤석
$ cd ~/catkin_ws     (catkin 폴더로 이동)
$ catkin_make     (catkin 빌드 실행)

위의 명령어로 oroca_ros_tutorials 패키지의 메지시 파일, 발행자 노드, 구독자 노드가 빌드되었다.

  • oroca_ros_tutorials 패키지의 소스는 ~/catkin_ws/src/oroca_ros_tutorials/src 에 존재하고,
  • oroca_ros_tutorials 패키지의 메시지 파일은 ~/catkin_ws/src/oroca_ros_tutorials/msg 에 존재한다.

이를 기반으로 빌드된 결과물은 ~/catkin_ws/build 및 ~/catkin_ws/devel 에 각각 생성된다.

  • ~/catkin_ws/build 에는 캐킨 빌드에서 사용된 설정 내용이 저장되며,
  • ~/catkin_ws/devel/lib/oroca_ros_tutorials 에는 실행 파일이,
  • ~/catkin_ws/devel/include/oroca_ros_tutorials 에는 메시지 파일로부터 자동 생성된 메시지 헤더파일이 저장된다.

각 생성 결과물이 궁금하다면 이 경로에 생성된 결과물을 확인해 보자.

9. 발행자 실행

(※ 주의! 노드 실행에 앞서서 roscore를 실행해주는 것을 잊지 말자!)

$ rosrun oroca_ros_tutorials ros_tutorial_msg_publisher

ROS 노드 실행 명령어인 rosrun 을 이용하여, oroca_ros_tutorials 패키지의 ros_tutorial_msg_publisher 노드를 구동하라는 명령어이다. 이를 실행하게 되면 아래와 같은 출력 화면을 볼 수 있다. 내부에 선언된 count 값이 표시되고 있으며, ROS 메시지로 발부되고 있다.

이전에 익힌 rostopic 명령어를 이용하여 현재 ROS 네트워크에서 사용중인 토픽의 목록을 확인해보고, 위에서 실행한 발행자 노드에서 발행중인 메시지를 확인해보도록 하자.

$ rostopic list

/ros_tutorial_msg
/rosout
/rosout_agg

$ rostopic echo /ros_tutorial_msg

rostopic list 라는 옵션을 붙인 명령어로 "ros_tutorial_msg" 토픽이 있음을 확인하였다. rostopic echo /ros_tutorial_msg 이라는 명령어로 "ros_tutorial_msg" 토픽의 메시지를 확인해보자. 아래와 같이 실시간으로 발행되는 메시지를 확인할 수 있을 것이다.

10. 구독자 실행

$ rosrun oroca_ros_tutorials ros_tutorial_msg_subscriber 

ROS 노드 실행 명령어인 rosrun 을 이용하여, oroca_ros_tutorials 패키지의 ros_tutorial_msg_subscriber 노드를 구동하라는 명령어이다. 이를 실행하게 되면 아래와 같은 출력 화면을 볼 수 있다. 발행자에서 발행된 "ros_tutorial_msg" 토픽의 메시지를 수신받아 값이 표시되고 있다.

11. ROS Graph

ROS Graph의 단일 수행 명령어인 rqt_graph 를 실행하던가,

$ rqt_graph

또는

$ rqt

rqt 를 실행 후, 플러그인(plugins)에서 ROS Graph 를 선택하면 아래의 그림처럼 현재 ROS 에서 구동중인 노드 및 메시지를 확인할 수 있다. 현재 ROS 네트워크상에는, 구독자 노드 (ros_tutorial_msg_publisher) 에서 발행한 토픽 (ros_tutorial_msg) 이 발행중이고 이를 구독자 노드 (ros_tutorial_msg_subscriber) 에서 수신하고 있음을 확인할 수 있다.


[출처] 로봇 운영체제 강좌 : 메시지 발행자 노드와 구독자 노드 작성 및 실행 (오픈소스 소프트웨어 & 하드웨어: 로봇 기술 공유 카페 (오로카)) | 작성자 표윤석

좋은 웹페이지 즐겨찾기