본문 바로가기

ROS

[ Server / Client ] 서버 클라이언트 작성법 + Custom service file 작성법

ROS의 Server / Client 노드를 작성하는 법을 알아보자.

먼저, Server와 Client를 사용하기 위해 Service file을 작성해야 한다. 이를 위해 저번에 만들어 놓았던 message pkg인 study_msgs에 들어가서 CMakeLists.txt (경로 : ~/catkin_ws/src/study_msgs/)를 다음과 같이 수정해 준다.

cmake_minimum_required(VERSION 3.0.2)
project(study_msgs)


find_package(catkin REQUIRED COMPONENTS
  std_msgs
  message_generation
)


add_message_files(
  FILES
  my_msg.msg
)

#####서비스 파일 추가 ######
add_service_files(
  FILES
  my_service.srv
)
############################

generate_messages(
  DEPENDENCIES
  std_msgs 
)

catkin_package(
   CATKIN_DEPENDS std_msgs message_runtime
)

include_directories(
  ${catkin_INCLUDE_DIRS}
)

여기서 add_service_files()를 보면 custom message를 만들때와 마찬가지로 my_service.srv라는 서비스 파일을 추가 해 주었으므로 my_service.srv를 만들어 주어야 한다.

ROS의 service file은 srv라는 폴더에 만들도록 약속이 되어 있으므로 message pkg에 srv폴더를 다음과 같이 만들어 준다.

~/catkin_ws/src/study_msgs $ mkdir srv
~/catkin_ws/src/study_msgs$ cd srv
~/catkin_ws/src/study_msgs/srv $

이제 여기서 my_service.srv를 작성해 주면 된다.

ROS의 service는 Request 와 Response 부분으로 나뉘어져 있다. 이를 service file 에서는 '---'으로 구분하고, '---' 위쪽이 Request , 아래 쪽이 Response 에 해당한다. 이에 대한 my_service.srv 예시는 아래와 같다.

int32 mode
float64 data
---
float64 result_1
float64 result_2

자료형은 기존 message file을 작성할때의 자료형을 사용하며, .msg로 만들어준 custom message 자료형도 가능하다. 그에 대한 예시는 다음과 같다.

study_msgs/my_msgs to_do
sensor_msgs/Imu data
---
geometry_msgs/Twist result

이렇게 작성한 후의 파일 구조는 다음과 같다.

~/catkin_ws |-src    |-study_msgs     |-msg            |-my_msg.msg
            |        |                |-srv            |-my_srv.srv
            |        |                |-CMakeLists.txt
            |        |                |-package.xml
            |        |                |-include
            |        |          
            |        |-study_pkg      |-src            |-talker.cpp
            |        |                |                |-listener.cpp
            |        |                |-CMakLists.txt
            |        |                |-package.xml
            |        |                |-include
            |        |-CMakeLists.txt
            |-devel
            |-build

이상태에서 빌드를 하게 되면 custom service file을 만들게 되었다.

이제 이를 이용해서 service node와 client node를 작성해 보자.

먼저, study_pk의 CMakeLists.txt를 다음과 같이 수정해준다.

cmake_minimum_required(VERSION 3.0.2)
project(study_pkg)

add_compile_options(-std=c++11)

find_package(catkin REQUIRED COMPONENTS
  study_msgs
  roscpp
  std_msgs
)

catkin_package(
  INCLUDE_DIRS include
  LIBRARIES study_pkg
  CATKIN_DEPENDS study_msgs roscpp std_msgs
  DEPENDS system_lib
)

include_directories(
 include
  ${catkin_INCLUDE_DIRS}
)

add_executable(talker src/talker.cpp)
add_dependencies(talker ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})
target_link_libraries(talker
   ${catkin_LIBRARIES} 
)

add_executable(listener src/listener.cpp)
add_dependencies(listener ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})
target_link_libraries(listener
   ${catkin_LIBRARIES} 
)

##### server file 추가
add_executable(server src/server.cpp)
add_dependencies(server ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})
target_link_libraries(server
   ${catkin_LIBRARIES} 
)

##### client file 추가
add_executable(client src/client.cpp)
add_dependencies(client ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})
target_link_libraries(client
   ${catkin_LIBRARIES} 
)

CMakeLists.txt에서 client.cpp와 server.cpp를 추가해 주었으니 이제 src 폴더 안에 server.cpp , client.cpp 코드를 작성해 주어야 한다.

