본문 바로가기

Robot/Robot Program - ROS

ROS 메세지 - 토픽과 서비스 실습하기

ROS 설치하기 몹시 초보용을 실습[바로가기]한 후 또 여기 저기 자료를 뒤적거리며 따라하기 중인데요. 이번에는 좋은 자료 하나를 소개하면서 또 그걸 따라해볼까 합니다. 물론 표윤석 박사님의 책[바로가기]에도 나와 있지만, slideshare,net에 표윤석 박사님이 자신의 교재에 있는 내용보다 더 좋은 내용(^^)으로 공개하셨더라구요. 참 대단한 오픈마이드죠... ^^ 아무튼 그 slideshare에 ROS의 메세징 방법인 topic, service에 대해 다루는 기초 자료가 있습니다.[바로가기] 오늘은 저도 이 자료를 그대로 따라하려고 합니다^^.

먼저 catkin_create_pkg 명령으로 std_msgs와 roscpp를 사용하는 oroca_ros_tutorials라는 이름의 패키지를 생성합니다. 따라한다는 것을 이미 고백했으니 팩키지 이름도 똑같이 하는 거죠^^.

저는 sublime text를 좋아하니까... subl 명령으로 일단 package.xml을 편집합니다.

이 부분도 수정하고...

더 중요한 이 부분도...

이렇게.. message_generationmessage_runtimebuild_dependrun_depend에 각각 추가합니다. package.xml 전체는 아래와 같습니다.

<?xml version="1.0"?>
<package>
  <name>oroca_ros_tutorials</name>
  <version>0.0.0</version>
  <description>The oroca_ros_tutorials package</description>

  <!-- One maintainer tag required, multiple allowed, one person per tag --> 
  <!-- Example:  -->
  <!-- <maintainer email="jane.doe@example.com">Jane Doe</maintainer> -->
  <maintainer email="pinkwink@todo.todo">pinkwink</maintainer>


  <!-- One license tag required, multiple allowed, one license per tag -->
  <!-- Commonly used license strings: -->
  <!--   BSD, MIT, Boost Software License, GPLv2, GPLv3, LGPLv2.1, LGPLv3 -->
  <license>TODO</license>


  <!-- Url tags are optional, but mutiple are allowed, one per tag -->
  <!-- Optional attribute type can be: website, bugtracker, or repository -->
  <!-- Example: -->
  <!-- <url type="website">http://wiki.ros.org/oroca_ros_tutorials</url> -->


  <!-- Author tags are optional, mutiple are allowed, one per tag -->
  <!-- Authors do not have to be maintianers, but could be -->
  <!-- Example: -->
  <!-- <author email="jane.doe@example.com">Jane Doe</author> -->


  <!-- The *_depend tags are used to specify dependencies -->
  <!-- Dependencies can be catkin packages or system dependencies -->
  <!-- Examples: -->
  <!-- Use build_depend for packages you need at compile time: -->
  <!--   <build_depend>message_generation</build_depend> -->
  <!-- Use buildtool_depend for build tool packages: -->
  <!--   <buildtool_depend>catkin</buildtool_depend> -->
  <!-- Use run_depend for packages you need at runtime: -->
  <!--   <run_depend>message_runtime</run_depend> -->
  <!-- Use test_depend for packages you need only for testing: -->
  <!--   <test_depend>gtest</test_depend> -->
  <buildtool_depend>catkin</buildtool_depend>

  <build_depend>roscpp</build_depend>
  <build_depend>std_msgs</build_depend>
  <build_depend>message_generation</build_depend>

  <run_depend>roscpp</run_depend>
  <run_depend>std_msgs</run_depend>
  <run_depend>message_runtime</run_depend>


  <!-- The export tag contains other, unspecified, tags -->
  <export>
    <!-- Other tools can request additional information be placed here -->

  </export>
</package>

이제 CMakeList.txt도 수정해 주어야 합니다. 아참.. sublime text에서 CMake 파일에 문법을 강조하는 글을 올린적이 있어요.. 한번 보세요~~[바로가기] 아무튼...

역시.. find_package부분에서 message_generation을 추가하고..

