在本章中,我们将学习如何使用 Python 编写最小化的发布者和订阅者节点。相比于C++,Python的语法更加简洁,能让初学者快速体会 ROS2 的运行机制。
编写python发布者节点
首先,打开一个新的终端窗口,并加载 ROS2 环境变量(如果没有将加载命令写入到 ~/.bashrc中)。
source /opt/ros/jazzy/setup.bash
导航到之前创建的工作空间 ros2_ws 目录中的 src 文件夹,执行以下命令创建功能包:
ros2 pkg create --build-type ament_python --license Apache-2.0 py_pubsub
执行成功后,终端将确认功能包 py_pubsub 已经创建,并生成必要的文件和目录。
1 编写发布者节点
导航至功能包内的Python代码目录:
cd ros2_ws/src/py_pubsub/py_pubsub
在此目录下创建 publisher_member_function.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 # 秒
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)
# 显式销毁节点
# (可选 - 否则当垃圾收集器销毁节点对象时会自动完成)
minimal_publisher.destroy_node()
rclpy.shutdown()
if __name__ == '__main__':
main()
发布者节点代码解释
发布者节点的Python代码分为以下几个部分:
1. 导入库和模块
import rclpy
from rclpy.node import Node
from std_msgs.msg import String
rclpy是 ROS2 提供的 Python 客户端库,允许你使用 Python 编写 ROS2 节点。- 从
rclpy.node导入的Node类用于创建节点,这个类是所有 ROS2 节点的基类。 - 从
std_msgs.msg导入的String是 ROS2 内置的标准消息类型之一,用于发布或订阅字符串数据。
2. 创建发布者节点类
class MinimalPublisher(Node):
- 定义了一个名为
MinimalPublisher的类,继承自Node类,意味着它将具备 ROS2 节点的全部功能。
3. 节点构造函数__init__
def __init__(self):
super().__init__('minimal_publisher')
self.publisher_ = self.create_publisher(String, 'topic', 10)
timer_period = 0.5 # 秒
self.timer = self.create_timer(timer_period, self.timer_callback)
self.i = 0
-
super().__init__('minimal_publisher'):- 调用父类
Node的构造函数,并将节点命名为minimal_publisher。
- 调用父类
-
self.create_publisher(String, 'topic', 10):-
创建一个发布者,用于在名为
topic的话题上发布类型为String的消息。 -
数字
10表示消息队列大小,当消息发布速度快于订阅者处理速度时,最多可保留10条消息。
-
-
self.create_timer(timer_period, self.timer_callback):- 创建一个定时器,每隔0.5秒调用一次
timer_callback函数,这样节点就能以固定的频率发布消息。
- 创建一个定时器,每隔0.5秒调用一次
-
self.i = 0:- 初始化一个计数器,用于记录消息的序号。
4. 定时器回调函数 timer_callback
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
-
msg = String():- 创建一个新的字符串消息实例。
-
msg.data = f'Hello World: {self.i}':- 给消息的数据部分赋值,消息中包含当前计数器的值。
-
self.publisher_.publish(msg):- 使用发布者将消息发布到指定的话题
topic上。
- 使用发布者将消息发布到指定的话题
-
self.get_logger().info(f'Publishing: "{msg.data}"'):- 通过 ROS2 的日志系统将发布的消息内容打印到控制台,以便实时监控节点的运行情况。
-
self.i += 1:- 每发布一次消息后,计数器递增一次,确保每条消息都具有唯一编号。
5. 主函数(main)
def main(args=None):
rclpy.init(args=args)
minimal_publisher = MinimalPublisher()
rclpy.spin(minimal_publisher)
# 显式销毁节点
# (可选 - 否则当垃圾收集器销毁节点对象时会自动完成)
minimal_publisher.destroy_node()
rclpy.shutdown()
rclpy.init(args=args)- 初始化 ROS2 通信环境,这是 ROS2 节点启动前必须调用的函数。
minimal_publisher = MinimalPublisher()- 创建
MinimalPublisher类的一个实例(节点对象)。
- 创建
rclpy.spin(minimal_publisher)- 启动节点,并持续运行,调用节点内部定义的回调函数(如
timer_callback)。
- 启动节点,并持续运行,调用节点内部定义的回调函数(如
minimal_publisher.destroy_node()- 明确销毁节点对象,释放资源。这是一个推荐的做法。
rclpy.shutdown()- 关闭ROS 2通信环境,确保ROS 2环境正常关闭并释放所有相关资源。
6. 程序入口
if __name__ == '__main__':
main()
- 检测Python脚本是否作为主程序运行,如果是,则执行
main()函数启动ROS 2节点。
3 添加依赖和入口点
添加依赖
使用编辑器打开功能包根目录ros2_ws/src/py_pubsub中的package.xml文件,编辑以下标签:
-
<description>:功能包的简单描述 -
<maintainer>:维护者的姓名和联系方式(电子邮件) -
<license>:代码的许可方式,这里使用Apache License 2.0
填写示例如下:
<description>Examples of minimal publisher/subscriber using rclpy</description>
<maintainer email="you@email.com">name</maintainer>
<license>Apache License 2.0</license>
上述标签不是必须的。在上述标签之后,添加程序运行时所需的ROS 2依赖项:
<exec_depend>rclpy</exec_depend>
<exec_depend>std_msgs</exec_depend>
-
<exec_depend>标签表示功能包在运行节点时需要的依赖库:-
rclpy:ROS 2的Python客户端库,提供ROS节点的基本功能。 -
std_msgs:标准消息类型库,这里用于发送字符串类型的消息。
-
完成后保存文件。
添加入口点
编辑 setup.py 文件,确保以下字段与之前 package.xml 文件中的信息保持一致:
maintainer='name',
maintainer_email='you@email.com',
description='Examples of minimal publisher/subscriber using rclpy',
license='Apache License 2.0',
找到 entry_points 字段,修改为以下内容,告诉 ROS2 如何启动Python节点程序:
entry_points={
'console_scripts': [
'talker = py_pubsub.publisher_member_function:main',
],
},
-
talker:命令行中用于启动节点的名称。 -
py_pubsub.publisher_member_function:main:运行publisher_member_function.py文件中的main函数。
完成修改后保存文件。
检查 setup.cfg 文件
打开 setup.cfg 文件,一般创建功能包时该文件会自动正确生成,其默认内容如下:
[develop]
script_dir=$base/lib/py_pubsub
[install]
install_scripts=$base/lib/py_pubsub
这部分定义了 Python 可执行脚本安装后的存放路径,ROS2 默认在 lib 目录中查找并执行这些脚本,因此一般无需改动。
[develop]→ 开发模式下脚本路径[install]→ 安装模式下脚本路径
这两个路径指向 lib/py_pubsub,是 ROS2 约定的节点可执行文件目录,确保 ros2 run 可以找到并执行 Python 节点。完成确认后保存文件。