INTRODUCCIÓN.

Este tutorial, es una extensión del anterior artículo sensor mpu-6050. EL módulo MPU6050 contiene un giroscopio de tres ejes con el que podemos medir velocidad angular y un acelerómetro también de 3 ejes con el que medimos los componentes X, Y y Z de la aceleración, el acelerómetro trabaja sobre el principio piezo eléctrico, posee además de un sensor de temperatura.

Los acelerómetros internamente tienen un MEMS (Micro Electro Mechanical Systems) que de forma similar a un sistema masa-resorte permite medir la aceleración. Los giroscopios utilizan un MEMS (MicroElectroMechanical Systems) para medir la velocidad angular usando el efecto Coriolis.

En esta ocasión presento un ejemplo simple de cómo interconectar el MPU-6050 con una placa Arduino. El MPU6050 es un sensor de movimiento, que tiene un conversor ADC de 16 bits que convierte los datos a un valor digital, el módulo de giroscopio se comunica con el Arduino a través de la comunicación serie I2C a través del reloj serial (SCL) y datos (SDA), el chip MPU6050 necesita 3.3V pero un regulador de voltaje en la tarjeta GY-521 le permite alimentarlo hasta 5V, en nuestro caso en un Arduino con el que haremos unos ejercicios.

El procesador interno del IMU (Inertial Measurment Units) es capaz de realizar cálculos precisos de los valores que miden sus sensores internos que son, aceleraciones lineales y angulares, para informarnos de valores útiles como los ángulos de inclinación con respecto a los 3 ejes principales. Un dato importante es que ni la aceleración ni la velocidad lineal afectan la medición de giro.

La dirección de los ejes está indicado en el módulo el cual hay que tener en cuenta para no equivocarnos en el signo de las aceleraciones. Como la comunicación del módulo es vía I2C, esto le permite trabajar con la mayoría de microcontroladores. En el módulo los pines SCL y SDA tienen una resistencia pull-up en placa para una conexión directa al microcontrolador que estemos utilizando.

IMU_60500.jpg
Fig. 1

Alguien como Jeff Rowberg ya ha hecho el trabajo duro por nosotros, escribió algunas bibliotecas de Arduino para obtener los datos del acelerómetro/giroscopio y manejar todos los cálculos. Las librerías están disponibles como un archivo zip desde aquí.

Y también las puede descargar de:
https://github.com/jrowberg/i2cdevlib/tree/master/Arduino/MPU6050
y la encargada de I2Cdev de:
https://github.com/jrowberg/i2cdevlib/tree/master/Arduino/I2Cdev
Instalar las librerías en el IDE Arduino, con ellas podremos trabajar los siguientes ejercicios. Una vez descomprimido, copie las dos carpetas «I2Cdev» y «MPU6050» en su carpeta Arduino «libraries» en el siguiente directorio: C: \ Archivos de programa (x86) \ Arduino \ libraries

EL CIRCUITO.

esquema_mpu6050.jpg
Fig. 2 Circuito.

Abra su Arduino si no lo tiene abierto ya, vaya al menú Archivos/Ejemplos… y navegue hasta MPU6050> y encontrará el archivo MPU6050_DMP6, ábralo y seleccione el puerto COM adecuado, suba el boceto. En el monitor Serial elija la velocidad de 115200 Baudios.

archivo-mpu6050_dmp6.gif
Fig. 3 Archivo MPU6050_DMP6.

Le aparecerá una indicación, pruebe a escribir algún dato y pulse Enter, en ese momento deberían aparecer los datos, lo que indica que todo está saliendo bien.

mpu6050_dmp6_3.gif
Fig. 4 Monitor Serial.

CALIBRANDO EL MPU6050

Muchos de los ejemplos que podemos encontrar, tienen un problema y es que presentan muchas vibraciones y ruido en las medidas, además cuando tengamos instalado el módulo MPU6050 en el proyecto, siempre puede haber un desnivel en sus componentes, motivo por el cual debemos calibrar el módulo, asegurándonos de que no haya un error de desnivel agregado en cada componente.

ejes-Tait-Brayan_-o_Euler.jpg
Fig. 5 Ejes Tait-Brayan o Euler

