Skip to content

Un Node Publisher Simple

El Código

Copia todo el código a continuación en tu archivo publisher.py y revisa las anotaciones para entender cómo funciona todo.

¡No olvides el Shebang!

La primera línea del código parece un comentario, pero en realidad es una parte muy crucial del script:

#!/usr/bin/env python3

Esto se llama el Shebang, y le indica al sistema operativo qué intérprete usar para ejecutar el código. En nuestro caso, le indica al sistema operativo dónde encontrar el intérprete de Python correcto que debe usarse para ejecutar el código.

publisher.py
#!/usr/bin/env python3
# A simple ROS2 Publisher

import rclpy # (1)!
from rclpy.node import Node

from example_interfaces.msg import String # (2)!

class SimplePublisher(Node): # (3)!

    def __init__(self):
        super().__init__("simple_publisher") # (4)!

        self.my_publisher = self.create_publisher(
            msg_type=String,
            topic="my_topic",
            qos_profile=10,
        ) # (5)!

        publish_rate = 1 # Hz
        self.timer = self.create_timer(
            timer_period_sec=1/publish_rate, 
            callback=self.timer_callback
        ) # (6)!

        self.get_logger().info(
            f"The '{self.get_name()}' node is initialised." # (7)!
        )

    def timer_callback(self): # (8)!
        ros_time = self.get_clock().now().seconds_nanoseconds()

        topic_msg = String()
        topic_msg.data = f"The ROS time is {ros_time[0]} (seconds)."
        self.my_publisher.publish(topic_msg)
        self.get_logger().info(f"Publishing: '{topic_msg.data}'")

def main(args=None): # (9)!
    rclpy.init(args=args)
    my_simple_publisher = SimplePublisher()
    rclpy.spin(my_simple_publisher)
    my_simple_publisher.destroy_node()
    rclpy.shutdown()

if __name__ == '__main__': # (10)!
    main()
  1. rclpy es la Biblioteca Cliente de ROS para Python.

    Esta es una importación vital que nos permite crear nodes de ROS e inicializarlos en la red ROS.

    También importamos la clase Node de la biblioteca rclpy.node. Esta es una Clase Python ya preparada que contiene toda la funcionalidad necesaria que un Node ROS Python podría necesitar, por lo que la usaremos como base para nuestro propio node (que crearemos en breve).

  2. También necesitamos importar el tipo de mensaje String de la biblioteca example_interfaces.msg para publicar nuestros mensajes.

  3. Creamos una clase Python llamada SimplePublisher(), que usaremos para encapsular toda la funcionalidad de nuestro node.

    La gran mayoría de la funcionalidad de este node se hereda de la Clase Node() de rclpy.node, que importamos arriba.

  4. Usando el método super() llamamos al método __init__() de la clase Node padre de la que deriva nuestra clase SimplePublisher.

    Proporcionamos un nombre aquí, que es el nombre que se usará para registrar nuestro node en la red ROS (podemos llamar al node como queramos, pero es una buena idea llamarlo con algo significativo).

  5. Luego usamos el método create_publisher() (heredado de la clase Node) para proporcionar a nuestro node la capacidad de publicar mensajes en un ROS Topic. Al llamar a esto proporcionamos 3 datos clave:

    1. msg_type: El tipo de mensaje que queremos publicar.

      En nuestro caso, un mensaje String del módulo example_interfaces.msg.

    2. topic: El nombre del topic al que queremos publicar estos mensajes.

      Este podría ser un topic existente (en cuyo caso, necesitaríamos asegurarnos de usar el tipo de mensaje correcto), o un nuevo topic (en cuyo caso, el nombre puede ser cualquier cosa que queramos).

      En nuestro caso, queremos crear un nuevo topic en la red ROS llamado "my_topic".

    3. qos_profile: Un tamaño de cola, que es una configuración de "Calidad de Servicio" (QoS) que limita la cantidad de mensajes que se ponen en cola en un buffer.

      En nuestro caso, lo estamos configurando en 10, lo que generalmente es apropiado para la mayoría de las aplicaciones en las que trabajaremos.

  6. Aquí, estamos llamando al método create_timer(), que usaremos para controlar la frecuencia a la que se publican mensajes en nuestro topic. Aquí definimos 2 cosas:

    1. timer_period_sec: La frecuencia a la que queremos que se ejecute el timer. Esto debe proporcionarse como un período, en segundos. En la línea anterior, hemos especificado una frecuencia de publicación (en Hz):

      publish_rate = 1 # Hz

      Entonces el período de tiempo asociado (en segundos) es:

      \(T = \frac{1}{f}\)

    2. callback: Esta es una función que se ejecutará cada vez que el timer transcurra a la frecuencia deseada (1 Hz). Estamos especificando una función llamada timer_callback, que definiremos más adelante en el código...

  7. Finalmente, usamos el método get_logger().info() para enviar un mensaje de Log a la terminal para informarnos que la inicialización de nuestro node está completa.

  8. Aquí definimos la función callback del timer. Todo lo que esté aquí se ejecutará a la frecuencia que especificamos cuando creamos la instancia create_timer() antes. En nuestro caso:

    1. Usamos el método get_clock() para obtener el Tiempo ROS actual.
    2. Instanciamos un mensaje String() (definido como topic_msg).
    3. Llenamos este mensaje con datos. En nuestro caso, una declaración que incluye el Tiempo ROS obtenido anteriormente.
    4. Llamamos al método publish() de nuestro objeto my_publisher, para publicar realmente este mensaje en el topic "my_topic".
    5. También enviamos los datos del mensaje a la terminal como un mensaje de log, para que podamos ver qué es cuando nuestro Node está realmente ejecutándose.
  9. Con la funcionalidad de nuestra clase SimplePublisher ahora establecida, definimos una función main() para el Node. Esto será bastante común en la mayoría de los Nodes Python que creemos, con los siguientes 5 procesos clave:

    1. Inicializar la biblioteca rclpy.
    2. Crear una instancia de nuestro node SimplePublisher().
    3. Hacer "spin" al node para mantenerlo vivo de modo que cualquier callback pueda ejecutarse según sea necesario (en nuestro caso aquí, solo el timer_callback()).
    4. Destruir el node una vez que se solicite la terminación (activada al ingresar Ctrl+C en la terminal).
    5. Apagar la biblioteca rclpy.
  10. Finalmente, llamamos a la función main() para poner todo en marcha. Hacemos esto dentro de una instrucción if, para asegurarnos de que nuestro node sea el ejecutable principal (es decir, ha sido ejecutado directamente (a través de ros2 run), y no ha sido llamado por otro script).

Definición de Dependencias del Package

Aquí estamos importando un par de bibliotecas Python en nuestro node, lo que significa que nuestro package tiene dos dependencias: rclpy y example_interfaces:

import rclpy 
from rclpy.node import Node

from example_interfaces.msg import String

Es una buena práctica añadir estas dependencias a tu archivo package.xml. Localiza este archivo (ros2_ws/src/part1_pubsub/package.xml), ábrelo y encuentra la siguiente línea:

<exec_depend>rclpy</exec_depend>

rclpy ya está definido como una dependencia de ejecución (lo que significa que nuestro package necesita esta biblioteca para ejecutar nuestro código), pero también necesitamos añadir example_interfaces, así que añade la siguiente línea adicional debajo:

<exec_depend>example_interfaces</exec_depend>

Listo. Guarda el archivo y ciérralo.