1背景知识
1.1 服务是什么
ROS程序由多个功能组成;一个功能由多个节点完成;各节点的功能独立,节点与节点间通过不同的通信方式,实现数据的传输,完成功能。即总功能由分功能组成,各分功能由节点的子功能及节点间的通信完成。
服务是一种节点与节点间进行一对一、双向传输的数据通信方式,数据通过服务的请求与响应实现传送。
1.2 服务的通信机制
参与节点及节点功能:
- 服务端节点Server:提供服务。
- 客户端节点Client: 请求服务。
- 节点管理器ROS Master:保管节点注册信息,匹配话题的订阅者和发布者,并帮助服务端和客户端的建立连接。
五步骤如下:
步骤 | 步骤内容 | 类比 |
---|---|---|
1.服务端Server注册 | 向节点管理器ROS Master注册相关信息,包括:节点信息、提供的服务 | 在婚姻介绍所登记信息 |
2.客户端Client注册 | 向节点管理器ROS Master注册相关信息,包括:节点信息、请求的服务 | 在婚姻介绍所登记信息 |
3.节点管理器进行话题匹配 | 保管注册信息,匹配服务相同的Server和Client,通过RPC向Client发送Server的TCP地址 | 婚姻介绍所匹配信息,推荐相亲对象 |
4.客户端请求服务 | Client通过TCP,与Server建立网络连接,发送请求数据 | 看对眼了,加微信 |
5.服务端提供服务 | Server 接收、解析请求的数据,并产生响应结果返回给 Client | 通过好友请求,开始聊天 |
1.3 服务通信的特点
服务实现一对一,双向的通信,客户端请求服务,服务端响应服务,处理得到的反馈,并将反馈返回给请求服务的客户端。通信通过反馈实现数据的双向流通。
服务是一种同步通信方式,客户端请求服务,服务端立即响应服务,在服务端未处理完请求前,客户端将等待,直到服务端反馈请求的数据,客户端才会继续执行程序。
1.4 验证
打开一终端,运行roscore
$ roscore
打开另一终端,运行turtlesim_node节点
$ rosrun turtlesim turtlesim_node
[ INFO] [1681894353.864916325]: Starting turtlesim with node name /turtlesim
[ INFO] [1681894353.871969072]: Spawning turtle [turtle1] at x=[5.544445], y=[5.544445], theta=[0.000000]
再启另一终端,查看/turtlesim 节点提供的服务,并请求/spawn服务
$ rosnode info /turtlesim
--------------------------------------------------------------------------------
Node [/turtlesim]
Publications:
* /rosout [rosgraph_msgs/Log]
* /turtle1/color_sensor [turtlesim/Color]
* /turtle1/pose [turtlesim/Pose]
Subscriptions:
* /turtle1/cmd_vel [unknown type]
Services:
* /clear
* /kill
* /reset
* /spawn
* /turtle1/set_pen
* /turtle1/teleport_absolute
* /turtle1/teleport_relative
* /turtlesim/get_loggers
* /turtlesim/set_logger_level
contacting node http://ubuntu:39225/ ...
Pid: 20104
Connections:
* topic: /rosout
* to: /rosout
* direction: outbound (41501 - 127.0.0.1:34348) [24]
* transport: TCPROS
$ rosservice info /spawn
Node: /turtlesim
URI: rosrpc://ubuntu:37257
Type: turtlesim/Spawn
Args: x y theta name
$ rosservice call /spawn 3 6 7.8 "hello"
name: "hello"
上述过程中, /turtlesim 节点提供了服务/spawn,服务/spawn能够生成新的乌龟,并返回新乌龟的名字,表明请求服务成功。
服务通过请求与响应实现数据传递,例如:查看服务/spawn传递的数据:
$ rossrv show turtlesim/Spawn
float32 x
float32 y
float32 theta
string name
---
string name
前提:ROS 创建工作空间和软件包
当前所处软件包为beginner_tutorials,该软件包的文件系统结构如下图:
2 自定义服务Server
服务消息的数据结构:由两组数据构成 ; 两组由仅包含三个破折号的一行为界, - - -上为请求,- - -下为响应。例如:请求"两数相加的和"服务中,其AddTwoNumber.srv文件如下:
int32 a
int32 b
- - -
int32 sum
自定义服务的文件的后缀为.srv。
2.1 创建srv文件夹 ,并编辑.srv文件**
创建srv文件夹和两数相加之和的.srv文件
$ roscd beginner_tutorials //进入软件包
$ mkdir srv //新建msg 目录 放置消息
$ touch AddTwoNumber.srv
编辑AddTwoNum.srv文件,内容如下:
int32 a
int32 b
---
int32 sum
2.2 添加srv的依赖
编辑package.xml文件(修改后部分)
<build_depend>message_generation</build_depend>
<exec_depend>message_runtime</exec_depend>
编辑CMakeLists.txt 文件(修改后部分)
//需添加部分1
find_package(catkin REQUIRED COMPONENTS
roscpp
rospy
std_msgs
message_generation
)
//需添加部分2:添加运行时的依赖关系
catkin_package(
...
CATKIN_DEPENDS message_runtime ...
...)
//需修改部分3:
1)删除前面的# 符号
2)替换Service1.srv为AddTwoNumber.srv
add_message_files(
FILES
AddTwoNumber.srv
)
查看服务是否创建成功
$ rossrv show beginner_tutorials/AddTwoNumber
int64 a
int64 b
---
int64 sum
$ rossrv show AddTwoNumber
[beginner_tutorials/AddTwoNumber:
int64 a
int64 b
---
int64 sum
2.3 编译,使.srv文件能被转换为C++、Python和其他语言的使用的源代码
要使用自定义服务,需将.srv文件编译为 .h 文件,再引入到程序中 。
$ roscd beginner_tutorials
$ cd ~/.catkin_ws
$ catkin_make
编译后,将在/devel/include/beginner_tutorials/下出现三个与srv相关的头文件。
1. AddTwoNumberRequest.h :请求头文件
2. AddTwoNumberResponse.h:响应头文件
3. AddTwoNumber.h :#include 响应和请求头文件的头文件
3 服务的客户端
- 初始化节点
- 创建句柄
- 创建客户端对象,请求的服务,请求服务的数据
- 请求服务,接收服务的响应
3.1 编辑add_two_number_client.cpp
$ touch add_two_number_client.cpp
编辑add_two_number_client.cpp文件
#include "ros/ros.h"
#include "beginner_tutorials/AddTwoNumber.h"
#include <cstdlib>
int main(int argc, char **argv)
{
ros::init(argc, argv, "add_two_ints_client");
if (argc != 3)
{
ROS_INFO("usage: add_two_ints_client X Y");
return 1;
}
ros::NodeHandle n;
ros::ServiceClient client = n.serviceClient<beginner_tutorials::AddTwoNumber>("add_two_ints");
beginner_tutorials::AddTwoNumber srv;
srv.request.a = atoll(argv[1]);
srv.request.b = atoll(argv[2]);
if (client.call(srv))
{
ROS_INFO("Sum: %ld", (long int)srv.response.sum);
}
else
{
ROS_ERROR("Failed to call service add_two_ints");
return 1;
}
return 0;
}
3.2 编译,生成可执行文件
add_executable(add_two_ints_client src/add_two_number_client.cpp.cpp)
target_link_libraries(add_two_ints_client ${catkin_LIBRARIES})
add_dependencies(add_two_ints_client beginner_tutorials_gencpp)
在工作空间下编译,生成可执行文件。
$ cd catkin_ws
$ catkin_make
$ source ./devel/setup.bash
可执行文件放置于:devel/lib/beginner_tutorials
4 服务的服务器
- 初始化节点
- 创建句柄
- 创建服务对象,处理服务的回调函数
- 回调函数处理请求,并产生响应
4.1 编辑add_two_number_server.cpp
touch add_two_number_server.cpp
编辑add_two_number_server.cpp
#include "ros/ros.h"
#include "beginner_tutorials/AddTwoNumber.h"
//服务的回调函数
bool add(beginner_tutorials::AddTwoNumber::Request &req,
beginner_tutorials::AddTwoNumber::Response &res)
{
res.sum = req.a + req.b;
ROS_INFO("request: the x number =%ld, the y number =%ld", (long int)req.a, (long int)req.b);
ROS_INFO("sending back response: [%ld]", (long int)res.sum);
return true;
}
int main(int argc, char **argv)
{
ros::init(argc, argv, "add_two_ints_server");
//初始化节点
ros::NodeHandle n;
//初始化句柄
ros::ServiceServer service = n.advertiseService("add_two_ints", add);
//服务对象,处理的服务,服务的回调函数
ROS_INFO("Ready to add two ints.");
ros::spin();
return 0;
}
4.2 编译,生成可执行文件
add_executable(add_two_ints_server src/add_two_number_server.cpp)
target_link_libraries(add_two_ints_server ${catkin_LIBRARIES})
add_dependencies(add_two_ints_server beginner_tutorials_gencpp)
在工作空间下编译, 生成可执行文件。
$ cd catkin_ws
$ catkin_make
$ source ./devel/setup.bash
可执行文件放置于:devel/lib/beginner_tutorials。
5 验证
打开终端,运行roscore
$ roscore
另起终端,先运行服务器节点add_two_ints_server节点
$ rosrun beginner_tutorials add_two_ints_server
再另起终端,运行add_two_ints_client节点
$ rosrun beginner_tutorials add_two_ints_client 23 67
结果:
当前软件包的文件系统结构:(红框为当前教程新增)
6 总结
ros指令:
rosserver show <package_name> :查看当前活跃的服务
rossrv show <package_name>/ :查看服务的消息格式
核心函数: