需求:
编写服务通信,客户端提交两个整数至服务端,服务端求和并响应结果到客户端
分析:
在模型实现中,ROS master 不需要实现,而连接的建立也已经被封装了,需要关注的关键点有三个:
服务端
客户端
数据
流程:
1、编写服务端实现;
2、编写客户端实现;
3、编辑配置文件;
4、编译并执行。
需要像之前自定义 msg 实现一样配置c_cpp_properies.json 文件,如果以前已经配置且没有变更工作空间,可以忽略,如果需要配置,配置方式与之前相同:
{
"configurations": [
{
"browse": {
"databaseFilename": "${workspaceFolder}/.vscode/browse.vc.db",
"limitSymbolsToIncludedHeaders": true
},
"includePath": [
"/home/zxr/cartographer_ws/install_isolated/include/**",
"/opt/ros/melodic/include/**",
"/home/zxr/Ros_zhao/learning_topic_ws/src/pub_sub/include/**",
"/usr/include/**",
"/home/zxr/Ros_zhao/learning_topic_ws/devel/include/**",
"/home/zxr/Ros_zhao/learning_topic_ws/src/server_client/include/**"
],
"name": "ROS",
"intelliSenseMode": "gcc-x64",
"compilerPath": "/usr/bin/gcc",
"cStandard": "gnu11",
"cppStandard": "c++17"
}
],
"version": 4
}
"/home/zxr/Ros_zhao/learning_topic_ws/src/server_client/include/"**这个是新加上去的;
#include "ros/ros.h"
#include "serve_client/Addints.h"
/*
服务端实现:解析客户端提交的数据,并运算再产生响应
1、包含头文件
2、初始化ROS节点
3、创建节点句柄
4、创建一个服务对象
5、回调函数 处理请求并响应
6、 由于请求多个,调用ros::spin()
*/
bool sum_back( serve_client::Addints::Request &req,
serve_client::Addints::Response &resp)
{
int num1 = req.num1;
int num2 = req.num2;
ROS_INFO("服务器收到的数据为:num1 = %d, num2 = %d",num1,num2);
//逻辑处理
if(num1<0||num2<0 ){
ROS_ERROR("提交的数据异常,数据不可以为负");
return false;
}
resp.sum = num1+num2;
return true;
}
int main(int argc,char **argv)
{
setlocale(LC_ALL, "");
//2、初始化节点
ros::init(argc,argv,"AddInts_Server");
//3、创建句柄
ros::NodeHandle nh;
//4、创建服务端对象
ros::ServiceServer server = nh.advertiseService("AddInts", sum_back);
ROS_INFO("服务已经启动.....");
//5、回调函数
ros::spin();
return 0;
}
#include"ros/ros.h"
#include"serve_client/Addints.h"
/*
需求:
编写两个节点实现服务通信,客户端节点需要提交两个整数到服务器
服务器需要解析客户端提交的数据,相加后,将结果响应回客户端,
客户端再解析
服务器实现:
1.包含头文件
2.初始化 ROS 节点
3.创建 ROS 句柄
4.创建 客户端 对象
5.请求服务,接收响应
*/
int main(int argc, char *argv[])
{
setlocale(LC_ALL,"");
ros::init(argc,argv,"AddInts_Client");
//3、创建句柄
ros::NodeHandle nh;
//4、创建 客户端对象
ros::ServiceClient client = nh.serviceClient<serve_client::Addints>("AddInts");
//等待服务启动成功
//方式1
ros::service::waitForService("AddInts");
//方式2:client.waitForExistence();
//5、组织请求数据
serve_client::Addints ai;
ai.request.num1 = atoi(argv[1]);
ai.request.num2 = atoi(argv[2]);
//6、发出请求,返回bool值,标记是否成功
bool flag = client.call(ai);
//7、处理响应
if(flag)
{
ROS_INFO("请求正常处理,响应结果为%d",ai.response.sum);
}
else{
ROS_ERROR("请求失败.....");
}
return 0;
}
add_executable(server src/AddInts_Server.cpp)
add_executable(client src/AddInts_Client.cpp)
#注意这种gen_cpp的写法
add_dependencies(server ${PROJECT_NAME}_gencpp)
add_dependencies(client ${PROJECT_NAME}_gencpp)
target_link_libraries(server
${catkin_LIBRARIES}
)
target_link_libraries(client
${catkin_LIBRARIES}
)
流程:
需要先启动服务: rosrun 包名 可执行命名
再启动客户端::rosrun 包名 可执行名 参数1 参数2
当只启动服务端时,也可以通过rosservice喂数据,格式为:
rosservice call 服务通信名 “参数1: 参数2:”
例子:
rosservice call AddInts "num1: 7 num2: 5"
AddInts是服务端的服务ID,输入完AddInts可以按tab自动补齐后面的两个输入参数。注意::和7之间有空格不要删掉
结果:
会根据提交的数据响应相加后的结果。
注意:
如果先启动客户端,那么会导致运行失败
优化:
在客户端发送请求前添加:client.waitForExistence();
或:ros::service::waitForService(“AddInts”);
这是一个阻塞式函数,只有服务启动成功后才会继续执行
此处可以使用 launch 文件优化,但是需要注意 args 传参特点