【ROS 通信】Services 服务通信
【ROS】Service 服务通信
- 前言
- 前置操作
- 创建一个 tutorial 功能包
- 定义服务接口
- 修改 CMakeLists.txt 文件
- 修改 find_package
- 修改 add_service_files
- 修改 generate_messages
- 修改 catkin_package
- find_package 和 catkin_package
- 修改 package.xml 文件
- 构建
- 服务通信的 Python 实现
- 服务端
- 创建服务端节点 add_server.py
- 编写服务端代码
- 添加可执行权限
- 添加 python 脚本安装规则
- 构建并测试
- 客户端
- 创建客户端节点 add_client.py
- 编写客户端代码
- 添加可执行权限
- 添加 python 脚本安装规则
- 构建并测试
前言
ROS 的服务通信采用同步的请求-响应模式。
节点角色:
- 服务端 (Server):提供服务并处理请求。
- 客户端 (Client):发送请求并等待服务端的响应。
工作流程:
- 客户端向服务端发送请求,并在等待服务端处理后接收响应。
适用场景:
- 服务通信适用于需要明确交互和结果的场景,如查询状态、获取配置或执行特定操作等。
本文通过一个简单的例子来讲解服务通信:
客户端向服务端发送两个整数请求,服务端计算这两个数的和并将结果返回给客户端。
前置操作
创建一个 tutorial 功能包
使用以下指令创建 tutorial
包,并包含常见的依赖项:
catkin_create_pkg tutorial roscpp rospy std_srvs std_msgs
定义服务接口
在 tutorial
功能包中创建一个 srv
目录,用于存放自定义的服务消息类型。
例如,创建一个 AddTwoInts.srv
文件,用于定义两个整数相加的服务消息。
在该文件中添加以下内容:
int64 a
int64 b
---
int64 sum
这个服务消息文件定义了两个输入参数:a
和 b
,以及一个输出参数:sum
,表示两个整数相加后的结果。
修改 CMakeLists.txt 文件
修改 find_package
在 find_package
中添加 message_generation
,以便 CMake 在构建时找到这个工具包。
修改后的内容如下:
find_package(catkin REQUIRED COMPONENTS
roscpp
rospy
std_msgs
std_srvs
message_generation
)
备注:
message_generation
是一个工具包,主要用于自动化处理自定义消息类型(如.msg
、.srv
、.action
文件)。
它可以在 CMake 构建时,根据这些消息文件自动生成对应的 C++ 头文件和 Python 模块。这样,你在编写 C++ 或 Python 代码时,就可以直接#include
或import
这些自动生成的消息文件。
因此,它就像一个代码生成器,自动生成各种编程语言所需的头文件和模块,使得在项目中使用自定义消息和服务更加方便。
修改 add_service_files
-
在
CMakeLists.txt
文件中添加add_service_files
,告知消息生成器需要转换的服务消息文件。
CMake 会根据这些文件自动生成对应的 C++ 头文件和 Python 模块。 -
在
add_service_files
中指定自定义的服务消息类型。
例如,使用AddTwoInts.srv
文件作为服务消息,CMake 会自动生成相关代码。
修改后的代码:
add_service_files(
FILES
AddTwoInts.srv
)
通过添加此配置,message_generation
将自动根据 AddTwoInts.srv
文件生成相应的 C++ 头文件和 Python 模块。
修改 generate_messages
-
在
CMakeLists.txt
文件中添加generate_messages
函数,调用消息生成器。
CMake 会自动编译.srv
和.msg
文件,并生成 C++ 头文件和 Python 模块。 -
在
generate_messages
中指定依赖项,告诉生成器哪些消息文件需要依赖。
例如,如果使用了std_msgs
中的bool
类型,就需要将std_msgs
添加为依赖项。
修改后的代码:
generate_messages(
DEPENDENCIES
std_msgs
std_srvs
)
通常需要添加 std_msgs
和 std_srvs
作为依赖项。如有其他依赖,根据实际情况修改。
修改 catkin_package
在 ROS 中进行服务、话题和动作通信时,需要一个消息翻译器,它能够将节点之间交互的数据转换为 ROS 可识别的格式,即字节流。为了确保在运行时能够使用这个翻译器,我们需要在 catkin_package
中添加对其的依赖。
修改后的代码:
catkin_package(
CATKIN_DEPENDS roscpp rospy std_msgs std_srvs message_runtime
)
通过添加 message_runtime
依赖,我们确保在运行时能够正确处理消息翻译,保证节点之间的通信能够顺利进行。
find_package 和 catkin_package
这两行代码虽然看起来相似,但有所不同:
-
find_package
用于指定编译时需要的依赖项。它告诉 CMake 在构建过程中需要哪些外部库或工具包。 -
catkin_package
用于声明当前功能包在导出时所需的依赖项。当其他功能包依赖于当前功能包时,它会自动根据catkin_package
中的声明包含这些依赖项,确保依赖当前功能包的功能包能够正确地使用所需资源。
简而言之,find_package
是编译构建时的依赖声明,而 catkin_package
是用于导出时的依赖声明(声明其他功能包包含当前功能包的时候需要包含的依赖),它确保当其他功能包依赖于当前功能包时,能够自动获取所需的依赖项。
1. find_package
包含 message_generation
,但不直接包含 message_runtime
,是因为:
-
message_generation
在编译时处理消息文件并生成 C++ 头文件和 Python 模块,同时隐式包含了message_runtime
,负责消息翻译。 -
因此,当包含
message_generation
时,message_runtime
会自动得到支持,无需额外声明。
- 在
catkin_package
中不包含message_generation
,是因为:
-
其他功能包会自动依赖已生成的 C++ 头文件和 Python 模块,无需重新使用
message_generation
。 -
但它们仍需依赖
message_runtime
,因为需要在运行时处理消息翻译。因此,必须在catkin_package
中声明message_runtime
。
修改 package.xml 文件
在 package.xml
中,需要添加以下依赖项:
-
构建依赖:
message_generation
(用于构建期间生成消息相关代码)<build_depend>message_generation</build_depend>
-
运行依赖:
message_runtime
(用于运行期间处理消息通信)<exec_depend>message_runtime</exec_depend>
package.xml
相当于功能包的“说明书”,用于声明构建和运行时所需的依赖。它本身不参与构建过程,但构建系统会读取其中的依赖信息,确保功能包可以正常编译和运行。
构建
使用 catkin_make
构建整个工作空间,服务消息生成成功时将输出类似如下信息:
构建成功后,工作空间根目录下的 devel/include
和 devel/lib/pythonX/site-packages
目录中会生成由 message_generation
自动生成的 C++ 头文件和 Python 模块,效果如下图所示:
服务通信的 Python 实现
服务端
创建服务端节点 add_server.py
编写服务端代码
#! /usr/bin/env python2
# -*- coding: utf-8 -*-
import rospy # 导入ROS Python库
# 从 tutorial 功能包的 srv 服务通信的子模块中,导入AddTwoInts、AddTwoIntsResponse、AddTwoIntsRequest 这三个类
# 该 srv 子模块位于 lby_ws/devel/lib/python2.7/dist-packages/tutorial/srv/_AddTwoInts.py中,是catkin_make构建时,message_generation自动生成的 python 模块
from tutorial.srv import AddTwoInts, AddTwoIntsResponse, AddTwoIntsRequest
# 从 tutorial 功能包的 srv 服务通信的子模块中,导入所有类
# from tutorial.srv import *
# 服务请求的回调函数
def add_server_callback(req):
sum = req.a + req.b
rospy.loginfo("收到请求: %d + %d = %d" % (req.a, req.b, sum))
# 将结果传入类 AddTwoIntsResponse() 中,创造一个服务通信的响应的实例并返回
# 返回服务消息的响应的实例,返还给客户端
return AddTwoIntsResponse(sum)
def add_server():
# 初始化ROS节点
rospy.init_node('add_server')
# 创建服务端
# 给 rospy.Service 这个类传入三个参数创建一个 服务端实例server
# 参数1:服务端实例s的名称
# 参数2:服务消息的类型(.srv文件)
# 参数3:服务端接受到请求后调用的回调函数
server = rospy.Service('add_server', AddTwoInts, add_server_callback)
rospy.loginfo("准备好进行加法运算。")
# 保持节点运行
rospy.spin()
if __name__ == "__main__":
try:
add_server()
except rospy.ROSInterruptException:
pass
添加可执行权限
chmod +x ~/lby_ws/src/tutorial/scripts/add_server.py
添加 python 脚本安装规则
在 CMakeLists.txt
中添加以下内容(确保放在 catkin_install_python(PROGRAMS 上):
catkin_install_python(PROGRAMS
scripts/add_server.py
DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
)
构建并测试
# 编译工作空间
cd ~/lby_ws
catkin_make
# 启动服务端
rosrun tutorial add_server.py
# 测试服务
# rosservice 是 ROS 提供的命令行工具,用于与 ROS 的服务通信进行交互
# call 是 rosservice 的子命令,用于 ”发送请求“ 给某个服务
# /add_two_ints 是服务端的名称
# "a: 10 b: 20" 是给服务端发送的请求的参数
rosservice call /add_server "a: 10 b: 20"
测试结果如下图:
客户端
创建客户端节点 add_client.py
编写客户端代码
#! /usr/bin/env python2
# -*- coding: utf-8 -*-
import rospy
# 从 tutorial 功能包的 srv 服务通信的子模块中,导入AddTwoInts、AddTwoIntsResponse、AddTwoIntsRequest 这三个类
# 该 srv 子模块位于 lby_ws/devel/lib/python2.7/dist-packages/tutorial/srv/_AddTwoInts.py中,是catkin_make构建时,message_generation自动生成的 python 模块
from tutorial.srv import AddTwoInts, AddTwoIntsResponse, AddTwoIntsRequest
# 从 tutorial 功能包的 srv 服务通信的子模块中,导入所有类
# from tutorial.srv import *
def add_client():
# 初始化ROS节点
rospy.init_node('add_client')
# 等待服务可用
# 参数:等待的服务端的名称(需要与服务端创建服务时使用的名称一致)
# 防止服务端还没有创建好,客户端就开始调用服务,导致报错
rospy.wait_for_service('add_server')
try:
# 创建客户端
# 给 rospy.ServiceProxy 这个类传入二个参数创建一个 客户端实例client
# 参数1:客户端实例client的名称
# 参数2:服务消息的类型(.srv文件)
client = rospy.ServiceProxy('add_server', AddTwoInts)
# 给AddTwoIntsRequest()类 传入a和b的参数,创建请求消息数据的实例
req = AddTwoIntsRequest(10, 20)
# 发送请求并等待响应
# 方式一:直接传递参数
# 调用创建的客户端实例client,传入两个参数作为服务消息的请求的参数
# 返回值是服务端返回的响应消息的实例 AddTwoIntsResponse
# resp1 = client(10, 20)
# rospy.loginfo("方式一 - 请求: 10 + 20 = %d" % resp1.sum)
# 方式二:使用请求对象
# 调用创建的客户端实例client,直接传入req(服务消息的AddTwoIntsRequest请求消息的实例)作为服务消息的请求的参数
# 返回值是服务端的中断回调函数最后 return返回的响应消息的实例 AddTwoIntsResponse
# 就是return AddTwoIntsResponse(sum)
resp2 = client(req)
rospy.loginfo("方式二 - 请求: %d + %d = %d" % (req.a, req.b, resp2.sum))
except rospy.ServiceException as e:
rospy.logerr("服务调用失败: %s" % e)
if __name__ == "__main__":
try:
add_client()
except rospy.ROSInterruptException:
pass
添加可执行权限
chmod +x ~/lby_ws/src/tutorial/scripts/add_client.py
添加 python 脚本安装规则
在 CMakeLists.txt
中添加以下内容(确保放在 catkin_install_python(PROGRAMS 上):
catkin_install_python(PROGRAMS
scripts/add_server.py
scripts/add_client.py
DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
)
构建并测试
# 编译工作空间
cd ~/lby_ws
catkin_make
# 启动服务端
rosrun tutorial add_server.py
# 启动客户端
rosrun tutorial add_client.py
测试结果如下图: