본문 바로가기

FOSCAR-(Autonomous Driving)/ROS 스터디

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

반응형

ros 프로그래밍 전 알아둬야 할 사항 (메시지 통신에 중요)

정회전 : 시계 반대 방향

토픽

ros에서 사용하는 단방향 메시지 통신. 송신측이 publisher, 수신측이 subscriber.

 

cd ~/catkin_ws/src

cd change directory (이동할 경로)

~ : 리눅스 - home user 안에 있는 폴더(?)

 

1)  패키지 생성

$ cd ~/catkin_ws/src -> 폴더 이동

$ catkin_create_pkg ros_tutorials_topic message_generation std_msgs roscpp

->

ros_tutorials_topic이라는 패키지 생성

의존성 추가

message_generation 사용자 정의 메시지를 생성할 때 필요

std_msgs ROS의 기본 메시지 타입을 사용하기 위해 필요

roscpp C++로 노드를 작성할 것이므로 필요

 

$ cd ros_tutorials_topic

$ ls -> (list) 현재 폴더에 있는 파일들을 보여준다

 

include 헤더 파일 폴더

src 소스 코드 폴더

CMakeLists.txt 빌드 설정 파일

package.xml 패키지 설정 파일

 

(패키지를 구성할 때 가장 기본이 되어야할 파일들)

 

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

-> 패키지 이름, 저작자, 라이선스, 의존성 패키지 등을 수정

 

<?xml version="1.0"?>

-> 특정 버전 이상부터 사용 가능 표시
<package>
<name>ros_tutorials_topic</name>

-> 패키지의 이름
<version>0.1.0</version>
<description>ROS turtorial package to learn the topic</description>
-> 패키지의 간략한 설명 2~3-> 나중에 자동변환되어 wiki에 검색 키워드로 사용.
<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> - 그냥 catkin 사용한다(?)

 

의존성 설정 부분 (다른 패키지의 기능을 가져와서 사용해야 하는 경우)
<build_depend>roscpp</build_depend>

C++로 노드를 작성할 것이므로 필요
<build_depend>std_msgs</build_depend>

기본적인 ROS 메시지 타입을 사용하기 위해 필요
<build_depend>message_generation</build_depend>

사용자 정의 메시지(Custom Message)만들 때 필요


<run_depend>roscpp</run_depend>

C++ 기반의 ROS 프로그램을 실행하려면 필요
<run_depend>std_msgs</run_depend>

기본 메시지 타입 사용을 위해 필요
<run_depend>message_runtime</run_depend>

사용자 정의 메시지를 실행할 때 필요

 

 

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

-> 노드를 빌드할 때 사용되는 옵션들

 

cmake_minimum_required(VERSION 2.8.3)

project(ros_tutorials_topic)

-> 패키지명

find_package(catkin REQUIRED COMPONENTS message_generation std_msgs roscpp)

-> 캐킨 빌드를 할 때 요구되는 구성 요소 패키지

-> 의존성 패키지로 message_generation, std_msgs, roscpp이며 이 패키지들이 존재하지 않으면 빌드 도중에 에러 발생

 

add_message_files(FILES MsgTutorial.msg)

-> 메시지 선언 : MsgTutorial.msg

generate_messages(DEPENDENCIES std_msgs)

-> 위에서 선언한 메시지에는 스탠다는 메시지가 포함된다 (의존성이 있다는 뜻)

catkin_package

(

LIBRARIES ros_tutorials_topic

CATKIN_DEPENDS std_msgs roscpp

)

-> 캐킨 패키지 옵션으로 라이브러리, 캐킨 빌드 의존성, 시스템 의존 패키지를 기술한다.

 

include_directories(${catkin_INCLUDE_DIRS})

-> 헤더 파일을 포함할 디렉터리를 설정. (include파일을 사용하려면 뒤에 include를 적어준다)

 

## topic_publisher 노드에 대한 빌드 옵션이다.

add_executable(topic_publisher src/topic_publisher.cpp)

-> 토픽을 보내는 publisher 노드를 만들고 만들 때 srctopic_publisher라는 cpp을 참고

