【ROS入门】使用 ROS 服务(Service)机制实现同步请求与答复

文章结构

  • 任务要求
  • 话题模型
  • 实现步骤
    • 自定义srv
      • 定义srv文件
      • 编辑配置文件
      • 编译
    • 自定义srv调用
      • vscode配置
      • 编写服务端实现
      • 编写客户端实现
    • 执行
      • 启动roscore
      • 编译
      • 启动客户端和服务端
        • 编译
        • 启动roscore
        • 启动节点

任务要求

编写代码实现 ROS 中的服务请求与答复:

  • 创建服务端,注册 Service
  • 当服务端收到客户端 Service 请求(携带整型参数 a,b)后服务端返回 a,b 的和给客户端
  • 客户端输出结果。

话题模型

【ROS入门】使用 ROS 服务(Service)机制实现同步请求与答复_第1张图片
ROS Master 负责保管 Server 和 Client 注册的信息,并匹配话题相同的 Server 与 Client ,帮助 Server 与 Client 建立连接,连接建立后,Client 发送请求信息,Server 返回响应信息。

实现步骤

自定义srv

在服务通信中,客户端提交两个整数至服务端,服务端求和并响应结果到客户端。因此,需要创建服务器与客户端通信的数据载体。

  1. 按照固定格式创建srv文件

  2. 编辑配置文件

  3. 编译生成中间文件

定义srv文件

进入工作空间,在src文件夹下创建新的功能包:

【ROS入门】使用 ROS 服务(Service)机制实现同步请求与答复_第2张图片

在新的功能包中创建名为srv的文件夹,并在该文件夹中创建srv文件,这里将其命名为addInts.srv:

【ROS入门】使用 ROS 服务(Service)机制实现同步请求与答复_第3张图片

服务通信中,数据分成两部分,请求与响应,在 srv 文件中请求和响应使用—分割,具体实现如下:

# 客户端请求时发送的两个数字
int32 a
int32 b
---
# 服务器响应发送的数据
int32 sum

注意,行末不要打分号

编辑配置文件

package.xml中添加编译依赖与执行依赖

  <build_depend>message_generationbuild_depend>
  <exec_depend>message_runtimeexec_depend>

【ROS入门】使用 ROS 服务(Service)机制实现同步请求与答复_第4张图片

CMakeLists.txt编辑 srv 相关配置:

find_package(catkin REQUIRED COMPONENTS
  roscpp
  rospy
  std_msgs
  message_generation
)
# 需要加入 message_generation,必须有 std_msgs

【ROS入门】使用 ROS 服务(Service)机制实现同步请求与答复_第5张图片

add_service_files(
  FILES
  addInt.srv
)

由于在srv文件内使用的数据类型都是基于标准消息的,即依赖于standard_messages,故有了下面的修改:

generate_messages(
  DEPENDENCIES
  std_msgs
)

【ROS入门】使用 ROS 服务(Service)机制实现同步请求与答复_第6张图片
前面的find_package是指创建的功能包所依赖的包,而catkin_package是指find_package所依赖的包:

catkin_package(
#  INCLUDE_DIRS include
#  LIBRARIES service_demo
 CATKIN_DEPENDS roscpp rospy std_msgs message_runtime
#  DEPENDS system_lib
)

【ROS入门】使用 ROS 服务(Service)机制实现同步请求与答复_第7张图片

编译

命令行中:

$ catkin_make

或者在vscode中:ctrl+shift+B:

【ROS入门】使用 ROS 服务(Service)机制实现同步请求与答复_第8张图片

自定义srv调用

编写服务通信,客户端提交两个整数至服务端,服务端求和并响应结果到客户端。

vscode配置

找到devel/include的路径
在这里插入图片描述
在.vscode中的c_cpp_properties.json中修改

{
    "configurations": [
        {
            "browse": {
                "databaseFilename": "",
                "limitSymbolsToIncludedHeaders": true
            },
            "includePath": [
                "/opt/ros/noetic/include/**",
                "/usr/include/**",
                "/xxx/yyy工作空间/devel/include/**" //配置 head 文件的路径 
            ],
            "name": "ROS",
            "intelliSenseMode": "gcc-x64",
            "compilerPath": "/usr/bin/gcc",
            "cStandard": "c11",
            "cppStandard": "c++17"
        }
    ],
    "version": 4
}

