AutoCar

본문 바로가기
사이트 내 전체검색


AutoCar
AutoCar

5. 서비스, 서버, 클리이언트 기본

페이지 정보

작성자 관리자 댓글 0건 조회 2,144회 작성일 21-01-12 23:03

본문

5. 서비스, 서버, 클리이언트 기본

1. 서비스 


서비스는 서비스를 요청하는 클라이언트와 요청에 응답을 처리하는 서버간의 동기적 양방향 메시지를 말합니다. 

앞서 토픽을 활용한 통신은 비동기 방식이라 필요에 따라 데이터를 전송하거나 전송받기에 매우 훌륭한 방법입니다. 

하지만 상황에 따라서는 동기방식의 통신방식도 필요합니다. 

ROS 는 동기 방식의 통신 기능을 서비스라는 이름으로 제공합니다. 

서비스는 토픽과 다르게 일회성으로 통신하는 방식입니다. 

클라이언트에서 서비스 요청이 발생하면 서비스를 제공하는 서버와 연결을 진행하고 응답이 완료 되는시점까지 연결을 유지합니다. 

요청에 따른 응답이 완료되면 서버와 클라이언트의 접속은 끊기게 됩니다. 

이러한 서비스는 특정 동작을 수행하도록 요청할 때 명령어로 사용되거나 특정 조건에서 이벤트를 처리하는 경우 사용됩니다. 

또한 일회성 통신으로 토픽 방식에 비해 통신의 부하가 적어 유용하게 사용이 가능합니다.



1.PNG



2. 서비스 서버


서비스를 등록하고 외부에서 서비스를 호출하였을 때 처리할 내용이 담긴 코드를 작성하기 위해 패키지를 생성합니다. 

서비스는 호출시 인자로 숫자를 두개 받아 두 숫자의 합을 반환해주는 서비스를 생성합니다.

이번 실습에서는 catkin 명령을 통해 패키지를 생성하고 빌드하는 과정을 함께 실습해보도록 하겠습니다. 

패키지 생성할 때는 catkin_create_pkg 명령을 사용합니다. 

생성되는 패키지에 rospy 와 std_msgs 패키지를 포함하도록 명령어 작성시 다음과 같이 입력합니다.


jklee@holdings:~/catkin_ws/src$ ls

CMakeLists.txt

jklee@holdings:~/catkin_ws/src$ catkin_create_pkg service_tutorial rospy std_msgs

Created file service_tutorial/package.xml

Created file service_tutorial/CMakeLists.txt

Created folder service_tutorial/src

Successfully created files in /home/jklee/catkin_ws/src/service_tutorial. Please adjust the values in package.xml.

jklee@holdings:~/catkin_ws/src$ cd service_tutorial/

jklee@holdings:~/catkin_ws/src/service_tutorial$



jklee@holdings:~$ catkin_create_pkg service_tutorial rospy std_msgs

Created file service_tutorial/package.xml

Created file service_tutorial/CMakeLists.txt

Created folder service_tutorial/src

Successfully created files in /home/jklee/service_tutorial. Please adjust the values in package.xml.


jklee@holdings:~$ cd service_tutorial/

jklee@holdings:~/service_tutorial$



명령어를 입력하고 나면 service_tutorial 이라는 폴더가 하나 생성됩니다. 
서비스 등록을 위한 각종 소스코드와 내용들은 이 폴더에 작성합니다. 
폴더로 이동하고 첫번째로 서비스 호출시 전달받을 인자를 설정합니다. 
srv 폴더를 생성하고 폴더 내부에 AddInts.srv 파일을 생성하고 내용을 편집합니다.

jklee@holdings:~/catkin_ws/src/service_tutorial$ mkdir srv
jklee@holdings:~/catkin_ws/src/service_tutorial$ vi srv/AddInts.srv

jklee@holdings:~/service_tutorial$ mkdir srv
jklee@holdings:~/service_tutorial$ vi srv/AddInts.srv

int64 a
int64 b
---
int64 sum


AddInts.srv 파일에 작성한 내용은 총 3 개의 변수의 내용을 담고 있습니다. 
먼저 a와 b 는 서비스 호출시 함께 전달하는 인자입니다. 
인자 개수는 호출될 때 꼭 지켜져야 합니다. 
지금 작성하는 예제는 서비스 호출시 2 개의 인자를 함께 전달해야 하는것 입니다. 
sum 은 반환할 변수입니다. 
서비스 동작 이후 호출한 노드에 반환하는 값입니다.

