服务(Service)
基本特点
服务 是 请求-响应 模式的通信机制,主要由客户端和服务器两部分组成。客户端发起一次请求,等待服务器处理并返回响应后继续。
- 一对一:客户端调用某个指定服务名,服务端监听并响应。(多个客户端同时调用同一个服务时,每个调用都由服务器依次或并发处理;若无服务器,则调用失败或超时)
- 同步:一旦调用,要等待服务器处理,调用线程阻塞直到收到响应或超时。
适合一次性、明确请求-回复、对延迟容忍度较高、需要确认结果的场景(如获取或设置参数、执行某个动作命令、查询状态快照等),通常调用频率低。
自定义服务
以一个服务 AddTwoInts 为例:
1 .srv 文件
在某个 ROS 包的 srv/
目录下新建一个 .srv
文件,文件内容分为请求/响应两部分,例如:
这表示客户端请求提供两个 int64
类型 a, b
,服务器响应返回一个 int64
的 sum
。
2. CMakeLists.txt 和 package.xml 设置
package.xml
中添加依赖:
其中,
<build_depend>message_generation</build_depend>
用于在编译时生成 C++ 头文件、Python 模块等。<exec_depend>message_runtime</exec_depend>
用于在安装或运行时加载并使用已生成的消息/服务代码。
CMakeLists.txt
中:
其中,
message_generation
是专门用于生成消息/服务的 CMake 模块,提供add_service_files, generate_messages
等宏;
message_runtime
代表运行时需要用到已生成的消息/服务类型;其他包在依赖这个包时,CMake 会将 message_runtime 及这个包的 include 路径自动加入。
3. 生成 C++ 头文件和 Python 模块
执行 catkin_make
或 catkin build
操作后:
catkin 会在 build 目录下生成对应的 C++ 头文件,位置在 <workspace>/build/your_package/your_package/AddTwoInts.h
,编译完成后,该头文件安装到 <workspace>/devel/include/your_package/AddTwoInts.h
。
此时,工作空间内的所有 C++ 代码中只需要 #include "your_package/AddTwoInts.h"
或 #include <your_package/AddTwoInts.h>
,并且 CMakeLists.h 中 include_directories(${catkin_INCLUDE_DIRS})
指定了 devel/include ,就能找到该服务的头文件。
同时,catkin 会生成 Python 模块,安装到
Swift | |
---|---|
执行 source <workspace>/devel/setup.bash
后,就可以在 Python 脚本中通过 from your_package.srv import AddTwoInts, AddTwoIntsRequest
使用服务。
服务端(Server)实现
advertiseService
需要两个参数,第一个为服务名(可以是绝对名或相对名,可通过 ROS namespace 机制管理);第二个为回调函数或可调用对象,签名必须为 bool callback(RequestType&, ResponseType&)
。
回调函数收到 Request,填充 Response,返回值 bool 表示自定义逻辑层面是否成功,若返回 false,服务器仍会发送响应。客户端 call() 返回 false 代表调用失败,通常只在服务器无法正常处理时出现。
客户端(Client)实现
客户端初始化
serviceClient<ServiceType>("service_name")
对应于模板函数
C++ | |
---|---|
返回一个 ros::ServiceClient
服务客户端对象。其中,service_name 对应要连接的服务名称,可以是相对名、全局名或私有名。
persistent 表示是否建立 TCP 通信持久连接,默认为 false,也就是每次 client.call(srv) 的时候建立一次 TCP 连接,服务完成后断开;若设为 true,则只在第一次建立连接,后续复用此连接。
非持久连接能自动适应服务端重启、网络异常等情况,但开销更大。
客户端调用服务
client.call(srv)
是同步阻塞调用,返回 true
表示通信成功并成功收到响应。但不代表业务成功,还需检查 srv.response
字段
可以先调用 waitForExistence()
等待服务出现,避免直接 call()
立即失败。
client.waitForExistence(ros::Duration(5.0))
代表最多等待服务器 5 s ,若服务器 5 s 后还没出现,返回 false;反之,若服务器在 5 s 内出现,则返回 true 。