编写简单的发布者和订阅者 (Python) [待校准@9432]
Goal目标: using使用Python创建并运行发布者和订阅者节点 [待校准@9433]
教程级别: 初学者 [Alyssa@7088]
Time时间: 20 20分钟 [待校准@7181]
背景
在本教程中,您将创建以string消息形式通过 topic 相互传递信息的 nodes 。这里使用的例子是一个简单的 “说话者” 和 “听众” 系统; 一个节点发布数据,另一个节点订阅话题,这样它就可以接收数据。 [待校准@9434]
这些例子中使用的代码可以找到 here 。 [待校准@9435]
先决条件
在之前的教程中,你学习了如何 create a workspace and create a package 。 [待校准@9349]
建议对Python有一个基本的了解,但不是完全必要的。 [待校准@9436]
任务
1创建包
打开一个新的终端和 source your ROS 2 installation ,这样 ros2
命令就可以工作了。 [待校准@9214]
导航到 dev_ws
目录创建 previous tutorial 。 [待校准@9215]
重新调用应该在 src
目录中创建的包,而不是工作区的根目录。因此,导航到 dev_ws/src
,并运行包创建命令: [待校准@9350]
ros2 pkg create --build-type ament_python py_pubsub
您的终端将返回一条消息,验证您的包 py_pubsub
及其所有必要的文件和文件夹的创建。 [待校准@9437]
2写入发布者节点 [待校准@9353]
导航到 dev_ws/src/py_pubsub/py_pubsub
。重新调用该目录是一个 Python package ,与它嵌套的ROS 2包同名。 [待校准@9438]
通过输入以下命令下载示例talker代码: [待校准@9354]
wget https://raw.githubusercontent.com/ros2/examples/foxy/rclpy/topics/minimal_publisher/examples_rclpy_minimal_publisher/publisher_member_function.py
wget https://raw.githubusercontent.com/ros2/examples/foxy/rclpy/topics/minimal_publisher/examples_rclpy_minimal_publisher/publisher_member_function.py
在Windows命令行提示符下: [待校准@8363]
curl -sk https://raw.githubusercontent.com/ros2/examples/foxy/rclpy/topics/minimal_publisher/examples_rclpy_minimal_publisher/publisher_member_function.py -o publisher_member_function.py
或者在powershell中: [待校准@8364]
curl https://raw.githubusercontent.com/ros2/examples/foxy/rclpy/topics/minimal_publisher/examples_rclpy_minimal_publisher/publisher_member_function.py -o publisher_member_function.py
现在将有一个名为 publisher_member_function.py
的新文件,毗邻 __init__.py
。 [待校准@9439]
使用您喜欢的文本编辑器打开文件。 [待校准@8591]
import rclpy
from rclpy.node import Node
from std_msgs.msg import String
class MinimalPublisher(Node):
def __init__(self):
super().__init__('minimal_publisher')
self.publisher_ = self.create_publisher(String, 'topic', 10)
timer_period = 0.5 # seconds
self.timer = self.create_timer(timer_period, self.timer_callback)
self.i = 0
def timer_callback(self):
msg = String()
msg.data = 'Hello World: %d' % self.i
self.publisher_.publish(msg)
self.get_logger().info('Publishing: "%s"' % msg.data)
self.i += 1
def main(args=None):
rclpy.init(args=args)
minimal_publisher = MinimalPublisher()
rclpy.spin(minimal_publisher)
# Destroy the node explicitly
# (optional - otherwise it will be done automatically
# when the garbage collector destroys the node object)
minimal_publisher.destroy_node()
rclpy.shutdown()
if __name__ == '__main__':
main()
2.1检查代码 [待校准@8007]
注释后的第一行代码导入 rclpy
,因此可以使用其 Node
类。 [待校准@9440]
import rclpy
from rclpy.node import Node
下一个语句导入节点用来构建传递给话题的数据的内置string消息类型。 [待校准@9441]
from std_msgs.msg import String
这些行表示节点的依赖关系。重新调用必须将依赖项添加到 package.xml
,您将在下一节中执行此操作。 [待校准@9442]
接下来,创建 MinimalPublisher
类,它继承于 Node
(或 Node
) 的子类。 [待校准@9443]
class MinimalPublisher(Node):
追踪是类的构造函数的定义。 super().__init__
调用 Node
类的构造函数,并为其提供节点名称,在本例中为 minimal_publisher
。 [待校准@9444]
[需手动修复的语法]``create_publisher`` declares that the node publishes messages of type String
(imported from the std_msgs.msg
module), over a topic named topic
,,“队列大小” 为10。队列大小是必需的QoS (服务质量) 设置,如果订户接收消息的速度不够快,则限制排队消息的数量。 [待校准@9445]
接下来,创建一个计时器,每0.5秒调用一次back以执行一次。 self.i
是调用中使用的计数器。 [待校准@9446]
def __init__(self):
super().__init__('minimal_publisher')
self.publisher_ = self.create_publisher(String, 'topic', 10)
timer_period = 0.5 # seconds
self.timer = self.create_timer(timer_period, self.timer_callback)
self.i = 0
timer_callback
creates a message with the counter value appended, and publishes it to the console with get_logger().info
. [待校准@9447]
def timer_callback(self):
msg = String()
msg.data = 'Hello World: %d' % self.i
self.publisher_.publish(msg)
self.get_logger().info('Publishing: "%s"' % msg.data)
self.i += 1
最后,定义了主要功能。 [待校准@9448]
def main(args=None):
rclpy.init(args=args)
minimal_publisher = MinimalPublisher()
rclpy.spin(minimal_publisher)
# Destroy the node explicitly
# (optional - otherwise it will be done automatically
# when the garbage collector destroys the node object)
minimal_publisher.destroy_node()
rclpy.shutdown()
首先初始化 rclpy
库,然后创建节点,然后 “旋转” 节点,以便调用其调用。 [待校准@9449]
2.2添加依赖项 [待校准@8644]
导航到 dev_ws/src/py_pubsub
目录,在那里已经为您创建了 setup.py
、 setup.cfg
和 package.xml
文件。 [待校准@9450]
用文本编辑器打开 package.xml
。 [待校准@8719]
如 previous tutorial, make sure to fill in the <description>
, <maintainer>
and ``<license>` 标签中所述: [待校准@9364]
<description>Examples of minimal publisher/subscriber using rclpy</description>
<maintainer email="you@email.com">Your Name</maintainer>
<license>Apache License 2.0</license>
在上面的行之后,添加与节点的导入语句相对应的以下依赖项: [待校准@8748]
<exec_depend>rclpy</exec_depend>
<exec_depend>std_msgs</exec_depend>
这表明当执行其代码时,包需要 rclpy
和 std_msgs
。 [待校准@9451]
确保保存文件。 [待校准@8647]
2.3添加入口点 [待校准@8750]
打开 setup.py
文件。再一次,将 maintainer
、 maintainer_email
、 description
和 license
田地与你的 package.xml
相匹配: [待校准@9264]
maintainer='YourName',
maintainer_email='you@email.com',
description='Examples of minimal publisher/subscriber using rclpy',
license='Apache License 2.0',
添加以下内 console_scripts
支架的 entry_points
领域: [待校准@9265]
entry_points={
'console_scripts': [
'talker = py_pubsub.publisher_member_function:main',
],
},
不要忘记保存。 [待校准@9266]
2.4检查设置。cfg [待校准@9452]
[需手动修复的语法] setup.cfg
文件的内容应正确填充自动调用y,如下所示: [待校准@9453]
[develop]
script-dir=$base/lib/py_pubsub
[install]
install-scripts=$base/lib/py_pubsub
这只是告诉setuptools将您的可执行文件放在 lib
中,因为 ros2 run
会在那里寻找它们。 [待校准@9454]
您现在可以构建包,源文件本地安装文件,并运行它,但是让我们先创建订阅节点,这样您就可以看到整个系统都在工作。 [待校准@9370]
3写入订户节点 [待校准@9371]
返回 dev_ws/src/py_pubsub/py_pubsub
创建下一个节点。在终端中输入以下代码: [待校准@9455]
wget https://raw.githubusercontent.com/ros2/examples/foxy/rclpy/topics/minimal_subscriber/examples_rclpy_minimal_subscriber/subscriber_member_function.py
wget https://raw.githubusercontent.com/ros2/examples/foxy/rclpy/topics/minimal_subscriber/examples_rclpy_minimal_subscriber/subscriber_member_function.py
在Windows命令行提示符下: [待校准@8363]
curl -sk https://raw.githubusercontent.com/ros2/examples/foxy/rclpy/topics/minimal_subscriber/examples_rclpy_minimal_subscriber/subscriber_member_function.py -o subscriber_member_function.py
或者在powershell中: [待校准@8364]
curl https://raw.githubusercontent.com/ros2/examples/foxy/rclpy/topics/minimal_subscriber/examples_rclpy_minimal_subscriber/subscriber_member_function.py -o subscriber_member_function.py
现在目录应该有这些文件: [待校准@9456]
__init__.py publisher_member_function.py subscriber_member_function.py
3.1检查代码 [待校准@9375]
使用文本编辑器打开 subscriber_member_function.py
。 [待校准@9457]
import rclpy
from rclpy.node import Node
from std_msgs.msg import String
class MinimalSubscriber(Node):
def __init__(self):
super().__init__('minimal_subscriber')
self.subscription = self.create_subscription(
String,
'topic',
self.listener_callback,
10)
self.subscription # prevent unused variable warning
def listener_callback(self, msg):
self.get_logger().info('I heard: "%s"' % msg.data)
def main(args=None):
rclpy.init(args=args)
minimal_subscriber = MinimalSubscriber()
rclpy.spin(minimal_subscriber)
# Destroy the node explicitly
# (optional - otherwise it will be done automatically
# when the garbage collector destroys the node object)
minimal_subscriber.destroy_node()
rclpy.shutdown()
if __name__ == '__main__':
main()
订阅节点的代码与发布者的代码几乎相同。构造函数使用与发布服务器相同的参数创建订阅服务器。从 topics tutorial 中重新调用,发布者和订阅者使用的话题名称和消息类型必须匹配以允许他们进行交流。 [待校准@9458]
self.subscription = self.create_subscription(
String,
'topic',
self.listener_callback,
10)
订阅服务器的构造函数和调用不包括任何计时器定义,因为它不需要计时器定义。它的调用在收到消息后立即被调用。 [待校准@9459]
调用back定义仅将信息消息及其接收到的数据打印到控制台。重新调用发布者定义 “msg.data = 'Hello World: % d' % self.i'' [待校准@9460]
def listener_callback(self, msg):
self.get_logger().info('I heard: "%s"' % msg.data)
[需手动修复的语法] main
的定义几乎完全相同,用订阅者代替了出版商的创建和旋转。 [待校准@9461]
minimal_subscriber = MinimalSubscriber()
rclpy.spin(minimal_subscriber)
由于此节点与发布者具有相同的依赖关系,因此没有新的内容要添加到 package.xml
。 setup.cfg
文件也可以保持不变。 [待校准@9462]
3.2添加入口点 [待校准@9463]
重新打开 setup.py
,并在出版商的入口点下方添加订户节点的入口点。 entry_points
字段现在应该是这样的: [待校准@9464]
entry_points={
'console_scripts': [
'talker = py_pubsub.publisher_member_function:main',
'listener = py_pubsub.subscriber_member_function:main',
],
},
确保保存文件,然后您的发布/子系统应该可以使用了。 [待校准@9385]
4构建和运行 [待校准@7896]
您可能已经将 rclpy
和 std_msgs
包安装为ROS 2系统的一部分。在构建之前,最好在工作区 ( dev_ws
) 的根目录下运行 rosdep
,以检查是否缺少依赖项: [待校准@9465]
rosdep install -i --from-path src --rosdistro foxy -y
rosdep仅在Linux上运行,因此您可以跳到下一步。 [待校准@9229]
rosdep仅在Linux上运行,因此您可以跳到下一步。 [待校准@9229]
仍然在你的工作空间的根, dev_ws
,建立你的新的包: [待校准@9387]
colcon build --packages-select py_pubsub
colcon build --packages-select py_pubsub
colcon build --merge-install --packages-select py_pubsub
打开一个新的终端,导航到 dev_ws
,源文件安装文件: [待校准@9231]
. install/setup.bash
. install/setup.bash
call install/setup.bat
现在运行talker节点: [待校准@8780]
ros2 run py_pubsub talker
终端应每0.5秒开始发布信息消息,如下所示: [待校准@8781]
[INFO] [minimal_publisher]: Publishing: "Hello World: 0"
[INFO] [minimal_publisher]: Publishing: "Hello World: 1"
[INFO] [minimal_publisher]: Publishing: "Hello World: 2"
[INFO] [minimal_publisher]: Publishing: "Hello World: 3"
[INFO] [minimal_publisher]: Publishing: "Hello World: 4"
...
打开另一个终端,再次从 dev_ws
内部源文件,然后启动监听节点: [待校准@9388]
ros2 run py_pubsub listener
侦听器将开始将消息打印到控制台,从发布者当时打开的任何消息计数开始,如下所示: [待校准@8782]
[INFO] [minimal_subscriber]: I heard: "Hello World: 10"
[INFO] [minimal_subscriber]: I heard: "Hello World: 11"
[INFO] [minimal_subscriber]: I heard: "Hello World: 12"
[INFO] [minimal_subscriber]: I heard: "Hello World: 13"
[INFO] [minimal_subscriber]: I heard: "Hello World: 14"
在每个终端中输入 “ctrl + c” 以停止节点旋转。 [待校准@9389]
总结
您创建了两个节点来发布和订阅一个话题上的数据。在运行它们之前,将它们的依赖项和入口点添加到包配置文件中。 [待校准@9466]
下一步
接下来,您将使用服务/客户端模型创建另一个简单的ROS 2包。同样,你可以选择用 C++ or Python 写。 [待校准@9391]