이제 실제로 서비스 등록과 호출시 동작할 소스코드를 작성합니다. 
src 폴더에server.py 를 작성합니다.

jklee@holdings:~/catkin_ws/src/service_tutorial$ vi src/server.py

jklee@holdings:~/service_tutorial$ vi src/server.py
#!/usr/bin/env python3

from service_tutorial.srv import *
import rospy

def add(req):
    return req.a + req.b

def add_server():
    rospy.init_node('add_server')
    s = rospy.Service('add',AddInts,add)
    rospy.spin()

if __name__=="__main__":
    add_server()

코드에서 사용할 rospy 와 앞서 작성한 AddInts.srv 를 import 합니다. 
이후 서비스 호출시 호출될 메소드를 작성합니다. 
여기서는 2 개의 인자를 전달받아 처리할 add 메소드를 작성합니다. 
add 메소드의 전달 인자 req 에는 AddInts.srv 에 작성한 a 와 b가 전달되는 형태입니다. 
메인 루프에서는 노드를 초기화 하며 등록합니다. 
노드의 명칭은 'add_server' 입니다. 
노드 초기화 이후 서비스를 등록합니다. 
서비스의 명칭은 'add' 서비스에 함께 사용할 인자관련 정보는 앞서 작성한 AddInts.srv 를 참조하도록 하고 서비스 호출시 호출될 메소드인 add 메소드를 인자로 전달합니다. 
작성을 완료하고 rosrun 을 통해 작성한 파일이 실행될 수 있도록 실행권한을 부여합니다.


jklee@holdings:~/catkin_ws/src/service_tutorial$ chmod +x src/server.py

jklee@holdings:~/service_tutorial$ chmod +x src/server.py

이제 컴파일을 위한 CMakeLists.txt 파일과 package.xml 파일을 수정합니다.
CMakeLists.txt 에는 컴파일 옵션들을 지정합니다. 
기본적인 내용은 패키지 생성시 미리 작성되어 있습니다. 
서비스 등록을 위한 내용을 추가합니다.

jklee@holdings:~/catkin_ws/src/service_tutorial$ vi CMakeLists.txt

jklee@holdings:~/service_tutorial$ vi CMakeLists.txt
### 생략
find_package(catkin REQUIRED COMPONENTS
  rospy
  std_msgs
  message_generation
)
### 생략
add_service_files(
   FILES
   AddInts.srv
)
### 생략
generate_messages(
   DEPENDENCIES
   std_msgs  # Or other packages containing msgs
)
### 생략


package.xml 파일에는 컴파일 할 때 참조하는 패키지나 실행할 때 참조하는 내용을 입력합니다. 
package.xml 도 CMakeLists.txt 파일과 마찬가지로 패키지 생성시 기본적인 내용은 작성되어 있는상태로 필요한 내용만 추가하거나 주석을 제거하면 됩니다.


jklee@holdings:~/catkin_ws/src/service_tutorial$ vi package.xml

jklee@holdings:~/service_tutorial$ vi package.xml
<!-- 생략 -->
<build_depend>message_generation</build_depend>
<!-- 생략 -->
<exec_depend>message_runtime</exec_depend>
<!-- 생략 -->

컴파일 및 실행을 위한 파일 수정은 완료되었습니다. 
catkin_make 명령을 통해 컴파일을 실행합니다. 

