Tabla de contenido:

Robot de equilibrio automático de Magicbit: 6 pasos
Robot de equilibrio automático de Magicbit: 6 pasos

Video: Robot de equilibrio automático de Magicbit: 6 pasos

Video: Robot de equilibrio automático de Magicbit: 6 pasos
Video: Robot Equilibrio con PID - PCB de Mini Robot 2024, Septiembre
Anonim

Este tutorial muestra cómo hacer un robot autoequilibrado usando la placa de desarrollo Magicbit. Estamos utilizando magicbit como placa de desarrollo en este proyecto que se basa en ESP32. Por lo tanto, en este proyecto se puede utilizar cualquier placa de desarrollo ESP32.

Suministros:

  • magicbit
  • Controlador de motor dual H-bridge L298
  • Regulador lineal (7805)
  • Batería Lipo 7.4V 700mah
  • Unidad de medida inercial (IMU) (6 grados de libertad)
  • motorreductores 3V-6V DC

Paso 1: historia

Historia
Historia
Historia
Historia

Hola chicos, hoy en este tutorial aprenderemos sobre cosas un poco complejas. Se trata de un robot autoequilibrado que usa Magicbit con Arduino IDE. Así que comencemos.

En primer lugar, veamos qué es un robot autoequilibrado. El robot de equilibrio automático es un robot de dos ruedas. La característica especial es que el robot puede equilibrarse sin utilizar ningún soporte externo. Cuando la energía está encendida, el robot se pondrá de pie y luego se equilibrará continuamente mediante movimientos de oscilación. Así que ahora todos tienen una idea aproximada sobre el robot autoequilibrado.

Paso 2: Teoría y Metodología

Teoría y Metodología
Teoría y Metodología

Para equilibrar el robot, primero obtenemos datos de algún sensor para medir el ángulo del robot con el plano vertical. Para ello utilizamos MPU6050. Después de obtener los datos del sensor, calculamos la inclinación al plano vertical. Si el robot está en posición recta y equilibrada, el ángulo de inclinación es cero. De lo contrario, el ángulo de inclinación es un valor positivo o negativo. Si el robot está inclinado hacia el frente, entonces el robot debe moverse hacia el frente. Además, si el robot se inclina hacia el lado inverso, el robot debe moverse en la dirección inversa. Si este ángulo de inclinación es alto, la velocidad de respuesta debería ser alta. Viceversa, el ángulo de inclinación es bajo, entonces la velocidad de reacción debería ser baja. Para controlar este proceso utilizamos un teorema específico llamado PID. PID es un sistema de control que solía controlar muchos procesos. PID significa 3 procesos.

  • P- proporcional
  • Yo- integral
  • D- derivado

Cada sistema tiene entrada y salida. De la misma forma este sistema de control también tiene alguna entrada. En este sistema de control, esa es la desviación del estado estable. A eso lo llamamos error. En nuestro robot, el error es el ángulo de inclinación desde el plano vertical. Si el robot está equilibrado, el ángulo de inclinación es cero. Entonces el valor del error será cero. Por lo tanto, la salida del sistema PID es cero. Este sistema incluye tres procesos matemáticos separados.

El primero es multiplicar el error de la ganancia numérica. Esta ganancia se suele denominar Kp

P = error * Kp

El segundo es generar la integral del error en el dominio del tiempo y multiplicarla a partir de algunas de las ganancias. Esta ganancia llamada Ki

I = Integral (error) * Ki

El tercero es una derivada del error en el dominio del tiempo y lo multiplica por alguna cantidad de ganancia. Esta ganancia se llama Kd

D = (d (error) / dt) * kd

Después de agregar las operaciones anteriores, obtenemos nuestro resultado final

SALIDA = P + I + D

Debido a la parte P, el robot puede obtener una posición estable que es proporcional a la desviación. La parte I calcula el área de error frente al gráfico de tiempo. Por lo tanto, intenta que el robot se coloque en una posición estable siempre con precisión. La parte D mide la pendiente en el tiempo frente al gráfico de error. Si el error aumenta, este valor es positivo. Si el error disminuye, este valor es negativo. Por eso, cuando el robot se mueve a una posición estable, la velocidad de reacción disminuirá y esto será útil para eliminar los excesos innecesarios. Puede obtener más información sobre la teoría PID en este enlace que se muestra a continuación.

www.arrow.com/en/research-and-events/articles/pid-controller-basics-and-tutorial-pid-implementation-in-arduino

La salida de la función PID está limitada al rango 0-255 (resolución PWM de 8 bits) y se alimentará a los motores como señal PWM.

Paso 3: configuración del hardware

configuración de hardware
configuración de hardware

Ahora bien, esta es la parte de configuración del hardware. El diseño del robot depende de ti. Cuando diseñó el cuerpo del robot, debe considerarlo simétrico con respecto al eje vertical que se encuentra en el eje del motor. La batería que se encuentra debajo. Por lo tanto, el robot es fácil de equilibrar. En nuestro diseño fijamos la placa Magicbit verticalmente al cuerpo. Usamos dos motorreductores de 12V. Pero puede utilizar cualquier tipo de motorreductor. eso depende de las dimensiones de su robot.

