文章只是个人学习过程中学习笔记,主要参考ROS教程1 2 3。
[ROS](01)创建ROS工作空间
[ROS](02)创建&编译ROS软件包Package
[ROS](03)CMakeLists.txt详解
[ROS](04)package.xml详解
[ROS](09)ROS通信 —— 话题(Topic)通信编程之msg(C++)(Python)
[ROS](10)ROS通信 —— 服务(Service)通信
- 1、概述
- 2、srv结构和类型
- 3、srv命令rossrv
- 4、srv的用法
- 4.1 创建srv
- 4.2 配置CMakeLists.txt
- 4.3 配置package.xml
- 4.4 编译package
- 5、srv命令实操
- 5.1 rossrv list
- 5.2 rossrv packages
- 5.3 rossrv package
- 5.4 rossrv show / rossrv info
- 5.5 rossrv md5
- 6、实操编程 -- 服务通信使用自定义服务
- 6.1 编写程序(C++)
- 6.1.1 服务端程序(add_two_ints_server.cpp)
- 6.1.2 客户端程序(add_two_ints_client.cpp)
- 6.1.3 配置CMakeLists.txt
- 6.1.4 编译、运行
- 6.2 编写程序(python)
- 6.2.1 服务端程序(add_two_ints_server.py)
- 6.2.2 客户端程序(add_two_ints_client.py)
- 6.2.3 配置CMakeLists.txt
- 6.1.4 编译、运行
srv(服务)一个srv文件描述一个服务。它由两部分组成:请求(request)和响应(response)。
srv文件则存放在srv目录下。
srv4 文件和msg文件一样,只是它们包含两个部分:请求和响应。这两部分用一条 --- 线隔开。任何两个用---连接在一起的.msg文件都是合法的服务描述。下面是一个srv文件的示例:
int64 a int64 b --- int64 sum
3、srv命令rossrv在上面的例子中,a和b是请求, sum是响应。
该srv文件见rospy_tutorials软件包(位于/opt/ros/melodic/share)的srv目录下。
msg文件可参考: https://blog.csdn.net/CynalFly/article/details/126072684
rossrv命令行工具显示有关 ROS 服务的信息。它的用法与rosmsg完全相同:
| 命令 | 功能 |
|---|---|
| rossrv show | 显示服务的描述(详细信息) |
| rossrv info | rossrv show的别名,功能一样 |
| rossrv list | 列出所有服务 |
| rossrv md5 | 显示md5加密的服务 |
| rossrv package | 列出某个软件包(package)的服务 |
| rossrv packages | 列出包含服务的软件包(packages) |
4、srv的用法 4.1 创建srvTips:使用rossrv
-h 帮助选项获取更详细的用法。
还是在原来创建的软件包 beginner_tutorials 中定义一个新的服务。
# 切换到软件包的目录路径 roscd beginner_tutorials # 创建srv文件夹目录 mkdir srv # 创建名为AddTwoInts.srv的文件 touch AddTwoInts.srv
4.2 配置CMakeLists.txtTips:如果想使用现有的srv文件,而不是手动创建新的srv。roscp是一个实用的命令行工具,用于将文件从一个包复制到另一个包。
用法:roscp [package_name] [file_to_copy_path] [copy_path]
举例: roscp rospy_tutorials AddTwoInts.srv srv/AddTwoInts.srv
1. 为已经存在里面的find_package调用添加message_generation依赖项(message_generation对msg和srv都适用),这样就能生成消息了。(添加 message_generation 依赖项,必须有 std_msgs 。)
find_package(catkin REQUIRED COMPONENTS roscpp rospy std_msgs message_generation )
2. 添加自定义的消息文件。将你srv文件夹中自定义的*.srv文件添加在这里。
add_service_files( FILES AddTwoInts.srv )
3. 用于生成所有定义的message文件,需要添加本文件需要依赖的packages。其实就是告诉编译器,编译 *.srv 文件时,需要依赖的库或package。
generate_messages( DEPENDENCIES std_msgs )
4. 导出消息的运行时依赖关系。
catkin_package( ... CATKIN_DEPENDS message_runtime ... ...)4.3 配置package.xml
添加编译依赖与执行依赖。
message_generation message_runtime
4.4 编译packageNode :在构建时,其实只需要message_generation,而在运行时,我们只需要message_runtime。
已经创建了一些新消息,所以需要重新make一下软件包:
cd cd catkin_ws catkin_make
srv目录中的任何.srv文件都将生成所有支持语言的代码。
- C++消息的头文件将生成在~/catkin_ws/devel/include/beginner_tutorials/
- python脚本将创建在~/catkin_ws/devel/lib/python2.7/dist-packages/beginner_tutorials/srv
rossrv list 列出ROS中的所有服务(srv)。
5.2 rossrv packagesrossrv packages [options] 列出包含服务的软件包。
# 列出包含服务的软件包,每行显示一个软件包名 rossrv packages # 在一行中列出包含服务的软件包 rossrv packages -s5.3 rossrv package
rossrv package [package-name] 列出某个软件包(package)的服务。
5.4 rossrv show / rossrv info rossrv show [service type] 显示服务的详细信息。
rossrv md5[service type] 显示md5加密的服务。如果编译的版本不匹配,这也会发出警告。
6、实操编程 – 服务通信使用自定义服务Node:md5 命令仅供专家用户使用。(??)
功能:基于服务通信,客户端提交两个整数至服务端,服务端求和并响应结果到客户端。
实现:
1. 创建服务端(add_two_ints_server)节点,该节点将接收两个整数,并返回它们的和。
2. 创建客户端(add_two_ints_client)节点,该节点将发送两个整数,并等待结果。
3. 创建自定义服务(AddTwoInts.srv),(见4.1小节 创建srv)
4. 服务请求(client–>server)
5. 服务响应(server–>client)
在beginner_tutorials软件包的src目录下创建发布者和订阅者源文件:
roscd beginner_tutorials cd src touch add_two_ints_server.cpp add_two_ints_client.cpp6.1.1 服务端程序(add_two_ints_server.cpp)
#include "ros/ros.h"
#include "beginner_tutorials/AddTwoInts.h"
bool addCallback(beginner_tutorials::AddTwoInts::Request &req,
beginner_tutorials::AddTwoInts::Response &res)
{
ROS_INFO("服务器收到的请求: x=%ld, y=%ld", req.a, req.b);
res.sum = req.a + req.b;
ROS_INFO("服务器发送的响应: sum=%ld",res.sum);
return true;
}
int main(int argc, char **argv)
{
setlocale(LC_ALL,"");
ros::init(argc,argv,"add_two_ints_server");
ros::NodeHandle nh;
ros::ServiceServer server = nh.advertiseService("add_two_ints",addCallback);
ROS_INFO("服务器Ready.");
ros::spin();
return 0;
}
6.1.2 客户端程序(add_two_ints_client.cpp)回调函数addCallback()提供了AddTwoInts服务,它接受srv文件中定义的请求(request)和响应(response)类型,并返回一个布尔值。
#include "ros/ros.h"
#include "beginner_tutorials/AddTwoInts.h"
int main(int argc, char **argv)
{
setlocale(LC_ALL,"");
ros::init(argc,argv,"add_two_ints_client");
if(argc != 3)
{
ROS_INFO("用法:输入两个整数 X Y");
return 1;
}
ros::NodeHandle nh;
ros::ServiceClient client = nh.serviceClient("add_two_ints");
beginner_tutorials::AddTwoInts srv;
srv.request.a = atoll(argv[1]);
srv.request.b = atoll(argv[2]);
if(client.call(srv))
{
ROS_INFO("请求服务成功, 求和结果sum:%ld", srv.response.sum);
}
else
{
ROS_ERROR("请求服务失败...");
}
return 0;
}
6.1.3 配置CMakeLists.txtif(client.call(srv)):此处实际上调用了服务。由于服务调用被阻塞,它将在调用完成后返回。如果服务调用成功,call()将返回true,并且srv.response中的值将是有效的。如果调用不成功,则call()将返回false且srv.response的值将不可用。
只需将这几行添加到CMakeLists.txt文件的底部:
add_executable(add_two_ints_server src/add_two_ints_server.cpp)
target_link_libraries(add_two_ints_server ${catkin_LIBRARIES})
add_dependencies(add_two_ints_server ${PROJECT_NAME}_gencpp)
add_executable(add_two_ints_client src/add_two_ints_client.cpp)
target_link_libraries(add_two_ints_client ${catkin_LIBRARIES})
add_dependencies(add_two_ints_client ${PROJECT_NAME}_gencpp)
6.1.4 编译、运行
编译后,在终端中执行过程如下所示。
在beginner_tutorials软件包的scripts目录下创建服务端和客户端源文件:
roscd beginner_tutorials cd scripts touch add_two_ints_server.py add_two_ints_client.py chmod +x add_two_ints_server.py add_two_ints_client.py6.2.1 服务端程序(add_two_ints_server.py)
#!/usr/bin/env python
# encoding: utf-8
import rospy
from beginner_tutorials.srv import AddTwoInts,AddTwoIntsResponse
def addCallback(req):
rospy.loginfo("服务器收到的请求: x=%ld, y=%ld", req.a, req.b)
sum = req.a + req.b
rospy.loginfo("服务器发送的响应: sum=%ld",sum)
return AddTwoIntsResponse(sum)
def add_two_ints_server():
# 初始化ROS节点
rospy.init_node("add_two_ints_server")
# 创建一个名为add_two_ints的server,注册回调函数addCallback
# Request请求信息将作为参数传递给addCallback进行处理
server = rospy.Service("add_two_ints",AddTwoInts, addCallback)
rospy.loginfo("服务器Ready.")
rospy.spin()
if __name__ == '__main__':
add_two_ints_server()
6.2.2 客户端程序(add_two_ints_client.py)
#!/usr/bin/env python
# encoding: utf-8
import rospy
import sys
from beginner_tutorials.srv import *
def add_two_ints_client(x,y):
# 阻塞,等待add_two_ints服务可用
rospy.wait_for_service("add_two_ints")
try:
# 创建sevice client
# 声明了服务类型 AddTwoInts ,它生成AddTwoIntsRequest对象
# 返回值是一个AddTwoIntsResponse对象
add_two_ints = rospy.ServiceProxy('add_two_ints',AddTwoInts)
# 参数传递的两种方式
# (1) 显式:创建 Request 实例传递
# req = AddTwoIntsRequest(x, y)
# resp = add_two_ints(req)
# (2) 隐式
resp = add_two_ints(x,y)
rospy.loginfo("请求服务成功, 求和结果sum:%ld", resp.sum)
# 当请求服务时发生错误,就会抛出该异常内容
except rospy.ServiceException as e:
rospy.loginfo("请求服务失败: %s"%e)
def usage():
return "%s [x y]"%sys.argv[0]
if __name__ == '__main__':
# 初始化ROS节点
# 对于客户端来说可以不是节点,不需要调用init_node()
rospy.init_node("add_two_ints_client")
if len(sys.argv) == 3:
# sys.argv[]是字符串,需要强制转化成整型
x = int(sys.argv[1])
y = int(sys.argv[2])
else:
# 如果没有初始化节点,日志信息是看不到的,可使用print
# print(usage())
rospy.logerr(usage())
sys.exit(1)
add_two_ints_client(x,y)
6.2.3 配置CMakeLists.txt
# 安装python可执行脚本
catkin_install_python(PROGRAMS
scripts/add_two_ints_server.py
scripts/add_two_ints_client.py
...
DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
)
6.1.4 编译、运行
编译后,在终端中执行过程如下所示。
ROS.otg. ROS教程[EB/OL]. 2020-12-22[2022-7-5].
http://wiki.ros.org/cn/ROS/Tutorials. ↩︎.ROS.org. 编写简单的服务和客户端(C++)[EB/OL]. 2020-12-28[2022-07-30]. https://wiki.ros.org/cn/ROS/Tutorials/WritingServiceClient%28c%2B%2B%29. ↩︎
.ROS.org. 编写简单的服务和客户端(Python)[EB/OL]. 2020-12-28[2022-07-30]. https://wiki.ros.org/cn/ROS/Tutorials/WritingServiceClient%28python%29. ↩︎
ROS.org. srv[EB/OL]. 2017-01-07[2022-07-30]. https://wiki.ros.org/srv. ↩︎


ROS通信 —— 服务(Service)通信编程之srv(C++)(Python) [ROS](11)ROS通信 —— 服务(Service)通信编程之srv(C++)(Python)](http://www.mshxw.com/aiimages/31/1037155.png)
