본문 바로가기

FOSCAR-(Autonomous Driving)/ROS 스터디

[2025 ROS 스터디] 이승찬 #3주차 - ROS 기본 프로그래밍

반응형

오늘은 ROS 기본 프로그래밍을 배워보겠다~

 

1.  ROS 프로그래밍 전에 알아둬야 할 사항

참고자료 : https://www.ros.org/reps/rep-0103.html

  • 표준 단위 : SI 단위 사용

  • 좌표 표현 방식
    • x : forward , y : left , z : up
    • 오른손 법칙
  • 프로그래밍 규칙

(이걸 왜 알고 있어야 할까? → 공식적으로 배포된 패키지가 (18년도 기준) 2,800여개 존재하고 이거와 같이 공식적으로 사용 및 배포 시에 이 규칙을 준수해야 함)


ROS 메시지 통신

앞서 배운 Topic, Service, Action을 간단히 정리해보았다.

 

매개변수(Parameter)

  1. roscore 실행 시 rosmaster 기능
  2. rosout 로그 기록 전담
  3. parameter server 구동 (user가 parameter 변경 시 node는 parameter 값 참조 → process,option 변경)

2.  퍼블리셔와 서브스크라이버 노드 작성 및 실행

1) 패키지 생성

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

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

 

2) 패키지 설정 파일(pakage.xml) 수정

 -  ROS의 필수 설정 파일 중 하나인 package.xml은 패키지 정보를 담은 XML 파일로서 패키지 이름, 저작자,

    라이선스, 의존성 패키지 등을 기술하고 있다.

$ gedit package.xml
--------------------------------------------------------------------------------------------
<?xml version="1.0"?>
<package format="2">
  <name>ros_tutorials_topic</name>
  <version>0.1.0</version>
  <description>ROS turtorial package to learn the topic</description>
  <license>Apache 2.0</license>
  <author email="pyo@robotis.com">Yoonseok Pyo</author>
  <maintainer email="pyo@robotis.com">Yoonseok Pyo</maintainer>
  <url type="website">http://www.robotis.com</url>
  <url type="repository">https://github.com/ROBOTIS-GIT/ros_tutorials.git</url>
  <url type="bugtracker">https://github.com/ROBOTIS-GIT/ros_tutorials/issues</url >
  <buildtool_depend>catkin</buildtool_depend>
  <depend>roscpp</depend>
  <depend>std_msgs</depend>
  <depend>message_generation</depend>
  <export></export >
</package>

 

<name> : 패키지 이름

<version> : 패키지 버전

<description> : 패키지 설명 / 2~3줄이 적당 *중요함

<author> : 저자 이름 / 여러명 가능

<maintainer> : 초기엔 저자와 같은 인물 / 추후에 maintainer는 변경되는 경우 다수

<url> : 참조할 만한 사이트

<buildtool_depend> : catkin

<depend>roscpp : c++ 에 관련된 api 사용

<depend>std_msgs : standard messages를 쓰기 위해

<depend>messages_generation : message를 만들어보기 위해

<run> : 이하동문


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

$ gedit 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})

 

cmake_minimum_required() : 최소 필요 버전

project() : 패키지 이름 (앞과 동일)

find_package() : catkin 빌드가 요구되는 구성 패키지 / message_generation, std_msgs, roscpp 이 세가지가 있어야 한다고 선언

add_message_files() : 메시지 선언 / 새로 만들 메시지 이름 작성

generate_messages() : 새 메시지를 만들 때 이 안에 std_msgs가 포함된다 / 의존성이 있다

catkin_package() : Libraries, catkin_build, 시스템 의존 패키지 등을 기술

include_directories() : 인클루드 디렉토리 설정 / 미리 설치된 catkin include file 사용 가능, 추가로 개인이 설정 가능

## topic_publisher 노드

add_executable(A B/C) : B 폴더에 C 파일을 참고하여 A라는 이름의 노드 생성

add_dependencies(D $E) : D 토픽 퍼블리셔 노드를 만들 때 E라는 dependencies를 참조해라

target_link_libraries() : target link 기술

## topic_subscriber 노드

add_executable() : publisher 와 동일

add_dependencies() : publisher 와 동일

target_link_libraries() : publisher 와 동일

더보기

catkin?

 

ROS의 공식 빌드 시스템으로, ROS 패키지의 빌드, 관리, 그리고 패키지 간 의존성 관리를 효율적으로 수행하기 위해 설계되었습니다. 기본적으로 CMake를 기반으로 하며, 이를 ROS에 특화되도록 확장한 형태입니다.

ROS 패키지를 빌드하고 관리하는 도구.

 

Catkin의 주요 특징:

CMake 기반: Catkin은 CMake(Cross Platform Make)를 기본으로 사용합니다. 각 ROS 패키지에는 CMakeLists.txt 파일이 포함되어 있으며, 이 파일에 빌드 환경과 설정을 기술합니다. 

패키지 관리: Catkin은 ROS 패키지의 빌드와 관리를 담당하며, 패키지 간의 의존 관계를 효율적으로 처리합니다. 이를 통해 개발자는 복잡한 의존성 문제를 쉽게 해결할 수 있습니다. 

