购买
下载掌阅APP,畅读海量书库
立即打开
畅读海量书库
扫码下载掌阅APP

2.3 使用ROS服务

在本节中,我们将创建两个ROS节点,它们使用我们已经自定义了的服务。我们创建的服务节点可以将一个字符串消息作为请求发送到服务器上,服务器节点将反馈另一条消息作为应答。

进入mastering_ros_demo_pkg/src文件夹,找到名为demo_service_server.cpp和demo_service_client.cpp的节点代码。

demo_service_server.cpp是服务器节点源码,定义如下:

让我们看看代码的解释。首先,我们在代码中包含用于定义要使用的服务的头文件:

这里包含了ros/ros.h头文件,它是编写ROS CPP节点代码必须要包含的头文件。mastering_ros_demo_pkg/demo_srv.h头文件是生成的一个头文件,它包含我们服务的定义,我们可以在代码中直接使用它。

这是在服务器节点上收到请求时执行的回调函数。服务器可以从客户端接收消息类型为mastering_ros_demo_pkg::demo_srv::Request的请求,也可以发送mastering_ros_demo_pkg:: demo_srv::Response类型的应答:

这里创建了一个名为demo_service的服务,并在请求到达此服务时执行回调函数。回调函数名为demo_service_callback,我们在上一节中学过:

接下来,让我们看看demo_service_client.cpp是如何工作的。以下是这段代码的定义:

此行代码将创建一个服务客户端,其消息类型为mastering_ros_demo_pkg::demo_srv,它可以与名为demo_service的ROS服务器进行通信:

将字符串填充到请求实例中:

如果收到响应,则会打印请求和响应:

在讨论了这两个节点之后,现在我们讨论如何编译生成这两个节点。将以下代码添加到CMakeLists.txt就可以来编译生成这两个节点了:

我们可以执行以下命令来编译该代码:

要启动节点,首先需要执行roscore,然后再使用下面的命令来启动节点:

图2.6显示了这些命令的输出。

图2.6 运行ROS服务的客户端和服务器节点

我们可以使用的rosservice命令如下。

●rosservice list:这将列出当前的ROS服务。

●rosservice type/demo_service:这将打印/demo_service的消息类型。

●rosservice info/demo_service:这将打印/demo_service的信息。

●rosservice call/service_name service args:这将从命令行调用服务服务器。

ROS的另一个重要元素是动作。在下一节中,我们将学习如何在ROS节点中使用actionlib来创建动作/服务器节点。

2.3.1 使用ROS actionlib

在ROS服务中,我们实现了两个节点间请求/应答式的交互,但是如果应答需要花费太多时间或者服务器没有完成指定的工作,那么我们就必须等待它完成。在等待请求的动作结束的过程中,主应用程序将被阻塞。此外,我们也可以通过调用客户端来监视远程进程的执行进度。这种情形下,我们应该使用actionlib来实现应用。ROS中的另一种用法是:如果请求没有像我们预期的那样按时完成,我们就可以抢占正在运行的请求,并发送另一个请求。actionlib软件包提供了实现这类抢占任务的标准方法。actionlib经常用在机械臂导航和移动机器人导航中。下面让我们来看看如何实现动作服务器和动作客户端。

与ROS服务一样,在actionlib中,我们也必须初始化动作规范。这个动作的规范存储在以.action结尾的文件中。该文件必须保存在ROS软件包内的action文件夹中。action文件包含以下各部分内容。

Goal (目标):动作客户端可以发送一个必须由动作服务器来执行的目标。这就类似于ROS服务中的请求。例如,如果机械臂关节想从45度转动到90度,那么这里的目标就是90度。

Feedback (反馈):动作客户端向动作服务器发送目标后,将开始执行回调函数。反馈只是简单地给出回调函数内当前操作的进度。通过使用反馈,我们可以获得当前任务的进度。在前面的例子中,机械臂关节必须移动到90度,在这种情况下,反馈就是机械臂从45度转动到90度之间的中间值。

Result (结果):完成目标后,动作服务器将发送完成的最终结果,它可以是计算结果或者一个确认。在前面的例子中,如果关节转动到90度,则完成了目标任务,结果就是可以表示目标完成的任何形式。