Cuando hablamos sobre el circuito, está alimentado por una batería Lipo de 7.4V. Magicbit usó 5V para alimentar. Por lo tanto, usamos el regulador 7805 para regular el voltaje de la batería a 5V. En versiones posteriores de Magicbit, ese regulador no es necesario. Porque alimenta hasta 12V. Suministramos directamente 7,4 V para el controlador del motor.

Conecte todos los componentes de acuerdo con el diagrama a continuación.

Paso 4: configuración del software

En el código usamos la biblioteca PID para calcular la salida PID.

Vaya al siguiente enlace para descargarlo.

www.arduinolibraries.info/libraries/pid

Descargue la última versión del mismo.

Para obtener mejores lecturas del sensor, usamos la biblioteca DMP. DMP significa proceso de movimiento digital. Esta es una característica incorporada de MPU6050. Este chip tiene una unidad de proceso de movimiento integrada. Por eso hace falta leer y analizar. Después genera salidas precisas silenciosas al microcontrolador (en este caso Magicbit (ESP32)). Pero hay muchos trabajos en el lado del microcontrolador para tomar esas lecturas y calcular el ángulo. Así que simplemente usamos la biblioteca MPU6050 DMP. Descárguelo yendo al siguiente enlace.

github.com/ElectronicCats/mpu6050

Para instalar las bibliotecas, en el menú de Arduino, vaya a herramientas-> incluir biblioteca-> agregar biblioteca.zip y seleccione el archivo de biblioteca que descargó.

En el código, debe cambiar el ángulo del punto de ajuste correctamente. Los valores de la constante PID son diferentes de un robot a otro. Entonces, al ajustar eso, primero establezca los valores de Ki y Kd en cero y luego aumente Kp hasta que obtenga una mejor velocidad de reacción. Más Kp provoca más sobreimpulsos. Luego aumente el valor de Kd. Aumentarlo siempre en muy poca cantidad. Este valor es generalmente más bajo que otros valores. Ahora aumente el Ki hasta que tenga muy buena estabilidad.

Seleccione el puerto COM correcto y el tipo de placa. cargue el código. Ahora puedes jugar con tu robot de bricolaje.

Paso 5: esquemas

Esquemas
Esquemas

Paso 6: Código

#incluir

