本教程將詳細介紹如何使用 ROS 2 實現一個節點訂閱另一個節點發布的消息,并將接收到的消息通過?espeakng
?庫進行朗讀的完整流程。以下步驟假設你已經安裝好了 ROS 2 環境(以 ROS 2 Humble 為例),并熟悉基本的 Linux 操作。
注意:本文在上一篇的基礎之上進階,所以請先參考上一篇《。。》
編寫發布者代碼
(直接在上一篇的基礎上改的)
1.進入功能包的 Python 代碼目錄:
cd ~/chapt3/topic_ws/src/demo_python_topic/demo_python_topic
2.創建 Python 腳本文件?novel_pub_node.py
,內容如下:
import rclpy
from rclpy.node import Node
import os
from example_interfaces.msg import Stringclass NovelPubNode(Node):def __init__(self, node_name):super().__init__(node_name)self.get_logger().info(f'{node_name},啟動!')self.publisher_ = self.create_publisher(String, 'novel', 10)def publish_novel_from_file(self):workspace_dir = '/home/elf/chapt3/topic_ws'file_path = os.path.join(workspace_dir, 'novel1.txt')self.get_logger().info(f"嘗試打開文件: {file_path}")try:with open(file_path, 'r', encoding='utf-8') as file:content = file.read()self.get_logger().info('開始發布小說內容:')msg = String()msg.data = contentself.publisher_.publish(msg)self.get_logger().info('小說內容發布完成')except FileNotFoundError:self.get_logger().error('未找到 novel1.txt 文件,請檢查文件是否存在。')except Exception as e:self.get_logger().error(f'讀取文件時出現錯誤:{e}')def main():rclpy.init()node = NovelPubNode('novel_pub')node.publish_novel_from_file()node.destroy_node()rclpy.shutdown()if __name__ == "__main__":main()
解釋:
- 導入必要的模塊,
rclpy
?用于 ROS 2 Python 開發,os
?用于文件操作,example_interfaces.msg.String
?是消息類型。 NovelPubNode
?類繼承自?Node
,在構造函數中初始化節點并創建發布者。publish_novel_from_file
?方法讀取指定文件內容,并將其發布到?novel
?話題。
?編寫訂閱者代碼
- 仍在?
~/chapt3/topic_ws/src/demo_python_topic/demo_python_topic
?目錄下,創建 Python 腳本文件?novel_sub_node.py
,內容如下:import espeakng import rclpy from rclpy.node import Node from example_interfaces.msg import String from queue import Queue import threading import timeclass NovelSubNode(Node):def __init__(self, node_name):super().__init__(node_name)self.get_logger().info(f'{node_name},啟動!')self.novel_queue = Queue()self.novel_subscriber = self.create_subscription(String, 'novel', self.novel_callback, 10)self.speech_thread = threading.Thread(target=self.speak_thread)self.speech_thread.start()def novel_callback(self, msg):self.novel_queue.put(msg.data)def speak_thread(self):speaker = espeakng.Speaker()speaker.voice = 'zh'while rclpy.ok():if self.novel_queue.qsize() > 0:text = self.novel_queue.get()self.get_logger().info(f'朗讀:{text}')speaker.say(text)speaker.wait()else:time.sleep(1)def main():rclpy.init()node = NovelSubNode('novel_sub')rclpy.spin(node)rclpy.shutdown()
解釋:
- 導入必要的模塊,
espeakng
?用于語音合成,rclpy
?等用于 ROS 2 開發。 NovelSubNode
?類繼承自?Node
,在構造函數中初始化節點、創建訂閱者和啟動語音線程。novel_callback
?方法將接收到的消息放入隊列。speak_thread
?方法從隊列中取出消息并進行語音朗讀。
配置setup.py文件
打開?~/chapt3/topic_ws/src/demo_python_topic/setup.py
?文件,在?entry_points
?部分添加以下內容:
'console_scripts': ['novel_pub_node = demo_python_topic.novel_pub_node:main','novel_sub_node = demo_python_topic.novel_sub_node:main',
],
解釋:console_scripts
?用于定義命令行可執行腳本,分別指定了發布者和訂閱者節點的可執行入口。
編譯工作空間?
cd ~/chapt3/topic_ws
colcon build
source install/setup.bash
解釋:重新編譯工作空間,使新添加或修改的代碼生效。編譯完成后設置工作空間環境變量。?
運行節點
- 打開一個新終端,運行發布者節點:
ros2 run demo_python_topic novel_pub_node
2.再打開一個新終端,運行訂閱者節點:
ros2 run demo_python_topic novel_sub_node
此時,訂閱者節點會接收到發布者節點發布的消息,并將消息內容通過?espeakng
?庫進行朗讀。