ROS2 C++服务通信超完整实战教程(零基础可落地)
前言
在 ROS2 机器人开发体系中,节点通信是多节点协作的核心基础,官方提供了两大核心通信机制:话题通信(Topic)与服务通信(Service),二者场景分工明确,是ROS2开发必须掌握的核心能力。
话题通信基于发布/订阅模型,为单向、持续、无应答的数据传输方式,适配激光雷达、摄像头、IMU等周期性输出的传感器数据流;而服务通信基于客户端-服务端(C/S)同步问答模型,由客户端主动发起请求,服务端接收请求、处理业务逻辑并返回唯一响应,属于单次、可控、有反馈的交互方式,完美适配设备启停控制、数值运算、设备状态查询、参数设置等单次指令交互场景。
本文将通过两数求和实战案例,从零实现 ROS2 自定义服务通信全流程,包含自定义服务接口、服务端/客户端C++代码开发、编译配置、运行测试与问题排查,全程可复刻、无遗漏,适合新手入门与工程复用。
一、案例需求与整体架构分析
1.1 业务需求
搭建一套完整的ROS2自定义服务通信程序,实现功能:
-
客户端支持终端命令行传入两个整型数字,封装为服务请求数据;
-
服务端常驻后台,持续监听服务请求,接收两个整数并完成求和运算;
-
服务端将运算结果封装为响应数据,回传给客户端;
-
两端打印详细运行日志,完整展示「请求发送-数据处理-结果返回」全流程;
-
最终实现效果:终端输入客户端指令
ros2 run cpp02_service demo02_client 100 200,输出求和结果300。
1.2 服务通信三大核心要素
ROS2服务通信必须包含三个核心组件,三者缺一不可,是服务通信的底层核心逻辑:
-
服务端(Server):被动监听节点,后台常驻运行,负责注册服务、监听客户端请求、触发回调函数处理业务逻辑、生成并返回响应结果。
-
客户端(Client):主动发起交互节点,主动连接服务端、封装请求参数、发送数据、阻塞/异步等待并接收服务端响应结果。
-
服务接口(.srv文件):两端数据交互的统一协议,严格规定请求、响应的数据类型与字段。文件中以
---分割上下两部分,上段为「客户端→服务端」的请求数据,下段为「服务端→客户端」的响应数据,编译后自动生成C++/Python可调用的结构体。
1.3 整体实现流程
本案例遵循ROS2标准化工程开发流程,6个核心步骤,层层递进、无跳过步骤:
-
自定义
.srv服务接口文件,配置接口包编译规则; -
编译接口包,刷新环境变量,校验接口有效性;
-
创建带依赖的C++服务功能包;
-
分别编写服务端、客户端完整业务代码;
-
配置功能包CMakeLists.txt编译与安装规则;
-
整体编译工作空间,多终端启动节点,完成功能测试与问题验证。
二、第一步:自定义服务通信接口 AddInts.srv
2.1 创建.srv 文件
在接口功能包base_interfaces_demo中新建srv文件夹,创建AddInts.srv文件,写入如下内容并保存:

语法核心说明:文件中 --- 为固定分隔符,上方区域为 Request 请求区(客户端传给服务端的数据),下方区域为 Response 响应区(服务端返回客户端的数据)。编译成功后,ROS2 会自动生成 AddInts::Request、AddInts::Response 两个结构体,供C++代码直接调用。
2.2 配置 CMakeLists.txt 编译脚本
原始功能包无法识别.srv文件,必须手动配置编译规则,让编译工具将自定义接口转换为C++可调用的头文件。打开 base_interfaces_demo/CMakeLists.txt,在文件中添加以下完整配置:

2.3 编译接口包
单独编译接口包,避免全量编译浪费时间,提升开发效率,终端执行以下指令:
colcon build --packages-select base_interfaces_demo
编译完成后必须刷新环境变量,让系统识别新生成的自定义接口,否则代码会报头文件找不到错误:
source install/setup.bash
2.4 接口有效性测试
为确保接口编译成功、格式无误,执行接口查询指令,校验接口定义:
ros2 interface show base_interfaces_demo/srv/AddInts
三、第二步:创建 C++ 服务功能包
在工作空间 src 目录下,通过官方指令创建C++类型功能包,自动绑定核心依赖,无需手动配置依赖文件。终端执行创建指令:
ros2 pkg create cpp02_service --build-type ament_cmake --dependencies rclcpp base_interfaces_demo
参数详细解析:
-
cpp02_service:自定义功能包名称,用于存放服务端、客户端源码; -
--build-type ament_cmake:指定编译类型为C++专用的ament编译体系; -
rclcpp:ROS2 C++核心库,提供节点创建、服务注册、日志输出、自旋等待等基础API; -
base_interfaces_demo:依赖自定义的服务接口包,用于调用AddInts服务结构体。
四、第三步:C++ 服务端代码实现
4.1 文件路径
在 cpp02_service/src/ 路径下新建源码文件 demo01_server.cpp
4.2 完整源码


