Suscriptores de ROS que usan Python

Puede parecer que escribir un suscriptor para un bot en simulación trae consigo una gran complejidad, pero definitivamente no es así. Progresar a través de los tutoriales para Robot Operating Systems (ROS) ofrece una gran cantidad de conocimiento, sin embargo, podría ser bueno proporcionar más información. Este artículo tiene como objetivo proporcionar una comprensión de cómo funcionan los suscriptores y están mejor escritos en Python. 

Suscriptores de ROS

Dado que los robots pretenden principalmente automatizar lo que los humanos harían de otro modo, estableceremos paralelismos entre la anatomía humana simple o la parafernalia hecha por el hombre y la construcción del robot para generar perspectiva. Por lo tanto, es crucial que el lector comprenda qué son los Nodes ROS y los temas ROS.

En pocas palabras, ¡un Node puede considerarse como un mini-buzón del Robot! Como resultado, existe en sí mismo para brindarnos cierta información y proceso que recibe de alguna parte (lo calificaremos pronto). También es importante tener en cuenta que un bot puede tener varios Nodes de este tipo (varios minibuzones), cada uno de los cuales tiene acceso a información importante. Pero, ¿qué tipo de información es esta? ¡Los Nodes tienen acceso a diferentes tipos de mensajes y Nodes mismos!

Mientras tanto, un tema de ROS se entiende mejor como un órgano del cuerpo (siguiendo la analogía humana). Cada órgano/tema hace que ciertos «tipos» de información sean accesibles al cerebro. Del siguiente diagrama, se puede inferir que para que nuestro robot tenga algo de visión (usar sus ojos), debe acceder al órgano correcto (ojos) que en ROS es el tema /cámara

Aquí, los temas de ROS son canales en los que el robot transmite información. (Imagen creada con Google Draw)

Entonces, ¿dónde entra el suscriptor en la imagen? El suscriptor es un Node (mini-buzón) que hace posible que el cerebro del robot se ‘ suscriba ‘ a los diversos temas que están constantemente dando información. Por lo tanto, el objetivo principal de un suscriptor es establecer este tipo de comunicación entre los temas y Nodes de ROS. Esto convierte a los suscriptores en la principal forma en que podemos controlar nuestro robot. Para mayor claridad, la definición formal se proporciona a continuación:

Un suscriptor en ROS es un ‘Node’ que es esencialmente un proceso o programa ejecutable, escrito para ‘obtener de’ o ‘suscribirse a’ los mensajes y la información que se publica en un tema de ROS. Un suscriptor no puede publicar o transmitir información por sí mismo.

requisitos previos

Para trabajar junto con los ejemplos, es necesario tener lo siguiente:

  • ROS noetic instalado en su máquina Windows nativa o en Ubuntu (preferible). Muchos usuarios también ejecutan ROS en Ubuntu a través de una máquina virtual.
  • Simulador Turtlebot3. Si bien viene incluido en la instalación de ROS noetic.

Lo primero es lo primero, asegúrese de tener un paquete de repuesto donde pueda almacenar su archivo de script de python. Eche un vistazo a esto si no sabe cómo crear un paquete. Esto podría ser algo así como un paquete de práctica o el paquete en el que desea comenzar a trabajar de inmediato.

Independientemente de cómo desee almacenar el archivo .py, continúe y navegue hasta su espacio de trabajo y la carpeta /src de su paquete. Cree su ficha de suscriptor aquí. Llamémoslo suscriptor.py.

$ cd ~/catkin_ws/src/package_name/src
$ gedit subscriber.py

Veamos ahora la plantilla.

Modelo

Python3

# DO NOT skip the next commented line
#!/usr/bin/env python
  
import rospy
from std_msgs.msg import String
  
  
def callback(data):
      
    # print the actual message in its raw format
    rospy.loginfo("Here's what was subscribed: %s", data.data)
      
    # otherwise simply print a convenient message on the terminal
    print('Data from /topic_name received')
  
  
def main():
      
    # initialize a node by the name 'listener'.
    # you may choose to name it however you like,
    # since you don't have to use it ahead
    rospy.init_node('listener', anonymous=True)
    rospy.Subscriber("/topic_name", String, callback)
      
    # spin() simply keeps python from
    # exiting until this node is stopped
    rospy.spin()
  
if __name__ == '__main__':
      
    # you could name this function
    try:
        main()
    except rospy.ROSInterruptException:
        pass

Explicación

  • Comenzando desde la función principal, observe cómo siempre es mejor usar un bloque de manejo de errores. Llame y ejecute la función principal desde aquí.
  • Comience inicializando el Node. Puede llamar a su Node por cualquier nombre. Aquí lo hemos llamado con el nombre de ‘oyente’. Al configurar anónimo = True , permitimos que el compilador agregue números al final del nombre del Node para garantizar el anonimato. Esto se visualiza mejor con gráficos rqt.
  • En la siguiente línea, definimos el aspecto de suscriptor de este Node.
    • Reemplace </topic_name> con el tema de ROS al que desea suscribirse. Una forma de saber qué temas están accesibles y publicados actualmente es usando los comandos a continuación.
    • El segundo campo indica el tipo de datos del mensaje que estamos suscribiendo. Aquí nos estamos suscribiendo a un tipo String. Importante: importe siempre este tipo de datos desde su paquete respectivo. Aquí, String es un ejecutable del paquete std_msgs.msg.
    • El tercer campo es el campo más crucial. Indica el nombre de la función a la que se enviarán los datos obtenidos . Cada vez que se recibe información del tema ROS especificado, se envía a esta función de devolución de llamada. NO agregue el sufijo ‘( )’ al nombre de la función.
