IT 스터디/ROS

ROS 설치, Catkin 빌드 그리고 VSCode 디버깅 (2)

KimDol2 2023. 6. 15. 14:38

 

 

지난 포스팅 에선 ROS 를 설치 하고 TurtleSim 프로그램까지 테스트해 봤다. 이번 포스팅에선 ROS 를 사용해 예제 프로그램을 짜보고 Catkin 빌드 시스템을 사용하여 빌드까지 해보려고 한다.

 

시작하기에 앞서 ROS Wiki 를 보면 Catkin 은 'ROS 를 위한 Low-level 빌드 시스템 매크로와 인프라' 라고 나와있다.  Catkin (버드나무의 '꽃송이') 이란 명칭은 Willow Garage 사의 Willow (버드나무) 에서 유래했는데 Willow Garage 사는 ROS 를 만든 회사이다. 캣킨의 컨셉은 아래 링크에 나와 있으니 참고하기 바란다.

 

 

catkin/conceptual_overview - ROS Wiki

Overview catkin is the official build system of ROS and the successor to the original ROS build system, rosbuild. catkin combines CMake macros and Python scripts to provide some functionality on top of CMake's normal workflow. catkin was designed to be mor

wiki.ros.org

 

그럼 캣킨을 사용해서 workspace 를 만들어 보자. 지난 포스팅을 따라왔다면 catkin 을 사용할 수 있는 환경은 이미 셋팅되어 있을 것이다.

 

1. Catkin 작업공간 만들기

$ mkdir -p ~/catkin_ws/src
$ cd ~/catkin_ws/
$ catkin_make

위와 같이 캣킨 작업 공간을 만들고 해당 디렉토리, 여기에서는 ~/catkin_ws/ 에서 catkin_make 명령어를 입력하면 아래와 같은 디렉토리 구조가 만들어진다.

catkin_ws/               -- WORKSPACE
  src/                   -- SOURCE SPACE
    CMakeLists.txt       -- CMakeLists.txt File
  build/                 -- BUILD SPACE
  devel/                 -- DEVEL SPACE
    setup.sh
    setup.bash
    setup.zsh
    ...

현재 생성한 캣킨 워크스페이스를 ROS 환경의 최상위에 오버레이 하기위해 아래와 같이 쉘 스크립트를 등록하고 ROS_PACKAGE_PATH 를 통해 올바르게 오버레이 되었는지 확인한다.

$ source devel/setup.bash
$ echo $ROS_PACKAGE_PATH
/home/youruser/catkin_ws/src:/opt/ros/kinetic/share

이제 위와 같이 생성된 캣킨 워크스페이스안에 ROS 프로그램을 만들어 보자.

 

2. Catkin 을 사용한 ROS 패키지 만들기

ROS 의 주요 구성요소는 노드, 패키지 그리고 메세지인데 노드는 프로그램 하나 단위이고 패키지는 노드를 묶어 배포하는 것 그리고 메세지는 노드간 데이터를 주고 받는 것을 말한다. 이러한 구성요소에 대한 세부적인 내용은 ROS 프로그램 개발에 앞서 알아둬야하는 내용들이다. 자세한 내용은 아래 링크를 통해 스터디를 하자.

 

 

GitHub - robotpilot/ros-seminar: ROS 수업, 세미나, 강연, 강의 등의 보조 자료

ROS 수업, 세미나, 강연, 강의 등의 보조 자료. Contribute to robotpilot/ros-seminar development by creating an account on GitHub.

github.com

 

이제 ROS 패키지를 만들어 보자. 패키지 생성 예제는 위 링크의 ros-seminar 자료의 '07_ROS 기본 프로그래밍' 슬라이드를 참고로 한다. (이하 내용에는 ros 명령어와 ros 프로그래밍을 이해하는데 필요한 개념, 용어가 많이 등장하는데 위 ros-seminar 자료의 슬라이드를 먼저 읽어보는 것을 권한다.)

 

$ cd ~/catkin_ws/src
$ catkin_create_pkg ros_tutorials_topic message_generation std_msgs roscpp

