반응형

ROS에서 서비스 통신은 아래와 같은 시스템을 갖는다.

 

즉 서버는 항상 유지된 상태로 Client의 요청이 있으면 이에 대한 응답을 진행하는 동기(Syncronized)방식으로 구성된다.

 

이를 위해선 Client의 요청을 기다리는 Server Node와 해당 서버노드에 정보를 요청하는 Client Node가 필요하다.

 

따라서 본 게시물에서는 ROS환경에서 Server Node와 Client Node를 생성하여 실행시켜 본다.

 

 

우선 catkin_ws 이름의 디렉토리를 생성하고 해당 디렉토리 내에 src 디렉토리를 생성한다.

 

$ mkdir catkin_ws  && cd catkin_ws && mkdir src

 

그리고 src경로로 이동 후 아래 명령어를 입력하여 패키지를 생성한다.

 

$ cd catkin_ws/src

$ catkin_create_pkg beginner_tutorials rospy roscpp

 

여기까지 되었으면 한번 catkin_make를 통해 빌드해주고 source 해주자.

 

빌드 후 catkin_ws/src/beginner_tutorials 경로에 srv 디렉토리를 하나 생성한 후 아래와 같이 AddTwoInts.srv 파일을 생성한다.

 

<AddTwoInts.srv>

int64 a
int64 b
---
int64 sum

여기서 int64로 선언된 a와 b는 Client에서 Request할때 서버로 전달 될 파라미터를 정의하는 부분이고

sum은 서버에서 a와 b를 통해 작업을 수행한 결과를 Client로 Response 해주기 위한 부분으로 볼 수 있다.

 

여기서 Request로 정의된 변수들과 Response로 정의된 변수들은 "---" 로 구분짓게 된다.

 

 

그럼 이제 본격적으로 Client와 Server Node에 대한 source code를 작성해보자.

 

이는 catkin_ws/src/beginner_tutorials/src 폴더에 add_two_client.cpp 와  add_two_server.cpp 파일로 작성한다.

 

//// add_two_client.cpp 파일의 code

#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;
}
//// add_two_server.cpp 파일의 code

#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;
}

 

 

src 파일의 추가가 완료되었으면 이를 빌드해주기 위한 CMakeLists.txt 파일의 내용을 수정해주어야 한다.

 

CMakeLists.txt 파일에 아래 내용을 추가하자.

 

find_package(catkin REQUIRED COMPONENTS
    message_generation
)

add_service_files(
    FILES
    AddTwoInts.srv
)

catkin_package(
    CATKIN_DEPENDS message_runtime
)

 

여기서 AddTwoInts.srv 는 앞에서 작성한 .srv 파일의 이름을 넣어준다.

 

그리고 이제 마지막으로 노드를 실행하기 위해 실행가능한 노드의 이름과 source 코드를 지정해주는 부분이다.

 

add_executable(add_two_ints_server src/add_two_server.cpp)
target_link_libraries(add_two_ints_server ${catkin_LIBRARIES})
add_dependencies(add_two_ints_server beginner_tutorials_gencpp)

add_executable(add_two_ints_client src/add_two_client.cpp)
target_link_libraries(add_two_ints_client ${catkin_LIBRARIES})
add_dependencies(add_two_ints_client beginner_tutorials_gencpp)

여기까지 진행되었으면 다시 catkin_make를 통해 빌드를 진행하고 source 해준다.

 

 

이제 server node와 client node를 실행시켜보자.

 

 

$ roscore

 

$ rosrun beginner_tutorials add_two_ints_server

$ rosrun beginner_tutorials add_two_ints_client 534 124

 

위의 명령어를 차례대로 입력하면

add_two_ints_server 노드가 먼저 실행되면서 Client의 Request를 기다리는 상황이 되고

add_two_ints_client 노드가 534, 124의 값을 서버로 전달함으로서 서버에서는

Request받은 534, 124의 값을 통해 연산을 진행한 후 그 결과(sum)를 반환(response)한다.

 

 

그리고 이떄 server_node.cpp 에서

ros::spin()  의 함수 대신

 

while(1){

  ros::spinOnce();

  ros::Duration(1.0).sleep();

}

 

위의 문장으로 바꿔봤는데 이때에는 client의 요청을 1초에 하나씩 받아서 처리된다.

즉 clinet에서 request값을 받은 후 서버로 요청하여 client.call(srv) 과정에 멈춰있다.

그리고 server로 부터 응답을 받으면 바로 client.call(srv) 후의 과정을 진행하게 된다.

 

즉, 클라이언트에서 아무리 빨리 정보를 request 하더라도 서버에서 spinOnce()를 사용할 경우

한번의 callback만 처리하므로 서버의 응답을 계속 대기하다가 응답을 받으면 다음 요청을 진행하게 된다.

 

 

서버의 spinOnce() 가 실행되기 이전에 client에서 요청이 있었으면 processing을 진행하여 client로 reponse를 해준다.

 

client에서 request  ->  spinOnce()   ->   server processing   ->    server response  ->   client got response  의 흐름이다.

 

그럼 서버에서 데이터 처리 후 1.0초의 딜레이를 갖는데 서버의 함수에서 1.5초의 딜레이를 갖게되면 어떻게 될까?

서버에서는 client의 request를 확인하고 1.5초동안 함수에서 진행 후 빠져나와 1.0초를 대기하고 다시 client의 request를 확인한다.

 

즉, spinOnce 함수를 사용하면 서버의 processing이 모두 완료 되어야지만 다음 request를 진행할 수 있다.

서버도 request받은 내용을 모두 처리한 후 response 해야지만 다음 request를 받을 수 있다.

 

 

반응형

+ Recent posts