赞
踩
您正在阅读较旧但仍受支持的 ROS 2 版本的文档。 有关最新版本的信息,请查看 Iron。
**目标:**使用 Python 创建并运行发布者和订阅者节点。
**教程级别:**初学者
**时间:**20分钟
内容
在本教程中,您将创建节点,这些节点以字符串消息的形式通过主题相互传递信息。 这里使用的示例是一个简单的“说话者”和“倾听者”系统; 一个节点发布数据,另一个节点订阅主题,以便它可以接收该数据。
这些示例中使用的代码可以在此处找到。
建议对 Python 有基本的了解,但不是完全必要的。
打开一个新终端并获取您的 ROS 2 安装,以便命令正常工作。ros2
导航到在上一教程中创建的目录。ros2_ws
回想一下,包应该在目录中创建,而不是在工作区的根目录中创建。 因此,导航到 ,然后运行包创建命令:src``ros2_ws/src
ros2 pkg create --build-type ament_python --license Apache-2.0 py_pubsub
您的终端将返回一条消息,验证您的包及其所有必要文件和文件夹的创建。py_pubsub
导航到 。 回想一下,此目录是一个 Python 包,与它嵌套的 ROS 2 包同名。ros2_ws/src/py_pubsub/py_pubsub
通过输入以下命令下载示例说话者代码:
Linux操作系统macOS 操作系统窗户
wget https://raw.githubusercontent.com/ros2/examples/humble/rclpy/topics/minimal_publisher/examples_rclpy_minimal_publisher/publisher_member_function.py
现在将有一个名为 相邻的新文件。publisher_member_function.py``__init__.py
使用您喜欢的文本编辑器打开文件。
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()
The first lines of code after the comments import so its class can be used.rclpy``Node
import rclpy
from rclpy.node import Node
The next statement imports the built-in string message type that the node uses to structure the data that it passes on the topic.
from std_msgs.msg import String
These lines represent the node’s dependencies. Recall that dependencies have to be added to , which you’ll do in the next section.package.xml
Next, the class is created, which inherits from (or is a subclass of) .MinimalPublisher``Node
class MinimalPublisher(Node):
Following is the definition of the class’s constructor. calls the class’s constructor and gives it your node name, in this case .super().__init__``Node``minimal_publisher
create_publisher` declares that the node publishes messages of type (imported from the module), over a topic named , and that the “queue size” is 10. Queue size is a required QoS (quality of service) setting that limits the amount of queued messages if a subscriber is not receiving them fast enough.`String``std_msgs.msg``topic
Next, a timer is created with a callback to execute every 0.5 seconds. is a counter used in the callback.self.i
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
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
Lastly, the main function is defined.
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()
First the library is initialized, then the node is created, and then it “spins” the node so its callbacks are called.rclpy
Navigate one level back to the directory, where the , , and files have been created for you.ros2_ws/src/py_pubsub``setup.py``setup.cfg``package.xml
Open with your text editor.package.xml
As mentioned in the previous tutorial, make sure to fill in the , and tags:<description>``<maintainer>``<license>
<description>Examples of minimal publisher/subscriber using rclpy</description>
<maintainer email="you@email.com">Your Name</maintainer>
<license>Apache License 2.0</license>
After the lines above, add the following dependencies corresponding to your node’s import statements:
<exec_depend>rclpy</exec_depend>
<exec_depend>std_msgs</exec_depend>
This declares the package needs and when its code is executed.rclpy``std_msgs
Make sure to save the file.
Open the file. Again, match the , , and fields to your :setup.py``maintainer``maintainer_email``description``license``package.xml
maintainer='YourName',
maintainer_email='you@email.com',
description='Examples of minimal publisher/subscriber using rclpy',
license='Apache License 2.0',
Add the following line within the brackets of the field:console_scripts``entry_points
entry_points={
'console_scripts': [
'talker = py_pubsub.publisher_member_function:main',
],
},
Don’t forget to save.
The contents of the file should be correctly populated automatically, like so:setup.cfg
[develop]
script_dir=$base/lib/py_pubsub
[install]
install_scripts=$base/lib/py_pubsub
This is simply telling setuptools to put your executables in , because will look for them there.lib``ros2 run
You could build your package now, source the local setup files, and run it, but let’s create the subscriber node first so you can see the full system at work.
Return to to create the next node. Enter the following code in your terminal:ros2_ws/src/py_pubsub/py_pubsub
LinuxmacOSWindows
wget https://raw.githubusercontent.com/ros2/examples/humble/rclpy/topics/minimal_subscriber/examples_rclpy_minimal_subscriber/subscriber_member_function.py
Now the directory should have these files:
__init__.py publisher_member_function.py subscriber_member_function.py
Open the with your text editor.subscriber_member_function.py
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()
订阅者节点的代码与发布者的代码几乎相同。 构造函数使用与发布者相同的参数创建订阅服务器。 回想一下主题教程中的内容,发布者和订阅者使用的主题名称和消息类型必须匹配,才能允许他们进行通信。
self.subscription = self.create_subscription(
String,
'topic',
self.listener_callback,
10)
订阅者的构造函数和回调不包含任何计时器定义,因为它不需要计时器定义。 一旦收到消息,就会立即调用其回调。
回调定义只是将信息消息及其接收的数据打印到控制台。 回想一下,发布者定义msg.data = 'Hello World: %d' % self.i
def listener_callback(self, msg):
self.get_logger().info('I heard: "%s"' % msg.data)
定义几乎完全相同,将发布者的创建和旋转替换为订阅者。main
minimal_subscriber = MinimalSubscriber()
rclpy.spin(minimal_subscriber)
由于此节点与发布者具有相同的依赖关系,因此没有新内容要添加到 . 文件也可以保持不变。package.xml``setup.cfg
重新打开订阅服务器节点的入口点,并将其添加到发布者的入口点下方。 该字段现在应如下所示:setup.py``entry_points
entry_points={
'console_scripts': [
'talker = py_pubsub.publisher_member_function:main',
'listener = py_pubsub.subscriber_member_function:main',
],
},
确保保存文件,然后您的发布/订阅系统应该准备就绪。
您可能已经将 和 软件包安装为 ROS 2 系统的一部分。 在构建之前,最好在工作区 () 的根目录中运行以检查缺少的依赖项:rclpy``std_msgs``rosdep``ros2_ws
Linux操作系统macOS 操作系统窗户
rosdep install -i --from-path src --rosdistro humble -y
仍在工作区的根目录中,生成新包:ros2_ws
Linux操作系统macOS 操作系统窗户
colcon build --packages-select py_pubsub
打开一个新终端,导航到 ,并获取安装文件:ros2_ws
Linux操作系统macOS 操作系统窗户
source install/setup.bash
现在运行 talker 节点:
ros2 run py_pubsub talker
终端应每 0.5 秒开始发布一次信息消息,如下所示:
[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"
...
打开另一个终端,再次从内部获取安装文件,然后启动侦听器节点:ros2_ws
ros2 run py_pubsub listener
侦听器将开始将消息打印到控制台,从发布者当时所在的任何消息计数开始,如下所示:
[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
您创建了两个节点来发布和订阅主题上的数据。 在运行它们之前,已将其依赖项和入口点添加到包配置文件中。
分析如下:
目录:
依赖文件和配置文件
package.xml
<?xml version="1.0"?>
<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
<package format="3">
<name>py_pubsub</name>
<version>0.0.0</version>
<description>TODO: Package description</description>
<maintainer email="a@todo.todo">a</maintainer>
<license>Apache-2.0</license>
<test_depend>ament_copyright</test_depend>
<test_depend>ament_flake8</test_depend>
<test_depend>ament_pep257</test_depend>
<test_depend>python3-pytest</test_depend>
############################################
<exec_depend>rclpy</exec_depend>
<exec_depend>std_msgs</exec_depend>
############################################
<export>
<build_type>ament_python</build_type>
</export>
</package>
setup .py
from setuptools import find_packages, setup
package_name = 'py_pubsub'
setup(
name=package_name,
version='0.0.0',
packages=find_packages(exclude=['test']),
data_files=[
('share/ament_index/resource_index/packages',
['resource/' + package_name]),
('share/' + package_name, ['package.xml']),
],
install_requires=['setuptools'],
zip_safe=True,
maintainer='a',
maintainer_email='a@todo.todo',
description='TODO: Package description',
license='Apache-2.0',
tests_require=['pytest'],
entry_points={
'console_scripts': [
'talker = py_pubsub.publisher_member_function:main',
'listener = py_pubsub.subscriber_member_function:main',
],
},
)
setup.cfg
默认可以不用修改
[develop]
script_dir=$base/lib/py_pubsub
[install]
install_scripts=$base/lib/py_pubsub
之后是主要的程序文件 :
init.py
可以不用管,没有定义
发布者函数文件
publisher_member_function.py
订阅者函数文件
subscriber_member_function.py
其中:
代码
发布者函数文件
publisher_member_function.py
# Copyright 2016 Open Source Robotics Foundation, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
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()
订阅者函数文件
subscriber_member_function.py
# Copyright 2016 Open Source Robotics Foundation, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
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()
运行程序:
回到终端目录为ros2_ws/
终端1:
source install/setup.bash
ros2 run py_pubsub talker
终端2:
source install/setup.bash
ros2 run py_pubsub listener
演示OK
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。