add_dependencies(topic_publisher ${${PROJECT_NAME}_EXPORTED_TARGETS}

${catkin_EXPORTED_TARGETS})

-> 이를 통해 topic_publisher가 올바르게 빌드되기 전에 필요한 의존성이 먼저 처리되도록 한다.

target_link_libraries(topic_publisher ${catkin_LIBRARIES})

-> topic_publisher 실행 파일을 빌드할 때 필요한 라이브러리를 링크하는 명령

 

## topic_subscriber 노드에 대한 빌드 옵션이다.

add_executable(topic_subscriber src/topic_subscriber.cpp)

-> subscriber 노드를 만들고, 만들 때 뒤에 나와있는 cpp 참고.

add_dependencies(topic_subscriber ${${PROJECT_NAME}_EXPORTED_TARGETS}

${catkin_EXPORTED_TARGETS})

target_link_libraries(topic_subscriber ${catkin_LIBRARIES})

 

최종 정리

CMakeLists.txt 파일은 ROS 패키지의 빌드 시스템 설정을 정의합니다.

find_package()로 의존성 패키지를 찾고,

add_message_files()로 사용자 정의 메시지를 설정하며,

generate_messages()로 메시지를 생성합니다.

catkin_package()로 패키지에 대한 설정을 지정하고,

add_executable()로 노드를 빌드하며,

target_link_libraries()로 라이브러리들을 링크합니다.

 

CMakeLists.txtpackage.xml 파일에서 노드에 대한 빌드 설정만 해준 것이기 때문에, 실제로 노드가 어떤 작업을 수행할지, 어떻게 동작할지에 대한 세부 구현은 별도로 작성해야 한다. (아래 4, 5, 6)

 

4) 메시지 파일 작성

$mkdir msg ros_tutorials_topic 패키지에 msg라는 메시지 폴더를 신규 작성

$cd msg 작성한 msg 폴더로 이동

$gedit MsgTutorial.msg MsgTutorial.msg 파일 신규 작성 및 내용 수정

time(메시지 형식) stamp(메시지 이름) -> 메시지 보낼 때 현재 시간

int32(메시지 형식) data(메시지 이름) -> 어떠한 데이터

이제 MsgTutorial.msg 파일은 발행된 시간과 정수 데이터를 포함하는 메시지를 정의하게 된다.

 

5) 퍼블리셔 노드 작성

$cd src

ros_tutorials_topic 패키지의 소스 폴더인 src폴더로 이동

$gedit topic_publisher.cpp

소스 파일 신규 작성 및 내용 수정

 

#include "ros/ros.h"

-> ROS 기본 헤더 파일 (중요)

#include "ros_tutorials_topic/MsgTutorial.h“

-> 앞서 작성한 MsgTutorial.msg 파일을 사용하기 위한 헤더 파일(빌드 후 자동 생성됨)

 

int main(intargc, char **argv) // 노드 메인 함수

{

ros::init(argc, argv, "topic_publisher");

-> 노드명 초기화 (노드는 고유한 이름을 가져야 하므로, 이 이름을 통해 다른 노드와 구분)

ros::NodeHandlenh;

-> 노드와 ROS 시스템 간의 통신을 담당하는 객체

 

ros::Publisherros_tutorial_pub

= nh.advertise<ros_tutorials_topic::MsgTutorial>("ros_tutorial_msg", 100);

->

ros::Publisher ros_tutorial_pub: 퍼블리셔 객체를 선언. 퍼블리셔는 MsgTutorial 메시지를 "ros_tutorial_msg"라는 토픽에 발행.

advertise<ros_tutorials_topic::MsgTutorial>("ros_tutorial_msg",100): "ros_tutorial_msg"라는 토픽 이름을 가진 메시지를 발행하는 퍼블리셔를 생성합니다.

100은 퍼블리셔의 큐(queue) 사이즈로, 발행된 메시지가 처리되기 전에 큐에 대기하는 최대 메시지 개수를 의미.

 

ros::Rate loop_rate(10);

-> 루프 주기를 설정한다. "10" 이라는것은10Hz를 말하는 것으로 0.1초간격으로 반복된다

 

ros_tutorials_topic::MsgTutorial msg;

-> 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); (ROS_INFOprintf와 같은 개념)

