Aprender a crear, guardar y ejecutar un archivo Python sencill?simo en The Construct. ?Todav?a sin ROS!
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!".
print 'Hola Mundo'
#!/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")
??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!
Aprender a crear el tipo de mensaje que necesitamos para decirle al robot c?mo moverse

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!).
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
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
#!/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)
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?
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!
Enviar nuestro mensaje Twist al robot para que se mueva un poquito. Introducimos el concepto de "Publisher".
Tenemos el mismo c?digo que antes y le vamos a ir a?adiendo distintas cosas:
Ahora creamos el cartero (Publisher):
pub = rospy.Publisher('/cmd_vel', Twist, queue_size=10)
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?:
#!/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:
Conseguir que el script se quede esperando acontecimientos. Esto nos va a permitir poder hacer programas m?s complejos
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.
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
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?:
#!/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
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.
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:
#!/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
Conseguir que el robot se mueva un tiempo en una direcci?n y se detenga transcurrido este tiempo.
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:
#!/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
Esto es perfectamente normal.