Reto 1.- ¡Nuestro Primer Programa Python! ("Hola Mundo")

Objetivo:

Aprender a crear, guardar y ejecutar un archivo Python sencill?simo en The Construct. ?Todav?a sin ROS!

Pasos:

  • Abrir el IDE (Editor de C?digo).
  • Crear un archivo nuevo (File -> New File...).
  • Escribir UNA sola l?nea de c?digo: print("Hola Mundo")
  • Guardar el archivo con el nombre holamundo.py.
  • Abrir la Terminal (Web Shell/Carcasa Web).
  • Ejecutar el archivo escribiendo: python holamundo.py y pulsando Enter.
  • ?A Probar!: Deber?a aparecer el mensaje "?Hola Robot desde Python!" en la terminal.

Explicaci?n Sencilla: "Acabamos de crear un 'gui?n' muy corto (holamundo.py) con una sola instrucci?n (print) para que el ordenador (la terminal) diga algo.

?Hemos usado Python por primera vez!".

In [ ]:
print 'Hola Mundo'

Reto 2.- Conectando nuestro Guion con ROS

Objetivo:

Escribir un script Python que se anuncie a s? mismo en la red ROS. ?El primer paso para hablar con robots!

Pasos:

  • En el IDE, crear un archivo nuevo llamado conectar_ros.py
  • Escribir estas l?neas:
In [ ]:
#!/usr/bin/env python
import rospy # Importamos la 'magia' de ROS para Python

# Inicializamos nuestro programa como un 'nodo' de ROS
# Es como darle un nombre y registrarlo en el sistema
rospy.init_node('mi_primer_nodo_ros')

# Mensaje para saber que ha llegado hasta aqui
print("Nodo ROS iniciado")
# Mejor aun, usar el sistema de mensajes de ROS:
rospy.loginfo("Nodo ROS iniciado correctamente")
  • Guardar el archivo.
  • En el terminal, escribir: roscore
  • Abrir otra ventana de terminal y escribir: python conectar_ros.py

??A Probar!: Deber?a aparecer el mensaje:

Nodo ROS iniciado

[INFO] [1746307286.399514]: Nodo ROS iniciado correctamente

Explicaci?n Sencilla: "Ahora nuestro 'guion' (conectar_ros.py) no solo habla con el ordenador, sino que tambi?n se presenta al sistema ROS (rospy.init_node). Es como si nuestro guionista se hubiera registrado oficialmente en el estudio de cine de robots". import rospy es como traer la caja de herramientas de ROS a nuestro script. rospy.loginfo es la forma "oficial" de ROS para mostrar mensajes informativos.

?Hemos creado nuestro primer nodo!

Reto 3.- Preparando el Mensaje de Velocidad

Objetivo:

Aprender a crear el tipo de mensaje que necesitamos para decirle al robot c?mo moverse

Pasos:

  • Lo primero que tenemos que hacer es crear una simulaci?n. Para ello, nos vamos a "Cenador abierto" y pulsamos en "Selecciona una simulaci?n"
  • Abrir el Terminal (Web Shell/Carcasa Web) y escribimos: roslaunch turtlebot_gazebo main.launch Esto lo que va a hacer es lanzar la simulaci?n del Turtlebot 2
  • Abrimos otra ventana del terminal y escribimos:

rostopic list

Nos saldr? una lista de t?picos que est?n dados de alta y se encargan de controlar las propiedades de nuestra Turtlebot 2:

