Go Down

Topic: 3 axes accel MMA7361 as Tilt meter (Read 3393 times) previous topic - next topic

Oct 10, 2012, 01:45 am Last Edit: Oct 11, 2012, 12:29 am by RonSpooner Reason: 1
This code works well.
Use AREF at 3.3V
connect SL to 3.3V  (keeps the unit out of sleep mode.)
Made the change to use atan2()

/*
//  I use this sketch for find the max and min  for each axes in counts from 0 to 1023
//  carfully and slowly tilt the MMA7361  in all possible aditudes to obtain the max and min.
//  This information is used to calibrate the MMA7361 - each one is slightly different.
long analog_x,analog_y,analog_z;
int  analog_mx_x, analog_mn_x, analog_mx_y, analog_mn_y, analog_mx_z, analog_mn_z;
int i;
void setup() {
Serial.begin(9600);
analogReference(EXTERNAL);
analog_mx_x = analog_mn_x =analog_mx_y = analog_mn_y =analog_mx_z = analog_mn_z = 512;
}
void loop()

analog_x = analog_y = analog_z = 0;
for (i = 0; i<1000; i++) {
   analog_x += analogRead(0);   
   analog_y += analogRead(1);
   analog_z += analogRead(2);
}
analog_x = analog_x/1000;
analog_y = analog_y/1000;
analog_z = analog_z/1000;

analog_mn_x = min(analog_x, analog_mn_x);
analog_mx_x = max(analog_x, analog_mx_x);
analog_mn_y = min(analog_y, analog_mn_y);
analog_mx_y = max(analog_y, analog_mx_y);
analog_mn_z = min(analog_z, analog_mn_z);
analog_mx_z = max(analog_z, analog_mx_z);

 
Serial.print("x:");
Serial.print(analog_x);
Serial.print("  ");
Serial.print(analog_mx_x);
Serial.print("  ");
Serial.print(analog_mn_x);

Serial.print("     y:");     
Serial.print(analog_y);
Serial.print("  ");
Serial.print(analog_mx_y);
Serial.print("  ");
Serial.print(analog_mn_y);

Serial.print("  z:");
Serial.print(analog_z);
Serial.print("  ");
Serial.print(analog_mx_z);
Serial.print("  ");
Serial.print(analog_mn_z);
Serial.println("");
delay(20);
}
*/

//  compute the tilt angle.
void setup()
{
  Serial.begin(9600);
  analogReference(EXTERNAL);   
}
long analog_x,analog_y,analog_z;     //  need a long  because I agerage 1000 times and the number gets big.
int i;
float g_x,g_y,g_z;                   //  need float for the asin() functions.       4 bytes
float degree_t,degree_y,degree_z;    //  degrees are also floating numbers.    4 bytes
void loop()

  analog_x = analog_y = analog_z = 0;
  for (i = 0; i < 1000; i++) {       // average 1000 readings
    analog_x += analogRead(0);       // reads between  0 and 1023
    analog_y += analogRead(1);
    analog_z += analogRead(2);
  }
  analog_x = analog_x/100;   //  10 times the average
  analog_y = analog_y/100;
  analog_z = analog_z/100;
 
  //  I use the calibration numbers abtained from the above sketch and map then from -1.000 to +1.000
  //  This mantains the resolution of the analogRead() values.   about 500 counts from the 1023 full scale
  g_x = map(analog_x,2710,7570,-10000,10000)/10000.0;  //  normalize 10 times the average into 10000 counts
  g_y = map(analog_y,3130,7990,-10000,10000)/10000.0;  //  this provides a bit more resolution
  g_z = map(analog_z,1830,6818,-10000,10000)/10000.0;  //  10000 counts = 1g

  if(g_z<=1&&g_z>=-1) // Check for overflow of asin(x).( If x>1 or x<-1, asin(x) not defined)   
  {
    degree_t=atan2(g_y, g_x)*180.0/(1*PI);    //calculate the tilt angle in degree
    degree_z=asin(g_z)*180.0/(1*PI);
  }
  //fix the overflow condition
   
  if(g_z>1)     degree_z=91;        //  I use 91 to indicate an overflow.
  if(g_z<-1)    degree_z=-91;

  Serial.print("Degrees t:");
  Serial.print(degree_t);
  Serial.print("      x:");
  Serial.print(g_x*100);  // prints out gravity in percent.
  Serial.print("%      y:");
  Serial.print(g_y*100);
  Serial.print("%      z:");
  Serial.print(degree_z);
  Serial.print("    z:");
  Serial.print(g_z*100);
  Serial.println("%");
}







MarkT

Tilt angles should be best calculated as  atan2 (x, z) and atan2 (y, z).  asin isn't the obvious trigonometric function to use and totally relies on good gain calibration.  atan2 only needs offset calibration and gain-matching and can't give NaN.

Calibration using max and min will be potentially badly skewed by vibration as you turn the device.  Normally you would callibrate by taking 6 measurements with the device static as each is taken, then compute zero-points and gain factors.

A quicker calibration is to take x,y,z readings with device in +z and -z directions only, but then you only get the offset settings for x and y.  For small tilt angles this is usually acceptable.
[ I won't respond to messages, use the forum please ]

Yes  atan2  is a better way.  thanks.   I average 1000 reading - this  helps reduce the noise a bit.  Ron

arkadiraf

Hi, i have done the same project using simulink with low pass filters.
I have attached a PDF explaining pretty good the math and principles.
look at eq. 37,38

Very nice.   My application is for a Dob telescope El angle.  My simple approach will work for that.  Yours is a much more general solution - nicely done.  Thanks.
Ron

Hi,
   I am doing sign language translator project.I am using MMA7361 accelerometer to find hand orientation.Can anyone tell me how to calibrate MMA7361 using arduino.

Calibration of the MMA7361

I used a large poster board and fastened at 5cm by 80cm poster-board pendulum to the upper corner.
Using drafting tools I made a large protractor with the pendulum fastening point as the center.  I fastened the MMA7361 to the pendulum being sure to mount it as squarely  as possible.
Bu moving the pendulum to known angles I was able to "calibrate" the tilt meter.


jremington

To get the most accurate readings, it is very important to individually calibrate the offset and gain of each accelerometer axis. My favorite procedure is described here: sailboatinstruments.blogspot.com/2011/08/improved-magnetometer-calibration.html
It is described for magnetometers but works equally well for accelerometers, providing the sensor is held still while each individual data point is collected.

Go Up