Control Brazo Robotico Kimo 6s desde Python
¡Bienvenidos al curso sobre el control de un brazo robótico con Arduino y Python!
A lo largo de este curso, aprenderás a conectar y controlar un brazo robótico de 6 servomotores SG90 usando un Arduino Uno y Python. Exploraremos la conexión electrónica entre el brazo y el Arduino, cargaremos el código en el Arduino y ejecutaremos un programa en Python para enviar comandos al brazo y controlar sus movimientos motor por motor.
Este curso está diseñado para principiantes en robótica y programación, ofreciendo una introducción paso a paso en un proyecto práctico y emocionante. Aprenderás los fundamentos esenciales para construir y controlar robots, adquiriendo habilidades valiosas en electrónica y programación a lo largo del camino.
Nuestro enfoque práctico te permitirá comprender cómo interactúan el hardware y el software para controlar un dispositivo físico como un brazo robótico. Aprenderás a escribir código Arduino y Python para controlar los movimientos del brazo, abriendo un mundo de posibilidades para la creación y experimentación en el campo de la robótica.
Sin más preámbulos, ¡vamos a sumergirnos en el emocionante mundo del control de brazos robóticos!
Materiales Necesarios:
Pasos a Seguir:
Debes realizar la siguiente conexión electrónica, se mostrara el esquema en Arduino UNO pero la conexión en expansión de Arduino Nano es la misma, si deseas descargar el esquema solo debes dar clic en la imagen del esquema:
2. Ahora debes cargar los códigos a tu Arduino, si no sabes como cargar códigos a tu Arduino a continuación puedes aprender como: APRENDER
Librerías Necesarias:
Necesitarás instalar la librería pySerial
en Python para poder establecer la comunicación serial entre Python y Arduino. Esta librería te permite enviar y recibir datos a través de los puertos serie de tu computadora. Puedes instalarla utilizando pip, el gestor de paquetes de Python. Ejecuta el siguiente comando en tu terminal para instalar pySerial
:
pip install pyserial
Primero exploraremos detalladamente el código de Python utilizado para controlar el brazo robótico. Aprenderemos cómo enviar comandos desde Python al Arduino para mover cada servomotor del brazo de manera precisa. Luego, nos sumergiremos en el código de Arduino, comprendiendo cómo recibir y ejecutar estos comandos. Finalmente, pondremos en práctica nuestro conocimiento, realizando movimientos manuales del brazo robótico para experimentar directamente con su funcionamiento y potencial.
Código Completo Python:
Primero te mostraremos el código completo y luego procederemos a explicar paso a paso.
import serial import time # Configuración del puerto serial puerto_serial = 'COM15' # Puerto serial al que está conectado el Arduino baudios = 115200 try: # Intentar inicializar la comunicación serial arduino = serial.Serial(puerto_serial, baudios, timeout=1) print("Puerto serial conectado correctamente.") except serial.SerialException: # Si hay un error al abrir el puerto serial, imprimir un mensaje de error y salir del programa print("Error al abrir el puerto serial. Asegúrate de que el Arduino esté conectado correctamente.") exit() # Función para enviar cinco valores al Arduino y esperar la confirmación def enviar_valores_confirmacion(valores): while True: valores_validos = [max(0, min(val, 180)) for val in valores] # Asegura que los valores estén en el rango [0, 180] mensaje = ' '.join(map(str, valores_validos)) # Convertir la lista de valores a una cadena separada por espacios arduino.write(mensaje.encode()) # Envía la cadena al Arduino print("Enviados valores:", mensaje) # Esperar a recibir el mensaje de vuelta de Arduino mensaje_recibido = arduino.readline().decode().strip() print("Mensaje de Arduino:", mensaje_recibido) if mensaje_recibido == "Mensaje recibido correctamente por Arduino.": break # Bucle infinito while True: try: # Solicitar al usuario que ingrese los valores para los servomotores valores_servomotores = [] for i in range(5): valor = int(input(f"Ingrese el valor para el servomotor {i+1}: ")) valores_servomotores.append(valor) # Envía los valores ingresados al Arduino enviar_valores_confirmacion(valores_servomotores) # Espera 4 segundos time.sleep(4) # Espera la confirmación de que Arduino ha recibido los valores while True: mensaje_recibido = arduino.readline().decode().strip() print("Mensaje de Arduino:", mensaje_recibido) if mensaje_recibido == "Mensaje recibido correctamente por Arduino.": break except KeyboardInterrupt: print("Interrupción del usuario. Saliendo...") break # Cierra la comunicación serial arduino.close()
Explicación Código Python:
a. Importación de Librerías:
import serial import time
serial
: Esta biblioteca permite la comunicación serial entre el programa y el Arduino.
time
: Esta biblioteca proporciona funciones para manipular el tiempo.
b. Configuración del puerto serial:
puerto_serial = 'COM15' # Puerto serial al que está conectado el Arduino baudios = 115200
Se especifica el puerto serial al que está conectado el Arduino y la velocidad de transmisión (baudios).
c. Inicialización de la comunicación serial:
try: arduino = serial.Serial(puerto_serial, baudios, timeout=1) print("Puerto serial conectado correctamente.") except serial.SerialException: print("Error al abrir el puerto serial. Asegúrate de que el Arduino esté conectado correctamente.") exit()
Python utiliza un bloque try-except
para manejar la inicialización de la comunicación serial con el Arduino. Aquí está la explicación paso a paso:
try:
: Se inicia un bloquetry
, donde se intenta realizar una operación que podría generar una excepción.
arduino = serial.Serial(puerto_serial, baudios, timeout=1)
: En este paso, intentamos inicializar la comunicación serial con el Arduino utilizando la bibliotecaserial
. Esto crea un objetoSerial
que representa la conexión serial con el dispositivo en el puerto especificado (puerto_serial
) y a la velocidad de transmisión especificada (baudios
). También se establece un tiempo de espera de 1 segundo (timeout=1
) para esperar la respuesta del Arduino.
print("Puerto serial conectado correctamente.")
: Si la inicialización del puerto serial se realiza con éxito, se imprime un mensaje indicando que la conexión se ha establecido correctamente.
except serial.SerialException:
: Este bloqueexcept
captura cualquier excepción de tipoSerialException
que pueda ocurrir durante el bloquetry
.
print("Error al abrir el puerto serial. Asegúrate de que el Arduino esté conectado correctamente.")
: Si se produce una excepción al intentar inicializar el puerto serial, se imprime un mensaje de error indicando que hubo un problema al abrir el puerto serial. Esto puede deberse a que el Arduino no está conectado correctamente, el puerto especificado no es válido o hay algún otro problema de comunicación.
exit()
: Después de imprimir el mensaje de error, se llama a la funciónexit()
para salir del programa inmediatamente.
d. Definición de la función enviar_valores_confirmacion
:
def enviar_valores_confirmacion(valores): while True: valores_validos = [max(0, min(val, 180)) for val in valores] mensaje = ' '.join(map(str, valores_validos)) arduino.write(mensaje.encode()) print("Enviados valores:", mensaje) mensaje_recibido = arduino.readline().decode().strip() print("Mensaje de Arduino:", mensaje_recibido) if mensaje_recibido == "Mensaje recibido correctamente por Arduino.": break
Donde tenemos:
- Definición de la función
enviar_valores_confirmacion
: Esta es una función que toma una lista de valores como entrada y se encarga de enviar esos valores al Arduino.
- Bucle
while True
: Este bucle se ejecuta infinitamente hasta que se encuentre una instrucciónbreak
dentro de él. Se utiliza para asegurar que se envíen los valores al Arduino y se reciba una confirmación correctamente.
- Validación de los valores:
"valores_validos = [max(0, min(val, 180)) for val in valores]"
Aquí se garantiza que los valores estén dentro del rango válido para los servomotores (entre 0 y 180 grados). Se crea una nueva listavalores_validos
que contiene los valores originales limitados dentro de este rango.
- Creación del mensaje a enviar: “
mensaje = ' '.join(map(str, valores_validos))
” Se convierte la lista de valores válidos en una cadena de texto, donde cada valor está separado por un espacio. Esto es necesario para enviar los valores al Arduino en un formato que pueda entender.
- Envío del mensaje al Arduino:
"arduino.write(mensaje.encode())"
Se utiliza el métodowrite()
del objetoarduino
para enviar el mensaje codificado al Arduino a través del puerto serial.
- Impresión de los valores enviados:
"print("Enviados valores:", mensaje)"
Se imprime en la consola los valores que se están enviando al Arduino.
- Lectura de la respuesta del Arduino:
"mensaje_recibido = arduino.readline().decode().strip()"
Se lee la respuesta del Arduino después de enviar los valores. El métodoreadline()
espera hasta que recibe una línea completa de datos desde el Arduino, luego se decodifica de bytes a cadena y se elimina cualquier espacio en blanco adicional alrededor del mensaje.
- Impresión del mensaje recibido:
"print("Mensaje de Arduino:", mensaje_recibido)"
Se imprime en la consola el mensaje recibido del Arduino.
- Verificación de la confirmación del Arduino: “if mensaje_recibido =
"Mensaje recibido correctamente por Arduino.":"
Se verifica si el mensaje recibido del Arduino es la confirmación esperada. Si es así, se sale del buclewhile True
usando la instrucciónbreak
, lo que indica que los valores han sido enviados y confirmados correctamente por el Arduino.
e. Bucle principal:
while True: try: valores_servomotores = [] for i in range(5): valor = int(input(f"Ingrese el valor para el servomotor {i+1}: ")) valores_servomotores.append(valor) enviar_valores_confirmacion(valores_servomotores) time.sleep(4) while True: mensaje_recibido = arduino.readline().decode().strip() print("Mensaje de Arduino:", mensaje_recibido) if mensaje_recibido == "Mensaje recibido correctamente por Arduino.": break except KeyboardInterrupt: print("Interrupción del usuario. Saliendo...") break arduino.close()
Bucle principal while True
:
- Este bucle se ejecuta infinitamente hasta que se interrumpe manualmente por el usuario o se produce alguna excepción.
Bloque try-except
:
- Encierra todo el código dentro de un bloque
try-except
para manejar posibles excepciones que puedan ocurrir durante la ejecución del programa.
Solicitud de valores para los servomotores:
- Se inicializa una lista vacía
valores_servomotores
que contendrá los valores que el usuario ingresará para cada uno de los servomotores del brazo robótico. - Se utiliza un bucle
for
para iterar cinco veces, una vez por cada servomotor. - Dentro del bucle, se solicita al usuario que ingrese el valor deseado para el servomotor actual.
- Cada valor ingresado se convierte a entero y se agrega a la lista
valores_servomotores
.
Envío de valores al Arduino y espera de confirmación:
- Después de que el usuario haya ingresado todos los valores para los servomotores, se llama a la función
enviar_valores_confirmacion()
para enviar los valores al Arduino y esperar una confirmación de que los valores han sido recibidos correctamente. - Después de enviar los valores, el programa espera 4 segundos antes de continuar. Esto permite que el Arduino tenga tiempo para procesar los comandos y realizar los movimientos correspondientes.
Espera de la confirmación del Arduino:
- Se utiliza otro bucle
while True
para leer continuamente el mensaje de confirmación del Arduino desde el puerto serial. - Una vez que se recibe el mensaje “Mensaje recibido correctamente por Arduino.”, el bucle se interrumpe con la instrucción
break
.
Manejo de interrupciones del usuario:
- Si el usuario presiona
Ctrl+C
desde la consola, se genera una excepciónKeyboardInterrupt
. - En este caso, se imprime un mensaje indicando que se ha producido una interrupción del usuario y se sale del bucle principal con la instrucción
break
.
Cierre de la conexión serial:
- Finalmente, fuera del bucle principal, se cierra la conexión serial con el Arduino utilizando el método
close()
.
Código Completo Arduino
#include <Servo.h> // Incluir la librería Servo para controlar los servomotores // Declaración de objetos Servo para controlar los servomotores Servo servomotor1; Servo servomotor2; Servo servomotor3; Servo servomotor4; Servo servomotor5; Servo servomotor6; void setup() { // Inicializar comunicación serial a 115200 baudios Serial.begin(115200); // Inicializar los servomotores y establecer la posición inicial de cada uno servomotor1.attach(2); // Conectar servomotor1 al pin 2 servomotor2.attach(4); // Conectar servomotor2 al pin 4 servomotor3.attach(5); // Conectar servomotor3 al pin 5 servomotor4.attach(7); // Conectar servomotor4 al pin 7 servomotor5.attach(12); // Conectar servomotor5 al pin 12 servomotor6.attach(13); // Conectar servomotor6 al pin 13 // Establecer las posiciones iniciales de los servomotores servomotor1.write(90); // Establecer la posición inicial de servomotor1 en 90 grados servomotor2.write(60); // Establecer la posición inicial de servomotor2 en 60 grados servomotor3.write(120); // Establecer la posición inicial de servomotor3 en 120 grados servomotor4.write(160); // Establecer la posición inicial de servomotor4 en 160 grados servomotor5.write(0); // Establecer la posición inicial de servomotor5 en 0 grados servomotor6.write(0); // Establecer la posición inicial de servomotor6 en 0 grados delay(500); // Esperar 500 milisegundos } void loop() { if (Serial.available() > 0) { // Si hay datos disponibles en el puerto serial // Leer la cadena de valores enviados desde Python hasta encontrar un salto de línea String mensaje = Serial.readStringUntil('\n'); // Leer la cadena de caracteres hasta encontrar un salto de línea mensaje.trim(); // Eliminar espacios en blanco al inicio y al final de la cadena // Dividir la cadena en valores individuales int valores[5]; // Declarar un arreglo de enteros para almacenar los valores recibidos int indice = 0; // Inicializar el índice del arreglo char *ptr = strtok((char *)mensaje.c_str(), " "); // Separar la cadena por espacios en blanco while (ptr != NULL && indice < 5) { // Mientras no se llegue al final de la cadena y no se hayan leído todos los valores valores[indice++] = atoi(ptr); // Convertir cada valor a entero y almacenarlo en el arreglo ptr = strtok(NULL, " "); // Obtener el siguiente valor } // Mostrar los valores recibidos en el monitor serial Serial.print("Valores recibidos: "); // Imprimir un mensaje indicando que se recibieron valores for (int i = 0; i < 5; i++) { // Recorrer el arreglo de valores recibidos Serial.print(valores[i]); // Imprimir cada valor Serial.print(" "); // Imprimir un espacio después de cada valor } Serial.println(); // Imprimir un salto de línea al final // Calcular el valor invertido basado en el valor recibido en valores[1], esto dado a que fisicamente // los motores del hombro estan invertidos. int valor_invertido; // Declarar una variable para almacenar el valor invertido switch (valores[1]) { // Utilizar una estructura switch para calcular el valor invertido case 0: valor_invertido = 180; // Si valores[1] es 0, establecer valor_invertido en 180 break; case 10: valor_invertido = 170; // Si valores[1] es 10, establecer valor_invertido en 170 break; case 20: valor_invertido = 160; // Si valores[1] es 20, establecer valor_invertido en 160 break; case 30: valor_invertido = 150; // Si valores[1] es 30, establecer valor_invertido en 150 break; case 40: valor_invertido = 140; // Si valores[1] es 40, establecer valor_invertido en 140 break; case 50: valor_invertido = 130; // Si valores[1] es 50, establecer valor_invertido en 130 break; case 60: valor_invertido = 120; // Si valores[1] es 60, establecer valor_invertido en 120 break; case 70: valor_invertido = 110; // Si valores[1] es 70, establecer valor_invertido en 110 break; case 80: valor_invertido = 100; // Si valores[1] es 80, establecer valor_invertido en 100 break; case 90: valor_invertido = 90; // Si valores[1] es 90, establecer valor_invertido en 90 break; default: valor_invertido = valores[1]; // Si valores[1] no coincide con ninguno de los casos anteriores, valor_invertido será igual a valores[1] break; } // Mover los servomotores a los ángulos correspondientes a los valores recibidos servomotor1.write(valores[0]); // Mover servomotor1 al ángulo especificado en valores[0] servomotor2.write(valores[1]); // Mover servomotor2 al ángulo especificado en valores[1] servomotor3.write(valor_invertido); // Mover servomotor3 al ángulo invertido basado en valores[1] servomotor4.write(valores[2]); // Mover servomotor4 al ángulo especificado en valores[2] servomotor5.write(valores[3]); // Mover servomotor5 al ángulo especificado en valores[3] servomotor6.write(valores[4]); // Mover servomotor6 al ángulo especificado en valores[4] // Enviar un mensaje de vuelta a Python para indicar que se recibieron los valores correctamente Serial.println("Mensaje recibido correctamente por Arduino."); // Imprimir un mensaje indicando que se recibieron los valores correctamente } }
Explicación Código Arduino:
a. Inclusión de la librería Servo:
#include <Servo.h>
Esta línea incluye la librería Servo, que proporciona funciones para controlar los servomotores.
b. Declaración de objetos Servo:
Servo servomotor1; Servo servomotor2; Servo servomotor3; Servo servomotor4; Servo servomotor5; Servo servomotor6;
- Aquí se declaran seis objetos de tipo
Servo
que representan los servomotores conectados al Arduino.
c. Función setup()
:
void setup() { Serial.begin(115200); // Inicialización de la comunicación serial a una velocidad de 115200 baudios. servomotor1.attach(2); servomotor2.attach(4); servomotor3.attach(5); servomotor4.attach(7); servomotor5.attach(12); servomotor6.attach(13); // Inicialización de los pines a los que están conectados los servomotores. // Cada servo se conecta a un pin específico del Arduino. servomotor1.write(90); servomotor2.write(60); servomotor3.write(120); servomotor4.write(160); servomotor5.write(0); servomotor6.write(0); // Establecimiento de las posiciones iniciales de los servomotores. }
- En la función
setup()
, se inicia la comunicación serial y se adjuntan los objetosServo
a los pines correspondientes del Arduino. Luego, se establecen las posiciones iniciales de los servomotores.
d. Función loop()
:
void setup() { Serial.begin(115200); // Inicialización de la comunicación serial a una velocidad de 115200 baudios. servomotor1.attach(2); servomotor2.attach(4); servomotor3.attach(5); servomotor4.attach(7); servomotor5.attach(12); servomotor6.attach(13); // Inicialización de los pines a los que están conectados los servomotores. // Cada servo se conecta a un pin específico del Arduino. servomotor1.write(90); servomotor2.write(60); servomotor3.write(120); servomotor4.write(160); servomotor5.write(0); servomotor6.write(0); // Establecimiento de las posiciones iniciales de los servomotores. }
Condición if (Serial.available() > 0)
:
- Esta línea verifica si hay datos disponibles en el puerto serial. Si
Serial.available()
devuelve un valor mayor que 0, significa que hay datos esperando ser leídos.
Lectura de la cadena de valores:
- Una vez que se confirma que hay datos disponibles, se procede a leer la cadena de valores enviados desde Python a través del puerto serial.
Serial.readStringUntil('\n')
lee la cadena de caracteres hasta encontrar un salto de línea ('\n'
). Esto permite recibir una línea completa de valores en cada envío desde Python.- La función
trim()
elimina los espacios en blanco al inicio y al final de la cadena.
División de la cadena en valores individuales:
- Se declara un arreglo
valores
para almacenar los valores que se recibirán. - La función
strtok()
se utiliza para dividir la cadena en valores individuales. Se utiliza como delimitador el espacio en blanco" "
. - Se convierten los valores individuales de la cadena de caracteres a enteros utilizando
atoi()
y se almacenan en el arreglovalores
.
Movimiento de los servomotores (fragmento omitido):
- Después de obtener los valores en el arreglo
valores
, se procede a mover los servomotores según estos valores. - Los valores recibidos se utilizan como ángulos para controlar la posición de los servomotores del brazo robótico.
Moviendo el Brazo Robótico.
Para finalizar vamos a ejecutar nuestro programa en Python y ingresaremos los valores de cada servomotor manualmente, como se observa en la siguiente imagen:
En el cuadro rojo podemos observar como se introdujeron los valores por cada motor y nuestro Arduino ha devuelto la confirmación de movimiento de todas las articulaciones.
Debe estar conectado para enviar un comentario.