Mostrando entradas con la etiqueta MMA7260. Mostrar todas las entradas
Mostrando entradas con la etiqueta MMA7260. Mostrar todas las entradas

domingo, 6 de febrero de 2011

Calculando la inclinación en grados de nuestro acelerómetro MMA7260

Una vez que disponemos de las aceleraciones estáticas de nuestro dispositivo tan sólo queda aplicar un poco de trigonometría para disponer para cada eje el ángulo.
Las formulas que he usado son las siguientes:

 x=analogRead(xaxis);
 y=analogRead(yaxis);
 z=analogRead(zaxis);

 unsigned long tt = micros();
 
 float aax = (((x*5000.0)/1023.0)-XError)/RESOLUTION;
 float aay = (((y*5000.0)/1023.0)-YError)/RESOLUTION;
 float aaz = (((z*5000.0)/1023.0)-ZError)/RESOLUTION;

 float rho = atan(aax/sqrt(pow(aay,2)+pow(aaz,2)))*(360/2*PI);
 float phi = atan(aay/sqrt(pow(aax,2)+pow(aaz,2)))*(360/2*PI);
 float theta = atan(sqrt(pow(aay,2)+pow(aax,2))/aaz)*(360/2*PI);
 
 tt = micros() - tt;

el último término es para pasar de radianes a grados.
Como puede verse, el cálculo es bastante costoso debido a la gran cantidad de cálculos que hay que realizar por lo que intentaremos acelerar y simplificar dichas operaciones. Utilizando la función micros() podemos ver los microsegundos que se tardan en resolver todos los cálculos lo que nos será muy útil para ver si vamos mejorando el algoritmo.

El valor de "tt" es aproximadamente de 980 microsegundos por lo que disponemos ahora de un indicador de lo que tarda en calcularse los grados a partir del voltaje de entrada del acelerómetro.

Aplicando algunas optimizaciones podemos disponer ya de los valores en grados de los tres ejes de nuestro acelerómetro

#define DEBUGMODE

#define _360div2xPI     57.29577951
#define _5000div1023    4.887585533 

#define MMA7260_RESOLUTION      800     //0.8 v/g -> resolucion de 1.5g -> 800mV/g
#define MMA7260_VOLTAGE         3300.0; //mv voltage al que está conectado el acelerómetro

const float ZOUT1G = 2450;   // mv Voltage en Zout a 1G
const float ZOUT0G = 1650;   // mv Voltage en Zout a 1G
#define MMA7260_ZOUT_1G 850   // mv Voltage en Zout a 1G

const float XOUT1G = 2450;   // mv Voltage en Zout a 1G
const float XOUT0G = 1650;   // mv Voltage en Zout a 1G

const float YOUT1G = 2450;   // mv Voltage en Zout a 1G
const float YOUT0G = 1650;   // mv Voltage en Zout a 1G

#define NADJ  50        // Número de lecturas para calcular el error

// Entradas analógicas donde van los sensores
#define XAXIS_PORT  0
#define YAXIS_PORT  1
#define ZAXIS_PORT  2

// Salida digital del led de la placa
#define LEDPIN_PORT  13

float XError,YError,ZError;
float z,x,y;

float AccelAdjust(int axis)
{
  float acc = 0;

  for (int j=0;j < NADJ;j++)
  {
    float lectura=analogRead(axis);
    acc = acc + (lectura*_5000div1023);
    delay(11); //número primo para evitar ciclos de lectura proporcionales
  }

  return acc/NADJ;
}

void setup()
{  
  Serial.begin(9600); // 9600 bps
  pinMode(XAXIS_PORT,INPUT);
  pinMode(YAXIS_PORT,INPUT);
  pinMode(ZAXIS_PORT,INPUT);
  pinMode(LEDPIN_PORT, OUTPUT);

  digitalWrite(LEDPIN_PORT, HIGH);
  XError =  AccelAdjust(XAXIS_PORT);
  YError =  AccelAdjust(YAXIS_PORT);
  ZError =  AccelAdjust(ZAXIS_PORT);
  ZError = ZError - MMA7260_ZOUT_1G;
  digitalWrite(LEDPIN_PORT, LOW);

#ifdef DEBUGMODE
  Serial.println("Ajustado acelerometro eje X");
  Serial.print("Error de X= ");
  Serial.println(XError);

  Serial.println("Ajustado acelerometro eje Y");
  Serial.print("Error de Y= ");
  Serial.println(YError);

  Serial.println("Ajustado acelerometro eje Z");
  Serial.print("Error de Z= ");
  Serial.println(ZError);
#endif   
}

