3. ROS2服务通信基础

任务目标

实现一个ROS2加法服务:服务器节点提供add_two_ints服务,接收两个整数请求并返回求和结果;客户端节点发送请求并打印响应。同时演示使用ROS2命令行工具直接调用该服务。

环境准备

需要已安装ROS2(Humble或更新版本),并创建好工作空间。本案例使用C++实现,需确保已安装ros-humble-desktop或ros-humble-ros-base及C++编译工具链。

实现代码

创建功能包并编写服务器与客户端节点。以下为完整实现:

# 创建工作空间与功能包(若已存在可跳过)
mkdir -p ~/ros2_ws/src
cd ~/ros2_ws/src
ros2 pkg create --build-type ament_cmake --dependencies rclcpp example_interfaces add_two_ints_service

# 编辑 src/add_two_ints_service/src/server.cpp
cat > src/add_two_ints_service/src/server.cpp << 'EOF'
#include "rclcpp/rclcpp.hpp"
#include "example_interfaces/srv/add_two_ints.hpp"

using AddTwoInts = example_interfaces::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"), 
                       "收到请求: %ld + %ld = %ld", req->a, req->b, res->sum);
        });
    
    RCLCPP_INFO(node->get_logger(), "加法服务已启动,等待请求...");
    rclcpp::spin(node);
    rclcpp::shutdown();
    return 0;
}
EOF

# 编辑 src/add_two_ints_service/src/client.cpp
cat > src/add_two_ints_service/src/client.cpp << 'EOF'
#include "rclcpp/rclcpp.hpp"
#include "example_interfaces/srv/add_two_ints.hpp"
#include <chrono>

using AddTwoInts = example_interfaces::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(), "等待服务时被中断");
            return 1;
        }
        RCLCPP_INFO(node->get_logger(), "等待服务上线...");
    }
    
    auto request = std::make_shared<AddTwoInts::Request>();
    request->a = 15;
    request->b = 27;
    
    auto future = client->async_send_request(request);
    
    // 等待响应(超时5秒)
    if (future.wait_for(5s) == std::future_status::ready) {
        auto response = future.get();
        RCLCPP_INFO(node->get_logger(), "服务返回: %ld + %ld = %ld", 
                   request->a, request->b, response->sum);
    } else {
        RCLCPP_ERROR(node->get_logger(), "请求超时");
        return 1;
    }
    
    rclcpp::shutdown();
    return 0;
}
EOF

# 编辑 CMakeLists.txt 添加编译规则
cat > src/add_two_ints_service/CMakeLists.txt << 'EOF'
cmake_minimum_required(VERSION 3.8)
project(add_two_ints_service)

if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
  add_compile_options(-Wall -Wextra -Wpedantic)
endif()

find_package(ament_cmake REQUIRED)
find_package(rclcpp REQUIRED)
find_package(example_interfaces REQUIRED)

add_executable(server src/server.cpp)
ament_target_dependencies(server rclcpp example_interfaces)

add_executable(client src/client.cpp)
ament_target_dependencies(client rclcpp example_interfaces)

install(TARGETS server client
  DESTINATION lib/${PROJECT_NAME})

ament_package()
EOF

# 编译
cd ~/ros2_ws
colcon build --packages-select add_two_ints_service
source install/setup.bash

运行与验证

按以下步骤验证服务通信是否正常:

  1. 启动服务器(终端1):
source ~/ros2_ws/install/setup.bash
ros2 run add_two_ints_service server

预期看到日志:加法服务已启动,等待请求...

  1. 命令行调用服务(终端2):
source ~/ros2_ws/install/setup.bash
ros2 service call /add_two_ints example_interfaces/srv/AddTwoInts "{a: 10, b: 32}"

预期输出包含:sum: 42,同时服务器终端打印收到请求的日志。

  1. 运行客户端节点(终端3):
source ~/ros2_ws/install/setup.bash
ros2 run add_two_ints_service client

预期客户端打印:服务返回: 15 + 27 = 42。

通过判定标准:三个终端均无报错,命令行调用返回正确求和结果,客户端节点成功获取响应,服务器日志显示收到两次请求(分别来自命令行和客户端节点)。

常见问题

  • 找不到功能包:确认已执行source install/setup.bash且编译无错误。
  • 服务调用超时:确认服务器节点正在运行且服务名拼写正确(ros2 service list可查看)。
  • 类型不匹配:命令行调用时需使用example_interfaces/srv/AddTwoInts完整类型名。