ROS学习笔记6-基于Ubuntu 16.04虚拟机编写服务器与客户端节点通讯

        服务由服务服务器(service server)和服务客户端(service client)组成,其中服务服务器仅在收到请求(request)时才会响应(response),而服务客户端则会发送请求并接收响应。与话题不同,服务是一次性消息通信。因此,当服务的请求和响应完成时,两个节点的连接会被断开。

这种服务通常在让机器人执行特定任务时用到。或者用于需要在特定条件下做出反应的节点。由于它是一次性的通信方式,因此对网络的负载很小,所以是一种非常有用的通信手段,例如被用作一种代替话题的通信手段。

创建功能包

以下命令创建ros_tutorials_service功能包。这个功能包依赖于message_generation、std_msgs和roscpp功能包,因此将他们用作依赖性选项。其中,message_generation是用于创建新消息的功能包,std_msgs是ROS的标准消息功能包,roscpp是在ROS中使用C/C++的客户端程序库,这些都必须在创建功能包之前安装好。用户可以在创建功能包时指定这些依赖关系,但也可以在创建功能包之后直接在package.xml中对其进行修改。

$ cd ~/catkin_ws/src

$ catkin_create_pkg ros_tutorials_service message_generation std_msgs roscpp

ROS学习笔记6-基于Ubuntu 16.04虚拟机编写服务器与客户端节点通讯

修改功能包配置文件(package.xml)

经过前面学习笔记4、5的经验,发现,这个文件其实可以不用改的,也就设置下版本号就行了。

修改构建配置文件(CMakeLists.txt)

$sudo gedit CMakeLists.txt

ROS学习笔记6-基于Ubuntu 16.04虚拟机编写服务器与客户端节点通讯

修改内容如下:

add_service_files(
    FILES
    SrvTutorial.srv
#   Service1.srv
#   Service2.srv
)
generate_messages(
  DEPENDENCIES
  std_msgs
)
catkin_package(
#  INCLUDE_DIRS include
   LIBRARIES ros_tutorials_service
#  CATKIN_DEPENDS message_generation roscpp std_msgs
   CATKIN_DEPENDS roscpp std_msgs
#  DEPENDS system_lib
)
add_executable(service_server src/service_server.cpp)
add_executable(service_client src/service_client.cpp)
add_dependencies(service_server ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})
add_dependencies(service_client ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})
target_link_libraries(service_server ${catkin_LIBRARIES})
target_link_libraries(service_client ${catkin_LIBRARIES})

 

创建服务文件与节点

创建服务器文件

CMakeLists.txt文件中加了下面的选项。

add_service_files(FILES SrvTutorial.srv)

这是在构建本次的节点中使用的SrvTutorial.srv时所包含的内容。现在您还没有创建

SrvTutorial.srv,请按以下顺序创建它。

$ roscd ros_tutorials_service  → 移动到功能包目录

$ mkdir srv                → 在功能包中创建一个名为srv的新服务目录

$ cd srv                   → 转到创建的srv目录

$ gedit SrvTutorial.srv        → 新建和修改SrvTutorial.srv文件

ROS学习笔记6-基于Ubuntu 16.04虚拟机编写服务器与客户端节点通讯

内容很简单。让我们以int64格式设计服务请求(request)a、b,和结果服务响应(response)result,如下所示。“---”是分隔符,用于分隔请求和响应。除了请求和响应之间有一个分隔符之外,它与上述话题的消息相同。

添加如下内容:

int64 a

int64 b

---

int64 result

ROS学习笔记6-基于Ubuntu 16.04虚拟机编写服务器与客户端节点通讯

 

创建服务服务器节点

在CMakeLists.txt文件中添加了如下选项来生成可执行文件。

add_executable(service_server src/service_server.cpp)

 

换句话说,是构建service_server.cpp文件来创建service_server可执行文件。我们按以下顺序编写一个具有服务服务器节点功能的程序吧。

$ roscd ros_tutorials_service/src    → 移动到功能包的源代码目录src

$ gedit service_server.cpp         → 创建和修改源文件

ROS学习笔记6-基于Ubuntu 16.04虚拟机编写服务器与客户端节点通讯

 

插入如下代码:

 

// ROS的基本头文件
#include "ros/ros.h"

// SrvTutorial服务头文件(构建后自动生成)
#include "ros_tutorials_service/SrvTutorial.h"

// 如果有服务请求,将执行以下处理
// 将服务请求设置为req,服务响应则设置为res。
bool calculation(ros_tutorials_service::SrvTutorial::Request &req,
ros_tutorials_service::SrvTutorial::Response &res)
{
    // 在收到服务请求时,将a和b的和保存在服务响应值中
    res.result = req.a + req.b;

    // 显示服务请求中用到的a和b的值以及服务响应result值
    ROS_INFO("request: x=%ld, y=%ld", (long int)req.a, (long int)req.b);
    ROS_INFO("sending back response: %ld", (long int)res.result);
    return true;
}

// 节点主函数
int main(int argc, char **argv)
{
    // 初始化节点名称
    ros::init(argc, argv, "service_server");

    // 声明节点句柄
    ros::NodeHandle nh;
    
    // 声明服务服务器
    // 声明利用ros_tutorials_service功能包的SrvTutorial服务文件的
    // 服务服务器ros_tutorials_service_server
    // 服务名称是ros_tutorial_srv,且当有服务请求时,执行calculation函数。
    ros::ServiceServer ros_tutorials_service_server = nh.advertiseService("ros_tutorial_srv", 
    calculation);
    ROS_INFO("ready srv server!");

    // 等待服务请求
    ros::spin();    
    return 0;
}

