Skip to content

ROS2 话题通信 ​

话题概念 ​

在 ROS 2 中,**话题(Topic)**是节点之间进行单向、异步消息通信的重要机制。

话题数据传输的特性是从一个节点到另外一个节点,发送数据的对象称之为发布者,接收数据的对象称之为订阅者,每一个话题都需要有一个名字,传输的数据也需要有固定的数据类型。

  • 发布者负责向某个话题发送消息;
  • 订阅者负责从某个话题接收消息;
  • 只要话题名称一致且消息类型匹配,节点之间就可以完成通信。
text
发布者节点 → 话题 (Topic) → 订阅者节点

说明示例 发布者不断发送字符串 "Hello ROS 2";订阅者监听同一个话题并打印收到的字符串。 显然,话题通信中需要至少两个节点,一个为发布者节点,一个为订阅者节点。

话题通信的特点 ​

ROS 2 话题通信具有以下特点:

  • 异步通信:发送方和接收方无需同步阻塞等待;
  • 一对多 / 多对多:一个发布者可对应多个订阅者,一个话题也可被多个发布者使用;
  • 解耦合:节点之间不需要直接知道对方程序细节,只需约定好话题名和消息类型。

异步的含义 ​

异步表示发布者发布消息后,订阅者并不一定立即接收到消息。即不是同步通信。

Python 发布者/订阅者程序的基本构成 ​

  • 导入 rclpy;
  • 导入 Node 基类;
  • 导入消息类型,例如 std_msgs.msg.String;
  • 定义节点类;
  • 在节点中创建 publisher 或 subscription;
  • 编写回调函数;
  • 在 main() 中初始化、创建节点并执行 spin()。

学习内容五:话题通信实践 ​

(一)创建实验功能包 ​

进入工作空间 src 目录,创建 Python 功能包:

bash
ros2 pkg create learn_topic --build-type ament_python --dependencies rclpy std_msgs

说明注意 这里依赖多了一个 std_msgs,std_msgs 是 ROS 2 常用标准消息包。接下来的部分要使用 String 消息类型。

(二)编写发布者节点 ​

回调函数:用于处理订阅消息、服务请求、定时器触发等事件的核心机制。ROS 2 使用基于 rclcpp(C++)或 rclpy(Python)的异步执行模型,通过回调函数响应各种事件。主要有订阅者(Subscriber)回调,定时器(Timer)回调,服务(Service)回调。

python
import rclpy                                     # ROS2 Python接口库
from rclpy.node import Node                      # ROS2 节点类
from std_msgs.msg import String                  # 字符串消息类型

"""
创建一个发布者节点
"""
class PublisherNode(Node):
    def __init__(self, node_name:str, topic_name: str):
        super().__init__(node_name)
        
        # 由于节点时继承自Node类,在Node类中已经实现了通过create_publisher方法来创建一个发布者 
        # msg_type表示传递的消息类型,topic表示话题通信的名称,对于发布者和订阅者要统一,qos_profile设置为10即可,不用深究
        self.publisher_ = self.create_publisher(msg_type=String,topic=topic_name,qos_profile=10) # 创建发布者对象

        # 创建一个定时器,表示每0.5秒执行一次timer_callback这个回调函数,也就是我们要实现的发布者要发布的消息都在回调函数中实现
        self.timer_ = self.create_timer(timer_period_sec=0.5, callback = self.timer_callback)

        self.i = 0

    def timer_callback(self):                                     # 创建定时器周期执行的回调函数
        msg = String()                                            # 创建一个String类型的消息对象
        msg.data = f'Hello ROS 2: [{self.i}]'                     # 填充消息对象中的消息数据
        self.publisher_.publish(msg)                               # 发布话题消息
        self.get_logger().info('已发布消息: "%s"' % msg.data)     # 输出日志信息,提示已经完成话题发布
        self.i += 1

def main(args = None):
    rclpy.init(args=args)
    pub_node = PublisherNode(node_name='pub',topic_name='learn_topic')  # 节点名 pub,话题名 learn_topic(要和订阅者一致)
    rclpy.spin(pub_node) # 启动节点
    pub_node.destroy_node()
    rclpy.shutdown()

(三)编写订阅者节点 ​

订阅者通过 create_subscription() 订阅话题,并在回调函数中处理接收到的消息。

python
import rclpy                      # ROS2 Python接口库
from rclpy.node   import Node     # ROS2 节点类
from std_msgs.msg import String   # ROS2标准定义的String消息

"""
创建一个订阅者节点
"""
class SubscriberNode(Node):
    def __init__(self, node_name:str, topic_name: str):
        super().__init__(node_name)                             # ROS2节点父类初始化
        self.sub = self.create_subscription(
            String, topic_name, self.listener_callback, 10) # 创建订阅者对象(消息类型、话题名、订阅者回调函数、队列长度)

    def listener_callback(self, msg: String):                      # 创建回调函数,执行收到话题消息后对数据的处理  
        self.get_logger().info(f'订阅到的数据:{msg.data}') # 输出日志信息,提示订阅收到的话题消息

def main(args=None):
    rclpy.init(args=args)
    sub_node = SubscriberNode(node_name='sub',topic_name='learn_topic')
    rclpy.spin(sub_node) # 启动节点
    sub_node.destroy_node()
    rclpy.shutdown()

if __name__ == '__main__':
    main()

(四)修改 setup.py ​

python
entry_points={
    'console_scripts': [
        'publisher = learn_topic.node_pub:main',
        'subscriber = learn_topic.node_sub:main',
    ],
},

(五)编译功能包 ​

返回工作空间根目录,编译并加载环境:

bash
colcon build
source install/setup.bash

(六)运行发布者与订阅者节点 ​

1. 运行发布者节点 ​

打开第一个终端:

bash
cd lq_ws/
source install/setup.bash
ros2 run learn_topic publisher

2. 运行订阅者节点 ​

第一个终端不关闭,打开第二个终端:

bash
cd lq_ws/
source install/setup.bash
ros2 run learn_topic subscriber

运行结果如下:

使用命令行工具验证话题通信 ​

1. 查看当前话题列表 ​

bash
ros2 topic list

2. 查看话题消息内容 ​

bash
ros2 topic echo /learn_topic # 注意替换为自己的消息名称

将持续显示发布者发送的消息内容。

3. 查看话题信息 ​

bash
ros2 topic info /learn_topic

可查看该话题的消息类型以及发布者/订阅者数量。