void loop()
{
  x=analogRead(XAXIS_PORT);
  y=analogRead(YAXIS_PORT);
  z=analogRead(ZAXIS_PORT);

  unsigned long tt = micros();

  float aax = ((x*_5000div1023)-XError)/MMA7260_RESOLUTION;
  float aay = ((y*_5000div1023)-YError)/MMA7260_RESOLUTION;
  float aaz = ((z*_5000div1023)-ZError)/MMA7260_RESOLUTION;

  float rho = atan(aax/sqrt((aay*aay)+(aaz*aaz)))*_360div2xPI;
  float phi = atan(aay/sqrt((aax*aax)+(aaz*aaz)))*_360div2xPI;
  float theta = atan(sqrt((aay*aay)+(aax*aax))/aaz)*_360div2xPI;

  tt = micros()-tt;

#ifdef DEBUGMODE
  Serial.print(aax);
  Serial.print(" ");
  Serial.print(aay);
  Serial.print(" ");          
  Serial.print(aaz);
  Serial.println();

  Serial.print(rho);
  Serial.print(" ");
  Serial.print(phi);
  Serial.print(" ");
  Serial.print(theta);

  Serial.println();

  Serial.print(tt);
  Serial.println();

#endif

  delay(1000);
}


En esta imagen puede verse, con el acelerómetro inclinado hacia arriba, el eje Y y Z cerca de 90º.

Con este código modificado dispondremos de las inclinaciones en reposo así como un tiempo de alrededor de 930 microsegundos (50 microsegundos más rápido). Se han cambiado algunas variables por #defines así como algunos cálculos se han convertido en constantes. Sin embargo el mayor tiempo se consume en el cálculo de las funciones trigonométricas que espero poder mejorar usando por ejemplo lookup-tables.

sábado, 5 de febrero de 2011

Midiendo las inclinaciones con el MMA7260

Siguiendo con nuestro trabajo con el acelerómetro MMA7260 toca recoger la información que nos suministra. Inicialmente lo utilizaremos para medir la aceleración estática y como en reposo está sometido a la fuerza de la gravedad podremos medir las inclinaciones en los tres ejes.

Existe un documento muy bueno y muy detallado desarrollado por el fabricante con los detalles técnicos que utilizaremos para esta aplicación.

Lo primero será conectar al Arduino las señales provenientes de cada eje de aceleración del acelerómetro. Para ello utilizaremos las entradas analógicas A0, A1 y A2 como vimos en el post anterior.


Una vez conectadas, programaremos el Arduino para que recoja los valores de esas entradas, las transformaremos y las mostraremos por pantalla.

Para programar con Arduino necesitaremos descargarnos el entorno de desarrollo desde la página web oficial (mejor en inglés ya que dispondrá de la versión más actualizada), podeis seguir las instrucciones para los que nos hayais programado nunca en este entorno.

A continuación os coloco el programa para medir la lectura de la aceleración en Gs:

#define DEBUGMODE

const float RESOLUTION=800; //0.8 v/g -> resolucion de 1.5g -> 800mV/g
const float VOLTAGE=3.3;  //voltage al que está conectado el acelerómetro

const float ZOUT1G = 2450;   // mv Voltage en Zout a 1G
const float ZOUT0G = 1650;   // mv Voltage en Zout a 1G
const float ZOUT_1G = 850;   // mv Voltage en Zout a 1G

const float XOUT1G = 2450;   // mv Voltage en Zout a 1G
const float XOUT0G = 1650;   // mv Voltage en Zout a 1G

const float YOUT1G = 2450;   // mv Voltage en Zout a 1G
const float YOUT0G = 1650;   // mv Voltage en Zout a 1G

const int NADJ  = 50;        // Número de lecturas para calcular el error

// Entradas analógicas donde van los sensores
const int xaxis = 0;
const int yaxis = 1;
const int zaxis = 2;

// Salida digital del led de la placa
const int LEDPIN = 13;

float XError,YError,ZError;
float xd,yd,zd,z,x,y;