jklee@holdings:~/catkin_ws/src/service_tutorial$ cd ~/catkin_ws/
jklee@holdings:~/catkin_ws$ catkin_make
Base path: /home/jklee/catkin_ws
Source space: /home/jklee/catkin_ws/src
Build space: /home/jklee/catkin_ws/build
Devel space: /home/jklee/catkin_ws/devel
Install space: /home/jklee/catkin_ws/install
####
#### Running command: "cmake /home/jklee/catkin_ws/src -DCATKIN_DEVEL_PREFIX=/home/jklee/catkin_ws/devel -DCMAKE_INSTALL_PREFIX=/home/jklee/catkin_ws/install -G Unix Makefiles" in "/home/jklee/catkin_ws/build"
####
-- Using CATKIN_DEVEL_PREFIX: /home/jklee/catkin_ws/devel
-- Using CMAKE_PREFIX_PATH: /home/jklee/catkin_ws/devel;/opt/ros/melodic
-- This workspace overlays: /home/jklee/catkin_ws/devel;/opt/ros/melodic
-- Found PythonInterp: /usr/bin/python2 (found suitable version "2.7.17", minimum required is "2")
-- Using PYTHON_EXECUTABLE: /usr/bin/python2
-- Using Debian Python package layout
-- Using empy: /usr/bin/empy
-- Using CATKIN_ENABLE_TESTING: ON
-- Call enable_testing()
-- Using CATKIN_TEST_RESULTS_DIR: /home/jklee/catkin_ws/build/test_results
-- Found gtest sources under '/usr/src/googletest': gtests will be built
-- Found gmock sources under '/usr/src/googletest': gmock will be built
-- Found PythonInterp: /usr/bin/python2 (found version "2.7.17")
-- Using Python nosetests: /usr/bin/nosetests-2.7
-- catkin 0.7.28
-- BUILD_SHARED_LIBS is on
-- BUILD_SHARED_LIBS is on
-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-- ~~  traversing 1 packages in topological order:
-- ~~  - service_tutorial
-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-- +++ processing catkin package: 'service_tutorial'
-- ==> add_subdirectory(service_tutorial)
-- Using these message generators: gencpp;geneus;genlisp;gennodejs;genpy
-- service_tutorial: 0 messages, 1 services
-- Configuring done
-- Generating done
-- Build files have been written to: /home/jklee/catkin_ws/build
####
#### Running command: "make -j4 -l4" in "/home/jklee/catkin_ws/build"
####
Scanning dependencies of target std_msgs_generate_messages_eus
Scanning dependencies of target std_msgs_generate_messages_nodejs
Scanning dependencies of target _service_tutorial_generate_messages_check_deps_AddInts
Scanning dependencies of target std_msgs_generate_messages_lisp
[  0%] Built target std_msgs_generate_messages_lisp
[  0%] Built target std_msgs_generate_messages_nodejs
[  0%] Built target std_msgs_generate_messages_eus
Scanning dependencies of target std_msgs_generate_messages_cpp
Scanning dependencies of target std_msgs_generate_messages_py
[  0%] Built target std_msgs_generate_messages_cpp
[  0%] Built target std_msgs_generate_messages_py
[  0%] Built target _service_tutorial_generate_messages_check_deps_AddInts
Scanning dependencies of target service_tutorial_generate_messages_cpp
Scanning dependencies of target service_tutorial_generate_messages_eus
Scanning dependencies of target service_tutorial_generate_messages_nodejs
Scanning dependencies of target service_tutorial_generate_messages_lisp
[ 28%] Generating EusLisp code from service_tutorial/AddInts.srv
[ 28%] Generating Javascript code from service_tutorial/AddInts.srv
[ 42%] Generating C++ code from service_tutorial/AddInts.srv
[ 57%] Generating Lisp code from service_tutorial/AddInts.srv
[ 57%] Built target service_tutorial_generate_messages_nodejs
[ 57%] Built target service_tutorial_generate_messages_lisp
[ 71%] Generating EusLisp manifest code for service_tutorial
Scanning dependencies of target service_tutorial_generate_messages_py
[ 85%] Generating Python code from SRV service_tutorial/AddInts
[100%] Generating Python srv __init__.py for service_tutorial
[100%] Built target service_tutorial_generate_messages_py
[100%] Built target service_tutorial_generate_messages_cpp
[100%] Built target service_tutorial_generate_messages_eus
Scanning dependencies of target service_tutorial_generate_messages
[100%] Built target service_tutorial_generate_messages
jklee@holdings:~/catkin_ws$


컴파일이 완료되고 나면 완료된 내용을 현재 사용중인 쉘에 적용해야 합니다. 
쉘에 변경사항을 적용하지 않는다면 새로 작성한 패키지의 내용을 찾지 못하거나 등록한 서비스의 내용을 정상적으로 확인하지 못할 수 있습니다. 
변경사항은 ~/catkin_ws/devel 폴더에 파일로 존재합니다. 