#include "I2Cdev.h" #include "MPU6050_6Axis_MotionApps20.h" #if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE #include "Wire.h" #endif MPU6050 mpu; bool dmpReady = falso; // establecer verdadero si DMP init fue exitoso uint8_t mpuIntStatus; // contiene el byte de estado de interrupción actual de MPU uint8_t devStatus; // devuelve el estado después de cada operación del dispositivo (0 = éxito,! 0 = error) uint16_t packetSize; // tamaño esperado del paquete DMP (el valor predeterminado es 42 bytes) uint16_t FIFoCount; // recuento de todos los bytes actualmente en FIFO uint8_t fifoBuffer [64]; // Búfer de almacenamiento FIFO Quaternion q; // [w, x, y, z] contenedor de cuaterniones VectorFloat gravity; // [x, y, z] vector de gravedad float ypr [3]; // [guiñada, cabeceo, balanceo] guiñada / cabeceo / balanceo contenedor y vector de gravedad double originalSetpoint = 172.5; punto de ajuste doble = punto de ajuste original; double movingAngleOffset = 0.1; doble entrada, salida; int moveState = 0; double Kp = 23; // establecer P primero double Kd = 0.8; // este valor generalmente pequeño doble Ki = 300; // este valor debe ser alto para una mejor estabilidad PID pid (& input, & output, & setpoint, Kp, Ki, Kd, DIRECT); // pid inicializar int motL1 = 26; // 4 pines para el accionamiento del motor int motL2 = 2; int motR1 = 27; int motR2 = 4; volátil bool mpuInterrupt = false; // indica si el pin de interrupción de MPU se ha elevado void dmpDataReady () {mpuInterrupt = true; } void setup () {ledcSetup (0, 20000, 8); // pwm setup ledcSetup (1, 20000, 8); ledcSetup (2, 20000, 8); ledcSetup (3, 20000, 8); ledcAttachPin (motL1, 0); // modo pin de motores ledcAttachPin (motL2, 1); ledcAttachPin (motR1, 2); ledcAttachPin (motR2, 3); // unirse al bus I2C (la biblioteca I2Cdev no hace esto automáticamente) #if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE Wire.begin (); Wire.setClock (400000); // Reloj I2C de 400 kHz. Comente esta línea si tiene dificultades de compilación #elif I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_FASTWIRE Fastwire:: setup (400, true); #endif Serial.println (F ("Inicializando dispositivos I2C …")); pinMode (14, ENTRADA); // inicializar la comunicación en serie // (se eligió 115200 porque es necesario para la salida de demostración de Teapot, pero // realmente depende de usted dependiendo de su proyecto) Serial.begin (9600); mientras (! Serial); // espera la enumeración de Leonardo, otros continúan inmediatamente // inicializa el dispositivo Serial.println (F ("Inicializando dispositivos I2C…")); mpu.initialize (); // verifica la conexión Serial.println (F ("Probando las conexiones del dispositivo …")); Serial.println (mpu.testConnection ()? F ("Conexión MPU6050 exitosa"): F ("Error de conexión MPU6050")); // carga y configura el DMP Serial.println (F ("Inicializando DMP…")); devStatus = mpu.dmpInitialize (); // proporcione aquí sus propias compensaciones giroscópicas, escaladas para sensibilidad mínima mpu.setXGyroOffset (220); mpu.setYGyroOffset (76); mpu.setZGyroOffset (-85); mpu.setZAccelOffset (1788); // 1688 predeterminado de fábrica para mi chip de prueba // asegúrese de que funcionó (devuelve 0 si es así) si (devStatus == 0) {// enciende el DMP, ahora que está listo Serial.println (F ("Habilitando DMP… ")); mpu.setDMPEnabled (verdadero); // habilita la detección de interrupciones de Arduino Serial.println (F ("Habilitando la detección de interrupciones (interrupción externa de Arduino 0)…")); attachInterrupt (14, dmpDataReady, RISING); mpuIntStatus = mpu.getIntStatus (); // establece nuestro indicador DMP Ready para que la función main loop () sepa que está bien usarlo Serial.println (F ("DMP listo! Esperando la primera interrupción …")); dmpReady = verdadero; // obtener el tamaño de paquete DMP esperado para una comparación posterior packetSize = mpu.dmpGetFIFOPacketSize (); // configurar PID pid. SetMode (AUTOMATIC); pid. SetSampleTime (10); pid. SetOutputLimits (-255, 255); } else {// ¡ERROR! // 1 = falló la carga de memoria inicial // 2 = fallaron las actualizaciones de la configuración DMP // (si se va a romper, normalmente el código será 1) Serial.print (F ("DMP Initialization failed (code")); Serial. print (devStatus); Serial.println (F (")")); }} void loop () {// si la programación falló, no intente hacer nada if (! dmpReady) return; // espere la interrupción de MPU o paquetes adicionales disponibles mientras (! mpuInterrupt && FIFoCount <packetSize) {pid. Compute (); // este período de tiempo se usa para cargar datos, por lo que puede usarlo para otros cálculos motorSpeed (producción); } // restablecer el indicador de interrupción y obtener el byte INT_STATUS mpuInterrupt = false; mpuIntStatus = mpu.getIntStatus (); // obtener el recuento FIFO actual FIFOCount = mpu.getFIFOCount (); // comprobar si hay desbordamiento (esto nunca debería suceder a menos que nuestro código sea demasiado ineficiente) if ((mpuIntStatus & 0x10) || FIFoCount == 1024) {// reiniciar para que podamos continuar limpiamente mpu.resetFIFO (); Serial.println (F ("¡Desbordamiento FIFO!")); // de lo contrario, verifique la interrupción de datos DMP listos (esto debería suceder con frecuencia)} else if (mpuIntStatus & 0x02) {// espere la longitud correcta de los datos disponibles, debería ser una espera MUY corta mientras (quinceCuenta 1 paquete disponible // (esto nos permite leer más inmediatamente sin esperar una interrupción) FIFoCount - = packetSize; mpu.dmpGetQuaternion (& q, FIFoBuffer); mpu.dmpGetGravity (& gravity, & q); mpu.dmpGetYawPitchRoll (ypr, & q, & gravity); #if LOG_INPUT Serial. print ("ypr / t"); Serial.print (ypr [0] * 180 / M_PI); // ángulos euler Serial.print ("\ t"); Serial.print (ypr [1] * 180 / M_PI); Serial.print ("\ t"); Serial.println (ypr [2] * 180 / M_PI); #endif input = ypr [1] * 180 / M_PI + 180;}} void motorSpeed (int PWM) {float L1, L2, R1, R2; if (PWM> = 0) {// dirección de avance L2 = 0; L1 = abs (PWM); R2 = 0; R1 = abs (PWM); if (L1> = 255) { L1 = R1 = 255;}} else {// dirección hacia atrás L1 = 0; L2 = abs (PWM); R1 = 0; R2 = abs (PWM); if (L2> = 255) {L2 = R2 = 255; }} // accionamiento del motor ledcWrite (0, L1); ledcWrite (1, L2); ledcWrite (2, R1 * 0.97); // 0.97 es el hecho de velocidad o, debido a que el motor derecho tiene una velocidad más alta que el motor izquierdo, la reducimos hasta que las velocidades del motor sean iguales a ledcWrite (3, R2 * 0.97);

}

Recomendado: