Repository navigation
ROS2 Services
Simran Cheema edited this page Oct 2, 2026
·
2 revisions
Services are a request/response communication pattern between nodes. Unlike topics which stream data continuously, a service call is a one-time transaction. One node sends a request and waits for a response.
Use a service when you need a guaranteed response rather than a continuous stream. For example, triggering the arm to move to a home position and confirming it got there, or requesting the current state of a subsystem.
The distinction from topics: topics are for continuous data streams, services are for discrete interactions.
Minimal Server (C++)
#include "rclcpp/rclcpp.hpp"
#include "std_srvs/srv/set_bool.hpp" // Built-in ROS2 service type that takes a bool request
// and returns a success bool + message string
class ArmService : public rclcpp::Node {
public:
ArmService() : Node("arm_service") {
// Create a service named "enable_arm" using the SetBool service type
// SetBool is a simple built-in type — request contains a bool, response contains
// a success bool and a string message. Good for on/off type interactions.
service_ = this->create_service<std_srvs::srv::SetBool>(
"enable_arm",
// Lambda callback that fires when a client sends a request
// request — the data sent by the client (contains request->data, a bool)
// response — the object we fill in and send back to the client
[this](
const std_srvs::srv::SetBool::Request::SharedPtr request,
std_srvs::srv::SetBool::Response::SharedPtr response)
{
if (request->data) {
RCLCPP_INFO(this->get_logger(), "Arm enabled");
response->success = true;
response->message = "Arm is now enabled";
} else {
RCLCPP_INFO(this->get_logger(), "Arm disabled");
response->success = true;
response->message = "Arm is now disabled";
}
// No return needed — response is passed by pointer and filled in place
});
}
private:
rclcpp::Service<std_srvs::srv::SetBool>::SharedPtr service_;
};
int main(int argc, char * argv[]) {
rclcpp::init(argc, argv);
// spin() keeps the node alive and waiting for incoming service requests
rclcpp::spin(std::make_shared<ArmService>());
rclcpp::shutdown();
return 0;
}Minimal Client (C++)
#include "rclcpp/rclcpp.hpp"
#include "std_srvs/srv/set_bool.hpp"
class ArmClient : public rclcpp::Node {
public:
ArmClient() : Node("arm_client") {
// Create a client that connects to the "enable_arm" service
// The service type must match the server exactly — std_srvs::srv::SetBool here
client_ = this->create_client<std_srvs::srv::SetBool>("enable_arm");
// Build the request object and set its fields
auto request = std::make_shared<std_srvs::srv::SetBool::Request>();
request->data = true; // We want to enable the arm
// Services aren't always immediately available — the server node might not
// have started yet. This loop waits and retries every second until it's up.
while (!client_->wait_for_service(std::chrono::seconds(1))) {
RCLCPP_INFO(this->get_logger(), "Waiting for arm service...");
}
// Send the request asynchronously — this doesn't block the node
// The future object can be used to check or wait for the response later
// In a real implementation you'd attach a callback here to handle the response
auto future = client_->async_send_request(request);
}
private:
rclcpp::Client<std_srvs::srv::SetBool>::SharedPtr client_;
};
int main(int argc, char * argv[]) {
rclcpp::init(argc, argv);
rclcpp::spin(std::make_shared<ArmClient>());
rclcpp::shutdown();
return 0;
}