Подписчик и Издатель (Вариант1)
Что вы узнаете в этом разделе:
- Как создать программу, которая одновременно является и издателем, и подписчиком
- Как обрабатывать входящие данные и сразу публиковать результат
- Что такое "синхронный" подход обработки данных
- Как создать комплексную программу, работающую с несколькими топиками
- Преимущества и ограничения синхронного подхода
Программа Подписчика и Издателя
Ранее мы создавали отдельно программы Подписчики и программы Издатели. На этом уроке мы попрактикуемся создавать комплексные примеры, в которых мы будем использовать сразу оба метода коммуникации.
Давайте создадим программу, которая будет принимать данные из одного топика и передавать в другой. Например, мы будем получать в топик /name данные типа String с именем человека, и возвращать приветствие в виде "Hello, Имя" в топик /greeting
Файл chapter5/pub_sub1.py
#!/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')
# Создаем подписчика на топик /name
self.subscription = self.create_subscription(
String,
'/name',
self.name_callback,
10 # Размер очереди
)
# Создаем издателя для топика /greeting
self.publisher = self.create_publisher(
String,
'/greeting',
10 # Размер очереди
)
self.get_logger().info('Greeting node started!')
def name_callback(self, msg):
name = msg.data
if name: # Проверяем, что имя не пустое
# Формируем приветствие
greeting = f"Hello, {name}"
# Создаем сообщение для отправки
greeting_msg = String()
greeting_msg.data = greeting
# Публикуем приветствие
self.publisher.publish(greeting_msg)
# Логируем для отладки
self.get_logger().info(f'Received: "{name}" -> Sent: "{greeting}"')
else:
self.get_logger().warn('Received empty name!')
def main(args=None):
rclpy.init(args=args)
greeting_node = GreetingNode()
try:
rclpy.spin(greeting_node)
except KeyboardInterrupt:
pass
if __name__ == '__main__':
main()
Запустим пример
python3 pub-sub1.py
[INFO] [1757345256.008554993] [greeting_node]: Greeting node started!
Как мы видим, логика нашей программы довольно простая. Мы создаем один Подписчик self.subscription и один Издатель self.publisher. Как только в подписчике появляется новое сообщение, то формируется ответ (в функции name_callback) и отправляется через созданный Издатель.
Поэтому, пока данные не придут в топик /name, программа ничего не выводит.
Отправим данные в топик через утилиту ros2 topic pub
ros2 topic pub /name "std_msgs/msg/String" "data: 'Petr'"
В выводе программы начнут выводиться приветственные сообщения
[INFO] [1757345323.974932442] [greeting_node]: Received: "Petr" -> Sent: "Hello, Petr"
[INFO] [1757345324.875742728] [greeting_node]: Received: "Petr" -> Sent: "Hello, Petr"
[INFO] [1757345325.875639273] [greeting_node]: Received: "Petr" -> Sent: "Hello, Petr"
Также мы можем это проверить, посмотрев топик /greeting
ros2 topic echo /greeting
===
data: Hello, Petr
---
data: Hello, Petr
---
data: Hello, Petr
Данный "стиль" обработки данных, можно назвать "синхронным". Как только данные появились, мы их обработали и моментально опубликовали.
Такой подход имеет ряд особенностей. Например, если данные не приходят, то и ответ программы отсутствует. Также мы не можем изменить частоту публикации ответов, она всегда будет равна частоте входящего топика.
Такой подход не подойдет, когда обработка данных будет занимать больше времени, чем есть у программы до получения следующего сообщения.