Подписчик и Издатель (Вариант2)
Что вы узнаете в этом разделе:
- Что такое "асинхронный" подход обработки данных
- Как хранить состояние программы между вызовами функций
- Как использовать таймеры для независимой публикации данных
- Разницу между синхронным и асинхронным подходами
- Когда использовать каждый из подходов
Программа Подписчика и Издателя
Рассмотрим подход, который можно назвать "асинхронным". Входящие топики никак не влияют на работу выходящих топиков, обработка данных и публикация происходит по таймеру с необходимой нам частотой.
Для этого добавим в программу переменную "хранилище" имени пользователя, так как нам необходимо хранить "состояние" программы.
# Хранилище для имени пользователя
self.user_name = "Nobody" # Значение по умолчанию
Функцию обработки данных от Подписчика, упростим. Она будет проверять и обновлять переменную с именем пользователя
#Обновляем имя пользователя, когда мы его получили
def name_callback(self, msg):
name = msg.data
if name: # Проверяем, что имя не пустое
self.user_name = name
self.get_logger().info(f'User name updated to: {name}')
А для публикации данных, воспользуемся таймером, с частотой 2 герца и функцией публикации данных publish_greeting
self.timer = self.create_timer(0.5, self.publish_greeting)
......
def publish_greeting(self):
#Публикует приветствие с текущим именем пользователя
greeting = f"Hello, {self.user_name}"
greeting_msg = String()
greeting_msg.data = greeting
self.publisher.publish(greeting_msg)
self.get_logger().info(f'Published: "{greeting}"')
Полный код получившейся "асинхронной" программы
#!/usr/bin/env python3
import rclpy
from rclpy.node import Node
from std_msgs.msg import String
class GreetingNode(Node):
def __init__(self):
super().__init__('greeting_node')
# Хранилище для имени пользователя
self.user_name = "Nobody" # Значение по умолчанию
# Создаем подписчика на топик /name
self.subscription = self.create_subscription(
String,
'/name',
self.name_callback,
10 # Размер очереди
)
# Создаем издателя для топика /greeting
self.publisher = self.create_publisher(
String,
'/greeting',
10 # Размер очереди
)
# Создаем таймер для публикации приветствия каждые 0.5 секунды
self.timer = self.create_timer(0.5, self.publish_greeting)
self.get_logger().info('Greeting node started!')
#Обновляем имя пользователя, когда мы его получили
def name_callback(self, msg):
name = msg.data
if name: # Проверяем, что имя не пустое
self.user_name = name
self.get_logger().info(f'User name updated to: {name}')
def publish_greeting(self):
#Публикует приветствие с текущим именем пользователя
greeting = f"Hello, {self.user_name}"
greeting_msg = String()
greeting_msg.data = greeting
self.publisher.publish(greeting_msg)
self.get_logger().info(f'Published: "{greeting}"')
def main(args=None):
rclpy.init(args=args)
greeting_node = GreetingNode()
try:
rclpy.spin(greeting_node)
except KeyboardInterrupt:
pass
if __name__ == '__main__':
main()
Запустим ее и сразу увидим что начинаются публиковаться сообщения с заданной частотой. В самом начале работы программы, имя пользователя Nobody
python3 pub-sub2.py
[INFO] [1757346870.166230525] [greeting_node]: Greeting node started!
[INFO] [1757346870.650511581] [greeting_node]: Published: "Hello, Nobody"
[INFO] [1757346871.150480584] [greeting_node]: Published: "Hello, Nobody"
[INFO] [1757346871.650390068] [greeting_node]: Published: "Hello, Nobody"
[INFO] [1757346872.150407664] [greeting_node]: Published: "Hello, Nobody"
Опубликуем новое имя
ros2 topic pub /name "std_msgs/msg/String" 'data: 'Иван''
[INFO] [1757346877.562927934] [greeting_node]: User name updated to: Иван
[INFO] [1757346877.650443192] [greeting_node]: Published: "Hello, Иван"
[INFO] [1757346878.150455047] [greeting_node]: Published: "Hello, Иван"
Программа работает, как мы и ожидали. Постоянно идут данные, а при поступлении нового имени приветствие меняется.
Финальный примеры программы, доступны в разделе примеры на GitHub turtlebro2-examples