[ROS] ROS 시작하기 및 구조 이해 1

1. ROS 시작하기

ROS kinetic    : ~raspberry pi 3, ~Ubuntu 16.04
ROS melodic : raspberry pi 4~, Ubuntu 18.04~, Jetson nano

ROS 버전은 보드나 Ubuntu 버전에 따라 다르고 그 이후 실행 방법은 같습니다.

kinetic install : wiki.ros.org/kinetic/Installation/Ubuntu
melodic install : wiki.ros.org/melodic/Installation/Ubuntu

설치 후 ROS 실행을 위해 다음 명령어들을 실행시키면 됩니다.

$ sudo apt-get install python-rosinstall
$ source /opt/ros/kinetic(or melodic)/setup.bash
$ mkdir -p ~/catkin_ws/src
$ cd ~/catkin_ws/src
$ catkin_init_workspace
$ cd ~/catkin_ws
$ catkin_make

그러면 catkin_ws안에 build, devel, src 폴더가 생성되어야 합니다.

$ source ~/catkin_ws/devel/setup.bash

설치가 정상적으로 되었음을 확인하기 위해 아래 명령어를 터미널 창에 입력해줍니다. (종료 : Ctrl + C)

$ roscore

또한 turtlebot을 사용하시는 분들은 필요한 패키지를 다운받기 위해  ~/catkin_ws/src 안에 git clone합니다.

$ git clone https://github.com/ROBOTIS-GIT/turtlebot3.git

 

2. ROS 개발 환경

자주 사용하는 명령어를 단축 명령어로 만드는 과정입니다.

$ gedit ~/.bashrc

bashrc파일을 열고 마지막 줄에 다음 내용을 추가해 줍니다.

# Set ROS Kinetic(or Melodic)
source /opt/ros/kinetic/setup.bash
source ~/catkin_ws/devel/setup.bash

# Set ROS Network
export ROS_HOSTNAME=xxx.xxx.xxx.xxx
export ROS_MASTER_URI=http://xxx.xxx.xxx.xxx:11311 (roscore를 실행할 pc의 ip주소)

# Set ROS alias command
alias cw='cd ~/catkin_ws'
alias cs='cd ~/catkin_ws/src'
alias cm='cd ~/catkin_ws && catkin_make'

저장 후 다시 터미널 창으로 들어와 bashrc의 내용을 반영하기 위한 명령어를 입력합니다.

$ source ~/.bashrc

 

3. ROS 구조 이해하기

- 노드(Node) : ROS에서 실행되는 최소 단위의 프로세서. 하나의 실행 가능한 프로그램

- 패키지 : ROS를 구성하는 기본 단위. ROS 응용 프로그램은 패키지 단위로 개발되며 패키지는 최소한 하나 이상의 노드를 포함하거나 다른 패키지의 노드를 실행하기 위한 설정 파일들을 포함한다.

- catkin : ROS의 빌드 시스템. CMake(Cross Platform Make)를 이용하고 있어 패키지 폴더에 CMakeList.txt라는 파일에 빌드 환경을 작성한다.

- roscore : ROS 마스터를 구동하는 명령어. 패키지들을 실행하기 전에 꼭 실행해야한다.

- rosrun : ROS의 기본 실행 명령어. 패키지에서 하나의 노드를 실행하는 데 사용됨.

- roslaunch : 여러 노드를 실행할 때 사용됨.

- package.xml : 패키지의 정보를 담은 파일. 패키지 이름, 저작자, 라이센스, 의존성 패키지 등의 정보가 있다.

메시지 통신

- 토픽(Topic)
토픽은 단방향 메시지 송수신 방식으로 정보(topic)를 송신하는 Publisher와 정보(topic)를 수신하는 Subscriber로 구성되어 있다. 정보들은 토픽 메시지 형태로 되어있고, 한 번의 접속으로 지속해서 메시지를 송수신하기 때문에 지속해서 메시지를 발송하는 센서 데이터에 적합하다. 토픽을 송수신하는 방법은 publish하는 토픽의 이름(ex. /ros_tutorial_msg)을 그대로 subscribe하면 된다.

어떤 식으로 통신을 주고 받는지 우선 이해합니다. 패키지를 만들고 CMakeList.txt 등을 작성하는 방법은 다음 글에서 소개해 드리도록 하겠습니다.

Publisher

