2. 话题通信基础

任务目标

本案例将创建两个ROS2节点:一个发布者(talker)周期发布自定义消息,一个订阅者(listener)接收并打印消息内容。通过运行这两个节点,观察话题通信中数据从发布端到订阅端的流动过程。

环境准备

需要已安装ROS2(Humble或更新版本),并创建好工作空间。自定义消息包 learning_interface 需提前编译,其内容定义如下:

# learning_interface/msg/Student.msg
string name
int64 age
float64 score

完整实现代码

以下代码包含发布者与订阅者两个节点,以及 main 入口。将两个节点分别保存为 talker.py 和 listener.py,放在 learning_topic/learning_topic/ 目录下。

# talker.py
import rclpy
from rclpy.node import Node
from learning_interface.msg import Student

class Talker(Node):
    def __init__(self):
        super().__init__('talker')
        self.publisher = self.create_publisher(Student, 'student_info', 10)
        self.timer = self.create_timer(1.0, self.timer_callback)
        self.count = 0

    def timer_callback(self):
        msg = Student()
        msg.name = f'Student_{self.count}'
        msg.age = 20 + self.count
        msg.score = 90.0 + self.count * 0.5
        self.publisher.publish(msg)
        self.get_logger().info(f'发布: name={msg.name}, age={msg.age}, score={msg.score}')
        self.count += 1

def main(args=None):
    rclpy.init(args=args)
    node = Talker()
    try:
        rclpy.spin(node)
    except KeyboardInterrupt:
        pass
    finally:
        node.destroy_node()
        rclpy.shutdown()

if __name__ == '__main__':
    main()
# listener.py
import rclpy
from rclpy.node import Node
from learning_interface.msg import Student

class Listener(Node):
    def __init__(self):
        super().__init__('listener')
        self.subscription = self.create_subscription(
            Student, 'student_info', self.listener_callback, 10)

    def listener_callback(self, msg):
        self.get_logger().info(f'收到: name={msg.name}, age={msg.age}, score={msg.score}')

def main(args=None):
    rclpy.init(args=args)
    node = Listener()
    try:
        rclpy.spin(node)
    except KeyboardInterrupt:
        pass
    finally:
        node.destroy_node()
        rclpy.shutdown()

if __name__ == '__main__':
    main()

编译与运行

在 learning_topic 包目录下,确保 setup.py 中已添加入口点:

entry_points={
    'console_scripts': [
        'talker = learning_topic.talker:main',
        'listener = learning_topic.listener:main',
    ],
},

然后执行编译与运行:

cd ~/ros2_ws
colcon build --packages-select learning_topic
source install/setup.bash
# 终端1
ros2 run learning_topic talker
# 终端2
ros2 run learning_topic listener

验证方法与判定标准

运行后,在发布者终端应看到每秒钟输出一条“发布: name=Student_X...”日志;在订阅者终端应同步看到“收到: name=Student_X...”日志,且两者的 name、age、score 字段值完全一致。若两个终端日志内容匹配且持续更新,说明话题通信成功。

额外验证:在第三个终端执行 ros2 topic echo /student_info,应能看到与发布者相同的数据内容,进一步确认消息在话题上正常流动。

预期输出示例

# talker终端
[INFO] 发布: name=Student_0, age=20, score=90.0
[INFO] 发布: name=Student_1, age=21, score=90.5
[INFO] 发布: name=Student_2, age=22, score=91.0

# listener终端
[INFO] 收到: name=Student_0, age=20, score=90.0
[INFO] 收到: name=Student_1, age=21, score=90.5
[INFO] 收到: name=Student_2, age=22, score=91.0
发布者节点 talker 话题 /student_info Student 消息 订阅者节点 listener publish subscribe 数据流方向 name + age + score → 序列化 → 话题 → 反序列化