위와 같이 catkin_create_pkg 스크립트를 사용하여 message_generation, std_msgs, roscpp 를 의존하는 ros_tutorials_topic 패키지를 생성한다. 생성된 ros_tutorials_topic 패키지의 폴더는 아래와 같이 구성된다.

$ cd ros_tutorials_topic
$ ls
include        → 헤더 파일 폴더
src            → 소스 코드 폴더
CMakeLists.txt → 빌드 설정 파일
package.xml    → 패키지 설정 파일

rospack 명령어를 사용하여 패키지 의존성을 확인할 수 있다. depends1 은 1차 의존성을 확인하는 것이고 depends 중첩된 모든 종속성을 제귀적으로 확인한다.

패키지 설정 파일인 package.xml 파일을 수정한다. package.xml 파일에는 패키지 이름, 저작자, 라이센스, 의존성 패키지 등을 기술한다. 자동으로 생성된 package.xml 파일의 주석을 제거하여 아래와 같이 정리한다.

 

3. 퍼블리셔, 서브스크라이버 코드 작성 및 Catkin 패키지 빌드

이제 캣킨 워크스페이스 안의 소스코드로 부터 catkin_make 를 사용하여 패키지를 빌드해 보자.

 

먼저 빌드를 하려면 소스 코드가 있어야 한다. 다음과 같이 퍼블리셔 노드를 작성하자.

 

topic_publisher.cpp

#include "ros/ros.h" // ROS 기본 헤더파일
#include "ros_tutorials_topic/MsgTutorial.h" // MsgTutorial 메시지 파일 헤더(빌드 후 자동 생성됨)

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

    // 퍼블리셔 선언, ros_tutorials_topic 패키지의 MsgTutorial 메시지 파일을 이용한
    // 퍼블리셔 ros_tutorial_pub 를 작성한다. 토픽명은 "ros_tutorial_msg" 이며,
    // 퍼블리셔 큐(queue) 사이즈를 100개로 설정한다는 것이다
    ros::Publisher ros_tutorial_pub = nh.advertise<ros_tutorials_topic::MsgTutorial>("ros_tutorial_msg", 100);
    
    // 루프 주기를 설정한다. "10" 이라는 것은 10Hz를 말하는 것으로 0.1초 간격으로 반복된다
    ros::Rate loop_rate(10);

    // MsgTutorial 메시지 파일 형식으로 msg 라는 메시지를 선언
    ros_tutorials_topic::MsgTutorial msg;

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

    while (ros::ok())
    {
	msg.stamp = ros::Time::now(); // 현재 시간을 msg의 하위 stamp 메시지에 담는다
	msg.data = count; // count라는 변수 값을 msg의 하위 data 메시지에 담는다
	
	
	ROS_INFO("send msg = %d", msg.stamp.sec); // stamp.sec 메시지를 표시한다
	ROS_INFO("send msg = %d", msg.stamp.nsec); // stamp.nsec 메시지를 표시한다
	ROS_INFO("send msg = %d", msg.data);  // data 메시지를 표시한다
	
	
	ros_tutorial_pub.publish(msg); // 메시지를 발행한다
	loop_rate.sleep(); // 위에서 정한 루프 주기에 따라 슬립에 들어간다
	++count; // count 변수 1씩 증가
    }
    return 0;
}

 

이제 서브스크라이버 노드를 작성하자.

topic_subscrber.cpp

#include "ros/ros.h" // ROS 기본 헤더파일
#include "ros_tutorials_topic/MsgTutorial.h" // MsgTutorial 메시지 파일 헤더 (빌드 후 자동 생성됨)

// 메시지 콜백 함수로써, 밑에서 설정한 ros_tutorial_msg라는 이름의 토픽
// 메시지를 수신하였을 때 동작하는 함수이다
// 입력 메시지로는 ros_tutorials_topic 패키지의 MsgTutorial 메시지를 받도록 되어있다
void msgCallback(const ros_tutorials_topic::MsgTutorial::ConstPtr& msg)
{
    ROS_INFO("recieve msg = %d", msg->stamp.sec);  // stamp.sec 메시지를 표시한다
    ROS_INFO("recieve msg = %d", msg->stamp.nsec); // stamp.nsec 메시지를 표시한다
    ROS_INFO("recieve msg = %d", msg->data);       // data 메시지를 표시한다
}

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

    // 서브스크라이버 선언, ros_tutorials_topic 패키지의 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;
}