- /cmd_vel.- Este es nuestro volante y acelerador. Es el topic al que enviamos los mensajes con velocidad lineal y angular
- /kobuki/laser/scan.- Aqu? viaja la informaci?n del sensor l?ser del robot. Nos dice las distancias a los objetos que hay alrededor del robot en un plano horizontal.
- /odom.- Es la estimaci?n de la posici?n y orientaci?n del robot (d?nde est? y hacia d?nde mira) calculada a partir del movimiento de sus ruedas.
- /camera/rgb/image_raw.- La imagen "normal", a color, que captura la c?mara del robot.
- /camera/depth/points.- Informaci?n de la c?mara de profundidad, pero procesada como una "nube" de puntos en 3D.
- /tf y /tf_static.- Mantiene la informaci?n sobre d?nde est? cada parte del robot con respecto a las dem?s (d?nde est?n las ruedas con respecto a la base, d?nde est? el l?ser con respecto a la base, d?nde est? la base con respecto al mundo "odom"). tf es para partes que se mueven, tf_static para las que est?n fijas entre s?.
- /clock.- Publica la hora actual de la simulaci?n (?no la hora real!).


  • A nosotros, en este momento, el t?pico que nos va a interesar es /cmd_vel pero ?qu? mensajes puede recibir? Para poder averiguarlo, se lo vamos a preguntar a ROS:

rostopic info /cmd_vel

La salida les mostrar?, entre otras cosas, una l?nea que dice: Type: geometry_msgs/Twist ?Aj?! Con esto descubrimos que el topic /cmd_vel utiliza mensajes del tipo geometry_msgs/Twist

  • Una vez que sabemos que el tipo de mensajes es geometry_msgs/Twist, vamos a investigar qu? "campos" o qu? informaci?n lleva dentro ese tipo de mensaje con el comando rosmsg show:

rosmsg show geometry_msgs/Twist

La salida ?mostrar? la estructura:

geometry_msgs/Vector3 linear
  float64 x
  float64 y
  float64 z
geometry_msgs/Vector3 angular
  float64 x
  float64 y
  float64 z

  • Ahora que ya sabemos el mensaje que se utiliza pra mandar informaci?n sobre la velocidad, estamos listos para crear nuestro script en Python.
  • En el IDE, crear un archivo nuevo llamado mensaje_velocidad.py.
In [ ]:
#!/usr/bin/env python
import rospy
# Importamos el tipo de mensaje para velocidad
from geometry_msgs.msg import Twist

rospy.init_node('creador_de_mensajes')

# Creamos un objeto 'Twist' vacio. Es como un formulario para rellenar.
mensaje = Twist()

# Rellenamos una parte del formulario: velocidad hacia adelante
mensaje.linear.x = 1 # Un poquito de velocidad hacia adelante

# Imprimimos el mensaje para ver como es por dentro
rospy.loginfo("He creado este mensaje de velocidad:")
rospy.loginfo(mensaje)
  • Guardamos el archivo.
  • En la segunda ventana del terminal, ejecutamos el archivo mensaje_velocidad.py (esta vez no vamos a poner la instrucci?n que se necesita para ello)

Nos aparecer? algo como lo siguiente:

[INFO] [1746385258.278719, 0.000000]: He creado este mensaje de velocidad:
[INFO] [1746385258.279473, 0.000000]: linear:
  x: 1
  y: 0.0
  z: 0.0
angular:
  x: 0.0
  y: 0.0
  z: 0.0

Hemos creado el mensaje, lo hemos ejecutado, pero el robot no se mueve. ?Qu? es lo que pasa?

  • Hemos escrito la 'carta' (mensaje = Twist()).
  • Hemos escrito lo que queremos decir en la carta (mensaje.linear.x = 1).
  • Hemos le?do la carta para asegurarnos de que est? bien (rospy.loginfo(mensaje)).
  • PERO... ?Todav?a no hemos ido a correos para enviarla! No hemos contratado al 'cartero' (rospy.Publisher) ni le hemos dicho que lleve esta 'carta' (mensaje) al 'buz?n' del robot (/cmd_vel).

El c?digo del Reto 3 solo prepara el mensaje en la memoria de tu programa Python, pero nunca llega a enviarlo al sistema ROS para que el robot lo reciba.

?Cu?ndo lo enviaremos?

?Precisamente en el Reto 4: ?Enviando la Carta!

Reto 4.- ¡Enviando la Carta!

Objetivo:

Enviar nuestro mensaje Twist al robot para que se mueva un poquito. Introducimos el concepto de "Publisher".