워크스페이스: Catkin은 작업 공간(workspace) 개념을 도입하여, 개발자가 자신의 ROS 프로젝트를 체계적으로 구성하고 관리할 수 있도록 지원합니다. 일반적으로 작업 공간은 src, build, devel 디렉토리로 구성되며, 각각 소스 코드, 빌드 파일, 개발 환경 설정을 포함합니다. 

 

Catkin을 사용하면 ROS 패키지의 빌드 프로세스가 단순화되고, 패키지 간의 의존성 관리가 용이해집니다. 또한, CMake를 기반으로 하기 때문에 다양한 플랫폼에서의 호환성과 유연성을 제공합니다.


 

4) 메시지 파일 작성

+ Linux : cd 경로 / ROS : cd 패키지명

$ roscd ros_tutorials_topic  → 패키지 폴더로 이동
$ mkdir msg                  → ros_tutorials_topic 패키지에 msg라는 메시지 폴더를 신규 작성
$ cd msg                     → 작성한 msg 폴더로 이동
$ gedit MsgTutorial.msg      → MsgTutorial.msg 파일 신규 작성 및 내용 수정
	time stamp
	int32 data           → 추가
$ cd ..                      → ros_tutorials_topic 패키지 폴더로 이동

 

Time (메시지 형식), stamp (메시지 이름) : Time 은 현재시간 작성

int32 (메시지 형식), data (메시지 이름) : int32인 어떤 데이터

메시지 타입은 time과 int32 이외에도 bool, int8, int16, float32, string, time, duration 등의 메시지 기본 타입과 ROS 에서 많이 사용되는 메시지를 모아놓은 common_msgs 등도 있다. 여기서는 간단한 예제를 만들어 보기 위한 것으로 time과 int32를 이용하였다.

(참고 : http://wiki.ros.org/msg)


 

5) Publisher 노드 작성

$ cd src                     → ros_tutorials_topic 패키지의 소스 폴더인 src 폴더로 이동
$ gedit 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;
 }

 

ros::init : 노드는 같은 이름 중복 실행 X → 노드 이름 초기화

ros::NodeHandle : NH 선언
ros::Publisher : 퍼블리셔 선언 / 퍼블리셔하는 메시지의 형태 기술 / 토픽의 이름 기술

(가장 중요한 3가지 = 노드이름, 토픽이름, 메시지 형태)

100 : 퍼블리셔 큐 사이즈

더보기

Queue?


 
ROS에서 퍼블리셔의 queue_size는 일반적인 알고리즘에서의 큐(queue)와 유사한 메모리 구조를 가집니다. 퍼블리셔가 메시지를 발행할 때, 이 메시지들은 지정된 크기의 큐에 저장됩니다. 큐의 크기는 queue_size 파라미터로 설정되며, 이 값은 퍼블리셔가 보낼 수 있는 최대 대기 메시지 수를 결정합니다. 만약 큐가 가득 찬 상태에서 새로운 메시지가 들어오면, 가장 오래된 메시지가 삭제되고 새로운 메시지가 추가됩니다. 

 

따라서, queue_size는 퍼블리셔가 발행한 메시지가 서브스크라이버에게 전달되기 전에 얼마나 많은 메시지를 버퍼에 저장할 수 있는지를 결정하며, 이는 일반적인 FIFO(First In, First Out) 방식의 큐와 동일한 메모리 구조를 가집니다.


Hz?

 

주파수의 단위. 1초동안 반복되는 n번의 주기. 즉 10Hz = 0.1초마다 반복.


6) Subscriber 노드 작성

$ roscd ros_tutorials_topic/src  → 패키지의 소스 폴더인 src 폴더로 이동
$ gedit topic_subscriber.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;
 }

 

void msgCallback(const ros_tutorials_topic::MsgTutorial::ConstPtr& msg)

  : ros_tutorials_topic 패키지에서 정의된MsgTutorial 타입의 msg 변수가 명명됨.

더보기

ros.spin()?

 

노드가 종료될 때까지 지속적으로 콜백 함수를 호출하는 역할을 합니다. 이를 통해 노드는 수신된 메시지나 이벤트에 대한 처리를 계속해서 수행할 수 있습니다. ros::spin()은 무한 루프처럼 동작하며, 호출된 이후의 코드는 실행되지 않으므로 주의가 필요합니다.

 

반면에, ros::spinOnce() 함수는 현재까지 큐에 쌓인 콜백 요청을 한 번 처리한 후 즉시 반환됩니다. 이를 통해 사용자는 별도의 반복문과 주기를 설정하여 콜백 함수를 주기적으로 호출할 수 있습니다. 예를 들어, 센서 데이터를 정해진 주기로 업데이트해야 할 때 유용합니다.


노드 내에서의 Number of Publishers and Subscriber?

 

1 By 1        (X)

1 By N       (O)

N1 By N2  (O) / N1 == N2 or N1 != N2

 

 

이후로 오류때문에 진행불가...

아마 ros-noetic package를 얼마나 받았냐의 차이인듯... 다시 해보고 재업하겠습니다ㅠ

 

 

 

 


+ Ubuntu에서 terminator 실행 중 창 수직 분할 커맨드 (Shift+Ctrl+E)가 먹히지 않는 분들은 참고하시오!!

(출처 : https://snowdeer.github.io/mac-os/2020/09/22/ctrl-shift-e-key-on-ubuntu-20p04/ )

 

반응형