本文记录下ROS服务通信的实现,首先明确,ROS中的服务通信主要适用于偶然的,有实时要求的场景。服务通信基于客户-服务的架构,在主节点下,由服务端和客户端组成,服务端负责对请求做出响应,客户端发出请求和得到响应。
1 定义服务信息,该部分和话题通信中自定义消息的过程非常类似
1.1 新建服务消息类型 srv
Addints.srv代码内容:
"—"上方为请求信息,下方为响应信息
int32 num1
int32 num2
---
int32 sum
1.2 功能包配置
1 打开 packages.xml ,添加相应的依赖内容即可
<build_depend>message_generation</build_depend>
<exec_depend>message_runtime</exec_depend>
2 打开 CMakeLists.txt,配置相关内容
对应自定义的服务信息格式文件
配置好以上信息后,就开始编译项目,然后会发现项目根目录下的 devel 文件夹下,出现了服务信息的编译内容
然后将生成的编译文件,配置到 c_cpp_properties.json,这个主要是为了能够在C++或Python代码中,能够引到编写的内容。
源代码工程目录
C++实现
1 服务端
#include "ros/ros.h"
#include "plumbing_server_client/Addints.h"
/**
* @brief
*
* @param argc
* @param argv
* @return int
* 服务端解析服务信息,且作出反应
* 1 初始化ros节点
* 2 创建句柄
* 3 声明一个服务对象
* 4 处理请求并作出响应
* 5 spin 循环的话 spin_once()
*/
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("receive the num : %d,%d",num1,num2);
//2 作出响应
int sum = num1 + num2;
response.sum = sum;
ROS_INFO("process result: %d",sum);
return true;
}
int main(int argc,char *argv[]){
ros::init(argc,argv,"server_01");
ros::NodeHandle nh;
ros::ServiceServer server = nh.advertiseService("addints",doNums);
ROS_INFO("server start...");
ros::spin();
return 0;
}
2 客户端代码
#include "ros/ros.h"
#include "plumbing_server_client/Addints.h"
/*
* 服务端解析服务信息,且作出反应
* 1 初始化ros节点
* 2 创建句柄
* 3 声明一个客户端对象
* 4 处理请求并作出响应
*优化:动态增加两个参数
rosrun 包名 节点名 para1 para2
如果客户端先启动,然后服务端再启动,会抛出异常
则如果客户端启动,可先挂起,等服务端启动后再执行
*/
int main(int argc,char *argv[]){
setlocale(LC_ALL,"");
//判断是不是3个参数
if(argc !=3){
ROS_INFO("paramer num is wrong!");
return 1;
}
ros::init(argc,argv,"client_01");
ros::NodeHandle nh;
ros::ServiceClient client = nh.serviceClient<plumbing_server_client::Addints>("addints");
//提交请求获取响应
plumbing_server_client::Addints ai;
//atoi:char to int
ai.request.num1 = atoi(argv[1]);
ai.request.num2 = atoi(argv[2]);
//wait the service start...
client.waitForExistence();
//这两个都可
//ros::service::waitForService("addints");
//处理响应
bool flag = client.call(ai);
if(flag){
ROS_INFO("process success... %d",ai.response.sum);
}else{
ROS_INFO("process false!");
}
return 0;
}
3 配置 CMakeLists.txt
增加节点编译依赖
add_dependencies(server_01_node ${PROJECT_NAME}_gencpp)
add_dependencies(client_01_node ${PROJECT_NAME}_gencpp)
4 配置好后,开始运行
roscore#启动主节点
启动服务端
source ./devel/setup.bash
rosrun plumbing_server_client server_01_node
启动客户端
source ./devel/setup.bash
#后面两个数值为传入的num1 和 num2
rosrun plumbing_server_client client_01_node 10 20
Python实现
1 服务端代码
#! /usr/bin/env python
# -*- coding:utf-8 -*-
import rospy
from plumbing_server_client.srv import Addints,AddintsRequest,AddintsResponse
#from plumbing_server_client.srv import *
# 0 导包
# 1 初始化 ros节点
# 2 新建服务端对象
# 3 处理逻辑(回调函数)
# 4 spin()
#封装request对象
def doNum(request):
num1 = request.num1
num2 = request.num2
sum = num1 + num2
response = AddintsResponse()
response.sum = sum
rospy.loginfo("num1 = %d , num2 = %d, response cal : %d", num1, num2, sum)
return response
if __name__=="__main__":
rospy.init_node("server_py")
server = rospy.Service("addints_py",Addints,doNum)
rospy.loginfo("server have started...")
rospy.spin()
2 客户端代码
#! /usr/bin/env python
# -*- coding:utf-8 -*-
import rospy
from plumbing_server_client.srv import Addints,AddintsRequest,AddintsResponse
import sys
#对于偶然 实时性要求的场景
#客户端:组织并提交请求,处理服务端响应
# 1 初始化ros节点
# 2 创建客户端对象
# 3 组织请求数据 并发送请求
# 4 处理响应
if __name__=="__main__":
#sys.argv[0]为文件名称
if len(sys.argv) != 3:
rospy.logerr("the paramers number is wrong!")
sys.exit(1)
rospy.init_node("client_py")
client = rospy.ServiceProxy("addints_py",Addints)
#解析传入的参数
num1=int(sys.argv[1])
num2=int(sys.argv[2])
#client.wait_for_service()
rospy.wait_for_service("addints_py")
response = client.call(num1,num2)
rospy.loginfo("response data:%d",response.sum)
CMakeLists.txt配置
准备运行:
需要切换到python的脚本文件夹中,更改Python脚本的权限
chmod +x *.py
roscore#启动主节点
启动服务端
source ./devel/setup.bash
rosrun plumbing_server_client server.py
启动客户端
source ./devel/setup.bash
rosrun plumbing_server_client client.py 10 20