ROS2服务通信开发指南:C++实现服务端与客户端
2026/7/18 5:39:16 网站建设 项目流程

1. ROS2服务端与客户端开发概述

在机器人系统开发中,服务(Service)是实现节点间同步通信的重要机制。与话题(Topic)的发布/订阅模式不同,服务采用请求-响应模型,适用于需要确认执行结果的场景。比如控制机械臂移动到指定位置后需要返回是否成功的状态,或者请求传感器数据时要求立即返回测量值。

ROS2中的服务基于DDS的RPC机制实现,底层采用IDL定义接口。一个完整的服务包含两个部分:

  • 服务端(Server):监听服务请求并返回响应
  • 客户端(Client):发送请求并等待响应

C++作为ROS2官方支持的核心语言之一,其服务开发接口提供了良好的类型安全和性能表现。下面我们将通过一个完整的示例,演示如何用C++实现ROS2服务通信。

2. 开发环境准备

2.1 基础环境配置

在开始编写代码前,需要确保已正确安装ROS2环境。推荐使用Ubuntu 22.04 LTS和ROS2 Humble版本组合:

# 设置软件源 sudo apt update && sudo apt install curl gnupg lsb-release sudo curl -sSL https://raw.githubusercontent.com/ros/rosdistro/master/ros.key -o /usr/share/keyrings/ros-archive-keyring.gpg echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/ros-archive-keyring.gpg] http://packages.ros.org/ros2/ubuntu $(source /etc/os-release && echo $UBUNTU_CODENAME) main" | sudo tee /etc/apt/sources.list.d/ros2.list > /dev/null # 安装完整版ROS2 sudo apt update sudo apt install ros-humble-desktop

验证安装是否成功:

source /opt/ros/humble/setup.bash ros2 run demo_nodes_cpp talker

2.2 创建工作空间

建议为每个项目创建独立的工作空间:

mkdir -p ~/ros2_ws/src cd ~/ros2_ws colcon build

提示:每次打开新终端时,需要先执行source /opt/ros/humble/setup.bashsource ~/ros2_ws/install/setup.bash来初始化环境。

3. 创建服务接口

3.1 定义服务类型

首先需要创建自定义服务接口。在ROS2中,服务接口使用.srv文件定义,包含请求和响应两部分:

# 在~/ros2_ws/src创建功能包 cd ~/ros2_ws/src ros2 pkg create --build-type ament_cmake cpp_srvcli --dependencies rclcpp example_interfaces # 创建srv目录和文件 mkdir -p cpp_srvcli/srv touch cpp_srvcli/srv/AddTwoInts.srv

编辑AddTwoInts.srv文件:

int64 a int64 b --- int64 sum

3.2 配置构建系统

修改CMakeLists.txtpackage.xml以包含服务定义:

# 在CMakeLists.txt中添加 find_package(rosidl_default_generators REQUIRED) rosidl_generate_interfaces(${PROJECT_NAME} "srv/AddTwoInts.srv" )
<!-- 在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>

编译功能包:

cd ~/ros2_ws colcon build --packages-select cpp_srvcli

4. 实现服务端节点

4.1 创建服务端代码

src目录下创建add_two_ints_server.cpp

#include "rclcpp/rclcpp.hpp" #include "cpp_srvcli/srv/add_two_ints.hpp" using AddTwoInts = cpp_srvcli::srv::AddTwoInts; class ServerNode : public rclcpp::Node { public: ServerNode() : Node("add_two_ints_server") { service_ = create_service<AddTwoInts>( "add_two_ints", [this](const std::shared_ptr<AddTwoInts::Request> request, std::shared_ptr<AddTwoInts::Response> response) { RCLCPP_INFO(get_logger(), "收到请求: %ld + %ld", request->a, request->b); response->sum = request->a + request->b; }); } private: rclcpp::Service<AddTwoInts>::SharedPtr service_; }; int main(int argc, char **argv) { rclcpp::init(argc, argv); auto node = std::make_shared<ServerNode>(); RCLCPP_INFO(node->get_logger(), "服务端已启动"); rclcpp::spin(node); rclcpp::shutdown(); return 0; }

4.2 配置服务端构建

修改CMakeLists.txt添加可执行文件:

add_executable(server src/add_two_ints_server.cpp) ament_target_dependencies(server rclcpp cpp_srvcli) install(TARGETS server DESTINATION lib/${PROJECT_NAME})

5. 实现客户端节点

5.1 创建客户端代码

