【多喝热水系列】从零开始的ROS2之旅——Day10 话题的订阅与发布1:Python

【多喝热水系列】从零开始的ROS2之旅------Day10 话题的订阅与发布1:Python

大家好,欢迎回到"多喝热水"ROS2系列专栏!经过前9天的铺垫,我们已经掌握了ROS2的基础环境配置、节点创建、日志打印等核心技能,今天终于要进入ROS2通信机制的核心------话题(Topic)。作为ROS2中最常用的异步通信方式,话题就像一个"广播电台",发布者(Publisher)往指定频道发消息,订阅者(Subscriber)监听频道收消息,完美适配"一方发送、多方接收"的场景。
一、核心目标

今天我们的核心任务是掌握:

  • 学会通过ros2去发布小说
  • 学会通过ros2去订阅小说

依赖工具/库:requests(下载小说)、espeakng(语音朗读)、queue(队列管理)、threading(多线程),以及ROS2内置的example_interfaces消息类型(用于文本传输)。

最重要的前提是要先启动本地HTTP服务器

二、Ros2发布小说

2.1 完整代码

python 复制代码
import rclpy
from rclpy.node import Node
import requests
from example_interfaces.msg import String
#从example_interfaces包中导入String消息类型,用于发布文本消息,  
from queue import Queue

class NovelPubNode(Node):
    def __init__(self,node_name):
        super().__init__(node_name)#继承父类的初始化方法
        self.novels_queue_ = Queue()
        #为什么要创建队列?因为下载的小说是多行的,发布消息时每次发布一行

        self.publisher_ = self.create_publisher(String, 'novel', 10)
        #创建发布者对象,发布String类型的消息,主题名为novel,队列长度10
        
        self.timer_=self.create_timer(1,self.timer_callback)
        #创建定时器对象,每1秒调用一次timer_callback函数

    def download_novel(self,url):
        response = requests.get(url)
        #requests库发送HTTP GET请求,获取小说内容

        response.encoding='utf-8'
        #设置响应内容的编码格式为utf-8,防止中文乱码

        self.get_logger().info(f'下载完成:{url}')
        #打印日志,提示下载完成

        for line in response.text.splitlines():
            self.novels_queue_.put(line)
            #将小说的每一行放入队列中

    def timer_callback(self):
        if self.novels_queue_.qsize()>0:
            msg=String()#创建消息对象的作用是为了发布消息

            msg.data=self.novels_queue_.get()
             #从队列中获取一行小说内容将获取的小说内容赋值给消息对象的data属性
             #data属性是String消息类型中用于存储文本内容的字段
            
            self.publisher_.publish(msg)
            #发布消息,将消息对象发布到主题上
            self.get_logger().info(f'发布消息:{msg.data}')


def main():
    rclpy.init()
    node=NovelPubNode('novel_pub')
    node.download_novel('http://localhost:8000/novel1.txt')  # 下载小说
    rclpy.spin(node)
    rclpy.shutdown()

    

    
    
    
python 复制代码
#setup.py添加节点
'novel_pub_node = demo_python_topic.novel_pub_node:main',

二、Ros2订阅小说

3.1依赖安装

bash 复制代码
#apt安装Python3包管理工具
sudo apt install python3-pip -y
#apt安装espeak工具
sudo apt install espeak-ng -y
# 安装espeakng的Python库(语音朗读)
pip3 install espeakng

3.2 完整代码

python 复制代码
import rclpy
from rclpy.node import Node
from example_interfaces.msg import String #从example_interfaces包中导入String消息类型
import threading
from queue import Queue
import time
import espeakng


class NovelSubNode(Node):
    def __init__(self, node_name):
        super().__init__(node_name)
        self.get_logger().info('小说朗读节点启动')

        self.novels_queue_= Queue()
        self.get_logger().info('正在创建小说订阅者...')

        self.novel_subscriber_ = self.create_subscription(
            String,'novel',self.novel_callback,10
        )
        self.get_logger().info('小说订阅者创建成功,正在等待小说内容...')

        self.speech_thread_=threading.Thread(target=self.speak_thread)
        self.get_logger().info('启动小说朗读线程...')

        self.speech_thread_.start()
        self.get_logger().info('小说朗读线程启动成功')




    def novel_callback(self, msg):
        # if msg.data=='':
        #     self.get_logger().info('收到空小说内容,忽略该消息')
        # else:
            self.novels_queue_.put(msg.data)
            self.get_logger().info('收到小说内容,已加入朗读队列')

    def speak_thread(self):
        speaker = espeakng.Speaker()
        self.get_logger().info('初始化语音引擎...')

        speaker.voice = 'zh'
        self.get_logger().info('语音引擎初始化完成,开始朗读小说内容...')

        
        
        
    
        while rclpy.ok() :
            if self.novels_queue_.qsize() > 0:
                self.get_logger().info('检测到朗读队列中有小说内容,开始朗读...')
                text=self.novels_queue_.get()
                self.get_logger().info(f'正在朗读小说内容:{text}')
                speaker.say(text)
                
                speaker.wait()
                self.get_logger().info('小说内容朗读完成')
            else:
                time.sleep(1)
                self.get_logger().info('朗读队列为空,等待新的小说内容...')
                # 等待一段时间再检查队列,避免忙等待



def main(args=None):
    rclpy.init(args=args)

    node = NovelSubNode("novel_read")
    rclpy.spin(node)
    rclpy.shutdown()
python 复制代码
#setup.py添加节点
'novel_sub_node = demo_python_topic.novel_sub_node:main',

小技巧总结:

解决订阅者队列接收不到数据的问题:

注释的快捷键:

ctrl+/

五、总结

通过今天的案例,我们不仅实现了话题的发布与订阅,还掌握了ROS2开发中的多个实用技巧。

相关推荐
我在人间贩卖青春几秒前
C++之数据类型的扩展
c++·字符串·数据类型
wazmlp00188736911 分钟前
python第三次作业
开发语言·python
娇娇乔木12 分钟前
模块十一--接口/抽象方法/多态--尚硅谷Javase笔记总结
java·开发语言
明月醉窗台24 分钟前
qt使用笔记六之 Qt Creator、Qt Widgets、Qt Quick 详细解析
开发语言·笔记·qt
wangjialelele28 分钟前
平衡二叉搜索树:AVL树和红黑树
java·c语言·开发语言·数据结构·c++·算法·深度优先
深蓝电商API29 分钟前
住宅代理与数据中心代理在爬虫中的选择
爬虫·python
苏宸啊29 分钟前
C++栈和队列
c++
lili-felicity35 分钟前
CANN性能调优与实战问题排查:从基础优化到排障工具落地
开发语言·人工智能
独自破碎E38 分钟前
【BISHI15】小红的夹吃棋
android·java·开发语言
森G39 分钟前
七、04ledc-sdk--------makefile有变化
linux·c语言·arm开发·c++·ubuntu