#include "ros/ros.h"
#include "ros_tutorials/MsgTutorial.h"

int main(int argc, char** argv)
{
	ros::init(argc, argv, "Node1");  //노드 이름을 Node1으로 초기화
    ros::NodeHandle nh;  // ROS 시스템과 통신을 위한 노드 핸들 선언
    
    //Publisher
    // ros_tutorials 패키지의 MsgTutorial 메시지 파일을 이용한 ros_tutorials_pub 작성
    // 토픽명은 "ros_tutorial_msg", 큐 사이즈 100
    //advertise(광고해서 사람들이 구독하게 한다고 생각하자)
    ros::Publisher ros_tutorial_pub = nh.advertise<ros_tutorial::MsgTutorial>("ros_tutorial_msg", 100);
    
    ...
    
}

Subscriber

#include "ros/ros.h"
#include "ros_tutorial/MsgTutorial.h"

// 메시지를 수신하였을 때 동작하는 함수
// ConstPtr -> const pointer
void msgCallback(const ros_tutorial::MsgTutorial::ConstPtr& msg);

int main(int argc, char** argv)
{
	ros::init(argc, argv, "Node2");
    ros::NodeHandle nh;
    
    //Subscriber
    ros::Subscriber ros_tutorial_sub = nh.subscribe("ros_tutorial_msg", 100, msgCallback);
    
    ...
}

void msgCallback(const ros_tutorial::MsgTutorial::ConstPtr& msg)
	ROS_INFO("recieve msg = %d", msg->stamp.sec); //msg 구조체 안의 stamp.sec 값을 표시한다.

 

- 서비스(Service)
서비스는 양방향 통신이나 일회성 메시지 통신이다. 따라서 서비스의 요청과 응답이 완료되면 연결된 두 노드의 접속이 끊긴다. 서버와 클라이언트로 이루어져 있으며 클라이언트가 서비스를 요청하면 서버가 응답을 한 후 통신이 완료된다.

Server

#include "ros/ros.h"
#include "ros_tutorial_service/SrvTutorial.h"

// 서비스 요청이 있을 경우 아래의 처리를 수행한다
bool calculation(ros_tutorial_service::SrvTutorial::Request &req, ros_tutorial_service::SrvTutorial::Response &res)
{
	res.result = req.a + req.b; // 서비스 요청 시 받은 a와 b값을 더하여 서비스의 응답값에 저장한다.
    return true;
}

int main(int argc, char** argv)
{
	ros::init(argc, argv, "server");
    ros::NodeHandle nh;
    
    // Service
    // ros_tutorial_service 패키지의 SrvTutorial 서비스 파일을 이용한 선언
    // 서비스명은 ros_tutorial_srv이며 서비스 요청이 있을 때 calculation 함수를 실행
    ros::ServiceServer ros_tutorial_service_server = nh.advertiseService("ros_tutorial_srv", calculation);
    
    ...
}

Client

#include "ros/ros.h"
#include "ros_tutorial_service/SrvTutorial.h"
#include <cstdlib>

int main(int argc, char** argv)
{
	ros::init(argc, argv, "client");
    ros::NodeHandle nh;
    
    ros::ServiceClient ros_tutorial_service_client = nh.serviceClient<ros_tutorial_service::SrvTutorial>("ros_tutorial_srv");
    
    //srv라는 이름으로 SrvTutorial 서비스 파일을 이용하는 서비스 선언
    ros_tutorial_service::SrvTutorial srv;
    
    srv.request.a = atoll(argv[1]);
    srv.request.b = atoll(argv[2]);
    
    if(ros_tutorial_service_client.call(srv))
    {
    	ROS_INFO("send srv, srv.Request.a and b: %ld, %ld", (long int)srv.request.a, (long int)srv.request.b);
        ROS_INFO("receive srv, srv.Response.result: %ld", (long int)srv.response.result);
    }
    
    ...
}

 

- 액션

액션 메시지 통신은 요청 처리 후 응답까지 오랜 시간이 걸리고 중간 결과값이 필요한 경우에 사용되는 메시지 통신방식이다. 서비스와 비슷하지만 액션은 중간 결과값이 필요할 때마다 데이터를 전송한다는 차이점이 있다. 액션도 서버와 클라이언트로 구성되어 있다.
액션은 따로 코드를 첨부하지 않겠습니다.

 

참고자료 : ROS 로봇 프로그래밍(개정보증판)