IMU MPU-9250 - Acelerómetro

Hola a todos de nuevo,

Hoy he estado probando esta plaquita que tiene una IMU (unidad de medición inercial) que lleva un acelerómetro, un giroscopio y un magnetómetro, al total 9 grados de libertad.

Realmente se trata de una placa compleja. Da para mucho más de lo que se va a ver en esta página, pero vamos a ir primero con lo simple. Hay mucha gente que se pregunta: ¿Puedo medir la velocidad con el acelerómetro? Así que me he dispuesto a hacer la prueba.

Usaré éste código modificado de www.luisllamas.es con una placa Arduino UNO, como veréis dejo comentada la parte de giroscopio y magnetómetro, por si queréis echarle un ojo, y he hecho otras modificaciones:

  • Calcular la salida en milésimas de g (aceleración de la gravedad, aprox. 9.8 \displaystyle m/s^{2})
  • Calcular la resultante de los tres ejes, como si de un vector se tratase:

\displaystyle Resultante = \sqrt{ax^{2}+ay^{2}+az^{2}}\ m/s^{2}

Con este cálculo la resultante en reposo debería ser exactamente g, es decir, daría 1000. Lo que pasa es que habrá ruido y offset, por lo que nunca va a estar tampoco justo ahí.

Después de programar esto, conecto todo y tomo los datos con Termite, a un archivo TXT. Como tengo la licencia de MATLAB student, he aprovechado para hacer los cálculos.

He hecho un movimiento hacia adelante, he parado un poco, y luego hacia atrás. Después he repetido otra vez, y después de un reposo, otra vez pero más lento.

Y la salida de la aceleración resultante es la siguiente:

La buena noticia aquí es que el valor en reposo oscila sobre 1g, como habíamos predicho, y eso es bueno. Podemos eliminarlo fácilmente restando 1000 a la señal. Pero el resto es todo ruido. Intentar hacer una integración es un suicidio, porque el ruido significa que hay un "drifting" (deriva) de la velocidad que es constante, y además muy rápido. Estoy hablando de que en reposo antes de empezar el movimiento indica una velocidad de 5 m/s.

Con ayuda de un par de filtros (paso alto y media movil, también conocido como paso bajo) ha mejorado algo:

Ahora hacemos la integración y obtenemos... "La velocidad":

Según esto, mi mano se ha movido a 25 m/s, que son exactamente 90 km/h. Puede ser que me haya emocionado un poco pero es algo excesivo. Lo bueno es que tras el filtro he podido detectar el movimiento en el sentido contrario e incluso parece que está ahí la parada más larga. Ya se observan al inicio los efectos del drifting, aunque bastante atenuados, ese error se acumula ahí para siempre.

Este ha sido el mejor resultado de una tarde entera de análisis de los datos. Creo que lo volveré a intentar poniendo una SD y dando una vuelta con el coche, ya que las aceleraciones en ese caso son menores, y puedo usar únicamente el valor de un eje para quitar ruidos, quitar la gravedad...

La conclusión de esto es, así de primeras, que es una terrible idea usar una IMU para intentar calcular la velocidad. Tanto con esta IMU barata como con una de 1000€, según me han contado.

Lo que sí me he llevado es la programación de un par de filtros muy simples y buenos.

Me he estado documentando un poco y hay un estudio de 2007 que dice que la deriva de una IMU usando únicamente el acelerómetro es de unos 150 metros por cada minuto, y que si se usa el giroscopio y el magnetómetro para tomar todas las referencias se puede reducir a 5 metros por cada minuto, lo cual ya es un logro y tengo que probarlo, pero que es imposible realizar una navegación con este sistema si no se corrige con algún otro.

- "Pero yo he visto por ahí que se usa en navegación, incluso de aviones y satélites"

Sí y no. Como acabo de decir, el sistema acumula errores con el tiempo, que en estos sistemas de navegación son corregidos mediante otros sistemas de posicionamiento, como el GPS, radar, triangulación con radio, los sensores de velocidad del viento y muchos datos externos.

Dejo el código:

//GND - GND
//VCC - VCC
//SDA - Pin A4
//SCL - Pin A5
 
#include <Wire.h>
 
#define    MPU9250_ADDRESS            0x68
#define    MAG_ADDRESS                0x0C
 
#define    GYRO_FULL_SCALE_250_DPS    0x00  
#define    GYRO_FULL_SCALE_500_DPS    0x08
#define    GYRO_FULL_SCALE_1000_DPS   0x10
#define    GYRO_FULL_SCALE_2000_DPS   0x18
 
