ROS2 Services - Uvic-Robotics-Club/uvic-rover-2027 GitHub Wiki

What are Services?

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;
}
⚠️ **GitHub.com Fallback** ⚠️