在此,我们讨论一个演示动作服务器和动作客户端。动作客户端将发送一个数字作为目标。动作服务器收到目标后,将从0开始计数,每秒加1,加到给定数字。如果在给定的时间内完成计数累加,它将发送结果,否则,该任务将被客户端抢占。这里的反馈是计算的进度。该任务的动作文件如下,动作文件名为Demo_action.action:

这里的count值是目标,服务器必须从0开始增加到该值。final_count是结果,即任务完成后最终的结果值。current_number是反馈值(即当前的计数值),表示任务的进度。

进入mastering_ros_demo_pkg/src文件夹,你就可以看到动作服务器节点的源码文件demo_action_server.cpp和动作客户端节点的源码文件demo_action_client.cpp。

创建ROS操作服务器

在本节中,我们将讨论demo_action_server.cpp。这个动作服务器将接收一个整型目标值。当服务器得到此目标值后,它将从0开始计数,直到该值。如果计数完成了,它将成功地完成动作。如果在完成任务之前被抢占,动作服务器将寻找另一个目标。

这段代码有点长,所以我们在这里只讨论重要的代码片段。

让我们从头文件开始。第一个头文件是用于实现动作服务器节点的标准动作库。第二个头文件是由存储的action文件生成的。它包含了我们的动作定义:

使用自定义动作消息创建一个简单的动作服务器实例。定义一个包含动作服务器定义的类:

创建一个反馈实例以便在操作过程中可以发送反馈:

创建一个结果实例来发送最终的结果:

然后,声明一个动作构造函数。这里创建的动作服务器还包含了一些参数,例如Nodehandle、name和executeCB,其中executeCB是所有动作完成后的回调函数:

这行代码是当动作被抢占时注册一个回调函数。preemptCB是动作客户端发出抢占请求时执行的回调函数名:

这是回调函数的定义。当动作服务器接收到目标值后就会执行该回调函数。它只有在检查了动作服务器的活动状态和是否已经被抢占后,才会执行该回调函数:

这个循环在目标值到达之前将会一直执行。它将不断地发送当前任务的进度作为反馈:

如果当前值达到目标值,则会发布结果:

在main()函数中,我们创建了一个Demo_actionAction的实例,它将启动动作服务器:

我们已经了解了服务器,下面学习如何创建动作客户端。

创建ROS动作客户端

在本节中,我们将讨论动作客户端的工作方式。demo_action_client.cpp是动作客户端节点的源文件,它负责发送目标值,即作为目标的一个数值。客户端从命令行的参数中获取目标值。客户端的第一个命令行参数是目标值,第二个参数是此任务的完成时间。

该目标值将被发送到服务器上,客户端将一直等待,直到到达指定的时间。时间以秒为单位。在等待指定时间后,客户端将检查任务是否完成,如果未完成,客户端将抢占该动作。

客户端代码有点长,所以我们只讨论代码的重要部分,在main()函数中,我们创建了Demo_actionAction的一个实例,它将启动动作服务器:

在ROS节点的main()函数中,创建一个动作客户端实例:

创建一个目标的实例,并从第一个命令行参数获取需要发送的目标值:

如果未完成,它将抢占该动作:

现在,让我们来看看构建ROS动作服务器和客户端。

2.3.2 编译ROS动作服务器和客户端

在src文件夹中创建了这两个源码文件后,我们必须编辑package.xml和CMakeLists.txt才能编译生成这两个节点。

package.xml文件应该包含消息生成和运行时的软件包,就像我们在创建ROS服务和消息中所做的那样。

我们必须在CMakeLists.txt中包含Boost库才能编译生成这些节点。此外,我们也必须把为此示例编写的动作文件添加进去。我们需要在find_package()中添加actionlib、actionlib_msgs和message_generation:

我们需要将Boost添加为系统依赖:

我们需要在generate_messages()中添加actionlib_msgs:

最后,我们可以定义这个节点编译后生成的可执行文件及其依赖项和链接库:

在catkin_make编译后,我们需要使用下面的命令来运行这些节点:

1. 运行roscore:

2. 启动动作服务器节点:

3. 启动动作客户端节点:

这些过程的输出如图2.7所示。

图2.7 运行ROS actionlib服务器和客户端

现在,让我们看看ROS的启动文件。 eFrOJP8c1I9MqwoxB7OPZeddATm3GEUsp2sKdYUie1ws51nyHDAyh8tDFXcth105

点击中间区域
呼出菜单
上一章
目录
下一章
×