【ROS入门】使用 ROS 服务(Service)机制实现同步请求与答复_第9张图片

编写服务端实现

编写两个节点实现服务通信,客户端节点需要提交两个整数到服务器。服务器需要解析客户端提交的数据,相加后,将结果响应回客户端,客户端再解析。

//1.头文件
#include "ros/ros.h"
#include "service_demo/addInt.h"    //该头文件名字和srv文件一致

//5.处理请求并产生响应
bool doNums(service_demo::addInt::Request &request,
            service_demo::addInt::Response &response)   //请求对象和响应对象以参数的方式传入
{
    //1.处理请求
    int a = request.a;
    int b = request.b;
    ROS_INFO("收到的请求数据:a = %d, b = %d",a,b);

    //2.组织响应
    int sum = a+b;
    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,"addInt_Server");   //节点名称需要保持唯一

    //3.创建ROS句柄
    ros::NodeHandle n;
    
    //4.创建服务对象
    //参数1为话题名称/主题名称,用于将服务端和客户端关联在一起
    //参数2是一个回调函数,用于处理请求,返回值为布尔类型
    ros::ServiceServer server = n.advertiseService("addInt",doNums);   

    //6.多请求,需要调用ros::spin()
    ros::spin();
    return 0;
}


随后在CMakeLists进行编译设置修改:

【ROS入门】使用 ROS 服务(Service)机制实现同步请求与答复_第10张图片

编写客户端实现

实现参数的动态提交:

  1. 格式:rosrun xxxx xxxx 12 34
  2. 节点执行时,需要获取命令中的参数,并组织进request
// 1.包含头文件
// 1.包含头文件
#include "ros/ros.h"
#include "service_demo/addInt.h"

int main(int argc, char *argv[])
{
    //如果输入的参数个数不对,会进行提示。argc为传入参数的个数。argv[]为参数的数组
    if(argc != 3)
    {
        ROS_INFO("提交的参数个数不对");
        return 1;
    }

    setlocale(LC_ALL,"");

    // 2.初始化 ROS 节点
    ros::init(argc,argv,"addInt_Client");

    // 3.创建 ROS 句柄
    ros::NodeHandle n;

    // 4.创建 客户端 对象
    ros::ServiceClient client = n.serviceClient<service_demo::addInt>("addInt");

    // 5.组织请求数据
    service_demo::addInt ai;

    //5-1.组织请求
    //从argv数组中提取参数,将字符串类型转为整形
    ai.request.a = atoi(argv[1]);
    ai.request.b = atoi(argv[2]);

    //5-2.处理响应
    //调用判断服务器状态的函数,用于等待服务
    //函数1
    client.waitForExistence();
    //函数2
    // ros::service::waitForService("addInt");
    //客户端请求服务器
    bool flag = client.call(ai);
    if (flag)
    {
        ROS_INFO("响应成功");
        //获取结果
        ROS_INFO("响应结果 = %d",ai.response.sum);
    }
    else
    {
        ROS_ERROR("请求处理失败....");
        return 1;
    }

    return 0;
}


随后在CMakeLists文件中修改配置:

【ROS入门】使用 ROS 服务(Service)机制实现同步请求与答复_第11张图片

执行

启动roscore

$ roscore

【ROS入门】使用 ROS 服务(Service)机制实现同步请求与答复_第12张图片

编译

命令行中:

$ catkin_make

或者在vscode中:ctrl+shift+B:

【ROS入门】使用 ROS 服务(Service)机制实现同步请求与答复_第13张图片

启动客户端和服务端

编译
$ catkin_make

【ROS入门】使用 ROS 服务(Service)机制实现同步请求与答复_第14张图片

启动roscore
$ roscore
启动节点
$ source ./devel/setup.bash
$ service_demo service_demo_server_c

再开一个终端

$ source ./devel/setup.bash
$ rosrun rosrun service_demo service_demo_client_c 10 10

效果如下:
【ROS入门】使用 ROS 服务(Service)机制实现同步请求与答复_第15张图片

你可能感兴趣的:(ROS,机器人)