서비스는 메시지 통신은 서비스를 요청하는 서비스 클라이언트(service client)와 서비스 응답(response)을 담당하는 서비스 서버(service server)간의 동기식 양방향 서비스 메시지 통신을 말한다.
결국 서비스는 특정 요청을 하는 클라이언트 단과 요청받은 일을 수행 후에 결괏값을 전달하는 서버 단과의 통신이라고 볼수있다. 서비스 요청 및 응답(Request/Response)또한 토픽(Topic)에서 언급한 msg
인터페이스의 변형으로 srv
인터페이스라고 한다.
서비스는 아래 그림과 같이 동일 서비스에 대해 복수의 클라이언트를 가질수 있도록 설계되었다. 단, 서비스 응답은 서비스 요청이 있었던 서비스 클라이언트에 대해서만 응답을 하는 형태로 Node C
의 Service Client가 Node B
의 Service Server에게 서비스 요청을 하였다면 Node B
의 Service Server는 요청받은 서비스를 수행한 후 Node C
의 Service Client에게만 서비스 응답을 한다.
먼저 turtlesim 패키지의 turtlesim_node 노드를 실행한다.
$ ros2 run turtlesim turtlesim_node
그 뒤 아래와 같이 서비스 목록 확인 명령어로 실행 중인 노드들의 서비스 목록을 확인해보자
$ ros2 service list
/clear
/kill
/reset
/spawn
/turtle1/set_pen
/turtle1/teleport_absolute
/turtle1/teleport_relative
/turtlesim/describe_parameters
/turtlesim/get_parameter_types
/turtlesim/get_parameters
/turtlesim/list_parameters
/turtlesim/set_parameters
/turtlesim/set_parameters_atomically
특정 서비스 형태가 궁금하다면 서비스 형태 확인 명령어인 'ros2 service type'을 이용하면 된다. 예를 들어 아래와 같이 clear서비스는 std_srvs/srv/Empty
형태이고 kill서비스는 turtlesim/srv/kill
형태임을 확인할 수 있다.
$ ros2 service type /clear
std_srvs/srv/Empty
$ ros2 service type /kill
turtlesim/srv/Kill
참고로 서비스 목록 확인 명령어에 옵션으로 '-t'를 붙여주면 서비스 목록과 함께 볼 수 있다.
$ ros2 service list -t
/clear [std_srvs/srv/Empty]
/kill [turtlesim/srv/Kill]
/reset [std_srvs/srv/Empty]
/spawn [turtlesim/srv/Spawn]
/turtle1/set_pen [turtlesim/srv/SetPen]
/turtle1/teleport_absolute [turtlesim/srv/TeleportAbsolute]
/turtle1/teleport_relative [turtlesim/srv/TeleportRelative]
(생략)
이번엔 해당 형태의 서비스를 사용하는 서비스명을 찾는 명령어도 있다.
사용법은 아래와 같이 'ros2 service find' 명령어에 매개변수로 특정 서비스 형태를 적어주면 된다.
$ ros2 service find std_srvs/srv/Empty
/clear
/reset
$ ros2 service find turtlesim/srv/Kill
/kill
이제 실제로 서비스 요청(Request)를 해보자. 서비스 요청은 다음과 같다.
$ ros2 service call <서비스명> <서비스 형태> <서비스 요청 내용>
↓
$ ros2 service call <service_name> <service_type> "<arguments>"
첫번째로 다룰 서비스는 /clear
서비스로 turtlesim 노드를 동작할 때 표시되는 이동 궤적을 지우는 서비스이다.
turtlesim을 이동시키기 위해 아래와 같이 'teleop_key' 노드를 실행시킨 후 방향키를 이용하여 거북이를 이동시켜보자
$ ros2 run turtlesim turtle_teleop_key
그 뒤 아래의 명령어로 clear
서비스를 요청해보자. 실행시키면 위 사진의 이동 궤적이 모두 사라진것을 확인할 수 있다. 아래 명령어에서는 "<arguments>"
가 생략되었는데 이는 std_srvs/srv/Empty 라는 서비스 형태가 아무런 내용이 없는 형태로 사용할 수 있기 때문이다.
$ ros2 service call /clear std_srvs/srv/Empty
requester: making request: std_srvs.srv.Empty_Request()
response:
std_srvs.srv.Empty_Response()
이번에는 /kill
서비스를 요청해보자. kill
서비스는 죽이고자 하는 거북이 이름을 서비스 요청의 내용으로 입력하면 되는데 아래와 같이 turtle1 이라고 이름을 지정하면 아래그림처럼 거북이가 사라짐을 확인 할 수 있다.
$ ros2 service call /kill turtlesim/srv/Kill "name: 'turtle1'"
requester: making request: turtlesim.srv.Kill_Request(name='turtle1')
response:
turtlesim.srv.Kill_Response()
이번에는 /reset
서비스를 요청해보자. 이 서비스는 아래그림과 같이 모든것이 리셋이 되면서 없어졋던 거북이가 다시 등장하거나 이동한 후에 궤적도 사라지고 원점에 재위치 시킨다는것을 확인 할 수 있다.
ros2 service call /reset std_srvs/srv/Empty
requester: making request: std_srvs.srv.Empty_Request()
response:
std_srvs.srv.Empty_Response()
이번에는 /set_pen
서비스를 요청해보자. 이 서비스는 지정한 거북이의 궤적 색과 크기를 변경하는 것으로 아래와 같이 r, g, b
값을 조합하여 색을 지정하고, width
로 궤적의 크기를 지정 할 수 있다.
$ ros2 service call /turtle1/set_pen turtlesim/srv/SetPen "{r: 255, g: 255, b: 255, width: 10}"
requester: making request: turtlesim.srv.SetPen_Request(r=255, g=255, b=255, width=10, off=0)
response:
turtlesim.srv.SetPen_Response()
마지막으로 /spawn
서비스를 요청해보자. 이 서비스는 지정한 위치 및 자세에 거북이를 추가시키게 된다. 이름은 지정하지 않으면 turtle2
처럼 자동으로 지정되며 동일한 이름을 사용 할 수 없다.
아래 예제에서는 기본으로 지정된 turtle1
을 /kill
서비스를 이용해 없애고 닌자거북이 4총사인 leonardo, raffaello, michelangelo, donatello를 생성한 모습이다. 아래와 같이 4마리의 거북이를 볼 수 있다.
$ ros2 service call /kill turtlesim/srv/Kill "name: 'turtle1'"
requester: making request: turtlesim.srv.Kill_Request(name='turtle1')
response:
turtlesim.srv.Kill_Response()
$ ros2 service call /spawn turtlesim/srv/Spawn "{x: 5.5, y: 9, theta: 1.57, name: 'leonardo'}"
requester: making request: turtlesim.srv.Spawn_Request(x=5.5, y=9.0, theta=1.57, name='leonardo')
response:
turtlesim.srv.Spawn_Response(name='leonardo')
$ ros2 service call /spawn turtlesim/srv/Spawn "{x: 5.5, y: 7, theta: 1.57, name: 'raffaello'}"
requester: making request: turtlesim.srv.Spawn_Request(x=5.5, y=7.0, theta=1.57, name='raffaello')
response:
turtlesim.srv.Spawn_Response(name='raffaello')
$ ros2 service call /spawn turtlesim/srv/Spawn "{x: 5.5, y: 5, theta: 1.57, name: 'michelangelo'}"
requester: making request: turtlesim.srv.Spawn_Request(x=5.5, y=5.0, theta=1.57, name='michelangelo')
response:
turtlesim.srv.Spawn_Response(name='michelangelo')
$ ros2 service call /spawn turtlesim/srv/Spawn "{x: 5.5, y: 3, theta: 1.57, name: 'donatello'}"
requester: making request: turtlesim.srv.Spawn_Request(x=5.5, y=3.0, theta=1.57, name='donatello')
response:
turtlesim.srv.Spawn_Response(name='donatello')
토픽 강좌에서는 메시지 인터페이스(message interface, msg)에 대해 알아보았다. 서비스 또한 토픽과 마찬가지로 별도의 인터페이스를 가지고 있는데 이를 서비스 인터페이스라고 부르며, 파일로는 srv
파일을 가르킨다.
예시로 /spawn
서비스 인터페이스를 알아보자. /spawn
의 데이터 타입은 'turtlesim/srv/Spawn' 이므로 'ros2 interface show'명령어를 사용하면 아래와 같이 'turtlesim/srv/Spawn' 은 float32
형태의 x, y, theta
그리고 string
형태로 name
이라고 두개의 데이터가 있음을 알 수 있다. 여기서 중요한것은 '---'
이다. 이는 "구분자"라 부르며 서비스 인터페이스의 서비스 요청과 응답(Request/Response)를 나누어 준다. 즉, x, y, theta, name
은 서비스 요청에 해당되고 ---
아래의 name
은 서비스 응답에 해당이된다.
$ ros2 interface show turtlesim/srv/Spawn.srv
float32 x
float32 y
float32 theta
string name
---
string name
실제로 우리는 /spawn
서비스를 이용할 때 신규 거북이를 생성하기위하여 x, y, theta
로 위치와 자세를 지정하고 name
에 신규 거북이의 이름을 지정하였다.
이번 강좌에서는 서비스에 대해 알아보았다. 다음시간에는 액션에 대해 알아보자.