Podemos solucionar estos problemas al configurar el módulo MPU6050 OFFSETS, para compensar dichos errores.

Este código nos sirve para calibrar los offset del MPU6050.

// calibrar_mpu6050.ino
// Librerias I2C para controlar el mpu6050 con Arduino,
// la libreria MPU6050.h necesita I2Cdev.h, la libreria I2Cdev.h necesita Wire.h

#include "I2Cdev.h"
#include "MPU6050.h"
#include "Wire.h"

// La dirección del MPU6050 puede ser 0x68 o 0x69, dependiendo 
// del estado de AD0. Si no se especifica, 0x68 estará implicito
MPU6050 sensor;

// Valores RAW (sin procesar) del acelerometro y giroscopio en los ejes x,y,z
int ax, ay, az;
int gx, gy, gz;

//Variables usadas por el filtro pasa bajos
long f_ax,f_ay, f_az;
int p_ax, p_ay, p_az;
long f_gx,f_gy, f_gz;
int p_gx, p_gy, p_gz;
int counter=0;

//Valor de los offsets
int ax_o,ay_o,az_o;
int gx_o,gy_o,gz_o;

void setup() {
  Serial.begin(57600);   //Iniciando puerto serial
  Wire.begin();           //Iniciando I2C  
  sensor.initialize();    //Iniciando el sensor

  if (sensor.testConnection()) Serial.println("Sensor iniciado correctamente");

  // Leer los offset los offsets anteriores
  ax_o=sensor.getXAccelOffset();
  ay_o=sensor.getYAccelOffset();
  az_o=sensor.getZAccelOffset();
  gx_o=sensor.getXGyroOffset();
  gy_o=sensor.getYGyroOffset();
  gz_o=sensor.getZGyroOffset();
  
  Serial.println("Offsets:");
  Serial.print(ax_o); Serial.print("\t"); 
  Serial.print(ay_o); Serial.print("\t"); 
  Serial.print(az_o); Serial.print("\t"); 
  Serial.print(gx_o); Serial.print("\t"); 
  Serial.print(gy_o); Serial.print("\t");
  Serial.print(gz_o); Serial.println("\t");

  Serial.println("nnEnvie cualquier caracter para empezar la calibracionnn");  
  // Espera un caracter para empezar a calibrar
  while (true){if (Serial.available()) break;}  
  Serial.println("Calibrando, no mover IMU");    
}

void loop() {
  // Leer las aceleraciones y velocidades angulares
  sensor.getAcceleration(&ax, &ay, &az);
  sensor.getRotation(&gx, &gy, &gz);

  // Filtrar las lecturas
  f_ax = f_ax-(f_ax>>5)+ax;
  p_ax = f_ax>>5;

  f_ay = f_ay-(f_ay>>5)+ay;
  p_ay = f_ay>>5;

  f_az = f_az-(f_az>>5)+az;
  p_az = f_az>>5;

  f_gx = f_gx-(f_gx>>3)+gx;
  p_gx = f_gx>>3;

  f_gy = f_gy-(f_gy>>3)+gy;
  p_gy = f_gy>>3;

  f_gz = f_gz-(f_gz>>3)+gz;
  p_gz = f_gz>>3;

  //Cada 100 lecturas corregir el offset
  if (counter==100){
    //Mostrar las lecturas separadas por un [tab]
    Serial.print("promedio:"); Serial.print("\t");
    Serial.print(p_ax); Serial.print("\t");
    Serial.print(p_ay); Serial.print("\t");
    Serial.print(p_az); Serial.print("\t");
    Serial.print(p_gx); Serial.print("\t");
    Serial.print(p_gy); Serial.print("\t");
    Serial.println(p_gz);

    //Calibrar el acelerometro a 1g en el eje z (ajustar el offset)
    if (p_ax>0) ax_o--;
    else {ax_o++;}
    if (p_ay>0) ay_o--;
    else {ay_o++;}
    if (p_az-16384>0) az_o--;
    else {az_o++;}
    
    sensor.setXAccelOffset(ax_o);
    sensor.setYAccelOffset(ay_o);
    sensor.setZAccelOffset(az_o);

    //Calibrar el giroscopio a 0º/s en todos los ejes (ajustar el offset)
    if (p_gx>0) gx_o--;
    else {gx_o++;}
    if (p_gy>0) gy_o--;
    else {gy_o++;}
    if (p_gz>0) gz_o--;
    else {gz_o++;}
    
    sensor.setXGyroOffset(gx_o);
    sensor.setYGyroOffset(gy_o);
    sensor.setZGyroOffset(gz_o);    

    counter=0;
  }
  counter++;
}