ROS_INFO("send msg = %d", msg.stamp.nsec);

ROS_INFO("send msg = %d", msg.data);

 

ros_tutorial_pub.publish(msg); // 메시지를 발행한다.

 

loop_rate.sleep();

++count;

}

 

return 0;

}

 

 

 

6) 서브스크라이버 노드 작성

 

#include "ros/ros.h"

-> ROS 기본헤더파일

#include "ros_tutorials_topic/MsgTutorial.h"

-> MsgTutorial 메시지 파일 헤더(빌드 후 자동 생성됨)

 

void msgCallback(constros_tutorials_topic::MsgTutorial::ConstPtr& msg)

{

ROS_INFO("recievemsg= %d", msg->stamp.sec); // stamp.sec 메시지를 표시한다

ROS_INFO("recievemsg= %d", msg->stamp.nsec); // stamp.nsec 메시지를 표시한다

ROS_INFO("recievemsg= %d", msg->data); // data 메시지를 표시한다

}

msgCallback 함수: 서브스크라이버가 구독하는 토픽에서 메시지를 수신하면 이 함수가 콜백 함수로 호출

msg->stamp.secmsg->stamp.nsec은 퍼블리셔 노드에서 보낸 현재 시간을 나타내며, msg->data는 수신한 데이터 값

ROS_INFO는 수신된 메시지의 내용을 출력하는 데 사용. 이는 printf와 비슷한 역할 수행.

 

int main(int argc, char **argv)

{

ros::init(argc, argv, "topic_subscriber");

-> 노드명 초기화

ros::NodeHandle nh;

-> ROS 시스템과 통신을 위한 노드 핸들 선언

 

ros::Subscriber ros_tutorial_sub = nh.subscribe("ros_tutorial_msg", 100, msgCallback);

-> 이 노드가 서브스크라이버 노드의 역할을 함을 표시함.

"ros_tutorial_msg"는 서브스크라이버가 구독할 토픽의 이름. 퍼블리셔에서 발행한 메시지가 이 토픽으로 전송.

100은 큐(queue) 사이즈로, 수신한 메시지를 대기할 수 있는 최대 개수를 설정.

msgCallback은 메시지를 수신할 때 호출될 콜백 함수.

 

ros::spin();

-> 메시지 수신 대기 및 콜백 호출

 

return 0;

}

 

7) ROS 노드 빌드 (cm)

 

cf) 실행파일은 builddevel쪽에 있음

 

8) 퍼블리셔 실행

 

서비스

: ROS에서 양방향 통신이 필요할 때 사용. 요청이 있을 때만 응답하는 서비스 서버요청하고 응답받는 서비스 클라이언트로 나뉨.

 

1) 패키지 생성

$ cd ~/catkin_ws/src

$ catkin_create_pkgros_tutorials_servicemessage_generationstd_msgsroscpp

 

$ cd ros_tutorials_service

$ ls (현재 폴더에 있는 파일과 폴더 목록 보여줌)

include 헤더파일폴더

src 소스코드폴더

CMakeLists.txt 빌드설정파일

package.xml 패키지설정파일

 

 

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

$ gedit package.xml

 

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

$ gedit CMakeLists.txt

서비스 선언

의존하는 메시지 설정 (std_msgs)

캐킨 패키지 옵션으로 라이브러리, 캐킨 빌드 의존성, 시스템 의존 패키지를 기술

실행파일노드 생성 (서비스 서버, 서비스 클라이언트)

 

4) 서비스 파일 작성

$ roscd ros_tutorials_service 패키지 폴더로 이동

$ mkdirsrv 패키지에 srv라는 서비스 폴더를 신규 작성

$ cdsrv 작성한 srv폴더로 이동

$ geditSrvTutorial.srv SrvTutorial.srv파일 신규 작성 및 내용 수정

 

