目录
简介
理论模型
服务通信自定srv
创建srv
编辑配置文件
C++实现
vscode配置
服务端实现
客户端实现
优化
Python实现
服务端实现
客户端实现
简介 服务通信也是ROS中一种极其常用的通信模式,服务通信是基于请求响应模式的,是一种应答机制。也即: 一个节点A向另一个节点B发送请求,B接收处理请求并产生响应结果返回给A
比如如下场景:
机器人巡逻过程中,控制系统分析传感器数据发现可疑物体或人... 此时需要拍摄照片并留存。
上述场景中,就使用到了服务通信。
- 一个节点需要向相机节点发送拍照请求,相机节点处理请求,并返回处理结果
文章图片
文章图片
服务器启动,客户端发送客户数据,服务端进行计算并返回结果
理论模型
文章图片
服务通信自定srv
文章图片
创建一个新的功能包
文章图片
文章图片
建立依赖
文章图片
创建srv 然后是创建自定义的srv
文章图片
复制以下代码
int32 num1
int32 num2
---
int32 sum
注意:---用来分隔请求与响应
编辑配置文件
文章图片
文章图片
构建的时候依赖这个包
文章图片
生成服务
文章图片
生成依赖
文章图片
这是find_package里面依赖的包
然后
ctrl shift b编译一下
文章图片
会生成很多中间文件
文章图片
生成请求端(客户端)文件
生成服务端文件
文章图片
请求内容
【ros|ROS服务通信(七)C++、Python实现】
文章图片
服务内容
python里也生成了对应文件
文章图片
小结:
实际上跟msg很像,只不过多了一个---来区分请求和响应的消息
C++实现
文章图片
文章图片
流程
- 编写服务端实现;
- 编写客户端实现;
- 编辑配置文件;
- 编译并执行。
文章图片
文章图片
复制路径
文章图片
服务端实现 复制以下代码
/*
需求:
编写两个节点实现服务通信,客户端节点需要提交两个整数到服务器
服务器需要解析客户端提交的数据,相加后,将结果响应回客户端,
客户端再解析服务器实现:
1.包含头文件
2.初始化 ROS 节点
3.创建 ROS 句柄
4.创建 服务 对象
5.回调函数处理请求并产生响应
6.由于请求有多个,需要调用 ros::spin()*/
#include "ros/ros.h"
#include "demo03_server_client/AddInts.h"// bool 返回值由于标志是否处理成功
bool doReq(demo03_server_client::AddInts::Request& req,
demo03_server_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
resp.sum = num1 + num2;
return true;
}int main(int argc, char *argv[])
{
setlocale(LC_ALL,"");
// 2.初始化 ROS 节点
ros::init(argc,argv,"AddInts_Server");
// 3.创建 ROS 句柄
ros::NodeHandle nh;
// 4.创建 服务 对象
ros::ServiceServer server = nh.advertiseService("AddInts",doReq);
ROS_INFO("服务已经启动....");
//5.回调函数处理请求并产生响应
//6.由于请求有多个,需要调用 ros::spin()
ros::spin();
return 0;
}
CMakeLists配置
文章图片
ctrl / 取消注释
文章图片
文章图片
上面的有些错误看下图改正一下即可
文章图片
然后测试一下
文章图片
客户端实现
/*
需求:
编写两个节点实现服务通信,客户端节点需要提交两个整数到服务器
服务器需要解析客户端提交的数据,相加后,将结果响应回客户端,
客户端再解析服务器实现:
1.包含头文件
2.初始化 ROS 节点
3.创建 ROS 句柄
4.创建 客户端 对象
5.请求服务,接收响应*/
// 1.包含头文件
#include "ros/ros.h"
#include "demo03_server_client/AddInts.h"int main(int argc, char *argv[])
{
setlocale(LC_ALL,"");
// 调用时动态传值,如果通过 launch 的 args 传参,需要传递的参数个数 +3
if (argc != 3)
// if (argc != 5)//launch 传参(0-文件路径 1传入的参数 2传入的参数 3节点名称 4日志路径)
{
ROS_ERROR("请提交两个整数");
return 1;
}// 2.初始化 ROS 节点
ros::init(argc,argv,"AddInts_Client");
// 3.创建 ROS 句柄
ros::NodeHandle nh;
// 4.创建 客户端 对象
ros::ServiceClient client = nh.serviceClient("AddInts");
//等待服务启动成功
//方式1
ros::service::waitForService("AddInts");
//方式2
// client.waitForExistence();
// 5.组织请求数据
demo03_server_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 1;
}return 0;
}
修改CMakeLists
文章图片
然后
ctrl shift b编译
开始测试
文章图片
输入数字
文章图片
优化
文章图片
文章图片
文章图片
再启动服务端
文章图片
此时就可以了
Python实现
文章图片
服务端实现新建文件并且复制以下代码
文章图片
#! /usr/bin/env python
"""
需求:
编写两个节点实现服务通信,客户端节点需要提交两个整数到服务器
服务器需要解析客户端提交的数据,相加后,将结果响应回客户端,
客户端再解析服务器端实现:
1.导包
2.初始化 ROS 节点
3.创建服务对象
4.回调函数处理请求并产生响应
5.spin 函数"""
# 1.导包
import rospy
from demo03_server_client.srv import AddInts,AddIntsRequest,AddIntsResponse
# 回调函数的参数是请求对象,返回值是响应对象
def doReq(req):
# 解析提交的数据
sum = req.num1 + req.num2#num1、num2来自于.srv文件
rospy.loginfo("提交的数据:num1 = %d, num2 = %d, sum = %d",req.num1, req.num2, sum)# 创建响应对象,赋值并返回
# resp = AddIntsResponse()
# resp.sum = sum
resp = AddIntsResponse(sum)
return respif __name__ == "__main__":
# 2.初始化 ROS 节点
rospy.init_node("addints_server_p")
# 3.创建服务对象
server = rospy.Service("AddInts",AddInts,doReq)
# 4.回调函数处理请求并产生响应
# 5.spin 函数
rospy.spin()
授予执行权限
文章图片
配置CMakeLists
文章图片
然后进行编译
文章图片
客户端实现 新建文件
文章图片
复制以下代码
#! /usr/bin/env python"""
需求:
编写两个节点实现服务通信,客户端节点需要提交两个整数到服务器
服务器需要解析客户端提交的数据,相加后,将结果响应回客户端,
客户端再解析客户端实现:
1.导包
2.初始化 ROS 节点
3.创建请求对象
4.发送请求
5.接收并处理响应优化:
加入数据的动态获取"""
#1.导包
import rospy
from demo03_server_client.srv import *
import sysif __name__ == "__main__":#优化实现
if len(sys.argv) != 3:
rospy.logerr("请正确提交参数")
sys.exit(1)# 2.初始化 ROS 节点
rospy.init_node("AddInts_Client_p")
# 3.创建请求对象
client = rospy.ServiceProxy("AddInts",AddInts)
# 请求前,等待服务已经就绪
# 方式1:
# rospy.wait_for_service("AddInts")
# 方式2
client.wait_for_service()
# 4.发送请求,接收并处理响应
# 方式1
# resp = client(3,4)
# 方式2
# resp = client(AddIntsRequest(1,5))
# 方式3
req = AddIntsRequest()
# req.num1 = 100
# req.num2 = 200 #优化
req.num1 = int(sys.argv[1])
req.num2 = int(sys.argv[2]) resp = client.call(req)
rospy.loginfo("响应结果:%d",resp.sum)
授予权限
文章图片
修改CMakeLists
文章图片
然后编译
文章图片
进行终端测试
文章图片
推荐阅读
- linux|Linux kernel移植笔记
- linux|虚拟机安装gcc
- 学习札记|【学习札记NO.00004】Linux Kernel Pwn学习笔记 I(一切开始之前)
- Python|Ubuntu18.04下使用Anaconda安装opencv4
- ROSERROR : CMake Error at /opt/ros/melodic/share/cv_bridge/cmake/cv_bridgeConfig.cmake:113 (message)
- centos|操作系统镜像资源下载
- 工具类|推荐7款超级好用的终端工具 —— SSH+FTP
- Linux|再见 Xshell~ 这款开源的终端工具逼格更高
- 数据库|redis