위 코드처럼 ros_tutorial_msg_pulishersubscriber를 추가합니다...

그리고 만들어진 팩키지 폴더에서....

msg폴더를 만들어 둡니다.

이제... 토픽에서 publisher를 할 ros_tutorial_msg_publisher.cpp를 만듭니다.

위 코드를 예제에서 사용합니다. 코드는

#include "ros/ros.h"
#include "oroca_ros_tutorials/msgTutorial.h"

int main(int argc, char **argv)
{
	ros::init(argc, argv, "ros_tutorial_msg_publisher");
	ros::NodeHandle nh;

	ros::Publisher ros_tutorial_pub = nh.advertise<oroca_ros_tutorials::msgTutorial>("ros_tutorial_msg", 100);

	ros::Rate loop_rate(10);

	int count = 0;
	while (ros::ok())	
	{
		oroca_ros_tutorials::msgTutorial msg;

		msg.data = count;

		ROS_INFO("send msg = %d", count);

		ros_tutorial_pub.publish(msg);

		loop_rate.sleep();
		++count;
	}
	return 0;
}

입니다. 이 정도 기초적이고 작은 양의 코드는 지금 바로 설명하고 싶지만.. 현재 저는 메세지(토픽과 서비스)의 구동을 따라하기 중이라 어설프게 설명하는 것보다 그냥 넘어가죠^^. 다시 멋지게 공부할 수 있을 겁니다. 그렇게 되어야 또 다음 단계를 넘어가니까요.

이제.. publisher를 만들었으니 subscriber.cpp를 만듭니다. ROS에서는 메세지의 한 종류인 토픽(topic)의 경우 일방적으로 말하는 publisher와 듣는 subscriber가 있는데 지금 그걸 학습중입니다.^^

코드는...

#include "ros/ros.h"
#include "oroca_ros_tutorials/msgTutorial.h"

void msgCallback(const oroca_ros_tutorials::msgTutorial::ConstPtr& msg)
{
	ROS_INFO("recieve msg: %d", msg->data);
}

int main(int argc, char **argv)
{
	ros::init(argc, argv, "ros_tutorial_msg_subscriber");
	ros::NodeHandle nh;

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

	ros::spin();
	
	return 0;
}

입니다.

이제 catkin_ws폴더에서 catkin_make명령으로 빌드하면 됩니다.

그리고 roscore를 실행하고 다른 터미널에서 rosrun oroca_ros_tutorials ros_tutorial_msg_publisher라고 하면... 위 그림처럼 메세지를 찍습니다.

rostopic list로 확인하면 ros_tutorial_msg가 있다는 것을 알 수 있죠.

토픽의 echo 옵션으로 확인할 수 있습니다.

이제 subscriber를 실행해 보죠. 그러면 받은 메세지를 또 확인할 수 있습니다.

rqt_graph로 현태 토픽의 상태와 노드의 상태를 확인해 볼 수 있습니다.

같이 보면, publisher가 보낸 메세지와 이를 수신하고 있는 subscriber의 메세지와 상태를 확인할 수 있네요.

이제 서비스를 실습해 보도록 하겠습니다. 일단 CMake.txt를 좀 더 수정하도록 하죠... 위 그림의 아까 코드에서...

srv_serversrv_client를 추가합니다. 최종적인 CMake.txt파일은 

cmake_minimum_required(VERSION 2.8.3)
project(oroca_ros_tutorials)

find_package(catkin REQUIRED COMPONENTS
  roscpp
  std_msgs
  message_generation
)

add_message_files(FILES msgTutorial.msg)
add_service_files(FILES srvTutorial.srv)

generate_messages(DEPENDENCIES std_msgs)


catkin_package(
	#INCLUDE_DIRS include
	LIBRARIES oroca_ros_tutorials
	CATKIN_DEPENDS roscpp std_msgs
	DEPENDS system_lib
)

include_directories(include ${catkin_INCLUDE_DIRS})

#ros_tutorial_msg_publisher
add_executable(ros_tutorial_msg_publisher src/ros_tutorial_msg_publisher.cpp)
target_link_libraries(ros_tutorial_msg_publisher ${catkin_LIBRARIES})
add_dependencies(ros_tutorial_msg_publisher oroca_ros_tutorials_generate_messages_cpp)

