一、创建srv文件
(1)在ROS工作空间下新建一个功能包plumbing_server_client,创建srv文件夹用以存放srv文件,新建一个srv文件命名为Addints.srv:
(2)编写srv文件:
(3)修改配置文件。首先打开package.xml文件,添加依赖:
再打开CMakeLists.txt文件,修改配置:
去掉注释,将文件名改为自定义srv文件的文件名:
去掉注释:
去掉注释,添加工作空间依赖:
编译工作空间:
二、使用C++实现服务通信
(1)服务端实现
在src文件夹下新建一个.cpp文件,命名为demo01_server.cpp,编写程序:
#include "ros/ros.h"
#include "plumbing_server_client/Addints.h"
/*
服务端实现:解析客户端提交的数据,并运算再产生响应
1.包含头文件;
2.初始化ROS节点;
3.创建节点句柄;
4.创建一个服务对象;
5.处理请求并产生响应
6.spin()
*/
bool doNums(plumbing_server_client::Addints::Request &request,
plumbing_server_client::Addints::Response &response){
//1.处理请求
int num1 = request.num1;
int num2 = request.num2;
ROS_INFO("收到的请求数据:num1 = %d, num2 = %d",num1,num2);
//2.组织响应
int sum = num1 + num2;
response.sum = sum;
ROS_INFO("求和结果为:sum = %d",sum);
return true;
}
int main(int argc, char *argv[])
{
setlocale(LC_ALL,"");
// 2.初始化ROS节点;
ros::init(argc,argv,"Server_01"); //节点名称要保证是唯一的
// 3.创建节点句柄;
ros::NodeHandle nh;
// 4.创建一个服务对象;
ros::ServiceServer server = nh.advertiseService("huati_addints",doNums); //括号第一项为话题名称,第二项为回调函数
ROS_INFO("服务器端已启动!!!");
// 5.处理请求并产生响应
// 6.spin()
ros::spin();
return 0;
}
打开CMakeLists.txt文件,修改文件名称:
编译:
打开终端,运行roscore,配置环境后运行程序:
在运行指令rosservice call huati_addints "num1: 0 num2: 0"时,num1 和 num2 后的数字可以更改,更改后sum输出的结果也会随之改变,其中huati_addints为在C++文件中自定义的话题名称。
(2)客户端实现
在src文件夹下新建一个.cpp文件,命名为demo02_client.cpp,编写客户端文件代码:
#include "ros/ros.h"
#include "plumbing_server_client/Addints.h"
/*
客户端:提交两个整数,并处理响应的结果
1.包含头文件;
2.初始化ROS节点;
3.创建节点句柄;
4.创建一个客户端对象;
5.提交请求并处理响应;
*/
int main(int argc, char *argv[])
{
setlocale(LC_ALL,"");
// 2.初始化ROS节点;
ros::init(argc,argv,"Client_01"); //节点名称要保证是唯一的
// 3.创建节点句柄;
ros::NodeHandle nh;
// 4.创建一个客户端对象;
ros::ServiceClient client = nh.serviceClient<plumbing_server_client::Addints>("huati_addints"); //话题名称要与服务器端保持一致
//5.提交请求并处理响应;
plumbing_server_client::Addints ai;
//5.1组织请求
ai.request.num1 = 100;
ai.request.num2 = 200;
//5.2处理响应
bool flag = client.call(ai);
if (flag)
{
ROS_INFO("响应成功!!!");
//获取响应结果
ROS_INFO("响应结果为:%d",ai.response.sum);
} else {
ROS_INFO("处理失败......");
}
return 0;
}
打开CMakeLists.txt文件,配置依赖:
编译:
打开终端,运行程序:
其中,num1和num2的数值在程序中已经给定,这样太过不方便,现在修改程序,使得能够在终端动态地修改num1和num2的数值:
#include "ros/ros.h"
#include "plumbing_server_client/Addints.h"
/*
客户端:提交两个整数,并处理响应的结果
1.包含头文件;
2.初始化ROS节点;
3.创建节点句柄;
4.创建一个客户端对象;
5.提交请求并处理响应;
实现参数的动态提交:
1.终端命令行格式: rosrun XXXX XXXX num1 num2
2.节点执行时,需要获取命令中的参数,并组织进 request
*/
int main(int argc, char *argv[])
{
setlocale(LC_ALL,"");
//优化实现,获取命令中的参数
if(argc != 3)
{
ROS_INFO("提交的参数个数不对!!!");
return 1;
}
// 2.初始化ROS节点;
ros::init(argc,argv,"Client_01"); //节点名称要保证是唯一的
// 3.创建节点句柄;
ros::NodeHandle nh;
// 4.创建一个客户端对象;
ros::ServiceClient client = nh.serviceClient<plumbing_server_client::Addints>("huati_addints"); //话题名称要与服务器端保持一致
//5.提交请求并处理响应;
plumbing_server_client::Addints ai;
//5.1组织请求
ai.request.num1 = atoi(argv[1]);
ai.request.num2 = atoi(argv[2]);
//5.2处理响应
bool flag = client.call(ai);
if (flag)
{
ROS_INFO("响应成功!!!");
//获取响应结果
ROS_INFO("响应结果为:%d",ai.response.sum);
} else {
ROS_INFO("处理失败......");
}
return 0;
}
打开终端,运行程序:
但程序还存在问题,如果先启动客户端,那么会出现请求异常,现在我们要做到如果先启动客户端,不要直接抛出异常,而是挂起,等服务器启动后,再正常请求。解决方案是在ROS中内置了相关函数,这些函数可以让客户端启动后挂起,等待服务器启动,修改程序:
#include "ros/ros.h"
#include "plumbing_server_client/Addints.h"
/*
客户端:提交两个整数,并处理响应的结果
1.包含头文件;
2.初始化ROS节点;
3.创建节点句柄;
4.创建一个客户端对象;
5.提交请求并处理响应;
实现参数的动态提交:
1.终端命令行格式: rosrun XXXX XXXX num1 num2
2.节点执行时,需要获取命令中的参数,并组织进 request
问题:
如果先启动客户端,那么会出现请求异常。
需求:
如果先启动客户端,不要直接抛出异常,而是挂起,等服务器启动后,再正常请求。
解决:
在ROS中内置了相关函数,这些函数可以让客户端启动后挂起,等待服务器启动。
*/
int main(int argc, char *argv[])
{
setlocale(LC_ALL,"");
//优化实现,获取命令中的参数
if(argc != 3)
{
ROS_INFO("提交的参数个数不对!!!");
return 1;
}
// 2.初始化ROS节点;
ros::init(argc,argv,"Client_01"); //节点名称要保证是唯一的
// 3.创建节点句柄;
ros::NodeHandle nh;
// 4.创建一个客户端对象;
ros::ServiceClient client = nh.serviceClient<plumbing_server_client::Addints>("huati_addints"); //话题名称要与服务器端保持一致
//5.提交请求并处理响应;
plumbing_server_client::Addints ai;
//5.1组织请求
ai.request.num1 = atoi(argv[1]);
ai.request.num2 = atoi(argv[2]);
//5.2处理响应
//调用判断服务器状态的函数
//函数1:
client.waitForExistence();
bool flag = client.call(ai);
if (flag)
{
ROS_INFO("响应成功!!!");
//获取响应结果
ROS_INFO("响应结果为:%d",ai.response.sum);
} else {
ROS_INFO("处理失败......");
}
return 0;
}
编译之后运行程序:
运行客户端后,程序挂起等待服务端,此时再运行服务端,程序正常执行:
三、使用Python实现服务通信
(1)创建服务端
在功能包目录下新建一个scirpts文件夹用来存放Python文件,在scripts文件夹下新建一个Python文件命名为demo01_server_p.py,编写服务端文件代码:
#! /usr/bin/env python
import rospy
from plumbing_server_client.srv import Addints,AddintsRequest,AddintsResponse
"""
服务端:解析客户端请求,产生响应。
1.导包;
2.初始化ROS节点;
3.创建服务端对象;
4.处理逻辑(回调函数);
5.spin()
"""
#参数:封装了请求数据
#返回值:响应数据
def doNum(request):
#1.解析提交的两个整数
num1 = request.num1
num2 = request.num2
#2.求和
sum = num1 + num2
#3.将结果封装进响应
response = AddintsResponse()
response.sum = sum
rospy.loginfo("服务器解析结果num1 = %d, num2 = %d, 响应的结果: sum = %d",num1,num2,sum)
return response
if __name__=="__main__":
# 2.初始化ROS节点;
rospy.init_node("Server_02")
# 3.创建服务端对象;
server = rospy.Service("huati02_addints",Addints,doNum)
rospy.loginfo("服务器已经启动!!!")
# 4.处理逻辑(回调函数);
# 5.spin()
rospy.spin()
在scripts文件夹下打开终端,赋予Python文件可执行权限:
打开CMakeList.txt文件,配置文件依赖:
保存后进行编译:
打开终端,运行roscore,运行程序,正常执行:
(2)创建客户端
在scripts文件夹下新建一个.py文件命名为demo02_client_p.py,编写客户端文件代码:
#! /usr/bin/env python
import rospy
from plumbing_server_client.srv import Addints,AddintsRequest,AddintsResponse
"""
客户端:组织并提交请求,处理服务端响应。
1.导包;
2.初始化ROS节点;
3.创建客户端对象;
4.组织请求数据,并发送请求;
5.处理响应;
"""
if __name__=="__main__":
# 2.初始化ROS节点;
rospy.init_node("Client_02")
# 3.创建客户端对象;
client = rospy.ServiceProxy("huati02_addints",Addints)
# 4.组织请求数据,并发送请求;
response = client.call(12,14)
# 5.处理响应;
rospy.loginfo("响应的数据:%d",response.sum)
添加可执行权限:
打开CMakeList.txt文件,配置文件依赖:
编译:
运行程序:
如上图所示,num1和num2是写在程序里面的,很不方便,现在优化实现动态传入参数,修改程序:
#! /usr/bin/env python
import rospy
import sys
from plumbing_server_client.srv import Addints,AddintsRequest,AddintsResponse
"""
客户端:组织并提交请求,处理服务端响应。
1.导包;
2.初始化ROS节点;
3.创建客户端对象;
4.组织请求数据,并发送请求;
5.处理响应;
优化实现:
可以在实现节点时,动态传入参数。
"""
if __name__=="__main__":
#判断参数长度
if len(sys.argv) != 3:
rospy.logerr("传入的参数不对!!!")
sys.exit(1)
# 2.初始化ROS节点;
rospy.init_node("Client_02")
# 3.创建客户端对象;
client = rospy.ServiceProxy("huati02_addints",Addints)
# 4.组织请求数据,并发送请求;
#解析传入的参数
num1 = int(sys.argv[1])
num2 = int(sys.argv[2])
response = client.call(num1,num2)
# 5.处理响应;
rospy.loginfo("响应的数据:%d",response.sum)
编译:
打开终端,运行程序:
程序运行成功。
但是程序依然存在一个问题,客户端先于服务端启动,会抛出异常。
现在需要户端先于服务端启动时,不要抛出异常而是挂起,等待服务端启动,再执行程序。解决方法是:ROS中内置了相关的函数,这些函数可以判断服务器的状态,如果服务端没有启动,那么就让客户端挂起。修改程序:
#! /usr/bin/env python
import rospy
import sys
from plumbing_server_client.srv import Addints,AddintsRequest,AddintsResponse
"""
客户端:组织并提交请求,处理服务端响应。
1.导包;
2.初始化ROS节点;
3.创建客户端对象;
4.组织请求数据,并发送请求;
5.处理响应;
优化实现:
可以在实现节点时,动态传入参数。
问题:
客户端先于服务端启动,会抛出异常。
需要:
客户端先于服务端启动时,不要抛出异常而是挂起,等待服务端启动,再执行程序。
实现:
ROS中内置了相关的函数,这些函数可以判断服务器的状态,如果服务端没有启动,那么就让客户端挂起。
"""
if __name__=="__main__":
#判断参数长度
if len(sys.argv) != 3:
rospy.logerr("传入的参数不对!!!")
sys.exit(1)
# 2.初始化ROS节点;
rospy.init_node("Client_02")
# 3.创建客户端对象;
client = rospy.ServiceProxy("huati02_addints",Addints)
# 4.组织请求数据,并发送请求;
#解析传入的参数
num1 = int(sys.argv[1])
num2 = int(sys.argv[2])
#等待服务启动
client.wait_for_service()
response = client.call(num1,num2)
# 5.处理响应;
rospy.loginfo("响应的数据:%d",response.sum)
当启动客户端时,程序挂起,不再抛出异常:
运行服务端,程序正常运行:
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/bian-cheng-ji-chu/106405.html