Pasos:

  • En el IDE, nos vamos al archivo mensaje_velocidad.py y lo guardamos como: enviar_una_vez.py
  • Tenemos el mismo c?digo que antes y le vamos a ir a?adiendo distintas cosas:

    • Al nodo, esta vez le vamos a llamar "enviador_de_velocidad"
    • Ahora creamos el cartero (Publisher):

      pub = rospy.Publisher('/cmd_vel', Twist, queue_size=10)

      • Le decimos que env?e mensajes al 'buz?n' /cmd_vel
      • Que las 'cartas' son de tipo Twist
      • queue_size=10 es por si enviamos muchas cartas r?pido
    • Creamos una pausa de 1 segundo para que el cartero est? listo:

      rospy.sleep(1.0)

    • Y, finalmente, enviamos el mensaje:

      pub.publish(mensaje)

    • El c?digo nos debe quedar as?:

In [ ]:
#!/usr/bin/env python
import rospy
# Importamos el tipo de mensaje para velocidad
from geometry_msgs.msg import Twist

rospy.init_node('enviador_de_velocidad')

#Creamos el publicador
pub = rospy.Publisher('/cmd_vel', Twist, queue_size=10)

# Esperamos un segundo para asegurarnos de que el cartero ests listo
rospy.sleep(1.0)

# Creamos un objeto 'Twist' vacio. Es como un formulario para rellenar.
mensaje = Twist()

# Rellenamos una parte del formulario: velocidad hacia adelante
mensaje.linear.x = 1 # Un poquito de velocidad hacia adelante

# Enviamos la carta
rospy.loginfo("Enviando comando")
pub.publish(mensaje)
rospy.loginfo("Comando enviado")

Y el resultado ser?a ?ste:

Reto 5.- Nuestro cartero manda la carta y descansa (el script termina)

Objetivo:

Conseguir que el script se quede esperando acontecimientos. Esto nos va a permitir poder hacer programas m?s complejos

Pasos:

  • Vamos a partir del c?digo del reto anterior.
  • Guardamos el archivo que ten?amos como uno nuevo, al que vamos a llamar "enviar_infinito.py"
  • Necesitaremos una instrucci?n nueva: while

Por qué necesitamos:

Si no la usamos, la instrucci?n pub.publish(mensaje_avance) se ejecutar?, el programa pasar? a la ?ltima l?nea, que tambi?n se ejecutar? y terminar?. Necesitamos que se espere en la instrucci?n pub.publish(mensaje). Para ello vamos a usar:

while not rospy.is_shutdown():

Significa, mientras que el python de ROS no est? cerrado hacer algo.

  • Probamos nuestro c?digo
  • Vemos que el robot tiene el mismo comportamiento pero ahora no se detiene nuestro script

Reto 6.- ¡Nuestro cartero no descansa nunca!

Objetivo:

Conseguir que el robot se mueva ?nicamente cuando el script est? en ejecuci?n.

Pasos:

  • Nos vamos al script anterior y lo guardamos como avanzar_parar.py
  • Ahora vamos a tener dos mensajes que mandar, uno de avance y otro de parada:

Antes:

mensaje = Twist()
mensaje.linear.x = 1

Ahora:

mensaje_avance = Twist()
mensaje_parada = Twist()
mensaje_avance.lineear.x = - 0.2
mensaje_parada.linear.x = 0.0
  • Ahora necesitamos aprender a crear nuestra primera funci?n dentro de un script. ?C?mo se hace eso?

Lo vamos a hacer con la instrucci?n def. Nuestra primera funci?n va a ser la funci?n de detener. Con lo que la declaraci?n de la funci?n nos quedar?a as?:

def detener():

  • ?Qu? cosas va a hacer nuestra funci?n detener?

    • Mostrar un mensaje diciendo: "Script interrumpido por Ctrl+C durante el movimiento."

      rospy.loginfo("Script interrumpido por Ctrl+C durante el movimiento.")

    • Publicar mensaje avisando al robot para que se detenga: mensaje_parada

      pub.publish(mensaje_parada)

  • ?Cu?ndo vamos a llamar a esta funci?n? Cuando se termine nuestro script de Python. Este evento es: rospy.on_shutdown. Por lo tanto, la instrucci?n completa ser?a:

    rospy.on_shutdown(detener)