이제 위의 각 노드 소스 코드에서 사용할 메세지 파일을 만들자.

위와 같이 ros_tutorials_topic 패키지 디렉터리 안에 msg 폴더를 만드로 MsgTutorial.msg 파일을 생성 한 후 아래와 같이 작성한다.

 

메세지 타입에 대한 자세한 설명은 다음 링크를 참고하면 된다.

 

 

msg - ROS Wiki

ROS uses a simplified messages description language for describing the data values (aka messages) that ROS nodes publish. This description makes it easy for ROS tools to automatically generate source code for the message type in several target languages. M

wiki.ros.org

 

위의 작업들을 모두 마쳤다면 ros_tutorials_topic 패키지의 디렉토리가 아래와 같이 구성되었을 것이다.

 

 

이제 작성한 코드를 빌드해 보자.

먼저 catkin_create_pkg 스크립트로 생성된 ~/catkin_ws/src/ros_tutorials_topic/CMakeLists.txt 파일을 아래와 같이 변경한다.

cmake_minimum_required(VERSION 2.8.3)
project(ros_tutorials_topic)

## 캐킨 빌드를 할 때 요구되는 구성요소 패키지이다.
## 의존성 패키지로 message_generation, std_msgs, roscpp이며 이 패키지들이 존재하지 않으면 빌드 도중에 에러가 난다.
find_package(catkin REQUIRED COMPONENTS message_generation std_msgs roscpp)

## 메시지 선언: MsgTutorial.msg
add_message_files(FILES MsgTutorial.msg)

## 의존하는 메시지를 설정하는 옵션이다.
## std_msgs가 설치되어 있지 않다면 빌드 도중에 에러가 난다.
generate_messages(DEPENDENCIES std_msgs)

## 캐킨 패키지 옵션으로 라이브러리, 캐킨 빌드 의존성, 시스템 의존 패키지를 기술한다.
catkin_package(
  LIBRARIES ros_tutorials_topic
  CATKIN_DEPENDS std_msgs roscpp
)

## 인클루드 디렉터리를 설정한다.
include_directories(${catkin_INCLUDE_DIRS})

## topic_publisher 노드에 대한 빌드 옵션이다.
## 실행 파일, 타깃 링크 라이브러리, 추가 의존성 등을 설정한다.
add_executable(topic_publisher src/topic_publisher.cpp)
add_dependencies(topic_publisher ${${PROJECT_NAME}_EXPORTED_TARGETS}
${catkin_EXPORTED_TARGETS})
target_link_libraries(topic_publisher ${catkin_LIBRARIES})

## topic_subscriber 노드에 대한 빌드 옵션이다.
add_executable(topic_subscriber src/topic_subscriber.cpp)
add_dependencies(topic_subscriber ${${PROJECT_NAME}_EXPORTED_TARGETS}
${catkin_EXPORTED_TARGETS})
target_link_libraries(topic_subscriber ${catkin_LIBRARIES})

 

위의 CMakeLists.txt 파일은 아래와 같이 빌드 옵션이 셋팅 되어 있다. 

 

add_message_files(FILES MsgTutorial.msg)

- 노드에서 사용할 메시지인 MsgTutorial.msg를 빌드할 때 포함하라는 이야기

 

add_executable(topic_publisher src/topic_publisher.cpp)

- src 폴더의 topic_publisher.cpp라는 파일을 빌드하여 topic_publisher 라는 실행 파일을 만들라는 이야기

 

add_executable(topic_subscriber src/topic_subscriber.cpp)

- topic_subscriber.cpp라는 파일을 빌드하여 topic_subscriber라는 실행 파일을 만들라는 이야기

 

이제 catkin_make 명령으로 ros_tutorials_topic 패키지를 빌드하자.