jklee@holdings:~/catkin_ws$ ls devel/
cmake.lock  lib               local_setup.zsh  _setup_util.py
env.sh      local_setup.bash  setup.bash       setup.zsh
include     local_setup.sh    setup.sh         share
jklee@holdings:~/catkin_ws$


현재 사용중인 쉘의 종류에 따라 적용이 될 수 있도록 파일이 구분되어 있습니다. 
현재 bash 를 사용중입니다. 
따라서 setup.bash파일을 source 명령을 통해 적용합니다.

워크스페이스를 등록합니다.

jklee@holdings:~/catkin_ws$ source devel/setup.bash


이제 프로그램 실행을 위해 분할된 터미널에서 마스터노드를 실행합니다. 
마스터 노드 실행 명령은 roscore 입니다.

jklee@holdings:~$ roscore
... logging to /home/jklee/.ros/log/ae4f275a-5638-11eb-be2e-000000000001/roslaunch-holdings-25146.log
Checking log directory for disk usage. This may take a while.
Press Ctrl-C to interrupt
Done checking log file disk usage. Usage is <1GB.

started roslaunch server http://localhost:35923/
ros_comm version 1.14.7


SUMMARY
========

PARAMETERS
 * /rosdistro: melodic
 * /rosversion: 1.14.7

NODES

auto-starting new master
process[master]: started with pid [25159]

setting /run_id to ae4f275a-5638-11eb-be2e-000000000001
process[rosout-1]: started with pid [25173]
started core service [/rosout]
 

새로 작성한 노드는 마스터 노드가 실행되어 있지 않았다면 정상적으로 실행되지 않습니다. 
마스터 노드 실행된 이후에 마스터노드가 실행되지 않은 두번째 터미널에서 rosrun 명령을 통해 컴파일한 패키지를 실행합니다.

jklee@holdings:~/catkin_ws$ rosrun service_tutorial server.py


패키지를 실행하고나면 터미널에는 별다른 메시지가 출력되지 않습니다. 
아무것도 실행되고 있지 않은 세번째 터미널에서 rosservice list 명령을 이용하면 현재 ros 에서 활용 가능한 서비스의 목록이 출력됩니다. 
새로 작성한 add 서비스가 동작중인 것을 확인할 수 있습니다.

jklee@holdings:~$ rosservice list
/add
/add_server/get_loggers
/add_server/set_logger_level
/rosout/get_loggers
/rosout/set_logger_level


서비스를 호출하여 사용할 때는 rosservice call 명령을 활용합니다. 
인자로 호출할 서비스의 명칭과 함께 전달할 인자를 같이 입력하면 동작중인 서비스에 전달하여 결과값을 터미널에 출력합니다.

jklee@holdings:~$ rosservice call /add 1 2
sum: 3
 

3. 서비스 클라이언트

앞서 작성한 서비스를 활용하고 이를 rosservice 명령을 활용하지 않고 프로그램을
통해 호출하고 반환된 값을 화면에 출력하도록 합니다. 서버와 마찬가지로 src 폴더에
프로그램을 작성합니다.

jklee@holdings:~$ cd catkin_ws/src/service_tutorial/
jklee@holdings:~/catkin_ws/src/service_tutorial$ vi src/client.py
#!/usr/bin/env python3
 
import rospy
import sys
from service_tutorial.srv import *

def service_client(x, y):
    rospy.wait_for_service('/add')
    try:
        add_method = rospy.ServiceProxy('/add',AddInts)
        return add_method(x,y)
    except rospy.ServiceException as e:
        print("Service call failed : %s"%e)

if len(sys.argv) == 3:
    a = int(sys.argv[1])
    b = int(sys.argv[2])
    print(service_client(a,b))
else:
    print("ex) rosrun service_tutorial 1 2")


jklee@holdings:~/catkin_ws/src/service_tutorial$ chmod +x src/client.py

서비스 클라이언트는 프로그램을 실행할 때 인자를 함께 전달하도록 작성되어 있습니다. 
더하기를 수행할 두개의 숫자를 함께 입력하면 숫자를 받아 service_client() 메소드에 전달합니다. 
서비스 클라이언트는 wait_for_service() 메소드를 통해 서비스가 등록되어 있는지 체크하고 등록되어 있지 않다면 대기합니다. 
원하는 서비스가 등록되어 있다면 서비스를 호출하고 호출한 서비스에 인자를 전달하여 반환된 값을 수신합니다. 
만약 서비스가 제대로 호출되지 않는다면 에러메시지를 화면에 출력합니다.

