티스토리 뷰

Robotics/ROS

ROS service 사용.

Rocknz 2017. 1. 9. 23:54
ROS-Concepts에서 보면 알 수 있듯. 참고 : http://blog.rakjoon.net/entry/ROS-Concepts

service와 message는 큰 차이점이 있다.

물론 message를 2개 잘섞어서 
서버측에서 메시지를 받은후 내가 메시지를 다시 보내는 식으로 설정해 놓으면 결국 service와 같게 만들 수 있다.

그렇다면 왜 service를 만든 것인가 ?

내생각에는.
첫째로는 일반적으로 메시지를 보내고나서 response를 받아야 할 경우가 생각보다 굉장히 많고, 
둘째로는 service를 이용하면 순차적으로 일을 처리하여 sync를 맞출 수 있기 때문이라고 생각한다.

메시지를 다닥다닥 보내고 ros::spin() 으로 대기타면서 메시지가 다닥다닥 돌아오기를 계속 기다리는 것 보다.
하나를 슉 보내고 반응을 확인한뒤 다음 과정을 슉 보내고 할 수 있기 때문이다.

예를 들어보자. 내가 한 serviceClient 를 만들었는데 4개의 advertiseService( server역할 ) 에 각각 들려서 확인을 받고,

모든 4개의 advertiseService에게 확인 사인을 받았을때 OK를 출력하는 프로그램을 만든다고 하자.

이를 일방향의 message를 이용하게되면

1번 한테 메시지를 보내고, 2번 한테 메시지를 보내고, 3번 한테 메시지를 보내고, 4번 한테 메시지를 보낸다.
그리고 내서 답변을 기다리는데 답변이 1번 부터 온다는 보장이 없다. 그렇기 때문에 우리는 전역변수로 몇번 한테 ok메시지가 왔는지 체크를 해주면서 4명한테 다왔는지 확인을 따로 해야한다. 즉 답변의 순서가 없다는 것이다.  ( 물론 억지로 만들 수는 있는데 소스가 굉장히 더러워진다. )

하지만 서비스를 이용하면 1번 한테 메시지를 보내고, ok를 받은 뒤,2번한테 메시지를 보내고, ok를 받은 뒤, 3번한테 메시지를 보내고, ok를 받은 뒤, 4번한테 메시지를 보내고, ok를 받은 뒤 OK 를 출력한다.

후자가 매우 직관적이지 않는가 ? ( 멀티 쓰레드에 익숙하지 않은사람들에게 훨씬 수월하게 코딩할 수 있도록 도와준다. )

이러한 이유로, 서비스를 배워두기로 하자.

service 파일 만들기, CMakeLists 및 package 설정.

service 파일을 만드는 방법과 CMakeLists.txt를 세팅해서 헤더파일로 가져오는 방식은 
만드는 방법을 참고하고, http://wiki.ros.org/ROS/Tutorials/CreatingMsgAndSrv
custom message publish와 비슷한데 그중 add_message_files만 add_service_files로 바꿔만 주면된다.

 AdvertiseService ( Server 역할 )

message와 달리 advertiser가 callback을 기다려야한다. 
message는 advertiser가 메시지를 보내고 끝이였지만, 
service에선 advertiseService를 발행한사람이 쿼리가 오기를 기다리는 서버같은 역할을 한다.
#include "ros/ros.h"
#include "beginner_tutorials/AddTwoInts.h"

bool add(beginner_tutorials::AddTwoInts::Request  &req,
         beginner_tutorials::AddTwoInts::Response &res)
{
  res.sum = req.a + req.b;
  ROS_INFO("request: x=%ld, y=%ld", (long int)req.a, (long int)req.b);
  ROS_INFO("sending back response: [%ld]", (long int)res.sum);
  return true;
}

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

  ros::ServiceServer service = n.advertiseService("add_two_ints", add);
  ROS_INFO("Ready to add two ints.");
  ros::spin();

  return 0;
}

NodeHandle을 통해 service를 발행하고 누가 쿼리를 날려주면 add 함수를 실행하고
정해놓은 service  타입인 beginner_tutorials 패키지의 AddTwoInts.srv로 만들어진 AddTwoInts.h파일을 이용하여 서비스타입을 받아온다. 그중 Request는 client에서 request값들이고 Response는 client로 보내줄 답변 값들이다.


serviceClient ( client 역할 )
#include "ros/ros.h"
#include "beginner_tutorials/AddTwoInts.h"
#include <cstdlib>

int main(int argc, char **argv)
{
  ros::init(argc, argv, "add_two_ints_client");
  if (argc != 3)
  {
    ROS_INFO("usage: add_two_ints_client X Y");
    return 1;
  }

  ros::NodeHandle n;
  ros::ServiceClient client = n.serviceClient<beginner_tutorials::AddTwoInts>("add_two_ints");
  beginner_tutorials::AddTwoInts srv;
  srv.request.a = atoll(argv[1]);
  srv.request.b = atoll(argv[2]);
  if (client.call(srv))
  {
    ROS_INFO("Sum: %ld", (long int)srv.response.sum);
  }
  else
  {
    ROS_ERROR("Failed to call service add_two_ints");
    return 1;
  }

  return 0;
}

NodeHandle을 이용해서 serviceClient에 등로갛고

client.call(srv)로 service request를 보낸다. 
client.call(srv) == true 인경우 전송이 잘되었고, Response값은 srv값에 잘 들어왔다는 뜻이고,

client.call(srv) == false 인경우 전송이 잘 안되었다는 뜻이다.


댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2025/02   »
1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28
글 보관함