#ros_tutorial_msg_subscriber
add_executable(ros_tutorial_msg_subscriber src/ros_tutorial_msg_subscriber.cpp)
target_link_libraries(ros_tutorial_msg_subscriber ${catkin_LIBRARIES})
add_dependencies(ros_tutorial_msg_subscriber oroca_ros_tutorials_generate_messages_cpp)

#ros_tutorial_srv_server
add_executable(ros_tutorial_srv_server src/ros_tutorial_srv_server.cpp)
target_link_libraries(ros_tutorial_srv_server ${catkin_LIBRARIES})
add_dependencies(ros_tutorial_srv_server oroca_ros_tutorials_generate_messages_cpp)

#ros_tutorial_srv_client
add_executable(ros_tutorial_srv_client src/ros_tutorial_srv_client.cpp)
target_link_libraries(ros_tutorial_srv_client ${catkin_LIBRARIES})
add_dependencies(ros_tutorial_srv_client oroca_ros_tutorials_generate_messages_cpp)

입니다.

그리고 srv라는 폴더안에 srvTutorial.srv를 만들죠~

위와 같이 편집합니다. ---기호 위는 요청(request), 아래는 응답(response)입니다.

그리고 server.cpp를 또 만들어야죠.

#include "ros/ros.h"
#include "oroca_ros_tutorials/srvTutorial.h"

bool calculation(oroca_ros_tutorials::srvTutorial::Request &req, oroca_ros_tutorials::srvTutorial::Response &res)
{
    res.result = req.a + req.b;

    ROS_INFO("request: x=%1d, y=%1d", (long int)req.a, (long int)req.b);
    ROS_INFO("sending back response: [%1d]", (long int)res.result);
    return true;
}

int main(int argc, char **argv)
{
    ros::init(argc, argv, "ros_tutorial_srv_server");
    ros::NodeHandle nh;

    ros::ServiceServer ros_tutorial_service_server = nh.advertiseService("ros_tutorial_srv", calculation);

    ROS_INFO("ready srv server!");

    ros::spin();

    return 0;
}

이렇게 단순히 두 값을 더하는 예제이지만...  srv를 작성하고... 

또.. client를

#include "ros/ros.h"
#include "oroca_ros_tutorials/srvTutorial.h"
#include <cstdlib>

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

    if (argc != 3)
    {
       ROS_INFO("cmd : rosrun ros_tutorial ros_tutorial_service_client arg0 arg1");
       ROS_INFO("arg0 : double number, arg1 : double number");

       return 1;
    }

    ros::NodeHandle nh;

    ros::ServiceClient ros_tutorial_service_client = nh.serviceClient<oroca_ros_tutorials::srvTutorial>("ros_tutorial_srv");
    oroca_ros_tutorials::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 : %1d, %1d", (long int)srv.request.a, (long int)srv.request.b);
       ROS_INFO("recieve srv, srv.Response.result : %1d", (long int)srv.response.result);
    }  
    else
    {
       ROS_ERROR("Failed to call service ros_tutorial_srv");
       return 1;
    }
    return 0;
}

이제...

다시 빌드하고~~~

oroca_tutorials 팩키지안에 ros_tutorial_srv_server를 실행한 다음... ros_tutorial_srv_client를 2, 3으로 옵션을 줘서 실행하면.. 더한 5라는 값을 얻을 수 있네요^^

rosservice call로 확인 가능하구요...

rqt를 실행해서...

service caller를 불러서 역시 같은 방법으로 확인할 수 있습니다.^^ 기초스러운 것을 또 그대~~~~로 따라해 봤네요.. 이제 저도 뭔가 좀 ROS 공부하는 분들께 도움이 되면서 저 스스로에게도 동기를 부여할 수 있는 뭔가를 해야하는데 말이죠.ㅠㅠ. 뭐 아무튼 이렇게 기초스러운 것으로도 ROS를 하고 있다는 기쁨이 더 큽니다^^ 마지막으로...

oroca_ros_tutorials.zip

를 추가합니다.^^

반응형