int64 a

int64 b

--

int64 result

int64 (메시지형식), a, b (서비스요청: request), result (서비스응답: response),

‘---’ (요청과 응답을 구분하는 구분자)

 

5) 서비스 서버 노드 작성

src 폴더의 service_server.cpp라는 파일을 빌드하여 service_server라는 실행 파

일을 만들라는 이야기

$ roscd ros_tutorials_service/src 패키지의 소스 폴더인 src폴더로 이동

$ gedit service_server.cpp 소스 파일 신규 작성 및 내용 수정

 

6) 서비스 클라이언트 노드 작성

$ roscd ros_tutorials_service/src 패키지의 소스 폴더인 src폴더로 이동

$ geditservice_client.cpp 소스 파일 신규 작성 및 내용 수정

 

7) ROS 노드 빌드

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

 

8) 서비스 서버 실행

rosrun ros_tutorials_service service_server

 

9) 서비스 클라이언트 실행

rosrun ros_tutorials_service service_client 2 3

23은 각각a, b 값으로 서비스를 요청하게 되고, 결과값으로 둘의합인 5를 전송받았다.

 

cf)

서비스는 일회성 이므로 rqt_graph를 확인할 수 없다

"rosservice call”이나 ServiceCaller를 이용해 서비스 클라이언트 노드를 실행 가능하다

 

cf) 하나의 노드는 복수의 퍼블리셔, 서브스크라이버, 서비스 서버, 서비스 클라이언트 역할이 가능하다

 

 

 

파라미터 (매개변수)

roscore를 실행하면 ros마스터 역할과 rosout에서 ros관련 log역할도 하면서 파라미터 역할도 한다.

 

외부의 변수를 통해 내부의 프로세서를 변화시키는 과정 (+ -> +, -, /, *)

-> service.cpp 소스를 수정하여 사용한다.

 

1) 파라미터를 활용한 노드 작성

수정된 부분

while (1)

{

nh.getParam("calculation_method", g_operator);

ros::spinOnce();

r.sleep();

}

-> 연산자를 매개변수로부터 받은 값으로 변경한다.

2) 노드 빌드 및 실행

3) 매개변수 목록 보기

 

 

 

roslaunch

rosrun 하나의 노드를 실행하는 명령을 하는 명령어지만,  roslaunch 하나 이상 정해진 노드를 실행시킨다.

패키지의 매개변수나 노드이름 변경, 노드 네임 스페이스 설정, 환경 변수 변경 등의 옵션을 붙일 수 있는 명령어.

 

roslaunch 사용법

$ roscd ros_tutorials_topic

$ mkdir launch

$ cd launch

$ gedit union.launch

 

<launch>

<node pkg="ros_tutorials_topic" type="topic_publisher" name="topic_publisher1"/>

<node pkg="ros_tutorials_topic" type="topic_subscriber" name="topic_subscriber1"/>

<node pkg="ros_tutorials_topic" type="topic_publisher" name="topic_publisher2"/>

<node pkg="ros_tutorials_topic" type="topic_subscriber" name="topic_subscriber2"/>

</launch>

 

$ roslaunch ros_tutorials_topic union.launch --screen

-> 실행

 

cf) info, error 표시가 되지 않기 때문에 screen 사용하여 출력한다.

 

문제점)

퍼블리셔의 12의 서로의 메시지를 서브스크라이브 12가 모두 서브스크라이브하고 있다.

-> 그룹이라는 태그를 넣는다.

<launch>

<group ns="ns1">

<node pkg="ros_tutorials_topic" type="topic_publisher" name="topic_publisher"/>

<node pkg="ros_tutorials_topic" type="topic_subscriber" name="topic_subscriber"/>

</group>

<group ns="ns2">

<node pkg="ros_tutorials_topic" type="topic_publisher" name="topic_publisher"/>

<node pkg="ros_tutorials_topic" type="topic_subscriber" name="topic_subscriber"/>

</group>

</launch>

그룹을 사용 안한 경우

 

그룹을 사용한 경우

 

그 외의 launch tag

반응형