프로그램 작성이 완료되면 rosrun 명령을 통해 서비스 클라이언트를 실행합니다. 
실행할 때 인자를 전달하지 않는다면 다음과 같은 메시지가 출력됩니다.

jklee@holdings:~/catkin_ws/src/service_tutorial$ cd
jklee@holdings:~$ rosrun service_tutorial client.py
ex) rosrun service_tutorial 1 2

인자를 함께 전달하여 호출하면 인자로 전달한 2 개의 숫자를 더한 값을 화면에 출력합니다. 
만약 문자를 입력하게 되면 입력한 값의 타입이 맞지 않아 에러를 출력합니다.

jklee@holdings:~$ rosrun service_tutorial client.py 20 30

실행시 아래 오류가 나오면 netifaces 패키지를 설치하고 실행한다.

ModuleNotFoundError: No module named 'netifaces'

root로 로그인 한 후 설치하고 다시 실행하면 됩니다.
root@holdings:~# pip3 install netifaces

jklee@holdings:~$ rosrun service_tutorial client.py 20 30
sum: 50

 
rospack find service_tutorial 로 방금 만든 팩키지를 찾아보면 찾아 집니다.

jklee@holdings:~$ rospack find service_tutorial
/home/jklee/catkin_ws/src/service_tutorial

service_tutorial와 첫 번째 depend관계를 찾는 명령이 rospack depends1입니다.

jklee@holdings:~$ rospack depends1 service_tutorial
[rospack] Error: the rosdep view is empty: call 'sudo rosdep init' and 'rosdep update'

jklee@holdings:~$ sudo rosdep init
[sudo] password for jklee:
jklee is not in the sudoers file.  This incident will be reported.
jklee@holdings:~$ exit
logout
root@holdings:~# vi /etc/sudoers
# User privilege specification
root    ALL=(ALL:ALL) ALL
jklee   ALL=(ALL:ALL) ALL

root@holdings:~# su - jklee
jklee@holdings:~$ sudo rosdep init
[sudo] password for jklee:
ERROR: default sources list file already exists:
        /etc/ros/rosdep/sources.list.d/20-default.list
Please delete if you wish to re-initialize
jklee@holdings:~$ rosdep update
reading in sources list data from /etc/ros/rosdep/sources.list.d
Skip end-of-life distro "ardent"
Skip end-of-life distro "bouncy"
Skip end-of-life distro "crystal"
Add distro "dashing"
Skip end-of-life distro "eloquent"
Add distro "foxy"
Skip end-of-life distro "groovy"
Skip end-of-life distro "hydro"
Skip end-of-life distro "indigo"
Skip end-of-life distro "jade"
Add distro "kinetic"
Skip end-of-life distro "lunar"
Add distro "melodic"
Add distro "noetic"
Add distro "rolling"
updated cache in /home/jklee/.ros/rosdep/sources.cache
jklee@holdings:~$


jklee@holdings:~$ rospack depends1 service_tutorial
message_runtime
rospy
std_msgs


package.xml을 열어보겠습니다. 
package.xml안에는 패키지의 이름과 버젼, 그리고 간략한 설명을 적을 수 있습니다.
팩키지 관리자의 연락처, license 종류도 명시할 수 있습니다.

jklee@holdings:~$ cd catkin_ws/src/service_tutorial/
jklee@holdings:~/catkin_ws/src/service_tutorial$ ls
CMakeLists.txt  package.xml  src  srv
jklee@holdings:~/catkin_ws/src/service_tutorial$ vi package.xml
<?xml version="1.0"?>
<package format="2">
  <name>service_tutorial</name>
  <version>0.0.0</version>
  <description>The service_tutorial package</description>
  <maintainer email="jklee@todo.todo">jklee</maintainer>
  <license>TODO</license>

 

댓글목록

등록된 댓글이 없습니다.


개인정보취급방침 서비스이용약관 모바일 버전으로 보기 상단으로

TEL. 063-469-4551 FAX. 063-469-4560 전북 군산시 대학로 558
군산대학교 컴퓨터정보공학과

Copyright © www.leelab.co.kr. All rights reserved.