#define    ACC_FULL_SCALE_2_G        0x00  
#define    ACC_FULL_SCALE_4_G        0x08
#define    ACC_FULL_SCALE_8_G        0x10
#define    ACC_FULL_SCALE_16_G       0x18
 
 
//Funcion auxiliar lectura
void I2Cread(uint8_t Address, uint8_t Register, uint8_t Nbytes, uint8_t* Data)
{
   Wire.beginTransmission(Address);
   Wire.write(Register);
   Wire.endTransmission();
 
   Wire.requestFrom(Address, Nbytes);
   uint8_t index = 0;
   while (Wire.available())
      Data[index++] = Wire.read();
}
 
 
// Funcion auxiliar de escritura
void I2CwriteByte(uint8_t Address, uint8_t Register, uint8_t Data)
{
   Wire.beginTransmission(Address);
   Wire.write(Register);
   Wire.write(Data);
   Wire.endTransmission();
}
 
 
void setup()
{
   Wire.begin();
   Serial.begin(115200);
 
   // Configurar acelerometro
   I2CwriteByte(MPU9250_ADDRESS, 28, ACC_FULL_SCALE_16_G);
   // Configurar giroscopio
   I2CwriteByte(MPU9250_ADDRESS, 27, GYRO_FULL_SCALE_2000_DPS);
   // Configurar magnetometro
   I2CwriteByte(MPU9250_ADDRESS, 0x37, 0x02);
   I2CwriteByte(MAG_ADDRESS, 0x0A, 0x01);
}
 
 
void loop()
{
   // ---  Lectura acelerometro y giroscopio --- 
   uint8_t Buf[14];

   I2Cread(MPU9250_ADDRESS, 0x3B, 14, Buf);
   
   // Convertir registros acelerometro
   int16_t ax = -(Buf[0] & 8 | Buf[1]);
   int16_t ay = -(Buf[2] & 8 | Buf[3]);
   int16_t az = Buf[4] & 8 | Buf[5];
 
   // Convertir registros giroscopio
   int16_t gx = -(Buf[8] & 8 | Buf[9]);
   int16_t gy = -(Buf[10] & 8 | Buf[11]);
   int16_t gz = Buf[12] & 8 | Buf[13];
 
 
 
   // ---  Lectura del magnetometro --- 
   uint8_t ST1;
   I2CwriteByte(MAG_ADDRESS, 0x0A, 0x01);
   do
   {
      I2Cread(MAG_ADDRESS, 0x02, 1, &ST1);
   } while (!(ST1 & 0x01));

   uint8_t Mag[7];
   I2Cread(MAG_ADDRESS, 0x03, 7, Mag);
 
   // Convertir registros magnetometro
   int16_t mx = -(Mag[3] & 8 | Mag[2]);
   int16_t my = -(Mag[1] & 8 | Mag[0]);
   int16_t mz = -(Mag[5] & 8 | Mag[4]);
 
 
   // --- Mostrar valores ---
   float axf = (float)ax*32000/65536.0;
   float ayf = (float)ay*32000/65536.0;
   float azf = (float)az*32000/65536.0;
   float aresf = sqrt(axf*axf+ayf*ayf+azf*azf);
   ax = (int16_t)axf;
   ay = (int16_t)ayf;
   az = (int16_t)azf;
   uint16_t ares = (int16_t)aresf;
   
   // Acelerometro
   Serial.print(ax, DEC);
   Serial.print("\t");
   Serial.print(ay, DEC);
   Serial.print("\t");
   Serial.print(az, DEC);
   Serial.print("\t");
   Serial.print(ares,DEC);
   Serial.println("");
 /*
   // Giroscopio
   Serial.print(gx, DEC);
   Serial.print("\t");
   Serial.print(gy, DEC);
   Serial.print("\t");
   Serial.print(gz, DEC);
   Serial.print("\t");
 
 
   // Magnetometro
   Serial.print(mx + 200, DEC);
   Serial.print("\t");
   Serial.print(my - 70, DEC);
   Serial.print("\t");
   Serial.print(mz - 700, DEC);
   Serial.print("\t");
   
   // Fin medicion
   Serial.println("");
   */
  delay(10);    
}

2 thoughts on “IMU MPU-9250 - Acelerómetro

  1. Yo llevaba semanas volviéndome loco y probando de todo hasta que encontré esto. https://github.com/RTIMULib/RTIMULib-Arduino Échale un ojo en profundidad que te ahorrarás muchas horas de pruebas y análisis. Con estas librerías en cosa de un par de horas he montado una mpu9250 con una wemos d1 mini pasando los datos sin drifting por udp a opentrack . Llevaba semanas intentándolo con resultados mediocres

Deja un comentario

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