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 |