ROS第五课:服务通信

服务通信简介

服务通信适用于对实时性有要求、具有一定逻辑处理的应用场景。
三个角色:master、server、client。
注意点:①保证顺序,客户端发起请求时,服务端需已启动;②客户端和服务端都可以存在多个。

自定义内容的服务通信

  • 服务通信创建srv
  1. 按照固定格式创建srv文件
    ①在工作空间下src文件夹中创建功能包plumbing_server_client,并添加依赖;
    ②在功能包中新建srv文件夹,并在文件夹下新建srv文件:AddInts.srv;
    ③srv文件中输入内容:
int32 num1
int32 num2
---
#请求与相应之间以---隔开
int32 sum
  1. 编辑内置文件
    ①package.xml文件配置
    与前相同;添加message_generation与message_runtime依赖;
    ②CMakeList.txt文件配置
    (1)find_package 下添加message_generation
    (2)释放add_service_files,并修改srv文件名为AddInts.srv;
    (3)释放generate_message对std_msgs的依赖;
    (4)catkin_package中释放CATKIN_DEPENDS行。//find_package是当前创建的功能包所依赖的包,catkin_package是依赖包所依赖的功能包。

  2. 编译生成中间文件
    编译一下,即可生成中间文件。
    其中C++使用的中间文件在工作空间下的devel/include/plumbing_server_client文件夹下;
    python使用的中间文件为devel/lib/python2.7(此处依实际python版本号而定)/plumbing_server_client下的AddInts.py。
    为了后续编辑代码时可以代码补齐,这里最好进行一下vscode的配置,将生成的中间文件所在的目录配置进c_cpp_properties.json的includePath中。具体操作方法同前。

  • 服务端实现
  1. 新建文件
    在功能包下src文件中新建demo01_server.cpp;
  2. 编辑代码
#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,"heiShui");//节点名称需唯一
        //  3.创建节点句柄
        ros::NodeHandle nh;
        //  4.创建一个服务对象
        ros::ServiceServer server = nh.advertiseService("addInts",doNums);
        ROS_INFO("服务器端启动");
        //  5.处理请求并产生响应
        //  6.spin()
        ros::spin();
    return 0;
}

  1. 编写配置CMakeList.txt文件
    ①释放(建议先复制)add_executable行,并写入文件名和映射名;
    ②释放target_link_librarues并写入映射名;
    ③释放add_dependencies//保证先编译srv文件再编译请求方和响应方文件;
    第一个参数为映射名,第二个为${PROJECT_NAME}_gencpp
  2. 编译并测试是否成功运行
    编译后即可运行,可使用下列命令进行测试,运行结果应为6:
rosservice call addInts "num1: 1 num2: 5"
  • 客户端实现
  1. 新建文件
    方法同前;
  2. 编辑代码
#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,"daBao");
        //  3.创建节点句柄
        ros::NodeHandle nh;
        //  4.创建一个客户端对象
        ros::ServiceClient client = nh.serviceClient<plumbing_server_client::AddInts>("addInts");
        //  5.提交请求并处理响应
        plumbing_server_client::AddInts ai;//创建一个AddInts对象
        //5-1.组织请求
        ai.request.num1 = 150;
        ai.request.num2 = 300;
        //5-2.处理响应
        bool flag = client.call(ai);
        if (flag)
        {
            ROS_INFO("响应成功!");
            ROS_INFO("响应结果 = %d",ai.response.sum);
        } else
        {
            ROS_INFO("处理失败....");
        }
   
    return 0;
}

  1. 编写配置文件
    方法同前
  2. 编译并运行
    使用命令分别依次运行roscore、服务端、客户端。

几点优化

  • 考虑客户端先于服务端运行的情况
    实际应用中,我们肯定要考虑客户端先于服务端运行的情况,此时的解决思路是,客户端运行之前自动检测服务端是否允许,若无,则一直等待而非运行客户端从而输出错误信息,可以在“处理响应”之前使用下列两个函数实现此功能:
        //函数1
        client.waitForExistence();
        //函数2
        ros::service::waitForService("addInts");//此处括号内为服务名称;
  • 内容可变的通信信息
    此时需注意三点:
    ①命令格式为rosrun <包名> <节点名> num1 num2
    ②需提前检验输入的数据个数是否正确,并获取命令中的参数,组织进request;
    ③使用atoi()将字符串转变为整型数字;
    代码如下:
#include "ros/ros.h"
#include "plumbing_server_client/AddInts.h"

/*
      客户端:提交两个整数,并处理响应的结果
         1.包含头文件
         2.初始化ROS节点
         3.创建节点句柄
         4.创建一个客户端对象
         5.提交请求并处理响应
         
      实现参数的动态提交:
         1.格式:rosrun <包名> <节点名> 12 34
         2.节点执行时,需要获取命令中的参数,并组织进request

*/

int main(int argc, char *argv[])
{
        setlocale(LC_ALL,"");
        //优化实现,要获取命令中的参数
        if (argc != 3) //argc三个参数??
        {
            ROS_INFO("提交的参数个数不对!");
            return 1;
        }
        
        //  2.初始化ROS节点
        ros::init(argc,argv,"daBao");
        //  3.创建节点句柄
        ros::NodeHandle nh;
        //  4.创建一个客户端对象
        ros::ServiceClient client = nh.serviceClient<plumbing_server_client::AddInts>("addInts");
        //  5.提交请求并处理响应
        plumbing_server_client::AddInts ai;//创建一个AddInts对象
        //5-1.组织请求
        ai.request.num1 = atoi(argv[1]);//atoi()将字符串类型变为整型值
        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;
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值