server.cpp

#include "ros/ros.h"
#include "study_msgs/my_service.h"
#include <iostream>
#include <cmath>

bool server_fun(study_msgs::my_service::Request &req, study_msgs::my_service::Response &res);

int main(int argc,char** argv){
    ros::init(argc,argv,"SERVER_NODE");
    ros::NodeHandle nh;
    ros::ServiceServer server = nh.advertiseService("study_server",server_fun);
    ROS_INFO("SERVER IS READY...");
    while(ros::ok()){
        ros::spinOnce();
    }
    return 0;
}

bool server_fun(study_msgs::my_service::Request &req, study_msgs::my_service::Response &res){
    if(req.mode == 0){
        ROS_INFO("THIS MODE IS POW and SQRT");
        res.result_1 = pow(req.data,2);
        res.result_2 = sqrt(req.data);
    }
    else if(req.mode==1){
        ROS_INFO("THIS MODE IS DOUBLE and HALF");
        res.result_1 = req.data*2;
        res.result_2 = req.data/2;
    }
    else{
    	ROS_INFO("MODE ERROR!!");
        return false;
    }
    return true;
}

client.cpp

#include "ros/ros.h"
#include "study_msgs/my_service.h"
#include <iostream>

using namespace std;

int main(int argc, char** argv){
    ros::ServiceClient client = nh.serviceClient<study_msgs::my_service>("study_server");
    study_msgs::my_service service_msg;
    while(ros::ok()){
    	cout<<"Mode : ";
        cin>>service_msg.request.mode;
        cout<<"Data :";
        cin>>service_msg.request.data;
        ROS_INFO("TRY TO REQUEST...");
        if(client.call(service_msg)){
           ROS_INFO("RESULT1 : %f , RESULT2 : %f",
                     service_msg.response.result_1,service_msg.response.result_2);
        }
        else{
        	ROS_ERROR("SERVER ERROR");
        }
    }
    return 0;
}

위의 코드를 살펴보면, server.cpp에서 콜백 함수인 server_fun은 ros::spinOnce()에서 이 클라이언트에 대해 서비스 요청이 있을 경우 실행이 되는 함수이다.

server_fun의 매개변수를 보면 study_msgs::my_service::Request 와 study_msgs::my_service::Response 가 주어지는데, 이는 service file을 작성할때 '---' 기준으로 만들어진 Request 변수들과 Response들이다. 이는 이 함수가 불려질때 자동 대입 된다.

client.cpp를 살펴보면 client.call(service_msg)를 사용한다. 이 함수는 서버에 service_msg를 건네주고 서비스를 요청한다. 만약 정상적으로 서비스가 진행되고 server의 server_fun이 true를 반환하면 true를 반환하게 되고, server_fun이 false를 반환하거나 서버가 응답이 없으면 false를 반환하게 된다.

이렇게 코드를 다 작성하게 되면 파일 구조는 다음과 같다.

~/catkin_ws |-src    |-study_msgs     |-msg            |-my_msg.msg
            |        |                |-srv            |-my_srv.srv
            |        |                |-CMakeLists.txt
            |        |                |-package.xml
            |        |                |-include
            |        |          
            |        |-study_pkg      |-src            |-talker.cpp
            |        |                |                |-listener.cpp
            |        |                |                |-server.cpp
            |        |                |                |-client.cpp
            |        |                |-CMakeLists.txt
            |        |                |-package.xml
            |        |                |-include
            |        |-CMakeLists.txt
            |-devel
            |-build

이제 catkin_make로 빌드를 하고, 다음과 같이 명령어를 치면 client와 server를 실행 시킬 수 있다.

rosrun study_pkg server
rosrun study_pkg client

추가적으로 rostopic 과 같이 임의로 server를 테스트 하는 방법이 존재한다.

rosservice 로 server에 대한 역할들을 할 수 있으나 이는 추후 업데이트 할 예정이다.

포스팅 끝.

 

 

틀린거나 이상한거 댓글 달아줘요.

'ROS' 카테고리의 다른 글

[ROS] Move Base (3) Move Base Code 분석  (0) 2022.02.21
[ROS] Move Base (2) Velocity & Acceleration  (0) 2022.02.18
[ROS] Move Base (1) Navigation Stack  (2) 2022.02.18
[ROS] XmlRpc 사용법  (0) 2022.02.14
[ROS] plugin 사용법  (0) 2022.02.14