3.4 ROS节点之间的通信——消息和主题
我们将逐步实现使两个节点相互通信的目标。首先,需要创建一个工作空间,然后下载代码库(https://github.com/PacktPublishing/Hands-On-ROS-for-Robotics-Programming),接着进入Chapter3_ROS_basics
文件夹自主练习。
3.4.1 创建工作空间
请按照以下步骤用命令行创建工作空间:
1)首先,创建文件夹,稍后将使用它们放置ROS软件包:
文件夹说明如下:
catkin_ws
是工作空间的根目录。src
是在ROS软件包中放置代码的位置。
注意,~
相当于主文件夹,例如:/home/bronquillo
。
2)移至最后一个文件夹,然后输入如下命令以初始化工作空间:
最后一条命令将生成~/catkin_ws/src/CMakeLists.txt
文件,其中包含工作空间的定义和配置。该文件实际上是指向ROS安装文件夹中定义工作空间配置路径的符号链接:
3)首次编译工作空间,文件夹此时为空没有关系:
通过列出内容,将看到两个新文件夹:
build
包含工作空间编译的结果,并稍后在创建用于使用ROS命令执行的软件包时,将放置所有可用代码。devel
包含工作空间的配置,并且每次打开终端时都会将其导入(请参阅以下步骤)。
请注意,编译必须在根文件夹~/catkin_ws
中完成,而工作空间初始化是在应用程序代码文件夹~/catkin_ws/src
中完成的。
4)要将工作空间添加到ROS环境中,需要提供生成的安装文件的来源:
5)为避免每次打开新终端时都必须运行此命令,请将其包含在.bashrc
文件中:
6)然后,应该在.bashrc
文件中添加如下两行:
请注意,第一行代表ROS系统配置,第二行代表自定义工作空间设置。
使用RoboWare创建工作空间并进行编译
下面介绍使用RoboWare IDE创建工作空间,而无须使用命令行:
1)启动RoboWare并单击New Workspace...项,见图3-4。
图 3-4
2)在弹出窗口中,指定工作空间的名称,然后选择要放置工作空间的文件夹,见图3-5。
图 3-5
设置文件夹名称后,RoboWare将为用户透明地执行catkin_init_workspace
,将在IDE窗口的左侧看到一个新文件和一个新文件夹。它们是src
文件夹,以及其中的文件CMakeLists.txt
,其中包含工作空间定义和配置,见图3-6。
图 3-6
到现在为止,已经注意到该文件已经有指向ROS安装文件夹/opt/ros/kinetic
中的ROS系统文件的符号链接了。该文件夹包含创建工作空间所有可能的通用配置。
可以在RoboWare中打开终端并使用ls-la
命令从特定的~/catkin/src
位置列出所有文件,见图3-7。
图 3-7
这类终端从顶部菜单的View项中下拉菜单的Integrated terminal(<Ctrl+`>)进入。
3)选择编译模式并将其设置为Debug,见图3-8。
图 3-8
4)然后,从顶部菜单中选择ROS | Build,见图3-9。
图 3-9
将在屏幕底部的Output窗口中看到日志,见图3-10。
图 3-10
如果成功,最后几行应如图3-11所示。
图 3-11
下一步,配置ROS软件包。
3.4.2 设置ROS软件包
请按照如下步骤设置ROS软件包:
1)在终端将库(https://github.com/PacktPublishing/Hands-On-ROS-for-Robotics-Programming)克隆到主文件夹:
2)将把本章的代码复制到ROS工作空间中。这样,将拥有一个更清洁的ROS环境:
请勿在src
文件夹名称后添加反斜杠\n
。否则文件将直接复制到src
文件夹中,而不是在src/Chapter3_ROS_basics
下。
3)在Chapter3_ROS_basics
文件夹中,可以找到本章的文件,该文件属于要使用的第一个ROS软件包。其配置包含在package.xml
文件中。请注意,软件包名称是在<name>ros_basics</name>
标签内定义的。可以在以下代码文件中找到它:
4)然后,转到工作空间根目录并再次构建:
通常,在至少两种情况下,必须重新编译工作空间:
- 每次添加新软件包时
- 如果代码包含编译型语言(例如C++)编写的代码段
在本书中,我们将主要使用Python(一种广泛使用的开放源代码语言),使ROS入门更加容易。由于Python是一种解释型语言,因此每次修改软件包的代码库时都不需要重新编译工作空间。因此,仅在添加或删除ROS软件包时才需要进行重建。该方法检查是否新添加的软件包(ros_basics
)众所周知,对于ROS而言执行如下简单的命令:
输出应如下所示:
尽管此处,我们将使用预编译的ROS软件包进行学习,但是了解如何从头开始创建自定义软件包是很重要的。从工作空间的src位置发出如下命令($ cd ~/catkin_ws/src/
):
<YOUR-PACKAGE-NAME>
代表分配给软件包的名称。<DEPENDENCIES>
是运行代码所需的ROS软件包的依赖列表。例如,如果软件包将包含Python和C++中的代码,则前者需要rospy
,后者需要roscpp
。然后,命令如下所示:
这将创建一个文件夹,该文件夹具有为软件包指定的名称和两个文件:
package.xml
:软件包配置如上所示。CMakelists.txt
:用于编译软件包的CMake构建系统的输入。
CMakelists.txt
还包含对<YOUR-PACKAGE-NAME>
的引用。对于此例而言,文件非常简单,如下所示:
使用RoboWare访问软件包文件并编译工作空间
下面介绍另一种方法。如下内容介绍克隆软件包库并使用RoboWare编译工作空间。
克隆并放置本章的代码后,如3.4.2节中所述,可以在IDE窗口左侧的文件树视图中浏览内容。单击任何文件将在主窗口中看到其中的内容,见图3-12。
图 3-12
最后,编译工作空间,请注意,这是每次创建自定义新软件包或克隆外部软件包时都必须执行的操作。为此,请转到顶部菜单栏,选择ROS,然后像以前一样单击Build。
3.4.3 发布主题的节点
在接下来的步骤中,由于需要同时处理多个终端,因此将使用非常方便的工具Terminator,该工具可让你同时处理多个终端。启动以下命令将其安装在系统上:
启动Terminator,然后将屏幕划分为四个终端(可以单击鼠标右键依次划分窗口)。将它们称为T1
、T2
、T3
和T4
,见图3-13。
图 3-13
在终端T1
(左上角的窗口)中启动roscore节点:
应该能够在T1
中看到图3-14中的输出。
图 3-14
这是ROS的根进程。由于ROS是集中式系统,因此roscore启动一个节点并启动主服务。始终需要主节点,保证其他节点可以执行,并且必须在任何其他节点之前启动它。
在下一个终端T2
中,运行以下命令以启动发布者节点:
这将启动topic_publisher
节点,但是什么也没有发生!这就对了。发布者就是发布者。需要一个订阅者接收正在发送的数据。
转到终端T3
并列出当前发布的主题:
在列表中,似乎/counter
是每0.5秒更新一次增量计数器的主题。另两个主题,/rosout
和/rosout_agg
,是控制台记录报告(http://wiki.ros.org/rosout)的机制。
文件topic_publisher.py
的下面这一行代码是用于设置/counter
主题发布者:
要查阅发布的消息,请在终端中启动以下命令:
这将输出如下五个消息,这些消息在/counter
主题中发布:
最后,我们演示一个实时视图,该视图将输出发送消息的实时频率:
按下<Ctrl+C>键停止T3
的日志。请记住,如果在之前的两个终端中的任何一个上执行此操作,则导致该终端所控制的进程死亡,并具有以下后果:
- 终端
T2
:发布者进程将结束,并且不再通过/counter
主题发送消息。 - 终端
T1
:在此终端中按<Ctrl+C>将杀死roscore,此进程是ROS中的单点故障,即所有相关进程(包括节点和消息)都将消失。
3.4.4 订阅主题的节点
由于/counter
主题中有一个节点发布增量计数器,因此现在将启动一个订阅该主题的节点。
首先,通过在T3
中输入如下命令可视化当前ROS图:
弹出如图3-15所示的窗口。
图 3-15
只有一个节点,即在T2
中启动的节点。该/counter
主题不会出现,因为没有被其他节点订阅。为了显示主题,在T4
中启动订阅者:
将在右下角的终端窗口中看到日志消息,这是一个无休止的实时数据流,除非杀死(按<Ctrl+C>)终端T1
或T2
,分别对应roscore
和topic_publisher
,见图3-16。
图 3-16
如果返回ROS图窗口并单击更新图标,将看到/topic_publisher
节点,以及/counter
主题传递消息,/topic_subscriber
节点订阅了消息,见图3-17。
图 3-17
这个简单的示例说明了ROS中通信的发布/订阅架构。已经了解它的工作原理以及底层通信原理的简单程度,这是ROS框架的基础。我们现在将做一些实际的事情。
让我们让计数器的行为像一个计时器。应该怎么做呢?这里,只需要修改脚本topic_publisher.py
,只需更改参数即可指定发送到该主题的消息每秒发送的次数,即发布频率(frequency),其单位为赫兹(=次/秒)。在脚本中,解决此问题的Python对象是:
在脚本下面的循环中,通过将rospy
的sleep
方法应用于定义的rate
对象来实现配置的行为:
请记住,rospy
是ROS库,使我们可以运行用Python代码编写的节点。rospy
定义了一组在ROS中使用内部功能的方法,例如以上述方式设置发布频率。
上面的代码是每秒发布两条消息,即每0.5秒发布一条消息。因此,如果将速率设置为1 Hz,则频率是每秒一条消息,因此模拟了计时器。可以修改此类脚本topic_publisher.py
,而无须停止ROS,并且再次执行该脚本后,该节点就会回到图中。下面介绍如何逐步进行:
1)在终端T2
按<Ctrl+C>停止节点的执行。
2)修改文件以1 Hz的速率发布,如前所示。
3)然后,重新启动该节点:
在启动topic_subscriber.py
的终端T4
上可以看到效果。因此,与之前的速率相比,2 Hz出现新行的速度(即计数)将翻倍。此时将每秒产生一个计数器更新(+1),对应于1 Hz的速率。
3.4.5 在同一节点中合并发布者和订阅者
节点可以像真实人类一样同时说话和收听吗?如果ROS是机器人技术的框架,那应该是可能的。让我们探索一下:
1)在前两个终端中启动这两个命令,每个命令运行在一个独立终端中:
/doubler
是订阅/number
主题的节点,由doubler.py
脚本的下面两行指定:
此外,/doubler
在/doubled
主题中发布其结果:
注意到没有任何反应,因为它需要输入一个可以乘以2的数字,如topic_subscriber.py
的回调所示:
2)启动一个终端来订阅/doubled
主题:
然后,手动发布/number
主题:
终端T3
的输出为4
,和预期一样:
3)尝试在T4
中发送其他数值,并检查T3
中是否立即显示了双精度值。
如果在第五个终端中发出rqt_graph
,则会看到类似图3-18的内容。
图 3-18
在这里,可以看到/number
和/doubled
主题、/doubler
节点以及两个其他具有类似计算机名称的节点,它们的对应关系如下:
- 图3-18左侧的节点
/rostopic_5469_
是由T4
中的命令创建的:
- 右边的节点
/rostopic_5631_
由在T3
中的命令创建的:
4)为了完成本练习,把数字输入/counter
主题,不从命令行,而从上一节中看到发布者节点的脚本topic_publisher.py
中输入。为了使脚本正常工作,必须在doubler.py
中修改主题名称,将其从number
重命名为counter
:
然后,在停止所有终端之后,在每个独立终端中执行下面每一行命令:
在此终端中,每次发布/counter
时,将看到counter
* 2的结果。看一下ROS图(记住单击刷新按钮),会发现它反映了正在发生的情况,见图3-19。
图 3-19
请记住,调试代码时rqt_graph
将提供非常有用的信息,例如,检测主题名称中的任何错误。如图3-20所示,由于/topic_publisher
订阅了count
主题(注意,缺少结尾er
字符),所以乘以2无效。由于输入错误的主题,节点彼此断开连接,并且不会出现消息,任何节点都未订阅,见图3-20。
图 3-20
在下一节中,我们将概述ROS软件包系统的扩展。