一、服务通信的理论模型
服务通信也是ROS中一种极其常用的通信模式,服务通信是基于请求响应模式的,是一种应答机制。也即: 一个节点A向另一个节点B发送请求,B接收处理请求并产生响应结果返回给A。就像是服务器与客户端,客户端发出请求,服务器给出响应。
在该模型中存在三个角色,管理者Master,客户端Client,服务端Server
Talker其实也是一个服务器,他为客户端的请求做出响应。
二、服务通信自定义srv
服务通信中,数据分成两部分,请求与响应,在 srv 文件中请求和响应使用---
分割,具体实现如下:
功能包下新建 srv 目录,添加 addNums.srv 文件,内容:
# 客户端请求时发送的两个数字
int32 num1
int32 num2
---
# 服务器响应发送的数据
int32 sum
package.xml中添加编译依赖与执行依赖
<build_depend>message_generation</build_depend>
<exec_depend>message_runtime</exec_depend>
CMakeLists.txt编辑 srv 相关配置
find_package(catkin REQUIRED COMPONENTS
roscpp
rospy
std_msgs
message_generation
)
add_service_files(
FILES
addNums.srv
)
generate_messages(
DEPENDENCIES
std_msgs
)
我们编译一下发现可以编译成功那基本代表着配置成功了
三、C++编码实现
3.1、实现服务端
#include "ros/ros.h"
#include "Server_Client_test/addNum.h"
bool doReq (Server_Client_test::addNum::Request& req,Server_Client_test::addNum::Response& res) {
res.sum = req.num1 + req.num2;
return true;
}
int main(int argc, char *argv[]) {
setlocale(LC_ALL,"");
// 2.初始化 ROS 节点
ros::init(argc,argv,"addNum_Server");
// 3.创建 ROS 句柄
ros::NodeHandle nh;
// 4.创建 服务 对象
ros::ServiceServer server = nh.advertiseService("addNum",doReq);
// 5.回调函数处理请求并产生响应
// 6.由于请求有多个,需要调用 ros::spin()
ros::spin();
return 0;
}
3.2、实现客户端
#include "ros/ros.h"
#include "Server_Client_test/addNum.h"
int main(int argc, char *argv[]) {
ros::init(argc,argv,"Client_test");
ros::NodeHandle nh;
ros::ServiceClient c = nh.serviceClient<Server_Client_test::addNum>("addNum");
Server_Client_test::addNum addNum;
addNum.request.num1 = 1;
addNum.request.num2 = 2;
bool flah = c.call(addNum);
if (flah) {ROS_INFO("答案是%d\n",addNum.response.sum);}
else {ROS_ERROR("无法解析答案");}
return 0;
}
3.3、运行服务器
# 编译,这里我们需要配置配置文件,配置文件之前我们已经多次配置,这里不在重复
catkin_make
# 运行服务端
rosrun Server_Client_test Server_test
# 运行客户端
rosrun Server_Client_test Client_test
四、服务通信相关命令rosservice && rossrv
rosservice args 打印服务参数
rosservice call 使用提供的参数调用服务
rosservice find 按照服务类型查找服务
rosservice info 打印有关服务的信息
rosservice list 列出所有活动的服务
rosservice type 打印服务类型
rosservice uri 打印服务的 ROSRPC uri
rossrv show 显示服务消息详情
rossrv info 显示服务消息相关信息
rossrv list 列出所有服务信息
rossrv md5 显示 md5 加密后的服务消息
rossrv package 显示某个包下所有服务消息
rossrv packages 显示包含服务消息的所有包
4.1、rosservice list
列出所有活动的 service
4.2、osservice args
打印服务参数
rosservice args /spawn
x y theta name
4.3、rosservice call
调用服务
为小乌龟的案例生成一只新的乌龟
rosservice call /spawn "x: 1.0
y: 2.0
theta: 0.0
name: 'xxx'"
name: "xxx"
//生成一只叫 xxx 的乌龟
4.4、rossrv list
会列出当前 ROS 中的所有 srv 消息
4.5、rossrv packages
列出包含服务消息的所有包
4.6、rossrv package
列出某个包下的所有msg
//rossrv package 包名
rossrv package turtlesim
4.7、rossrv show
显示消息描述
//rossrv show 消息名称
rossrv show turtlesim/Spawn
float32 x
float32 y
float32 theta
string name
---
string name
五、控制小乌龟实例
编码实现向 turtlesim 发送请求,在乌龟显示节点的窗体指定位置生成一乌龟,这是一个服务请求操作。
获取话题:/spawn
rosservice list
Copy
**获取消息类型:**turtlesim/Spawn
rosservice type /spawn
Copy
获取消息格式:
rossrv info turtlesim/Spawn
Copy
响应结果:
float32 x
float32 y
float32 theta
string name
---
string name
编写cpp文件
#include "ros/ros.h"
#include "turtlesim/Spawn.h"
int main(int argc, char *argv[]) {
setlocale(LC_ALL,"");
// 2.初始化 ros 节点
ros::init(argc,argv,"Test_turtle_creat");
// 3.创建 ros 句柄
ros::NodeHandle nh;
// 4.创建 service 客户端
ros::ServiceClient client = nh.serviceClient<turtlesim::Spawn>("/spawn");
// 5.等待服务启动
// client.waitForExistence();
ros::service::waitForService("/spawn");
// 6.发送请求
turtlesim::Spawn spawn;
spawn.request.x = 1.0;
spawn.request.y = 1.0;
spawn.request.theta = 1.57;
spawn.request.name = "my_turtle";
bool flag = client.call(spawn);
// 7.处理响应结果
if (flag) {
ROS_INFO("新的乌龟生成,名字:%s",spawn.response.name.c_str());
} else {
ROS_INFO("乌龟生成失败!!!");
}
return 0;
}
编译后执行,这里我们需要注意一点就是,我们这个功能包需要添加turtlesim这个包的依赖,否则我们无法获取消息格式。
# 运行乌龟节点
rosrun turtlesim turtlesim_node
# 运行生成乌龟的节点
rosrun Server_Client_test Test_turtle_creat