ROS学习笔记6-基于Ubuntu 16.04虚拟机编写服务器与客户端节点通讯

ROS学习笔记6-基于Ubuntu 16.04虚拟机编写服务器与客户端节点通讯

创建客户端文件与节点

在CMakeLists.txt文件中添加了一个选项来生成可执行文件。

add_executable(service_client src/service_client.cpp)

换句话说,是通过构建service_client.cpp文件来创建service_client可执行文件。我们按以下顺序编写一个执行服务客户端节点功能的程序吧。

$ roscd ros_tutorials_service/src   → 移动到功能包的源代码目录src

$ gedit service_client.cpp         → 创建和修改源文件

ROS学习笔记6-基于Ubuntu 16.04虚拟机编写服务器与客户端节点通讯

添加如下代码:

// ROS的基本头文件
#include "ros/ros.h"

// SrvTutorial服务头文件(构建后自动生成)
#include "ros_tutorials_service/SrvTutorial.h"

// 使用atoll函数所需的库
#include <cstdlib>

// 节点主函数
int main(int argc, char **argv)
{
  //  初始化节点名称
  ros::init(argc, argv, "service_client");

  // 处理输入值错误
  if (argc != 3)
  {
    ROS_INFO("cmd : rosrun ros_tutorials_service service_client arg0 arg1");
    ROS_INFO("arg0: double number, arg1: double number");
    return 1;
  }
  // 声明与ROS系统通信的节点句柄
  ros::NodeHandle nh;

  // 声明客户端,声明利用ros_tutorials_service功能包的SrvTutorial服务文件的
  // 服务客户端ros_tutorials_service_client。
  // 服务名称是"ros_tutorial_srv"
  ros::ServiceClient ros_tutorials_service_client = 
  nh.serviceClient<ros_tutorials_service::SrvTutorial>("ros_tutorial_srv");
  
  // 声明一个使用SrvTutorial服务文件的叫做srv的服务
  ros_tutorials_service::SrvTutorial srv;

  // 在执行服务客户端节点时用作输入的参数分别保存在a和b中
  srv.request.a = atoll(argv[1]);
  srv.request.b = atoll(argv[2]);

  // 请求服务,如果请求被接受,则显示响应值
  if (ros_tutorials_service_client.call(srv))
  {
    ROS_INFO("send srv, srv.Request.a and b: %ld, %ld", (long int)srv.request.a, (long int)srv.request.b);
    ROS_INFO("receive srv, srv.Response.result: %ld", (long int)srv.response.result);
  }
  else
  {
    ROS_ERROR("Failed to call service ros_tutorial_srv");
    return 1;
  }
  return 0;
}

ROS学习笔记6-基于Ubuntu 16.04虚拟机编写服务器与客户端节点通讯

然后保存退出

ROS学习笔记6-基于Ubuntu 16.04虚拟机编写服务器与客户端节点通讯

构建节点

使用以下命令构建ros_tutorials_service功能包的服务文件、服务服务器节点和客户端节点。ros_tutorials_service功能包的源文件位于“~/catkin_ws/src/ros_tutorials_service/src”目录中,服务文件位于“~/catkin_ws/src/ros_tutorials_service/srv”目录中。

$ cd ~/catkin_ws && catkin_make    → 转到catkin目录并运行catkin构建

生成的文件位于“~/catkin_ws/build”目录和“~/catkin_ws/devel”目录。目录“~/catkin_ws/build”包含catkin构建中使用的配置,“~/catkin_ws/devel/lib/ros_tutorials_service”包含可执行文件,“~/catkin_ws/devel/include/ros_tutorials_service” 保存从消息文件自动生成的服务头文件。如果您想知道这些文件,请进入各自的目录查看。

ROS学习笔记6-基于Ubuntu 16.04虚拟机编写服务器与客户端节点通讯

运行测试

 

运行服务器

$ roscore

$ rosrun ros_tutorials_service service_server

ROS学习笔记6-基于Ubuntu 16.04虚拟机编写服务器与客户端节点通讯

运行客户端

$ rosrun ros_tutorials_service service_client 2 3

ROS学习笔记6-基于Ubuntu 16.04虚拟机编写服务器与客户端节点通讯

从上面编写的代码可知,在运行服务客户端时输入的执行参数2和3会被作为服务请求值。结果,2和3分别请求服务作为a和b值,后来作为结果值,作为响应值收到了这两者的总和。在这种情况下,它只是用作执行参数,但在实际使用中,可以用指令代替,可以将要计算的值和触发变量用作服务请求值。

rosservice call命令的用法

服 务 请 求 可 以 由 s e r v i c e_c l i e n t 等 服 务 客 户 端 节 点 来 执 行 , 但 有 一 种 使 用“rosservice call”或者rqt的serviceCaller的方法。我们来看看如何使用rosservice call吧。

在下面的命令中执行rosservice call命令之后,写入相应的服务名称,例如/ros_tutorial_srv,然后写入服务请求所需的参数即可。

$ rosservice call /ros_tutorial_srv 10 2

result: 12

ROS学习笔记6-基于Ubuntu 16.04虚拟机编写服务器与客户端节点通讯

在前面的例子中,我们如下面的服务文件,将int64类型的a和b设置为请求,所以我们输入了10和2作为参数。服务响应的结果是int64的result以12的值返回。

int64 a

int64 b

---

int64 result

工程下载:

https://download.****.net/download/gs1069405343/11029730