src目录下创建add_two_ints_client.cpp

#include "rclcpp/rclcpp.hpp" #include "cpp_srvcli/srv/add_two_ints.hpp" using AddTwoInts = cpp_srvcli::srv::AddTwoInts; class ClientNode : public rclcpp::Node { public: ClientNode() : Node("add_two_ints_client") { client_ = create_client<AddTwoInts>("add_two_ints"); while (!client_->wait_for_service(std::chrono::seconds(1))) { RCLCPP_WARN(get_logger(), "等待服务端上线..."); } } int64_t send_request(int64_t a, int64_t b) { auto request = std::make_shared<AddTwoInts::Request>(); request->a = a; request->b = b; auto future = client_->async_send_request(request); if (rclcpp::spin_until_future_complete(shared_from_this(), future) != rclcpp::FutureReturnCode::SUCCESS) { RCLCPP_ERROR(get_logger(), "请求失败"); return -1; } return future.get()->sum; } private: rclcpp::Client<AddTwoInts>::SharedPtr client_; }; int main(int argc, char **argv) { if (argc != 3) { std::cerr << "用法: " << argv[0] << " <a> <b>" << std::endl; return 1; } rclcpp::init(argc, argv); auto node = std::make_shared<ClientNode>(); auto result = node->send_request(std::stol(argv[1]), std::stol(argv[2])); RCLCPP_INFO(node->get_logger(), "计算结果: %ld", result); rclcpp::shutdown(); return 0; }

5.2 配置客户端构建

CMakeLists.txt中添加:

add_executable(client src/add_two_ints_client.cpp) ament_target_dependencies(client rclcpp cpp_srvcli) install(TARGETS client DESTINATION lib/${PROJECT_NAME})

6. 编译与测试

6.1 完整编译项目

cd ~/ros2_ws colcon build --packages-select cpp_srvcli source install/setup.bash

6.2 运行服务端

ros2 run cpp_srvcli server

6.3 运行客户端测试

新开终端执行:

source ~/ros2_ws/install/setup.bash ros2 run cpp_srvcli client 12 34

预期输出:

[INFO] [add_two_ints_client]: 计算结果: 46

7. 高级配置与调试技巧

7.1 QoS配置

ROS2允许通过QoS策略控制服务质量:

// 服务端 rmw_qos_profile_t qos_profile = { RMW_QOS_POLICY_HISTORY_KEEP_LAST, 10, RMW_QOS_POLICY_RELIABILITY_RELIABLE, RMW_QOS_POLICY_DURABILITY_VOLATILE, RMW_QOS_DEADLINE_DEFAULT, RMW_QOS_LIFESPAN_DEFAULT, RMW_QOS_POLICY_LIVELINESS_SYSTEM_DEFAULT, RMW_QOS_LIVELINESS_LEASE_DURATION_DEFAULT, false }; service_ = create_service<AddTwoInts>( "add_two_ints", [this](...) {...}, qos_profile);

7.2 多线程处理

默认情况下,ROS2使用单线程执行器。对于高并发场景,可以使用多线程执行器:

rclcpp::executors::MultiThreadedExecutor executor; executor.add_node(node); executor.spin();

7.3 常见问题排查

  1. 服务不可见

    • 检查服务端节点是否正常运行
    • 使用ros2 service list查看服务列表
    • 确保客户端和服务端使用相同的QoS配置
  2. 请求超时

    • 增加wait_for_service的超时时间
    • 检查网络连接和防火墙设置
  3. 类型不匹配

    • 确保.srv文件修改后重新编译
    • 使用ros2 interface show <interface>检查接口定义

8. 性能优化建议

  1. 避免阻塞调用

    • 服务回调函数应尽快返回
    • 耗时操作应使用异步方式处理
  2. 合理设置QoS

    • 实时性要求高的场景使用RMW_QOS_POLICY_RELIABILITY_BEST_EFFORT
    • 关键数据使用RMW_QOS_POLICY_RELIABILITY_RELIABLE
  3. 资源管理

    • 及时释放不再使用的服务和客户端
    • 避免在循环中频繁创建/销毁节点
  4. 日志优化

    • 生产环境中降低日志级别
    • 使用RCLCPP_DEBUG替代频繁的RCLCPP_INFO

在实际机器人项目中,服务通常用于实现关键控制指令和状态查询。我曾在一个机械臂项目中遇到服务响应延迟的问题,最终发现是因为服务回调中进行了大量计算。将计算任务转移到工作线程后,性能提升了5倍以上。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询