Durante la calibración deberemos mantener el sensor sin moverlo en la posición de trabajo habitual, entonces el programa empieza por leer los offsets y nos pide que enviemos un carácter por el puerto serie. El programa trata de corregir los errores de las medidas, para ello modifica constantemente el offest, usando un filtro y cada 100 lecturas comprueba los valores si se acercan a los que deseamos leer, aumentando o disminuyendo los offsets. Esto hará que las lecturas filtradas se acerquen a:
-aceleración: p_ax=0 , p_ay=0 , p_az=+16384
-velocidad angular: p_gx=0 , p_gy=0 , p_gz=0

calibrado_boceto.gif
Fig. 6 Calibrado del PMU6050.

Tal como indica en el monitor debemos anotar las compensaciones obtenidas para configurarlas en nuestros proyectos, usando la función mpu.setXAccelOffset(), como se indica a continuación.

Abriremos el archivo MPU6050_PMT y buscaremos la parte de ‘Calibration results’ como se aprecia en la figura que sigue.

config_calibrado.gif
Fig. 7 Configurar el boceto.

La calibración solo es necesario hacerla una vez. EL filtro complemento en si, es para combinar el acelerómetro y el giroscopio. Si solo utilizáramos el acelerómetro para determinar el ángulo, cualquier aceleración generada por un desplazamiento generaría errores en el ángulo. En cambio sí solo usamos el giroscopio vamos a obtener un error acumulativo por causa de la integración de w (velocidad angular). Este filtro se utiliza acunado, queremos sensar el ángulo pero el MPU está en constante movimiento (Drones, robots móviles, etc). En cambio, si el PMU va a estar fijo podemos tomar sólo el vector de la aceleración de la gravedad para determinar los ángulos.

ESCALADO DE LECTURAS

Ahora, vamos a escalar las lecturas a valores con las unidades de aceleración y velocidad angular. Carguemos el siguiente programa que usa una ecuación para convertir el valor leído en un valor de aceleración o velocidad angular.

Este código nos permite escalar valores de aceleración y velocidad angular.

// escalar_valores.ino
// Librerias I2C para controlar el mpu6050 con Arduino,
// la libreria MPU6050.h necesita I2Cdev.h y la libreria I2Cdev.h necesita Wire.h
/* 
 Conociendo los rangos con los que está configurado nuestro MPU6050, 
 dichos rangos pueden ser 2g/4g/8g/16g para el acelerómetro y 
 250/500/1000/2000(°/s) para el giroscopio.
 Los rangos por defecto (2g y 250°/s)
 
 Variable        valor mínimo  valor central  valor máximo
 Lectura MPU6050    -32768        0             +32767 
 Aceleración         -2g          0g            +2g
 Velocidad angular  -250°/s       0°/s          +250°/s
*/

#include "I2Cdev.h"
#include "MPU6050.h"
#include "Wire.h"

// La dirección del MPU6050 puede ser 0x68 o 0x69, dependiendo 
// del estado de AD0. Si no se especifica, 0x68 estará implicito
MPU6050 sensor;

// Valores RAW (sin procesar) del acelerometro y giroscopio en los ejes x,y,z
int ax, ay, az;
int gx, gy, gz;

void setup() {
  Serial.begin(57600);    //Iniciando puerto serial
  Wire.begin();           //Iniciando I2C  
  sensor.initialize();    //Iniciando el sensor

  if (sensor.testConnection()) Serial.println("Sensor iniciado correctamente");
  else Serial.println("Error al iniciar el sensor");
}

