Explore ROS Services: Service Client and Service Server Guide
Services provide another means for nodes to communicate with each other to complete tasks. A service consists of two components: the service client and the service server. Client nodes make a remote procedure call to other nodes. Server nodes completes the computation and returns a result. Services are often used for quick tasks that require immediate response.
Client –– A service client raises a service request to handle some computation on its behalf. The client waits for the response from the service server.
Server –– A service server receives the service request, completes the computation and sends a response message back to the client node.
Service Interface
Service messages are described in a .srv
file. One node acts as the
client, which raises a request. Another node acting as the server completes the request
and reverts with a response. Hence, a service file comprises of two messages: one for
the request and another for the response.
You can view the list of available service message types by using
.rosmsg
list
rosmsg list
roscpp/EmptyRequest roscpp/EmptyResponse roscpp/GetLoggersRequest roscpp/GetLoggersResponse roscpp/Logger roscpp/SetLoggerLevelRequest roscpp/SetLoggerLevelResponse roscpp_tutorials/TwoIntsRequest roscpp_tutorials/TwoIntsResponse...
This list contains service message types of predefined message interface definitions.
You can create a message of any of these predefined types such as
roscpp_tutorials/TwoInts
.
This example shows how to use ROS services and create a service request.
Use rosmsg show
to view the structure of the request and the
response.
rosmsg show roscpp_tutorials/TwoIntsRequest
int64 A int64 B
rosmsg show roscpp_tutorials/TwoIntsResponse
int64 Sum
The request contains two integers, A
and B
. The
response will contain the sum of A
and B
in
Sum
.
Create a service request.
addRequest = rosmessage("roscpp_tutorials/TwoIntsRequest")
ROS TwoIntsRequest message with properties: MessageType: 'roscpp_tutorials/TwoIntsRequest' A: 0 B: 0
addRequest.A = int64(10)
ROS TwoIntsRequest message with properties: MessageType: 'roscpp_tutorials/TwoIntsRequest' A: 10 B: 0
addRequest.B = int64(20)
ROS TwoIntsRequest message with properties: MessageType: 'roscpp_tutorials/TwoIntsRequest' A: 10 B: 20
We will use this service request in the upcoming example.
Send Request to Service and Respond to that Request
Call and Provide ROS Services
ROS supports two main communication mechanisms: topics and services. Unlike topics with publishers and subscribers to send and receive messages (see Exchange Data with ROS Publishers and Subscribers), services implement a tighter coupling by allowing request-response communication. A service client sends a request message to a service server and waits for a response. The server will use the data in the request to construct a response message and sends it back to the client. Each service has a type that determines the structure of the request and response messages. Services also have a name that is unique in the ROS network.
This service communication has the following characteristics:
A service request (or service call) is used for one-to-one communication. A single node will initiate the request and only one node will receive the request and send back a response.
A service client and a service server are tightly coupled when a service call is executed. The server needs to exist at the time of the service call and once the request is sent, the client will block until a response is received.
The concept of services is illustrated in the following image:
This example shows you how to set up service servers to advertise a service to the ROS network. In addition, you will learn how to use service clients to call the server and receive a response.
Prerequisites: Get Started with ROS, Connect to a ROS Network, Exchange Data with ROS Publishers and Subscribers
Create Service Server
Before examining service concepts, start the ROS master in MATLAB® and the sample ROS network. exampleHelperROSCreateSampleNetwork
will create some service servers to simulate a realistic ROS network.
rosinit
Launching ROS Core... ...Done in 3.16 seconds. Initializing ROS master on http://192.168.178.1:55438. Initializing global node /matlab_global_node_55791 with NodeURI http://ah-csalzber:51640/
exampleHelperROSCreateSampleNetwork
Suppose you want to make a simple service server that displays "A service client is calling" when you call the service. Create the service using the rossvcserver
command. Specify the service name and the service message type. Also define the callback function as exampleHelperROSEmptyCallback
. Callback functions for service servers have a very specific signature. For details, see the documentation of rossvcserver
.
For faster performance, use services with messages in structure format.
testserver = rossvcserver("/test","std_srvs/Empty",@exampleHelperROSEmptyCallback,"DataFormat","struct")
testserver = ServiceServer with properties: ServiceType: 'std_srvs/Empty' ServiceName: '/test' NewRequestFcn: @exampleHelperROSEmptyCallback DataFormat: 'struct'
You can see your new service, /test
, when you list all services in the ROS network.
rosservice list
/add /matlab_global_node_55791/get_loggers /matlab_global_node_55791/set_logger_level /node_1/get_loggers /node_1/set_logger_level /node_2/get_loggers /node_2/set_logger_level /node_3/get_loggers /node_3/set_logger_level /reply /test
You can get more information about your service using rosservice
info
. The global node is listed as node where the service server is reachable and you also see its std_srvs/Empty
service type.
rosservice info /test
Node: /matlab_global_node_55791 URI: rosrpc://ah-csalzber:51639 Type: std_srvs/Empty Args: MessageType
Create Service Client
Use service clients to request information from a ROS service server. To create a client, use rossvcclient
with the service name as the argument.
Create a service client for the /test
service that we just created.
testclient = rossvcclient("/test","DataFormat","struct")
testclient = ServiceClient with properties: ServiceType: 'std_srvs/Empty' ServiceName: '/test' DataFormat: 'struct'
Create an empty request message for the service. Use the rosmessage
function and pass the client as the first argument. This will create a service request function that has the message type and format that is specified by the service.
testreq = rosmessage(testclient)
testreq = struct with fields:
MessageType: 'std_srvs/EmptyRequest'
Ensure that the service is connected to the client, waiting for them to connect if necessary.
waitForServer(testclient,"Timeout",3)
When you want to get a response from the server, use the call
function, which calls the service server and returns a response. The service server you created before will return an empty response. In addition, it will call the exampleHelperROSEmptyCallback
function and displays the string "A service client is calling". You can also define a Timeout
parameter, which indicates how long the client should wait for a response.
testresp = call(testclient,testreq,"Timeout",3);
If the call function above fails, it will error. Instead of an error, if you would prefer to react to a call failure using conditionals, return the status
and statustext
outputs from the call function. The status
output indicates if the call succeeded, while statustext
provides additional information. Similar outputs can be returned from waitForServer
.
numCallFailures = 0; [testresp,status,statustext] = call(testclient,testreq,"Timeout",3); if ~status numCallFailures = numCallFailues + 1; fprintf("Call failure number %d. Error cause: %s\n",numCallFailures,statustext) else disp(testresp) end
MessageType: 'std_srvs/EmptyResponse'
Create a Service for Adding Two Numbers
Up to now, the service server has not done any meaningful work, but you can use services for computations and data manipulation. Create a service that adds two integers.
There is an existing service type, roscpp_tutorials/TwoInts
, that we can use for this task. You can inspect the structure of the request and response messages by calling rosmsg show
. The request contains two integers, A
and B
, and the response contains their addition in Sum
.
rosmsg show roscpp_tutorials/TwoIntsRequest
int64 A int64 B
rosmsg show roscpp_tutorials/TwoIntsResponse
int64 Sum
Create the service server with this message type and a callback function that calculates the addition. For your convenience, the exampleHelperROSSumCallback
function already implements this calculation. Specify the function as a callback.
sumserver = rossvcserver("/sum","roscpp_tutorials/TwoInts",@exampleHelperROSSumCallback,"DataFormat","struct")
sumserver = ServiceServer with properties: ServiceType: 'roscpp_tutorials/TwoInts' ServiceName: '/sum' NewRequestFcn: @exampleHelperROSSumCallback DataFormat: 'struct'
To call the service server, you have to create a service client. Note that this client can be created anywhere in the ROS network. For the purposes of this example, we will create a client for the /sum
service in MATLAB.
sumclient = rossvcclient("/sum","DataFormat","struct")
sumclient = ServiceClient with properties: ServiceType: 'roscpp_tutorials/TwoInts' ServiceName: '/sum' DataFormat: 'struct'
Create the request message. You can define the two integers, A
and B
, which are added together when you use the call
command.
sumreq = rosmessage(sumclient); sumreq.A = int64(2); sumreq.B = int64(1)
sumreq = struct with fields:
MessageType: 'roscpp_tutorials/TwoIntsRequest'
A: 2
B: 1
The expectation is that the sum of these two numbers will be 3. To call the service, use the following command. The service response message will contain a Sum
property, which stores the addition of A
and B
. Ensure that the service server is available before making a call, and react appropriately if it is not.
if isServerAvailable(sumclient) sumresp = call(sumclient,sumreq,"Timeout",3) else error("Service server not available on network") end
sumresp = struct with fields:
MessageType: 'roscpp_tutorials/TwoIntsResponse'
Sum: 3
Shut Down ROS Network
Remove the sample nodes and service servers from the ROS network.
exampleHelperROSShutDownSampleNetwork
Shut down the ROS master and delete the global node.
rosshutdown
Shutting down global node /matlab_global_node_55791 with NodeURI http://ah-csalzber:51640/ Shutting down ROS master on http://192.168.178.1:55438.
Custom Message Support for ROS Services
ROS also enables the creation of custom message types for services, providing flexibility in exchanging information between nodes based on your application’s specifications and design. For instance, if you require a service message type to handle the addition of 3 integers at once, you can achieve this by defining a custom service message. The request section of this custom service type would then contain 3 integers, compared to the 2 integers in the previous example.
The ROS Toolbox supports custom message generation for services, detailed in ROS Custom Message Support. To
generate ROS custom messages, you can use the rosgenmsg
function to read ROS custom message definitions in the
specified folder path. The designated folder should contain one or more ROS packages
containing service message definitions in .srv
files. Additionally,
you can create a shareable ZIP archive of the generated custom messages. This archive
can be utilized to register the custom messages on another machine by using rosRegisterMessages
.
In MATLAB®, you typically don't write .srv
files directly.
Instead, you create custom messages using ROS package tools and then use them in
MATLAB.
See Also
rosmessage
| rossvcclient
| rossvcserver