- 操作系统:ubuntu22.04
- IDE:Visual Studio Code
- 编程语言:C++11
- ROS版本:2
ROS 2(Robot Operating System 2)中的服务是一种允许节点之间进行请求/响应式通信的机制。与话题(Topics)不同,话题主要用于发布/订阅模式的持续数据流传输,而服务更适合于一次性或偶尔的数据交换场景,比如查询状态、执行操作等。
ROS2服务的基本概念
-
服务类型:每个服务都有其类型,这定义了请求和响应消息的数据结构。服务类型由两个部分组成:请求消息类型和响应消息类型。
-
服务客户端(Client):发起请求的一方,发送特定类型的请求并等待响应。
-
服务端(Server):接收请求并处理后返回响应的一方。
-
srv文件:服务的消息格式通过.srv文件定义,位于包的srv/目录下。一个.srv文件定义了一个服务的请求和响应格式。
创建和使用ROS 2服务
定义服务消息
首先,你需要为你的服务创建一个.srv文件。例如,创建一个名为MyService.srv的服务,用于计算两个整数的和:
int64 a
int64 b
---
int64 sum
上面的文件定义了一个接受两个长整型数字作为输入,并返回它们的和的服务。
修改package.xml
确保在package.xml中添加对rosidl_default_runtime以及任何你用到的消息包(如std_msgs)的依赖:
<?xml version="1.0"?>
<package format="3">
<name>robot_nodes</name>
<version>0.0.0</version>
<description>My multi-node ROS 2 package</description>
<maintainer email="76457551@qq.com">User Name</maintainer>
<license>Apache-2.0</license>
<buildtool_depend>ament_cmake</buildtool_depend>
<build_depend>rosidl_default_generators</build_depend>
<exec_depend>rosidl_default_runtime</exec_depend>
<depend>rclcpp</depend>
<depend>std_msgs</depend>
<depend>geometry_msgs</depend>
<depend>example_interfaces</depend>
<member_of_group>rosidl_interface_packages</member_of_group>
<export>
<build_type>ament_cmake</build_type>
</export>
</package>
修改CMakeLists.txt
在CMakeLists.txt中找到find_package命令,并确保它包含了rosidl_default_generators:
cmake_minimum_required(VERSION 3.8)
project(robot_nodes)
# 查找必要的依赖
find_package(ament_cmake REQUIRED)
find_package(rclcpp REQUIRED)
find_package(rosidl_default_generators REQUIRED)
find_package(geometry_msgs REQUIRED)
find_package(example_interfaces REQUIRED) # 假设你使用的是自定义的服务接口
# 生成自定义接口
rosidl_generate_interfaces(${PROJECT_NAME}
"srv/MyService.srv"
DEPENDENCIES # 如果有其他依赖(如 std_msgs)
)
# 添加子目录(每个节点)
add_subdirectory(nodes/node1)
add_subdirectory(nodes/node2)
ament_package()
编写服务端代码
下面是一个简单的服务端示例,它实现了上述AddTwoInts服务:
#include "rclcpp/rclcpp.hpp"
#include "example_interfaces/srv/add_two_ints.hpp" // 引入服务接口
class MinimalService : public rclcpp::Node
{
public:
MinimalService() : Node("minimal_service")
{
service_ = this->create_service<example_interfaces::srv::AddTwoInts>("add_two_ints",
std::bind(&MinimalService::handle_add_two_ints, this, std::placeholders::_1, std::placeholders::_2));
}
private:
void handle_add_two_ints(const example_interfaces::srv::AddTwoInts::Request::SharedPtr request,
const example_interfaces::srv::AddTwoInts::Response::SharedPtr response)
{
response->sum = request->a + request->b;
RCLCPP_INFO(rclcpp::get_logger("rclcpp"), "Incoming request\na: %ld" " b: %ld", request->a, request->b);
RCLCPP_INFO(rclcpp::get_logger("rclcpp"), "sending back response: [%ld]", (long int)response->sum);
}
rclcpp::Service<example_interfaces::srv::AddTwoInts>::SharedPtr service_;
};
int main(int argc, char **argv)
{
rclcpp::init(argc, argv);
auto node = std::make_shared<MinimalService>();
rclcpp::spin(node);
rclcpp::shutdown();
return 0;
}
编写客户端代码
客户端将调用服务来执行请求:
#include "rclcpp/rclcpp.hpp"
#include "example_interfaces/srv/add_two_ints.hpp"
class MinimalClientAsync : public rclcpp::Node
{
public:
MinimalClientAsync() : Node("minimal_client_async"), client_(this->create_client<example_interfaces::srv::AddTwoInts>("add_two_ints"))
{
while (!client_->wait_for_service(std::chrono::seconds(1)))
{
if (!rclcpp::ok())
{
RCLCPP_ERROR(rclcpp::get_logger("rclcpp"), "Interrupted while waiting for the service. Exiting.");
return;
}
RCLCPP_INFO(rclcpp::get_logger("rclcpp"), "service not available, waiting again...");
}
send_request();
}
private:
void send_request()
{
auto request = std::make_shared<example_interfaces::srv::AddTwoInts::Request>();
request->a = 1;
request->b = 2;
using ServiceResponseFuture = rclcpp::Client<example_interfaces::srv::AddTwoInts>::SharedFuture;
auto response_received_callback = [this](ServiceResponseFuture future)
{
auto result = future.get();
RCLCPP_INFO(this->get_logger(), "Result: %ld", result->sum);
};
auto future_result = client_->async_send_request(request, response_received_callback);
}
rclcpp::Client<example_interfaces::srv::AddTwoInts>::SharedPtr client_;
};
int main(int argc, char **argv)
{
rclcpp::init(argc, argv);
auto node = std::make_shared<MinimalClientAsync>();
rclcpp::spin(node);
rclcpp::shutdown();
return 0;
}
运行结果
分别执行两个节点:
node1:
source install/setup.bash
ros2 run robot_nodes node1
node2:
source install/setup.bash
ros2 run robot_nodes node2
终端输出:
ros2 run robot_nodes node1
[2025-05-21 16:51:15:471][node1][info][node1.cpp:58]:==========main===========
[INFO] [1747817521.071953435] [rclcpp]: Incoming request
a: 1 b: 2
[INFO] [1747817521.071991360] [rclcpp]: sending back response: [3]
ros2 run robot_nodes node2
[2025-05-21 16:52:01:064][node2][info][node2.cpp:76]:==========main===========
[INFO] [1747817521.072052185] [minimal_client_async]: Result: 3