【多喝热水系列】从零开始的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开发中的多个实用技巧。

相关推荐
yeflx2 小时前
Ubuntu下Colmap源码编译调试
ubuntu·3d
羊小猪~~2 小时前
【QT】--文件操作
前端·数据库·c++·后端·qt·qt6.3
宇宙帅猴2 小时前
【Ubuntu踩坑及解决方案(一)】
linux·运维·ubuntu·go
Frank Castle2 小时前
【C语言】详解C语言字节打包:运算符优先级、按位或与字节序那些坑
c语言·开发语言
kk哥88992 小时前
分享一些学习JavaSE的经验和技巧
java·开发语言
_Xiaosz2 小时前
Photo-SLAM / ORB-SLAM3 编译报错解决:undefined reference to DUtils::Random
linux·ubuntu
橙露2 小时前
时间序列分析实战:用 Python 实现股票价格预测与风险评估
人工智能·python·机器学习
2501_940315262 小时前
【无标题】1.17给定一个数将其转换为任意一个进制数(用栈的方法)
开发语言·c++·算法
神云瑟瑟2 小时前
看langchain理解python中的链式调用
python·langchain·链式调用