$ roscore
# Press Ctrl+shift_T to open a new terminal
#now build your executables. This is good practice.
$ catkin_make
$ rostopic list
  • Los datos recibidos por nuestro Node suscriptor del tema ROS ahora se envían a callback().
  • En el ejemplo anterior, simplemente imprimimos esta información de datos en un archivo de registro usando el comando ros.loginfo(). Sin embargo, el trabajo real radica en mejorar para qué usamos estos datos obtenidos. Por lo tanto, las aplicaciones reales siempre presentarán códigos extensos después de la devolución de llamada().

Uso de arquitectura orientada a objetos

Siempre es recomendable optar por crear instancias de tus Nodes. Esto hace que la analogía robot-humano o robot-‘mini-buzón’ sea mucho más evidente y más fácil de entender, si no solo la idea de depuración.

En este ejemplo, intentaremos obtener la velocidad a la que se mueve nuestro robot Turtle. Guarde este archivo como subscriber.py, solo por conveniencia.

Python3

#!/usr/bin/env python
  
import rospy
# in this example we try to obtain linear
# and angular velocity related information.
# So we import Twist
from geometry_msgs.msg import Twist
  
  
class basic_subscriber:
  
    def __init__(self):
        # initialize the subscriber node now.
        # here we deal with messages of type Twist()
        self.image_sub = rospy.Subscriber("/cmd_vel", 
                                          Twist, self.callback)
        print("Initializing the instance!")
  
    def callback(self, Twist):
        
        # now simply display what
        # you've received from the topic
        rospy.loginfo(rospy.get_caller_id() + "The velocities are %s",
                      Twist)
        print('Callback executed!')
  
  
def main():
    # create a subscriber instance
    sub = basic_subscriber()
      
    # follow it up with a no-brainer sequence check
    print('Currently in the main function...')
      
    # initializing the subscriber node
    rospy.init_node('listener', anonymous=True)
    rospy.spin()
  
if __name__ == '__main__':
    try:
        main()
    except rospy.ROSInterruptException:
        pass

Explicación

  • La ejecución del código comienza en el bloque try-except. Aquí se llama a la función main().
  • En principal( ):
    • Cree un objeto o una instancia de tipo basic_subscriber(). Esto inicializa la clase.
    • La ejecución continúa en la siguiente línea donde creamos e inicializamos el Node ‘escucha’.
    • rospy.spin( ) asegura que el código se ejecute en un ciclo infinito hasta que se reciba una señal de apagado.
  • En la clase basic_subscriber, usamos el constructor equivalente __init__(self) para definir los atributos de la clase.
  • La entidad suscriptora ya está definida.
    • Se suscribe al tema ‘/cmd_vel’.
    • El tipo de mensaje al que nos hemos suscrito aquí es del tipo Twist(), el que nos indica las velocidades.
    • El último campo llama ahora a la función callback().
      • Aquí es donde imprimimos la información suscrita en el terminal y también en un archivo de registro.
      • Para confirmar que nuestra función de devolución de llamada() se ejecutó con éxito, mostramos un mensaje simple ‘Devolución de llamada ejecutada’.

Asegúrese de hacer de este archivo un ejecutable. Para hacerlo, navegue hasta el directorio de su paquete y escriba lo siguiente:

$ chmod +x subscriber.py

Implementación de ROS

Primero, ejecute turtlebot3 desde la Terminal.

$roscore

# Presione Ctrl+Shift+T para abrir una nueva terminal

$caktin_hacer

# siempre obtenga sus archivos base en cada nueva terminal que abra.

$fuente de desarrollo/setup.bash

# ahora ejecuta el simulador de turtlebot3

$roslaunch turtlebot3_gazebo turtlebot3_world.launch

Notará que Gazebo se inicia y el predeterminado Una vez más, en una nueva Terminal, escriba lo siguiente:

$fuente de desarrollo/setup.bash

$roslaunch turtlebot3_teleop turtlebot3_teleop_key.launch

Ahora podrá controlar el simulador Turtlebot3 que se abre en una ventana separada usando su teclado. Ahora ejecute su secuencia de comandos de suscriptor. En una nueva terminal, ejecute lo siguiente:

$ rosrun <package_name> subscriber.py

Producción:

Cuando el turtlebot3 está parado.

Después de mover el turtlebot3 usando teleop_key.

Demostración de código

Publicación traducida automáticamente

Artículo escrito por siddhant_baroth y traducido por Barcelona Geeks. The original can be accessed here. Licence: CCBY-SA

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *