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:
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.
#!/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()
-
rclpyes 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
Nodede la bibliotecarclpy.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). -
También necesitamos importar el tipo de mensaje
Stringde la bibliotecaexample_interfaces.msgpara publicar nuestros mensajes. -
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()derclpy.node, que importamos arriba. -
Usando el método
super()llamamos al método__init__()de la clase Node padre de la que deriva nuestra claseSimplePublisher.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).
-
Luego usamos el método
create_publisher()(heredado de la claseNode) para proporcionar a nuestro node la capacidad de publicar mensajes en un ROS Topic. Al llamar a esto proporcionamos 3 datos clave:-
msg_type: El tipo de mensaje que queremos publicar.En nuestro caso, un mensaje
Stringdel móduloexample_interfaces.msg. -
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". -
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.
-
-
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:-
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 # HzEntonces el período de tiempo asociado (en segundos) es:
\(T = \frac{1}{f}\) -
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 llamadatimer_callback, que definiremos más adelante en el código...
-
-
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. -
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:- Usamos el método
get_clock()para obtener el Tiempo ROS actual. - Instanciamos un mensaje
String()(definido comotopic_msg). - Llenamos este mensaje con datos. En nuestro caso, una declaración que incluye el Tiempo ROS obtenido anteriormente.
- Llamamos al método
publish()de nuestro objetomy_publisher, para publicar realmente este mensaje en el topic"my_topic". - 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.
- Usamos el método
-
Con la funcionalidad de nuestra clase
SimplePublisherahora establecida, definimos una funciónmain()para el Node. Esto será bastante común en la mayoría de los Nodes Python que creemos, con los siguientes 5 procesos clave:- Inicializar la biblioteca
rclpy. - Crear una instancia de nuestro node
SimplePublisher(). - 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()). - Destruir el node una vez que se solicite la terminación (activada al ingresar Ctrl+C en la terminal).
- Apagar la biblioteca
rclpy.
- Inicializar la biblioteca
-
Finalmente, llamamos a la función
main()para poner todo en marcha. Hacemos esto dentro de una instrucciónif, para asegurarnos de que nuestro node sea el ejecutable principal (es decir, ha sido ejecutado directamente (a través deros2 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:
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:
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:
Listo. Guarda el archivo y ciérralo.