首页 关于
树枝想去撕裂天空 / 却只戳了几个微小的窟窿 / 它透出天外的光亮 / 人们把它叫做月亮和星星
目录

简单的Service服务器和客户端

我们在介绍ROS系统运行时的Graph和Node时,介绍过Node之间的通信主要有Topic和Service两种形式。 我们已经实现了一种简单的Topic发布者和订阅者, 以及描述Topic消息类型的msg文件。 在本文中,我们将讨论Node之间的另外一种主要通信方式Service,创建一个服务器计算两个数的加法。

1. 创建srv文件

与Topic类似,我们需要用一个文本文件来描述通信数据的格式,这个用于Service的文件就称为srv文件。srv文件与msg文件基本一致,也是一行定义一个变量,由数据类型和数据名称两个部分。 由于Service是服务,一次通信是有数据输入和输出的。比如说我们将要创建的加法服务器,它就需要输入两个数据作为两个加数,然后输出一个数据作为和。所以,srv文件是由两个部分组成的, 之间用一行'---'分割,上面为输入数据,下面为输出数据。

让我们在beginner_tutorials下建立一个叫做srv的子目录,并创建一个AddTwoInts.srv的文件。文件的内容如下面右边的代码所示,定义了两个int64的数A和B作为输入,和一个int64的数Sum作为输出。

        $ roscd beginner_tutorials 
        $ mkdir srv
        $ touch AddTwoInts.srv
int64 A
int64 B
---
int64 Sum

我们要使用srv文件就需要能够在C++或者python代码中访问它。这就需要生成相应的.h和.py文件。这个过程也需要依赖message_generation,而在运行时也依赖于message_runtime, 这点可以参考msg文件。所以,我们同样需要在package.xml文件中添加两个依赖:

        <build_depend>message_generation</build_depend>
        <run_depend>message_runtime</run_depend>
修改CMakeLists.txt添加srv文件的规则:
        find_package(catkin REQUIRED COMPONENTS roscpp rospy std_msgs message_generation)
        add_service_files(FILES AddTwoInts.msg) 
        generate_messages(DEPENDENCIES std_msgs)
没有意外的话,我们已经可以通过rossrv工具查看新建的srv文件了。
        $ rossrv show beginner_tutorials/AddTwoInts 
        int64 A
        int64 B
        ---
        int64 Sum
此时,如果我们回到工作空间的根目录下catkin_make一下,就可以在'~/catkin_ws/devel/include/beginner_tutorials/'的目录下找到一个叫做'AddTwoInts.h', 'AddTwoIntsRequests.h' 和'AddTwoIntsResponse.h'的文件。在'~/catkin_ws/devel/lib/python2.7/dist-packages/beginner_tutorials/srv'目录下找到'_AddTwoInts.py'文件。

2. 创建服务器

我们在beginner_tutorials/src目录下创建文件add_two_ints_server.cpp,并在其中编辑内容:

        #include "ros/ros.h"
        #include "beginner_tutorials/AddTwoInts.h"
        
        bool add(beginner_tutorials::AddTwoInts::Request &req,
                 beginner_tutorials::AddTwoInts::Response &res) {
            res.Sum = req.A + req.B;
            ROS_INFO("request: x=%ld, y=%ld", (long int)req.A, (long int)req.B);
            ROS_INFO("sending back response: [%ld]", (long int)res.Sum);
            return true;
        }
        
        int main(int argc, char *argv[]) {
            ros::init(argc, argv, "add_two_ints_server");
            ros::NodeHandle n;
        
            ros::ServiceServer service = n.advertiseService("add_two_ints", add);
            ROS_INFO("Ready to add two ints.");
            ros::spin();
        
            return 0;
        }
在main函数中,我们先通过ros::init()完成了对Node的初始化工作,主要是向roscore注册Node运行时名称。接着定义了一个ros::NodeHandle的对象,并通过它实现了一个服务器对象service。 service也是通过回调函数来响应请求的,在这里的advertiseService函数的第二个参数add就是我们实际的服务函数。

在服务函数add中,我们只是简单的把请求中的两个数据'A'和'B'求和,并把结果赋予了输出数据'Sum'。下面我们在实现一个客户端add_two_ints_client.cpp:

        #include "ros/ros.h"
        #include "beginner_tutorials/AddTwoInts.h"
        #include <cstdlib>
        
        int main(int argc, char *argv[]) {
            ros::init(argc, argv, "add_two_ints_client");
            if (argc != 3) {
                ROS_INFO("Usage: add_two_ints_client X Y");
                return 1;
            }
        
            ros::NodeHandle n;
            ros::ServiceClient client = n.serviceClient<beginner_tutorials::AddTwoInts>("add_two_ints");
            beginner_tutorials::AddTwoInts srv;
            srv.request.A = atoll(argv[1]);
            srv.request.B = atoll(argv[2]);
            if (client.call(srv)) {
                ROS_INFO("Sum: %ld", (long int)srv.response.Sum);
            } else {
                ROS_INFO("Failed to call service add_two_ints");
                return 1;
            }
            return 0;
        }

在客户端程序中,我们通过模板函数serviceClient()定义了一个客户端对象。通过client.call()调用请求服务,如果服务被正常响应,就会更新response中的数据Sum,同时返回true。 如果服务出错,就会返回false。

接下来,我们在CMakeLists.txt中添加编译规则:

        add_executable(add_two_ints_server src/add_two_ints_server.cpp)
        target_link_libraries(add_two_ints_server ${catkin_LIBRARIES})
        
        add_executable(add_two_ints_client src/add_two_ints_client.cpp)
        target_link_libraries(add_two_ints_client ${catkin_LIBRARIES})

回到工作空间目录'~/catkin_ws'下编译:
        $ cd ~/catkin_ws
        $ catkin_make
分别运行服务器和客户端,我们可以看到类似下面的输出。需要注意的是在运行这两个Node之前,必须保证roscore已经运行了,这两个Node可能需要在两个不同的终端下分别运行。
        $ rosrun beginner_tutorials add_two_ints_server 
        [ INFO] [1511878262.032095902]: Ready to add two ints.
        [ INFO] [1511878299.060222178]: request: x=12, y=33
        [ INFO] [1511878299.060251988]: sending back response: [45]
        $ rosrun beginner_tutorials add_two_ints_client 12 33
        [ INFO] [1511878299.060360389]: Sum: 45

3. 总结

在本文中,我们先创建了一个srv文件,并用C++程序实现了一个对两个数求和的服务。

要使用srv文件,我们需要先创建对应的.h文件,同城保存在'~/catkin_ws/devel/include/beginner_tutorials/'目录下, 在C++程序中需要通过#include "PACKAGE_NAME/SRV_NAME.h"使用自定义的srv文件。

在服务器程序中,通过roscpp的advertiseService定义服务器对象,并指明回调函数。在客户端程序中,通过roscpp的serviceClient模板函数定义客户端对象,并通过call()请求服务。 call()函数的返回值将反应服务请求是否被正常响应,同时服务的输出将保留在srv指定的输出数据中。




Copyright @ 高乙超. All Rights Reserved. 京ICP备16033081号-1