当前位置:   article > 正文

【编写简单的发布者和订阅者 (Python)——ROS2+ubuntu22.04】_python ros2 订阅

python ros2 订阅

您正在阅读较旧但仍受支持的 ROS 2 版本的文档。 有关最新版本的信息,请查看 Iron。

编写简单的发布者和订阅者 (Python)

**目标:**使用 Python 创建并运行发布者和订阅者节点。

**教程级别:**初学者

**时间:**20分钟

内容

背景

在本教程中,您将创建节点,这些节点以字符串消息的形式通过主题相互传递信息。 这里使用的示例是一个简单的“说话者”和“倾听者”系统; 一个节点发布数据,另一个节点订阅主题,以便它可以接收该数据。

这些示例中使用的代码可以在此处找到。

先决条件

在前面的教程中,你学习了如何创建工作区创建包

建议对 Python 有基本的了解,但不是完全必要的。

任务

1 创建包

打开一个新终端并获取您的 ROS 2 安装,以便命令正常工作。ros2

导航到在上一教程中创建的目录。ros2_ws

回想一下,包应该在目录中创建,而不是在工作区的根目录中创建。 因此,导航到 ,然后运行包创建命令:src``ros2_ws/src

ros2 pkg create --build-type ament_python --license Apache-2.0 py_pubsub
  • 1

您的终端将返回一条消息,验证您的包及其所有必要文件和文件夹的创建。py_pubsub

2 写入发布者节点

导航到 。 回想一下,此目录是一个 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
  • 1

现在将有一个名为 相邻的新文件。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()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
2.1 Examine the code

The first lines of code after the comments import so its class can be used.rclpy``Node

import rclpy
from rclpy.node import Node
  • 1
  • 2

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
  • 1

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):
  • 1

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
  • 1

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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

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()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

First the library is initialized, then the node is created, and then it “spins” the node so its callbacks are called.rclpy

2.2 Add dependencies

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>
  • 1
  • 2
  • 3

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>
  • 1
  • 2

This declares the package needs and when its code is executed.rclpy``std_msgs

Make sure to save the file.

2.3 Add an entry point

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',
  • 1
  • 2
  • 3
  • 4

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',
        ],
},
  • 1
  • 2
  • 3
  • 4
  • 5

Don’t forget to save.

2.4 Check setup.cfg

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
  • 1
  • 2
  • 3
  • 4

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.

3 Write the subscriber node

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
  • 1

Now the directory should have these files:

__init__.py  publisher_member_function.py  subscriber_member_function.py
  • 1
3.1 Examine the code

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()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37

订阅者节点的代码与发布者的代码几乎相同。 构造函数使用与发布者相同的参数创建订阅服务器。 回想一下主题教程中的内容,发布者和订阅者使用的主题名称和消息类型必须匹配,才能允许他们进行通信。

self.subscription = self.create_subscription(
    String,
    'topic',
    self.listener_callback,
    10)
  • 1
  • 2
  • 3
  • 4
  • 5

订阅者的构造函数和回调不包含任何计时器定义,因为它不需要计时器定义。 一旦收到消息,就会立即调用其回调。

回调定义只是将信息消息及其接收的数据打印到控制台。 回想一下,发布者定义msg.data = 'Hello World: %d' % self.i

def listener_callback(self, msg):
    self.get_logger().info('I heard: "%s"' % msg.data)
  • 1
  • 2

定义几乎完全相同,将发布者的创建和旋转替换为订阅者。main

minimal_subscriber = MinimalSubscriber()

rclpy.spin(minimal_subscriber)
  • 1
  • 2
  • 3

由于此节点与发布者具有相同的依赖关系,因此没有新内容要添加到 . 文件也可以保持不变。package.xml``setup.cfg

3.2 添加入口点

重新打开订阅服务器节点的入口点,并将其添加到发布者的入口点下方。 该字段现在应如下所示:setup.py``entry_points

entry_points={
        'console_scripts': [
                'talker = py_pubsub.publisher_member_function:main',
                'listener = py_pubsub.subscriber_member_function:main',
        ],
},
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

确保保存文件,然后您的发布/订阅系统应该准备就绪。

4 构建和运行

您可能已经将 和 软件包安装为 ROS 2 系统的一部分。 在构建之前,最好在工作区 () 的根目录中运行以检查缺少的依赖项:rclpy``std_msgs``rosdep``ros2_ws

Linux操作系统macOS 操作系统窗户

rosdep install -i --from-path src --rosdistro humble -y
  • 1

仍在工作区的根目录中,生成新包:ros2_ws

Linux操作系统macOS 操作系统窗户

colcon build --packages-select py_pubsub
  • 1

打开一个新终端,导航到 ,并获取安装文件:ros2_ws

Linux操作系统macOS 操作系统窗户

source install/setup.bash
  • 1

现在运行 talker 节点:

ros2 run py_pubsub talker
  • 1

终端应每 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"
...
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

打开另一个终端,再次从内部获取安装文件,然后启动侦听器节点:ros2_ws

ros2 run py_pubsub listener 
  • 1

侦听器将开始将消息打印到控制台,从发布者当时所在的任何消息计数开始,如下所示:

[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"

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

在每个终端中输入以阻止节点旋转。Ctrl+C

总结

您创建了两个节点来发布和订阅主题上的数据。 在运行它们之前,已将其依赖项和入口点添加到包配置文件中。

以上是原文,参考链接: https://docs.ros.org/en/humble/Tutorials/Beginner-Client-Libraries/Writing-A-Simple-Py-Publisher-And-Subscriber.html

分析如下:
目录:
在这里插入图片描述

依赖文件和配置文件
在这里插入图片描述

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>   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

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',


        ],
    },
)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31

setup.cfg
默认可以不用修改

[develop] 
script_dir=$base/lib/py_pubsub 
[install] 
install_scripts=$base/lib/py_pubsub 
  • 1
  • 2
  • 3
  • 4

之后是主要的程序文件 :
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()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53

订阅者函数文件
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()      
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69

运行程序:
在这里插入图片描述

回到终端目录为ros2_ws/
终端1:

source install/setup.bash 
  • 1
ros2 run py_pubsub talker
  • 1

在这里插入图片描述

终端2:

source install/setup.bash
  • 1
ros2 run py_pubsub listener
  • 1

在这里插入图片描述
演示OK

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/从前慢现在也慢/article/detail/982357
推荐阅读
相关标签
  

闽ICP备14008679号