from palette import colorful_colors

[ROS] 기본 개념, Publisher와 Subscriber 실습(with c++ 코드) 본문

Embedded, 제어/ROS

[ROS] 기본 개념, Publisher와 Subscriber 실습(with c++ 코드)

colorful-palette 2023. 3. 22. 23:27

1. 기본개념

ROS(Robot Operating System):

  • 로봇(터틀봇 등)을 build할 때 사용하는 소프트웨어 라이브러리와 tool등을 모아둔 집합이다. 리눅스 계열만 지원하며, 현재 ROS1, ROS2, ROS-industrial 버전이 있다.
  • Meta-Operating-System으로, 기존 OS에 분산 컴퓨팅 자원을 활용한다. 또한 우리가 사용하는 General purpose OS(리눅스, 윈도우)가 아니고, 기존 운영체제에 추가적인 설치를 수행한다.
  • 로봇 제어의 표준이 되어, 어플리케이션간(로봇 또는 노드) 유기적 통신을 가능하게 하고 ROS 시스템을 통해 시스템 변경이 필요할 때 일부 모듈만 수정해도 쉽게 제어할 수 있다.

Node:

실행 가능한 최소한의 단위 ex) 도어락 시스템이라면 번호입력, 문개방 등이 하나의 노드

 

Package

여러 개의 노드가 합쳐진, 노드 실행을 위한 정보집

 

Master

ROS1에서 roscore 세팅하며, 노드들간의 연결을 도와준다.

 

Message

노드들이 주고받는 메시지. 일방향 통신인 topic, 양방향 통신인 service 있다.

 

Publisher

메시지를 뿜는 노드

 

Subscriber

메시지를 받는 노드

 

(아래 실습 포스팅은 catkin_ws가 있다고 가정하고 실습합니다)

아직 없으신 분들은 https://colorful-palette.tistory.com/33 의 1-3 참고하기!

 

2. Publisher 실습

터미널에 다음 명령어를 입력해 catkin_ws/src로 이동해준다

cd ~/.catkin_ws/src

 

 

터미널에 다음 명령어를 입력해 talker라는 패키지를 만들어준다

catkin_create_pkg talker std_msgs rospy roscpp

 

~/.catkin_ws/src/talker/src 로 이동해주고 gedit으로 cpp파일을 만들고 열어준다. (혹은 code . 하고 talker.cpp 파일 만들기)

cd ~/.catkin_ws/src/talker/src
gedit talker.cpp

 

 

https://raw.githubusercontent.com/ros/ros_tutorials/kinetic-devel/roscpp_tutorials/talker/talker.cpp

위 링크 안의 내용을 그대로 복사 붙여넣기하여 talker.cpp 파일에 붙여주고 저장한다.

 

 

talker 디렉토리로 다시 이동 후 Cmake 파일을 연다. 

cd ~/catkin_ws/src/talker
gedit Cmakelists.txt

파일의 맨 밑에 다음 줄을 추가한다.

add_executable(talkersrc/talker.cpp)
target_link_libraries(talker${catkin_LIBRARIES})

 