Nuestro c?digo quedar?a algo as?:

In [ ]:
#!/usr/bin/env python
import rospy
# Importamos el tipo de mensaje para velocidad
from geometry_msgs.msg import Twist

rospy.init_node('enviador_de_velocidad')

#Creamos el publicador
pub = rospy.Publisher('/cmd_vel', Twist, queue_size=10)

# Esperamos un segundo para asegurarnos de que el cartero ests listo
rospy.sleep(1.0)

# Creamos dos objeto 'Twist' vacio. Es como un formulario para rellenar.
mensaje_avance = Twist()
mensaje_parada = Twist()

# Rellenamos una parte del formulario: velocidad hacia adelante
mensaje_avance.linear.x = 0.2
mensaje_parada.linear.x = 0.0 # Debemos poner 0.0, no 0

def detener():
    rospy.loginfo("Script interrumpido por Ctrl+C durante el movimiento.")
    pub.publish(mensaje_parada)

rospy.on_shutdown(detener)

rate = rospy.Rate(10) # Por ejemplo, 10 veces por segundo (10 Hz)

# Enviamos las cartas
rospy.loginfo("Enviando comando")
while not rospy.is_shutdown():
    pub.publish(mensaje_avance)
    rate.sleep() # Esperamos para mantener la frecuencia
  • Probamos nuestro c?digo y vemos que todo ha funcionado.

No obstante, vamos a hacer una modificaci?n a nuestro c?digo para asegurarnos que todo funcione bien ante cualquier situaci?n. Para ello, vamos a enviar el mensaje de parada 10 veces y dejaremos una decima de segundo entre cada uno de los mensajes para permitir que el mensaje se propague. Para ello, la primera instrucci?n que vamos a necesitar es for, que es la encargada de hacer que algo se repita.

  • for ? in ? : Recorre uno a uno los valores de esa lista y ejecuta el bloque que est? metido dentro.
  • (guion bajo) Es la variable donde normalmente guardar?amos cada n?mero. Aqu? la llamamos para decir: ?no me importa su valor?.
  • range(10) Crea una ?lista? de 10 n?meros: 0, 1, 2, 3, 4, 5, 6, 7, 8 y 9.

Con estos componentes, si queremos que se repita una acci?n 10 veces, nuestra instrucci?n quedar?a as?:

for _ in range(10):

Tambi?n vamos a necesitar rospy.sleep() para dar 0.1 segundos entre mensaje y mensaje para dar tiempo a que los mensajes lleguen a su destino.

Con todo esto, nuestro nuevo c?digo quedar?a de la siguiente manera:

In [ ]:
#!/usr/bin/env python
import rospy
# Importamos el tipo de mensaje para velocidad
from geometry_msgs.msg import Twist

rospy.init_node('enviador_de_velocidad')

#Creamos el publicador
pub = rospy.Publisher('/cmd_vel', Twist, queue_size=10)

# Esperamos un segundo para asegurarnos de que el cartero ests listo
rospy.sleep(1.0)

# Creamos dos objeto 'Twist' vacio. Es como un formulario para rellenar.
mensaje_avance = Twist()
mensaje_parada = Twist()

# Rellenamos una parte del formulario: velocidad hacia adelante
mensaje_avance.linear.x = 0.2
mensaje_parada.linear.x = 0.0 # Debemos poner 0.0, no 0

def detener():
    rospy.loginfo("Script interrumpido por Ctrl+C durante el movimiento.")
    for _ in range(10):
        pub.publish(mensaje_parada)
        rospy.sleep(0.1)

rospy.on_shutdown(detener)

rate = rospy.Rate(10) # Por ejemplo, 10 veces por segundo (10 Hz)

# Enviamos las cartas
rospy.loginfo("Enviando comando")
while not rospy.is_shutdown():
    pub.publish(mensaje_avance)
    rate.sleep() # Esperamos para mantener la frecuencia