void loop() {
  // Leer las aceleraciones y velocidades angulares
  sensor.getAcceleration(&ax, &ay, &az);
  sensor.getRotation(&gx, &gy, &gz);
  float ax_m_s2 = ax * (9.81/16384.0);
  float ay_m_s2 = ay * (9.81/16384.0);
  float az_m_s2 = az * (9.81/16384.0);
  float gx_deg_s = gx * (250.0/32768.0);
  float gy_deg_s = gy * (250.0/32768.0);
  float gz_deg_s = gz * (250.0/32768.0);

  //Mostrar las lecturas separadas por un [tab]
  Serial.print("a[x y z](m/s2) g[x y z](deg/s):\t");
  Serial.print(ax_m_s2); Serial.print("\t");
  Serial.print(ay_m_s2); Serial.print("\t");
  Serial.print(az_m_s2); Serial.print("\t");
  Serial.print(gx_deg_s); Serial.print("\t");
  Serial.print(gy_deg_s); Serial.print("\t");
  Serial.println(gz_deg_s);

  delay(100);
}

Con el MPU6050, sólo podemos obtener los ángulos X e Y, con el filtro de complemento usamos los ángulos obtenidos del acelerómetro, los cuales se limitan a X e Y, puesto que una rotación en Z del acelerómetro no la detectará ya que usamos la gravedad para determinar el ángulo. Para esto es mejor usar un Magnetómetro similar al HMC5883L.

escalar_valoresg.gif
Fig. 8 Escalar valores de acel/grad.

Los valores obtenidos ya están escalados a unidades de aceleración y velocidad angular, hemos convertido la aceleración a valores en m/s^2 por lo que se reemplazó el valor de g=9.81 si el sensor se mantiene en posición horizontal se deben obtener mediciones cercanas a 9.8 m/s^2 (aceleración de la gravedad terrestre) es la componente z de la aceleración.

SIMULACIÓN 3D.

Para ejecutar la demostración o simulación 3D, vamos a utilizar una sencilla maqueta de un Eurofighter para que se comprenda mejor la evolución de los parámetros YawRoll y Pitch a medida que se generan al cambiar la posición del IMU.

El primer paso es subir el código al IDE Arduino o si lo prefiere al nodeMCU, vamos a probar con Arduino, en ese caso, nos sirve el circuito descrito en la figura 2, en el que la conexión INT del MPU6050 no es necesaria.

En el caso de utilizar el nodeMCU12, el esquema es el siguiente:


Fig. 9 Conexiones nodeMCU e IMU.

EL CÓDIGO.

El código obtener inclinación viene bastante bien descrito y no hará falta muchas aclaraciones.

/*
*   obtener_inclinacion.ino
* Con el código se leen datos de la MPU-6050, este código 
* es modificado de la web: 
* http://robologs.net/2014/10/15/tutorial-de-arduino-y-mpu-6050/
* 
* Descrito muy bien en el vídeo: 
* https://www.youtube.com/watch?v=uN8SYfGwYVw&t=164s
*
*  El factor de conversión: w= Lectura*(250/32768) = lectura*(1/131)
*/

#include  // librería Wire.h
 
//Direccion I2C de la IMU
#define MPU 0x68
 
//Ratios de conversion
#define A_R 16384.0    // 32768/2
#define G_R 131.0       // 32768/250
 
//Conversion de radianes a grados 180/PI
#define RAD_A_DEG = 57.295779
 
//MPU-6050 da los valores en enteros de 16 bits
//Valores RAW
int16_t AcX, AcY, AcZ, GyX, GyY, GyZ;
 
//Angulos
float Acc[2];
float Gy[3];
float Angle[3];

String valores;

long tiempo_prev;
float dt;

void setup()
{
 Wire.begin(); // D2(GPIO4)=SDA / D1(GPIO5)=SCL
 Wire.beginTransmission(MPU);
 Wire.write(0x6B);
 Wire.write(0);
 Wire.endTransmission(true);
 Serial.begin(115200);
}

