3. ROS2服务通信基础
任务目标
本案例将创建两个ROS2节点:一个服务端节点,提供整数加法服务;一个客户端节点,向服务端发送两个整数并接收求和结果。通过此案例掌握ROS2服务通信的完整流程:服务接口定义、服务端实现、客户端调用以及编译运行。
环境依赖
- 已安装ROS2(Humble或更高版本),并完成环境配置(
source /opt/ros/humble/setup.bash) - 已创建ROS2工作空间(如
~/ros2_ws/src)
实现步骤
1. 创建功能包并定义服务接口
在终端执行以下命令创建功能包和自定义服务接口(使用example_interfaces/AddTwoInts也可,但为展示自定义接口流程,此处新建):
cd ~/ros2_ws/src
ros2 pkg create --build-type ament_cmake service_demo
mkdir -p service_demo/srv
创建service_demo/srv/AddTwoInts.srv文件,内容如下:
int64 a
int64 b
---
int64 sum
编辑service_demo/CMakeLists.txt,在find_package部分添加rosidl_default_generators,并在文件末尾添加:
rosidl_generate_interfaces(${PROJECT_NAME}
"srv/AddTwoInts.srv"
)
编辑service_demo/package.xml,添加以下依赖:
<build_depend>rosidl_default_generators</build_depend>
<exec_depend>rosidl_default_runtime</exec_depend>
<member_of_group>rosidl_interface_packages</member_of_group>
2. 服务端节点代码
在service_demo/src/目录下创建server.cpp:
#include <rclcpp/rclcpp.hpp>
#include <service_demo/srv/add_two_ints.hpp>
using AddTwoInts = service_demo::srv::AddTwoInts;
int main(int argc, char** argv) {
rclcpp::init(argc, argv);
auto node = std::make_shared<rclcpp::Node>("add_two_ints_server");
auto server = node->create_service<AddTwoInts>(
"add_two_ints",
[](const std::shared_ptr<AddTwoInts::Request> req,
std::shared_ptr<AddTwoInts::Response> res) {
res->sum = req->a + req->b;
RCLCPP_INFO(rclcpp::get_logger("server"),
"Received: %ld + %ld = %ld", req->a, req->b, res->sum);
});
RCLCPP_INFO(node->get_logger(), "Service server ready.");
rclcpp::spin(node);
rclcpp::shutdown();
return 0;
}
3. 客户端节点代码
在service_demo/src/目录下创建client.cpp:
#include <rclcpp/rclcpp.hpp>
#include <service_demo/srv/add_two_ints.hpp>
#include <chrono>
using AddTwoInts = service_demo::srv::AddTwoInts;
using namespace std::chrono_literals;
int main(int argc, char** argv) {
rclcpp::init(argc, argv);
auto node = std::make_shared<rclcpp::Node>("add_two_ints_client");
auto client = node->create_client<AddTwoInts>("add_two_ints");
// 等待服务上线
while (!client->wait_for_service(1s)) {
if (!rclcpp::ok()) {
RCLCPP_ERROR(node->get_logger(), "Interrupted while waiting for service.");
return 1;
}
RCLCPP_INFO(node->get_logger(), "Waiting for service...");
}
// 构造请求
auto request = std::make_shared<AddTwoInts::Request>();
request->a = 15;
request->b = 27;
// 发送请求并等待响应
auto future = client->async_send_request(request);
if (rclcpp::spin_until_future_complete(node, future) ==
rclcpp::FutureReturnCode::SUCCESS) {
auto response = future.get();
RCLCPP_INFO(node->get_logger(), "Result: %ld + %ld = %ld",
request->a, request->b, response->sum);
} else {
RCLCPP_ERROR(node->get_logger(), "Failed to call service.");
return 1;
}
rclcpp::shutdown();
return 0;
}
4. 编译配置
编辑service_demo/CMakeLists.txt,在rosidl_generate_interfaces之后添加:
add_executable(server src/server.cpp)
ament_target_dependencies(server rclcpp)
target_include_directories(server PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>)
add_executable(client src/client.cpp)
ament_target_dependencies(client rclcpp)
target_include_directories(client PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>)
install(TARGETS server client
DESTINATION lib/${PROJECT_NAME})
编译与运行
cd ~/ros2_ws
colcon build --packages-select service_demo
source install/setup.bash
# 终端1:运行服务端
ros2 run service_demo server
# 终端2:运行客户端
ros2 run service_demo client
验证方法
在客户端终端观察输出:
[INFO] Result: 15 + 27 = 42
服务端终端应显示:
[INFO] Received: 15 + 27 = 42
同时可在第三个终端使用ros2 service list查看服务列表,应包含/add_two_ints;使用ros2 service type /add_two_ints应返回service_demo/srv/AddTwoInts。
常见问题
- 找不到服务接口:确认已执行
source install/setup.bash,且功能包编译成功。 - 客户端等待超时:确认服务端已启动且服务名称一致(均为
add_two_ints)。 - 编译错误:检查CMakeLists.txt中
rosidl_generate_interfaces是否在add_executable之前调用。