Reto 7.- ¡Le ponemos jornada laboral a nuestro robot!

Objetivo:

Conseguir que el robot se mueva un tiempo en una direcci?n y se detenga transcurrido este tiempo.

Pasos:

  • Nos vamos al script anterior y lo guardamos como avanzar_5seg.py
  • Lo siguiente que vamos a necesitar es una variable donde almacenar el tiempo deseado de movimiento, en este caso, 5 segundos.

tiempo_movimiento = 5.0

  • Tambi?n necesitamos controlar los tiempos para controlar que ha pasado el tiempo establecido:

    • tiempo de inicio.- ?C?mo podemos saber cu?ndo ha empezado nuestro script? Pues almacenando el contador de tiempo de ROS en una variable:

      tiempo_inicio = rospy.Time.now()

    • y, en el while que tenemos, ya no ser? hasta que se cierre el script, sino hasta que pase el tiempo establecido en la variable tiempo_movimiento:

      while (rospy.Time.now() - tiempo_inicio) < rospy.Duration(tiempo_movimiento):

    - rospy.Time.now() - tiempo_inicio. Restas la hora actual menos la hora de salida ? eso te da cu?nto tiempo llevas corriendo.
    - rospy.Duration() es un constructor, un molde para crear objetos-duraci?n en ROS.

Con estos cambios, nuestro c?digo nos deber?a quedar de la siguiente manera:
In [ ]:
#!/usr/bin/env python
import rospy
# Importamos el tipo de mensaje para velocidad
from geometry_msgs.msg import Twist

rospy.init_node('enviador_de_velocidad')

#Creamos el publicador
pub = rospy.Publisher('/cmd_vel', Twist, queue_size=10)

# Esperamos un segundo para asegurarnos de que el cartero ests listo
rospy.sleep(1.0)

# Creamos dos objeto 'Twist' vacio. Es como un formulario para rellenar.
mensaje_avance = Twist()
mensaje_parada = Twist()

# Variables necesarias
mensaje_avance.linear.x = 0.2
mensaje_parada.linear.x = 0.0 # Debemos poner 0.0, no 0
tiempo_movimiento = 5.0

def detener():
    rospy.loginfo("Script interrumpido por Ctrl+C durante el movimiento.")
    for _ in range(10):
        pub.publish(mensaje_parada)
        rospy.sleep(0.1)

rospy.on_shutdown(detener)

rate = rospy.Rate(10) # Por ejemplo, 10 veces por segundo (10 Hz)

# Guardamos el instante en el que se inicia el movimiento
tiempo_inicio = rospy.Time.now()

# Enviamos las cartas
rospy.loginfo("Enviando comando")
while (rospy.Time.now() - tiempo_inicio) < rospy.Duration(tiempo_movimiento):
    pub.publish(mensaje_avance)
    rate.sleep() # Esperamos para mantener la frecuencia

Explicar diferencia entre rospy.sleep(0.1) y rate.sleep() si rate = rospy.Rate(10)?

Aunque nos aparezca el siguiente error:

INFO] [1746813149.354020, 3416.501000]: Enviando comando ^C[INFO] [1746813154.421952, 3420.926000]: Script interrumpido por Ctrl+C durante el movimiento. Traceback (most recent call last): File "avanzar_parar.py", line 36, in rate.sleep() # Esperamos para mantener la frecuencia File "/opt/ros/kinetic/lib/python2.7/dist-packages/rospy/timer.py", line 103, in sleep sleep(self._remaining(curr_time)) File "/opt/ros/kinetic/lib/python2.7/dist-packages/rospy/timer.py", line 166, in sleep raise rospy.exceptions.ROSInterruptException("ROS shutdown request") rospy.exceptions.ROSInterruptException: ROS shutdown request

Esto es perfectamente normal.

  • Probamos nuestro c?digo y vemos que todo ha funcionado.

Evidencias

Mapeado y navegaci?n:

Exhibiciones y ferias: