Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Подписчик и Издатель (Вариант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

Данный "стиль" обработки данных, можно назвать "синхронным". Как только данные появились, мы их обработали и моментально опубликовали.

Такой подход имеет ряд особенностей. Например, если данные не приходят, то и ответ программы отсутствует. Также мы не можем изменить частоту публикации ответов, она всегда будет равна частоте входящего топика.

Такой подход не подойдет, когда обработка данных будет занимать больше времени, чем есть у программы до получения следующего сообщения.