float AccelAdjust(int axis)
{
 float acc = 0;

 for (int j=0;j
 {
   float lectura=analogRead(axis);
   acc = acc + ((lectura*5000)/1023.0);
   delay(11); //número primo para evitar ciclos de lectura proporcionales
 }

 return acc/NADJ;
}

void setup()
{  
 Serial.begin(9600); // 9600 bps
 pinMode(xaxis,INPUT);
 pinMode(yaxis,INPUT);
 pinMode(zaxis,INPUT);
 pinMode(LEDPIN, OUTPUT);

 digitalWrite(LEDPIN, HIGH);
 XError =  AccelAdjust(xaxis);
 YError =  AccelAdjust(yaxis);
 ZError =  AccelAdjust(zaxis);
 ZError = ZError - ZOUT_1G;
 digitalWrite(LEDPIN, LOW);

#ifdef DEBUGMODE
 Serial.println("Ajustado acelerometro eje X");
 Serial.print("Error de X= ");
 Serial.println(XError);

 Serial.println("Ajustado acelerometro eje Y");
 Serial.print("Error de Y= ");
 Serial.println(YError);

 Serial.println("Ajustado acelerometro eje Z");
 Serial.print("Error de Z= ");
 Serial.println(ZError);
#endif  
}

void loop()
{
 x=analogRead(xaxis);
 y=analogRead(yaxis);
 z=analogRead(zaxis);

 float aax = (((x*5000.0)/1023.0)-XError)/RESOLUTION;
 float aay = (((y*5000.0)/1023.0)-YError)/RESOLUTION;
 float aaz = (((z*5000.0)/1023.0)-ZError)/RESOLUTION;

#ifdef DEBUGMODE
 Serial.print(aax);
 Serial.print(" ");
 Serial.print(aay);
 Serial.print(" ");          
 Serial.print(aaz);
 Serial.println();
#endif

 delay(500);
} 

Una vez ejecutado el programa podemos ver como se realiza un ajuste del error estando en reposo y se recogen valores del mismo.

Los ejes X e Y (los dos primeros valores) están próximos a cero y el eje Z debido a la gravedad está en torno a 1. En realidad la oscilación de valores es mucho mayor pero gracias al ajuste del principio podemos disponer de valores mucho más estables y fiables. Si movemos el acelerómetro veremos como la fuerza G se ve aplicada a cada uno de los ejes. A partir de estos valores podremos calcular por ejemplo los grados de inclinación y utilizar el acelerómetro como un inclinómetro pero eso en el siguiente post.

viernes, 4 de febrero de 2011

El acelerómetro MMA7260

¿Qué es un acelerómetro y para que sirve?, pues es un dispositivo electrónico que sirve para medir las aceleraciones en uno o varios ejes. Para mis primeras pruebas decidí adquirir el modelo MMA 7260 de Freescale Semiconductor que es ligero, barato (15€) y robusto y permite medir las aceleraciones en los tres ejes.

Las principales caracteristicas son:
  • Dispone de sensivilidad ajustable de 1,5G/2G/4G y 6G
  • Bajo consumo. En torno a 500µA
  • Modo de reposo con un consumo de 3µA
  • Alta sensibilidad (800 mV/g para 1,5G) 
  • Incluye filtros paso bajo ideal para no tener que añadir más hardware
Atención: su voltaje de operación es entre 2.2V y 3.6V, esto es importante si lo vamos a conectar al Arduino ya que podemos estar acostumbrados a conectar dispositivos y sensores que funcionan a 5V y podríamos freirlo.

El acelerómetro viene con una serie de cables para alimentarlo y para recoger las señales que serán un valor analógico en milivoltios para representar el valor de la aceleración.

En esta imagen se puede ver con detalle el significado de cada conector, el selector de la sensibilidad del acelerómetro y los tres ejes de medición X,Y y Z.

Para los más intrépidos os pongo aquí el datasheet

Para conectarlo al Arduino y así poder capturar la aceleración del dispositivo tenemos que primero, alimentarlo correctamente y luego conectar las señales procedentes del acelerómetro a entradas analógicas del Arduino.

Os recuerdo que la alimentación es de 3.3V y para ello conectamos cualquiera de los cables rojos y negro a la alimentación y masa respectivamente.

En la fotografía anterior puede verse el conexionado del cable de masa y tensión a los conectores correspondientes del Arduino. No me hartaré de repertirlo: 3.3V!!! lo digo por experiencia... :-(
La conexión de alimentación se puede conectar a cualquiera de los 3 mazos de cable que trae siempre respetando conectarlo al sitio adecuado.

A continuación, para conectar las señales de acelaración suministradas por el acelerómetro tendremos que usar las entradas analógicas que trae el Arduino. Para ello usamos el cable azul que viene del acelerómetro

en este caso usando un cable verde y lo conectamos a la entrada analógica A2
comprobamos que todo está correctamente conectado, conectamos el arduino al puerto usb para encenderlo y veremos que además de encenderse el Arduino se enciende un pequeño led rojo del acelerómetro
si queremos podemos conectar las otras dos señales de los dos ejes de aceleración que faltan (no es necesario conectar más cables de alimentación, sólo el cable azul del resto de mazo de cables)

y ya podemos proceder a realizar la lectura de las aceleraciones desde el Arduino... pero esto lo dejaré para más adelante.