4.3 核心代码逐段解析
-
服务创建函数:
create_service<AddInts>("add_ints", 回调函数)服务名add_ints是全局唯一标识,客户端必须使用完全一致的名称,否则无法建立通信连接;回调函数用于处理所有客户端请求。 -
回调函数双参数:
req存储客户端传入的请求数据,只读不可修改;res为空响应体,用于写入业务处理结果,ROS2自动封装返回给客户端。 -
自旋函数:
rclcpp::spin(server)阻塞主线程,让节点持续运行,实时监听服务事件、触发回调,是服务端能够常驻后台的核心。
五、第四步:C++ 客户端代码实现
5.1 文件路径
在 cpp02_service/src/ 路径下新建源码文件 demo02_client.cpp
5.2 完整源码



5.3 核心代码解析
-
wait_for_service(1s):循环轮询服务端状态,解决客户端先启动、服务端未就绪导致的连接失败问题,提升程序稳定性;
-
async_send_request():异步发送请求,不阻塞主线程,返回Future异步结果对象,适配ROS2标准通信规范;
-
spin_until_future_complete():阻塞节点运行,直到接收服务端响应,避免提前读取空结果,保证数据完整性;
-
atoi(argv[]):将终端输入的字符串参数转为整型,实现自定义动态传参,灵活适配不同数值求和测试;
-
参数校验:判断终端参数数量,避免用户输入格式错误导致程序崩溃,提升代码健壮性。
六、第五步:配置 CMakeLists.txt 编译规则
源码编写完成后,必须配置编译安装规则,否则编译后无法生成可执行文件,无法通过ros2 run启动节点。打开 cpp02_service/CMakeLists.txt,在文件末尾追加以下完整配置:
功能包创建时已自动生成package.xml的依赖声明,无需手动修改依赖配置,直接编译即可生效。
七、第六步:编译与运行测试
7.1 编译功能包
终端进入工作空间根目录,执行针对性编译指令,仅编译当前功能包,提升效率:
colcon build --packages-select cpp02_service
source install/setup.bash
7.2 双终端分步启动
终端 1:启动服务端

输出日志:

终端 2:启动客户端,传入参数100 200

输出日志:

两端日志正常输出、数据交互准确,代表整套自定义服务通信程序开发、编译、运行全部完成。
八、拓展知识点:服务通信深度总结
8.1 话题通信 vs 服务通信对比
|
对比维度 |
话题通信(Topic) |
服务通信(Service) |
|---|---|---|
|
交互模式 |
单向发布订阅,无应答、无反馈 |
双向一问一答,同步响应、有明确反馈 |
|
适用场景 |
传感器、图像、雷达等持续高频数据流 |
设备控制、数值计算、状态查询、参数设置等单次指令 |
|
启动顺序 |
发布者、订阅者无先后启动要求 |
必须先启动服务端,再启动客户端 |
|
消息载体 |
.msg 单文件,仅单向数据格式 |
.srv 文件,区分请求/响应双格式 |
|
通信特性 |
异步通信、无连接校验、数据可能丢失 |
同步通信、有连接校验、数据可靠传输 |
8.2 开发高频报错与解决方案
-
汇总新手开发90%会遇到的报错问题,精准定位原因、给出可直接执行的解决方案:
-
报错1:Could not find executable 核心原因:CMakeLists.txt 未添加编译安装代码、未执行编译指令、未刷新环境变量 解决方案:补全编译配置脚本,执行
colcon build重新编译,刷新source install/setup.bash环境变量 -
报错2:客户端无法连接服务端 核心原因:服务端与客户端服务名字符串不一致、自定义接口包未编译、环境未刷新 解决方案:统一两端服务名,重新编译base_interfaces_demo接口包,刷新环境变量后重启终端测试
-
报错3:编译报头文件找不到 核心原因:功能包缺少base_interfaces_demo依赖、接口未编译、编译缓存异常 解决方案:删除build、install缓存文件,重新编译接口包,使用带完整依赖的指令创建功能包
-
报错4:参数传入无效、数值为0 核心原因:终端参数数量错误、数据类型不匹配 解决方案:严格按照 数字1 数字2 格式传参,保证传入整型数值。
九、文末总结
本文通过两数求和实战案例,完整复刻 ROS2 自定义服务通信全工程开发流程,补齐了常规教程缺失的配置细节、原理讲解与报错排查,全程闭环可落地。通过本文学习,你可完全掌握以下核心能力:
-
熟练掌握 .srv 自定义服务接口的定义、语法规则、编译配置与有效性测试方法;
-
精通 ROS2 C++ 服务端节点开发模板,掌握服务注册、回调绑定、业务逻辑处理核心逻辑;
-
掌握 C++ 客户端节点服务校验、异步请求、阻塞等待、结果解析的标准化开发流程;
-
理清话题通信与服务通信的核心差异与工程选型标准;
-
熟练解决服务通信开发中的高频报错,具备独立开发、调试、落地自定义服务的工程能力。
本文代码可直接复用至机器人设备控制、参数调试、状态查询等实际工程场景,是 ROS2 进阶多节点协同开发的核心基础。
更多推荐



所有评论(0)