$ cd ~/catkin_ws → catkin 폴더로 이동
$ catkin_make    → catkin 빌드 실행

 

아래는 catkin_make 빌드 로그이다.

kbk@kbk-Lenovo-Yoga-3-14:~/catkin_ws$ catkin_make
Base path: /home/kbk/catkin_ws
Source space: /home/kbk/catkin_ws/src
Build space: /home/kbk/catkin_ws/build
Devel space: /home/kbk/catkin_ws/devel
Install space: /home/kbk/catkin_ws/install
####
#### Running command: "cmake /home/kbk/catkin_ws/src -DCATKIN_DEVEL_PREFIX=/home/kbk/catkin_ws/devel -DCMAKE_INSTALL_PREFIX=/home/kbk/catkin_ws/install -G Unix Makefiles" in "/home/kbk/catkin_ws/build"
####
-- Using CATKIN_DEVEL_PREFIX: /home/kbk/catkin_ws/devel
-- Using CMAKE_PREFIX_PATH: /opt/ros/melodic
-- This workspace overlays: /opt/ros/melodic
-- Found PythonInterp: /usr/bin/python2 (found suitable version "2.7.17", minimum required is "2") 
-- Using PYTHON_EXECUTABLE: /usr/bin/python2
-- Using Debian Python package layout
-- Using empy: /usr/bin/empy
-- Using CATKIN_ENABLE_TESTING: ON
-- Call enable_testing()
-- Using CATKIN_TEST_RESULTS_DIR: /home/kbk/catkin_ws/build/test_results
-- Found gtest sources under '/usr/src/googletest': gtests will be built
-- Found gmock sources under '/usr/src/googletest': gmock will be built
-- Found PythonInterp: /usr/bin/python2 (found version "2.7.17") 
-- Using Python nosetests: /usr/bin/nosetests-2.7
-- catkin 0.7.29
-- BUILD_SHARED_LIBS is on
-- BUILD_SHARED_LIBS is on
-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-- ~~  traversing 1 packages in topological order:
-- ~~  - ros_tutorials_topic
-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-- +++ processing catkin package: 'ros_tutorials_topic'
-- ==> add_subdirectory(ros_tutorials_topic)
-- Using these message generators: gencpp;geneus;genlisp;gennodejs;genpy
-- ros_tutorials_topic: 1 messages, 0 services
-- Configuring done
-- Generating done
-- Build files have been written to: /home/kbk/catkin_ws/build
####
#### Running command: "make -j4 -l4" in "/home/kbk/catkin_ws/build"
####
Scanning dependencies of target rosgraph_msgs_generate_messages_py
Scanning dependencies of target rosgraph_msgs_generate_messages_nodejs
Scanning dependencies of target roscpp_generate_messages_cpp
Scanning dependencies of target std_msgs_generate_messages_eus
[  0%] Built target roscpp_generate_messages_cpp
[  0%] Built target rosgraph_msgs_generate_messages_py
[  0%] Built target std_msgs_generate_messages_eus
[  0%] Built target rosgraph_msgs_generate_messages_nodejs
Scanning dependencies of target rosgraph_msgs_generate_messages_lisp
Scanning dependencies of target rosgraph_msgs_generate_messages_eus
Scanning dependencies of target roscpp_generate_messages_py
Scanning dependencies of target roscpp_generate_messages_lisp
[  0%] Built target rosgraph_msgs_generate_messages_lisp
[  0%] Built target roscpp_generate_messages_py
[  0%] Built target rosgraph_msgs_generate_messages_eus
[  0%] Built target roscpp_generate_messages_lisp
Scanning dependencies of target std_msgs_generate_messages_py
Scanning dependencies of target roscpp_generate_messages_nodejs
Scanning dependencies of target roscpp_generate_messages_eus
Scanning dependencies of target std_msgs_generate_messages_cpp
[  0%] Built target std_msgs_generate_messages_py
[  0%] Built target roscpp_generate_messages_nodejs
[  0%] Built target roscpp_generate_messages_eus
[  0%] Built target std_msgs_generate_messages_cpp
Scanning dependencies of target rosgraph_msgs_generate_messages_cpp
Scanning dependencies of target _catkin_empty_exported_target
Scanning dependencies of target std_msgs_generate_messages_nodejs
Scanning dependencies of target std_msgs_generate_messages_lisp
[  0%] Built target rosgraph_msgs_generate_messages_cpp
[  0%] Built target _catkin_empty_exported_target
[  0%] Built target std_msgs_generate_messages_nodejs
[  0%] Built target std_msgs_generate_messages_lisp
Scanning dependencies of target _ros_tutorials_topic_generate_messages_check_deps_MsgTutorial
[  0%] Built target _ros_tutorials_topic_generate_messages_check_deps_MsgTutorial
Scanning dependencies of target ros_tutorials_topic_generate_messages_cpp
Scanning dependencies of target ros_tutorials_topic_generate_messages_lisp
Scanning dependencies of target ros_tutorials_topic_generate_messages_eus
Scanning dependencies of target ros_tutorials_topic_generate_messages_nodejs
[ 18%] Generating EusLisp code from ros_tutorials_topic/MsgTutorial.msg
[ 18%] Generating C++ code from ros_tutorials_topic/MsgTutorial.msg
[ 27%] Generating Lisp code from ros_tutorials_topic/MsgTutorial.msg
[ 36%] Generating Javascript code from ros_tutorials_topic/MsgTutorial.msg
[ 36%] Built target ros_tutorials_topic_generate_messages_lisp
[ 36%] Built target ros_tutorials_topic_generate_messages_nodejs
[ 45%] Generating EusLisp manifest code for ros_tutorials_topic
Scanning dependencies of target ros_tutorials_topic_generate_messages_py
[ 54%] Generating Python from MSG ros_tutorials_topic/MsgTutorial
[ 54%] Built target ros_tutorials_topic_generate_messages_cpp
[ 63%] Generating Python msg __init__.py for ros_tutorials_topic
[ 63%] Built target ros_tutorials_topic_generate_messages_py
[ 63%] Built target ros_tutorials_topic_generate_messages_eus
Scanning dependencies of target topic_subscriber
Scanning dependencies of target topic_publisher
Scanning dependencies of target ros_tutorials_topic_generate_messages
[ 63%] Built target ros_tutorials_topic_generate_messages
[ 72%] Building CXX object ros_tutorials_topic/CMakeFiles/topic_subscriber.dir/src/topic_subscriber.cpp.o
[ 81%] Building CXX object ros_tutorials_topic/CMakeFiles/topic_publisher.dir/src/topic_publisher.cpp.o
[ 90%] Linking CXX executable /home/kbk/catkin_ws/devel/lib/ros_tutorials_topic/topic_publisher
[100%] Linking CXX executable /home/kbk/catkin_ws/devel/lib/ros_tutorials_topic/topic_subscriber
[100%] Built target topic_publisher
[100%] Built target topic_subscriber
kbk@kbk-Lenovo-Yoga-3-14:~/catkin_ws$

이제 빌드된 퍼블리셔와 서브스크라이버를 실행해 보자.

roscore 를 먼저 실행하고 rosrun ros_tutorials_topic topic_publisher 와 rosrun ros_tutorials_topic topic_subscriber 를 각각 실행한다. publisher 에서 메시지를 전송하고 subscriber 에서 메세지를 수신하는 것을 확인할 수 있다.

 

rqt_graph 를 통해 실행된 노드들의 통신상태를 그래프로 보자.

이번 포스팅에서는 캣킨 워크스페이스와 패키지를 생성하고 토픽에서 사용되는 퍼블리셔와 서브스크라이버 노드의 소스코드를 작성하고 이를 빌드하고 실행해서 노드 간 토픽 통신 방법을 알아보았다. (상세한 내용은 위의 ros_seminar 깃헙 링크의  '07_ROS 기본 프로그래밍' 문서를 참고하기 바란다.)

 

다음 포스팅에서는 이제 작성한 코드를 VS Code 를 사용하여 디버깅 하는 방법에 대해 알아 볼 것이다.

 

 

 

반응형