이후 아래와 같이 cm 명령어로(catkin_make 단축키) 빌드하고 sb를 한다. (해당 명령어를 모르겠거나 없다고 에러가 뜨면 https://colorful-palette.tistory.com/33 의 1-3 참고!)

cm 또는 cd ~/catkin_ws && catkin_make
sb 또는 source ~/.bashrc

 

이후 터미널에서 roscore를 실행한 후, 

roscore

 

ctrl + alt + T 로 새 터미널 창을 열어 다음 명령어를 입력한다.(talker 패키지의 talker 노드를 실행하겠다는 뜻)

rosrun talker talker

 

제대로 토픽을 뿜어내는 것을 볼 수 있다.

 

3. Sublisher 실습

위의 publisher 노드 생성과 과정이 비슷하다. 

터미널에 다음 명령어를 입력해 catkin_ws/src로 이동해준다

cd ~/.catkin_ws/src

 

터미널에 다음 명령어를 입력해 listener라는 패키지를 만들어준다

catkin_create_pkg listener std_msgs rospy roscpp

 

~/.catkin_ws/src/talker/src 로 이동해주고 gedit으로 cpp파일을 만들고 열어준다. (혹은 code . 하고 listener.cpp 파일 만들기)

cd ~/.catkin_ws/src/talker/src
gedit listener.cpp

 

https://raw.githubusercontent.com/ros/ros_tutorials/kinetic-devel/roscpp_tutorials/listener/listener.cpp

위 링크 안의 내용을 그대로 복사 붙여넣기하여 listener.cpp 파일에 붙여주고 저장한다.

 

 

listener 디렉토리로 다시 이동 후 Cmake 파일을 연다. 

cd ~/catkin_ws/src/listener
gedit Cmakelists.txt

파일의 맨 밑에 다음 줄을 추가한다.

add_executable(listener src/listener.cpp)
target_link_libraries(listener${catkin_LIBRARIES})

 

 

여기서 이전 talker 파일에 변화를 주자! talker.cpp 파일에 아래쪽을 살펴보면  ROS_INFO 문장이 있을건데, 이걸 앞에 //을 붙여 주석처리해준다.

gedit ~/catkin_ws/src/talker/src/talker.cpp
그 다음 ROS_INFO("%s", msg.data.c_str()); 주석처리하기

 

 

이후 아래와 같이 cm 명령어로(catkin_make 단축키) 빌드하고 sb를 한다. (해당 명령어를 모르겠거나 없다고 에러가 뜨면 https://colorful-palette.tistory.com/33 의 1-3 참고!)

cm 또는 cd ~/catkin_ws && catkin_make
sb 또는 source ~/.bashrc

 

 

이후 터미널에서 roscore를 실행한 후, 

roscore

 

ctrl + alt + T 로 새 터미널 창을 열어 다음 명령어를 입력한다.(talker 패키지의 talker 노드를 실행하겠다는 뜻)

rosrun talker talker

 

또 ctrl + alt + T 로 새 터미널 창을 열어 다음 명령어를 입력한다.(talker 패키지의 talker 노드를 실행하겠다는 뜻)

rosrun listener listener

 

 

왼쪽이 roscore, 우측위가 talker노드 실행, 우측 아래가 listener노드를 실행한 모습이다.

talker는 ROS_INFO 부분을 주석처리했으니 콘솔에 출력이 없고, 반면 우측 아래의 listener는 받은 토픽을 잘 출력하는 것을 볼 수 있다.

 

4. 코드 설명 

참고하실분은 참고하셔도 됩니다.

 

 

talker.cpp 코드 설명:

#include "ros/ros.h"
#include "std_msgs/String.h"
#include <sstream>
int main(int argc, char **argv)
{
  ros::init(argc, argv, "talker");  //ros 초기화, 세번째 인수는 노드의 이름
  ros::NodeHandle n;  //ROS시스템과 통신을 위한 main access point다. nodehandle이 파괴되면 노드가 닫힌다. 
  ros::Publisher chatter_pub = n.advertise<std_msgs::String>("chatter", 1000);
//ROS에게 주어진 이름에 대해 ROSmaster에게 publish하고 싶다고 알린다. 이후 master노드는 이 topic으로 subscribe 받고 싶은 모두에게 알리고 peer-to-peer연결 
// chatter라는 이름으로 1000의 대기열만큼 advertise한다.

ros::Rate loop_rate(10);  //프로그램이 loop에서 돌아갈때 빈도수 설정. 여기선 1초에 10번 돌아간다.
  int count = 0;
  while (ros::ok()) //ROS가 계속 실행되는 한 계속 실행됨
  {
    std_msgs::String msg;   //msg 유형 변수 선언. 노드 간에 문자열 메시지를 보내기 위한 유형이다.
    std::stringstream ss;   //보낼 메시지를 작성하는데 사용한다.
    ss << "hello world " << count;      // stringstream에 "hello world"를 추가하고 count의 현재 값을 추가한다. 
    msg.data = ss.str();                // 메시지를 str로 바꿔준다.
   ROS_INFO("%s", msg.data.c_str());  //콘솔에 메시지를 출력하는 함수다. msg변수에 저장된 메시지를 콘솔에 출력한다. 
    chatter_pub.publish(msg); //msg변수에 저장된 메시지를 연결된 ROS 주제에 개시한다. 메시지는 이 노드를 subscribe하는 모든 노드로 전송된다.

    ros::spinOnce();  //노드가 메인 스레드를 차단하지 않고 topic 및 서비스에서 수신한 대기 중인 메시지를 지속적으로 처리할 수 있도록한다.
    loop_rate.sleep();    //위에서 설정한 loop_rate만큼 실행하기 위해 잠시 멈춘다.
    ++count;
  }
  return 0;
}

 

 

listener.cpp 코드 설명: 

#include "ros/ros.h"
#include "std_msgs/String.h"
void chatterCallback(const std_msgs::String::ConstPtr& msg)
{
  ROS_INFO("I heard: [%s]", msg->data.c_str());
}
// "chatter" 토픽에 대한 메시지가 수신될 때마다 실행될 함수. std_msgs::String::ConstPtr 인수는 "chatter" 항목에서 받은 메시지에 대한 공유 포인터.
// ROS_INFO로 "I heard: [받은 메시지]"를 뿜어준다. 

int main(int argc, char **argv)
{
  
   
  ros::init(argc, argv, "listener");

 
  ros::NodeHandle n;

  
  ros::Subscriber sub = n.subscribe("chatter", 1000, chatterCallback);  
  //대기열 크기가 1000인 "chatter" topic을 subscribe하고 함수를 "chatterCallback"으로 설정한다.
  //메시지가 "chatter topic" 으로 게시될때마다 "chatterCallback"함수가 호출된다. 
 
 ros::spin();  //기본적으로 노드가 종료될 때까지 무기한 실행되는 루프

  return 0;
}