void loop()
{
   //Leer los valores del Acelerometro de la IMU
   Wire.beginTransmission(MPU);
   Wire.write(0x3B); //Pedir el registro 0x3B - corresponde al AcX
   Wire.endTransmission(false);
   Wire.requestFrom(MPU,6,true);   //A partir del 0x3B, se piden 6 registros
   AcX=Wire.read()<<8|Wire.read(); //Cada valor ocupa 2 registros
   AcY=Wire.read()<<8|Wire.read();
   AcZ=Wire.read()<<8|Wire.read();
 
   //A partir de los valores del acelerometro, se calculan los angulos Y, X
   //respectivamente, con la formula de la tangente.
   Acc[1] = atan(-1*(AcX/A_R)/sqrt(pow((AcY/A_R),2) + pow((AcZ/A_R),2)))*RAD_TO_DEG;
   Acc[0] = atan((AcY/A_R)/sqrt(pow((AcX/A_R),2) + pow((AcZ/A_R),2)))*RAD_TO_DEG;
 
   //Leer los valores del Giroscopio
   Wire.beginTransmission(MPU);
   Wire.write(0x43);
   Wire.endTransmission(false);
   Wire.requestFrom(MPU,6,true);   //A partir del 0x43, se piden 6 registros
   GyX=Wire.read()<<8|Wire.read(); //Cada valor ocupa 2 registros
   GyY=Wire.read()<<8|Wire.read();
   GyZ=Wire.read()<<8|Wire.read();
 
   //Calculo del angulo del Giroscopio
   Gy[0] = GyX/G_R;
   Gy[1] = GyY/G_R;
   Gy[2] = GyZ/G_R;

   dt = (millis() - tiempo_prev) / 1000.0;
   tiempo_prev = millis();
 
   //Aplicar el Filtro Complementario
   Angle[0] = 0.98 *(Angle[0]+Gy[0]*dt) + 0.02*Acc[0];
   Angle[1] = 0.98 *(Angle[1]+Gy[1]*dt) + 0.02*Acc[1];

   //Integración respecto del tiempo paras calcular el YAW
   Angle[2] = Angle[2]+Gy[2]*dt;
 
   //Mostrar los valores por consola
   valores = "90, " +String(Angle[0]) + "," + String(Angle[1]) + "," + String(Angle[2]) + ", -90";
   Serial.println(valores);
   
   delay(10);
}

Copie y pegue el anterior código y guárdelo con un nombre por ej. simulador.ino, súbalo al IDE Arduino o al nogeMCU y ejecútelo. Cuando termine, si todo ha ido bien, al abrir el monitor serie podrá ver como surgen los valores que detecta el IMU.

inclinacion_mpu6050.gif
Fig. 10 Monitor Serial.

Para tener una imagen más aproximada de lo que representan estos valores, vamos a poder ver la evolución de estos datos con la ayuda del monitor llamado Serial Plotter, pulse CRTL+Mayús+L o vaya al menú Herramientas/Serial Plotter, se abre una ventana que nos muestra unas líneas de colores que indican el progreso de los parámetros YAW, PITCH y ROLL en la medida que se producen.

tres-ejes.gif
Fig. 11 Tres ejes.

En la nueva ventana aparecen unas líneas de colores que corresponden a los ya mencionados YAW, PITCH y ROLL (según se puede ver en la figura 11). Pueden apreciarse las variaciones de cada línea de color en su evolución.

trazado_de_yaw-pitch-roll.jpg
Fig. 12 Serial Plotter.

Estos datos que he intentado describir se aprecian mejor en el vídeo que he realizado y se muestra a continuación.


Vídeo.

Espero que les sirvan estos artículos para iniciarse en este mundo tan emocionante y con tantos caminos por andar. Esto es todo por este simple tutorial, como parte de una serie de artículos sobre estos nuevos dispositivos que nos permiten utilizar los recursos del Internet de las cosas.

Ayúdenos a mantener la comunidad en lo positivo y útil.
Sobre el tema, sea respetuoso con todas las edades y niveles
con la habitual responsabilidad.
Sea amable y no haga Spam – ¡Gracias!.

Referencias:
  • https://www.luisllamas.es/como-usar-un-acelerometro-arduino/
  • https://www.luisllamas.es/arduino-orientacion-imu-mpu-6050/
  • https://www.youtube.com/watch?v=uN8SYfGwYVw&t=164s
  • http://www.naylampmechatronics.com/blog/45_Tutorial-MPU6050-AceleróB3metro-y-Giroscopio.html
  • https://www.youtube.com/watch?v=ecgSCQDNkcQ&t=12s
Configurar el MPU6050.
Etiquetado en:            

Deja un comentario

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

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.