服务通信也是ROS中一种极其常用的通信模式,服务通信是基于请求响应模式的,是一种应答机制。也即: 一个节点A向另一个节点B发送请求,B接收处理请求并产生响应结果返回给A。
srv文件
服务通信需要用到srv文件,在srv文件中定义了请求数据和响应数据的类型
1. 定义srv文件
具体实现:在功能包下新建srv目录,添加.srv文件。
.srv文件内容如下:
# 客户端请求时发送的数据
int8 num
---
# 服务端响应时发送的数据
int32 result
2.编辑配置文件
package.xml中添加编译依赖与执行依赖
<build_depend>message_generation</build_depend>
<exec_depend>message_runtime</exec_depend>
<!--
exce_depend 以前对应的是 run_depend 现在非法
-->
CMakeLists.txt编辑 srv 相关配置
find_package(catkin REQUIRED COMPONENTS
roscpp
rospy
std_msgs
message_generation
)
# 需要加入 message_generation,必须有 std_msgs
add_service_files(
FILES
AddInts.srv
)
generate_messages(
DEPENDENCIES
std_msgs
)
3. 编译
编译后的中间文件查看:
C++ 需要调用的中间文件(…/工作空间/devel/include/包名/xxx.h)
4. vscode配置
配置c_cpp_properies.json文件,将中间文件的路径复制到"incluedePath"下
{
"configurations": [
{
"browse": {
"databaseFilename": "",
"limitSymbolsToIncludedHeaders": true
},
"includePath": [
"/opt/ros/noetic/include/**",
"/usr/include/**",
"/xxx/yyy工作空间/devel/include/**" //配置 head 文件的路径
],
"name": "ROS",
"intelliSenseMode": "gcc-x64",
"compilerPath": "/usr/bin/gcc",
"cStandard": "c11",
"cppStandard": "c++17"
}
],
"version": 4
}
一、ROS基本数据类型的服务通信实现
1. ROS整形数据的服务通信实现
案例:客户端发送一个整形数据,服务端实现整形数据的2次方运算
服务端实现
#include "ros/ros.h"
#include "std_msgs/Int8.h"
#include "int_server_client/int_server_client.h"
bool doReq(int_server_client::int_server_client::Request& req,
int_server_client::int_server_client::Response& res)
{
res.result = pow(req.num,2);
// ROS_INFO("接受到的数据为:%d",req.num);
// ROS_INFO("处理后的数据为:%d",res.result);
return true;
}
int main(int argc, char *argv[])
{
/* code */
//解决乱码问题
setlocale(LC_ALL,"");
//初始化ros节点
ros::init(argc,argv,"server");
//句柄
ros::NodeHandle nh;
//创建server对象
ros::ServiceServer server;
server = nh.advertiseService("intServer",doReq);
ros::spin();
return 0;
}
客户端实现
#include "ros/ros.h"
#include "std_msgs/Int8.h"
#include "int_server_client/int_server_client.h"
int main(int argc, char *argv[])
{
/* code */
//解决乱码问题
setlocale(LC_ALL,"");
//初始化ros节点
ros::init(argc,argv,"server");
//句柄
ros::NodeHandle nh;
//创建client对象
ros::ServiceClient client;
client = nh.serviceClient<int_server_client::int_server_client>("intServer");
//等待服务端启动
client.waitForExistence();
//组织数据
int_server_client::int_server_client ai;
ai.request.num = 10;
//发送请求
bool flag = client.call(ai);
if (flag == 1)
{
ROS_INFO("处理后的数据为%d",ai.response.result);
}
else
{
ROS_INFO("请求失败!");
}
ros::spin();
return 0;
}
launch文件
<launch>
<node pkg="int_server_client" type="int_server" name="intServer" output="screen"/>
<node pkg="int_server_client" type="int_client" name="intClint" output="screen"/>
</launch>
2.ROS整形数据的服务通信实现之采用动态传参的方式进行参数传输
服务端实现与上一节相同,不同的是客户端实现,客户端实现需要获取终端输入的参数,然后传递给服务端进行运算并响应。
客户端实现
#include "ros/ros.h"
#include "std_msgs/Int8.h"
#include "int_server_client/int_server_client.h"
int main(int argc, char *argv[])
{
/* code */
// argc 是指命令行输入参数的个数(以空白符分隔),默认包含一个程序名
if (argc != 2)
{
ROS_ERROR("请提交一个整数");
return 1;
}
//ROS_INFO("%s",argv[1]);
//解决乱码问题
setlocale(LC_ALL,"");
//初始化ros节点
ros::init(argc,argv,"server");
//句柄
ros::NodeHandle nh;
//创建client对象
ros::ServiceClient client;
client = nh.serviceClient<int_server_client::int_server_client>("intServer");
//等待服务端启动
client.waitForExistence();
//组织数据
int_server_client::int_server_client ai;
//atoi()将字符串转换为整形
ai.request.num = atoi(argv[1]);
//发送请求
bool flag = client.call(ai);
if (flag == 1)
{
ROS_INFO("处理后的数据为%d",ai.response.result);
}
else
{
ROS_INFO("请求失败!");
}
ros::spin();
return 0;
}
运行
与上一节不同,本节需要在终端中分别运行服务端和客户端,因为客户端需要获取终端中传入的参数
命令如下:
rosrun 功能包名 节点名 参数
注意:需要先启动roscore命令
3. ros字符串类型数据的服务通信实现
服务端实现
#include "ros/ros.h"
#include "string_server_client/string_srv.h"
bool doReq(string_server_client::string_srvRequest& req,
string_server_client::string_srvResponse& res)
{
ROS_INFO("名字:%s",req.name.c_str());
ROS_INFO("年龄:%s",req.age.c_str());
ROS_INFO("地址:%s",req.address.c_str());
std::stringstream ss;
ss<< req.name.c_str()<<"来自"<<req.address.c_str()<<"今年"<<req.age.c_str()<<"岁";
res.msg = ss.str();
}
int main(int argc, char *argv[])
{
/* code */
//乱码问题
setlocale(LC_ALL,"");
//初始化ros节点
ros::init(argc,argv,"server");
//句柄
ros::NodeHandle nh;
//创建服务端对象
ros::ServiceServer server;
server = nh.advertiseService("stringServerClient",doReq);
ros::spin();
return 0;
}
客户端实现
#include "ros/ros.h"
#include "string_server_client/string_srv.h"
int main(int argc, char *argv[])
{
/* code */
//解决乱码
setlocale(LC_ALL,"");
//判断参数数量是否正确
if (argc!=4)
{
ROS_INFO("参数数量不正确,请重新输入");
return 1;
}
//初始化ros节点
ros::init(argc,argv,"server");
//句柄
ros::NodeHandle nh;
//客户端对象
ros::ServiceClient client;
client = nh.serviceClient<string_server_client::string_srv>("stringServerClient");
string_server_client::string_srv clientData;
clientData.request.name = argv[1];
clientData.request.age = argv[2];
clientData.request.address = argv[3];
//等待服务启动
client.waitForExistence();
bool flag = client.call(clientData);
if (flag==1)
{
ROS_INFO("响应的数据为:");
ROS_INFO("%s",clientData.response.msg.c_str());
}
return 0;
}