Analogic 3 axis accelerometer data reading

hi,
i'm quite new in this world, and i'm actually playing with a 3 axis analogic accelerometer (MMA7361)
i use it in the +/- 1,5G range (sensitivity of 0,800 v/g)
i can easyly read data from the accelerometer, but the values are not very stables.... so when i convert them to angle, the result is really very instable.
someone can give help me...?

//Lebenj
//february 2011

//Created using version 0021 of the Arduino Development Environment

//playing with an accelerometer and display data on the serial monitor

#define wDelay 500//no ; here. Sets how long each "message" appears

// These constants won't change:
const int pinL = A2;         // pin Xsensor
const int pinP = A1;         // pin Ysensor
const int pinH = A0;         // pin Zsensor
const int ledPin = 9;        // pin that the LED is attached to
//const int range = 1.5;          // accelerometer range
//const int voltageACC = 3.3;      // accelerometer Voltage
const int voltageBOARD = 5;      // arduino board Voltage
const int pi = 314159265;

// variables:
float sensitivityV = 0.800; // voltage accelerometer sensitivity
float  sensitivityd = 1023 / voltageBOARD * sensitivityV ; // digital accelerometer sensitivity
int sensitivityD = int(sensitivityd);

// variables Laterales:
int sensorLvalue  ;         // the sensor value
int sensorLvalue1 ;         // the sensor value
int sensorLvalue2 ;         // the sensor value
int sensorLvalue3 ;         // the sensor value
int sensorLvalue4 ;         // the sensor value
int sensorLvalue5 ;         // the sensor value
int sensorLmin = 1023;      // minimum sensor value
int sensorLmax = 0;         // maximum sensor value
int sensorLave;             // average min/max during calibration
int accL ;                  // acceleration laterale
int angle1dL;               // roll angle (laterale) calcul 21(roulie)
int angle2dL;               // roll angle (laterale) 2D(roulie)
int angle3dL;               // roll angle (laterale) 3D(roulie)

// variables pitch:
int sensorPvalue  ;         // the sensor value
int sensorPvalue1 ;         // the sensor value
int sensorPvalue2 ;         // the sensor value
int sensorPvalue3 ;         // the sensor value
int sensorPvalue4 ;         // the sensor value
int sensorPvalue5 ;         // the sensor value
int sensorPmin = 1023;      // minimum sensor value
int sensorPmax = 0;         // maximum sensor value
int sensorPave = 0;         // average min/max during calibration
int accP ;                  // acceleration pitch
float angleP;               // angle Pitch (tanguage)

// variables verticales:
int sensorHvalue  ;         // the sensor value
int sensorHvalue1 ;         // the sensor value
int sensorHvalue2 ;         // the sensor value
int sensorHvalue3 ;         // the sensor value
int sensorHvalue4 ;         // the sensor value
int sensorHvalue5 ;         // the sensor value
int sensorHmin = 1023;      // minimum sensor value
int sensorHmax = 0;         // maximum sensor value
int sensorHave = 0;         // average min/max during calibration
int accH ;                  // acceleration verticale
float angleH;               // angle ?????

void setup(){

Serial.begin(9600);

Serial.println("   ");
  pinMode(13, OUTPUT);
  digitalWrite(13, HIGH);
  Serial.print("CALIBRATION");
  Serial.println("");
  

  // calibrate during the first five seconds 
  while (millis() < 4000) {
    sensorLvalue = analogRead(pinL);
    sensorPvalue = analogRead(pinP);
    sensorHvalue = analogRead(pinH);

    // record the maximum sensor value(LPH)
    if (sensorLvalue > sensorLmax) {
      sensorLmax = sensorLvalue;
    }
    if (sensorPvalue > sensorPmax) {
      sensorPmax = sensorPvalue;
    }
    if (sensorHvalue > sensorHmax) {
      sensorHmax = sensorHvalue;
    }

    // record the minimum sensor value (LPH)
    if (sensorLvalue < sensorLmin) {
      sensorLmin = sensorLvalue;
    }
     if (sensorPvalue < sensorPmin) {
      sensorPmin = sensorPvalue;
    }
     if (sensorHvalue < sensorHmin) {
      sensorHmin = sensorHvalue;
    }

  }
  
  sensorLave = (sensorLmin + sensorLmax) / 2;
  sensorPave = (sensorPmin + sensorPmax) / 2;
  sensorHave = ((sensorHmin + sensorHmax) / 2);
    Serial.print("tension arduino: ");
    Serial.print(voltageBOARD); // print board voltage
    Serial.println(""); // retour charriot
    Serial.print("sensitivite VOLT: ");
    Serial.print(sensitivityV); // print voltage sensitivity
    Serial.println(""); // retour charriot
    Serial.print("sensitivite digitale: ");
    Serial.print(sensitivityD); // print digital sensitivity
    Serial.println(""); // retour charriot
    Serial.print("calibration laterale: ");
    Serial.print(sensorLave); // print the average of L
    Serial.println(""); // retour charriot
    Serial.print("   "); // print nothing
    Serial.print("calibration Pitch: ");
    Serial.print(sensorPave); // print the average of H
    Serial.println(""); // retour charriot
    Serial.print(" "); // print nothing
    Serial.print("calibration hauteur: ");
    Serial.print(sensorHave); // print the average of P
    Serial.println(""); // retour charriot
       delay(1000);
    Serial.print("FIN CALIBRATION"); // calibration END
    delay(1000);
    Serial.println(""); // retour charriot
  // signal the end of the calibration period
  digitalWrite(13, LOW);
  
};//end "setup()"

void loop(){
  delay(wDelay);
  // read the sensor:
  sensorLvalue1 = analogRead(pinL);
  sensorPvalue1 = analogRead(pinP);
  sensorHvalue1 = analogRead(pinH);
  delay(10);
  sensorLvalue2 = analogRead(pinL);
  sensorPvalue2 = analogRead(pinP);
  sensorHvalue2 = analogRead(pinH);
  delay(10);
  sensorLvalue3 = analogRead(pinL);
  sensorPvalue3 = analogRead(pinP);
  sensorHvalue3 = analogRead(pinH);
  delay(10);
  sensorLvalue4 = analogRead(pinL);
  sensorPvalue4 = analogRead(pinP);
  sensorHvalue4 = analogRead(pinH);
  delay(10);
  sensorLvalue5 = analogRead(pinL);
  sensorPvalue5 = analogRead(pinP);
  sensorHvalue5 = analogRead(pinH);
  
  sensorLvalue = (sensorLvalue1 + sensorLvalue2 + sensorLvalue3 + sensorLvalue4 + sensorLvalue5) / 5;
  sensorPvalue = (sensorPvalue1 + sensorPvalue2 + sensorPvalue3 + sensorPvalue4 + sensorPvalue5) / 5;
  sensorHvalue = (sensorHvalue1 + sensorHvalue2 + sensorHvalue3 + sensorHvalue4 + sensorHvalue5) / 5;
  
  accL = 100 * (sensorLvalue - sensorLave) / sensitivityD; // calcul acceleration laterale
  accP = 100 * (sensorPvalue - sensorPave) / sensitivityD; // calcul acceleration pitch
  accH = 100 * (sensorHvalue - sensorHave) / sensitivityD; // calcul acceleration verticale
 
 angle1dL= 180 * (asin(accL)) / pi * 10000; //calcul avec la deviation de X uniquement
 angle2dL= 180 * (atan(accL / accH)) / pi * 10000; //calcul sur 2 axes X et Z
 angle3dL= 180 * (atan(accL / sqrt(accH * accH + accP * accP))) / pi * 10000; //calcul sur 3 axes
 
 Serial.print("         valeur laterale: "); 
 Serial.print(sensorLvalue);//write sensorValue
 Serial.print("         valeur pitch: ");
 Serial.print(sensorPvalue);//write sensorValue
 Serial.print("         valeur hauteur: ");
 Serial.print(sensorHvalue);//write sensorValue
   Serial.println("");//Send an "x" to turn a digit off
 Serial.print("    acceleration laterale: "); 
 Serial.print(accL);
 Serial.print("    acceleration pitch: "); 
 Serial.print(accP);
 Serial.print("    acceleration verticale: "); 
 Serial.print(accH);
   Serial.println("");//retour charriot
 Serial.print("    angle roulis(calcul 1D): ");
 Serial.println(angle1dL);
 Serial.print("    angle roulis(calcul 2D): ");
 Serial.println(angle2dL);  
 Serial.print("    angle roulis(calcul 3D): ");
 Serial.println(angle3dL);  
  delay(wDelay);
};

best regards.

Your code is very long and seems to repeat similar calculations for each axis.
Why not let the computer do the work?

When you set your accelerometer flat, do the x and y axes have values around say 512?

hi,
@groove
you are true, my code looks very long, but for the moment it's not a priority for me, i prefer it like that to keep it clear for me.
when i will get better results, i will manage to do it better.
to calculate one value, i make the average of 5 samples. it's the way i explore to get a more stable reading.

@liudr
when my accelerometer is flat i dont have 512 on X and Y, but around 340. (and around 175 for Z)
i think it's normal, because my arduino is in 5V but my accelerometer (and quite all accelerometers) are working at 3,3V. so the 0g give 3,3V / 2 = 1,65V wich mapped to a 0 1023 of 0 5V give the normal result of 340, no???

sorry for my poor englidh skill... :smiley: :smiley:

i was playing yesterday night with excell (wich i use better for calculation), and i find some bugs in my code. so i have to work on it.

i thinks i get well that analogic inputs, my problems are to get a stable value when the accelerometer is not moving, and a good calculation to get the angle.

thanks for your help...

Hey, that's good. To further improve your accuracy, you can use analogReference(EXTERNAL); and connect your aref to 3.3V on arduino. Then you will get 512, a better use of the 1024 levels.

I think your accelerometer is too sensitive, +-1.5g. Is there a jumper to go to +-6.0g? Use that if you want to tell angle. If you tap on your accelerometer at +-1.5g range with your finger, it may go half its scale. Accelerations are pretty big even for those small movements, it's not related to small change in position but change in velocity.

In my music box project, I have +-6.g sensitivity and tapping on the box that encloses arduino with accelerometer on it, not tapping on the accelerometer, can give me a large fraction of a g!

http://arduino.cc/forum/index.php/topic,49937.0.html

thanks,

you gave me 2 good ways to explore.... thanks a lot :smiley:

for the 1,5G or 6G, i must make tries. i have also a ADXL3xx wich is 3G, as it works in the same way i can easyly change it for the other. i have only to modify the sensitivity.

i will get a look on your project. i hope it will help me a lot.

when i make tries to mesure angles, i never touch the accelerometer, only the board.

with your experience, do you really thing i need to make a calibration???

regards
Benjamin

and i find some bugs in my code

If you reduce the size of your code by factoring, you reduce the number of dark corners the bugs have to hide, and you will almost certainly make you code easier to maintain and debug.

Example:

sensorLvalue1 = analogRead(pinL);
  sensorPvalue1 = analogRead(pinP);
  sensorHvalue1 = analogRead(pinH);
  delay(10);
  sensorLvalue2 = analogRead(pinL);
  sensorPvalue2 = analogRead(pinP);
  sensorHvalue2 = analogRead(pinH);
  delay(10);
  sensorLvalue3 = analogRead(pinL);
  sensorPvalue3 = analogRead(pinP);
  sensorHvalue3 = analogRead(pinH);
  delay(10);
  sensorLvalue4 = analogRead(pinL);
  sensorPvalue4 = analogRead(pinP);
  sensorHvalue4 = analogRead(pinH);
  delay(10);
  sensorLvalue5 = analogRead(pinL);
  sensorPvalue5 = analogRead(pinP);
  sensorHvalue5 = analogRead(pinH);
  
  sensorLvalue = (sensorLvalue1 + sensorLvalue2 + sensorLvalue3 + sensorLvalue4 + sensorLvalue5) / 5;
  sensorPvalue = (sensorPvalue1 + sensorPvalue2 + sensorPvalue3 + sensorPvalue4 + sensorPvalue5) / 5;
  sensorHvalue = (sensorHvalue1 + sensorHvalue2 + sensorHvalue3 + sensorHvalue4 + sensorHvalue5) / 5;

becomes (uncompiled, untested)

  sensorLvalue = 0;
  sensorPvalue = 0;
  sensorHvalue = 0;
  for (int i = 0; i < N_READINGS; ++i) {
    sensorLvalue += analogRead (pinL);
    sensorPvalue += analogRead (pinP);
    sensorHvalue += analogRead (pinH);
    delay (10);
  }
  sensorLvalue /= N_READINGS;
  sensorPvalue /= N_READINGS;
  sensorHvalue /= N_READINGS;

Using arrays could make it even shorter.
Uses less memory too, even if you increase N_READINGS to the point where you have to make the variables "long"s instead of "int"s.

thanks.... i will study this function tonight !

hi,

i made a update of my code (thanks liudr and Groove)

but when i compare result in excel and in my serial monitor i have still trouble reading the angle!!! :cold_sweat:

//Lebenj
//february 2011

//Created using version 0021 of the Arduino Development Environment

//playing with an accelerometer and display data on the serial monitor

#define wDelay 500//no ; here. Sets how long each "message" appears



// These constants won't change:
const int pinL = A2;         // pin Xsensor
const int pinP = A1;         // pin Ysensor
const int pinH = A0;         // pin Zsensor
const int ledPin = 9;        // pin that the LED is attached to
//const int range = 1.5;          // accelerometer range
//const int voltageACC = 3.3;      // accelerometer Voltage
const int voltageBOARD = 5;      // arduino board Voltage
const int pi = 314159265;
const int Nsamples = 10;

// variables:
float Aref = 3.3;
float sensitivityV = 0.800; // voltage accelerometer sensitivity
float  sensitivityd = 1023 / Aref * sensitivityV ; // digital accelerometer sensitivity
int sensitivityD = int(sensitivityd);

// variables Laterales:
int sensorLvalue  ;         // the sensor value
int sensorLmin = 1023;      // minimum sensor value
int sensorLmax = 0;         // maximum sensor value
int sensorLave;             // average min/max during calibration
int sensorLaveT = 0;         // teoric X value calibration
int accL ;                  // acceleration laterale
float angle1dL;               // roll angle (laterale) calcul 21(roulie)
float angle2dL;               // roll angle (laterale) 2D(roulie)
float angle3dL;               // roll angle (laterale) 3D(roulie)

// variables pitch:
int sensorPvalue  ;         // the sensor value
int sensorPmin = 1023;      // minimum sensor value
int sensorPmax = 0;         // maximum sensor value
int sensorPave = 0;         // average min/max during calibration
int sensorPaveT = 0;         // teoric Y value calibration
int accP ;                  // acceleration pitch
float angleP;               // angle Pitch (tanguage)

// variables verticales:
int sensorHvalue  ;         // the sensor value
int sensorHmin = 1023;      // minimum sensor value
int sensorHmax = 0;         // maximum sensor value
int sensorHave = 0;         // average min/max during calibration
int sensorHaveT = 0;         // teoric Z value calibration
int accH ;                  // acceleration verticale
float angleH;               // angle ?????

void setup(){

Serial.begin(9600);
analogReference(EXTERNAL);

Serial.println("   ");
  pinMode(13, OUTPUT);
  digitalWrite(13, HIGH);
  Serial.print("CALIBRATION");
  Serial.println("");
  
  // calibrate during the first five seconds 
  while (millis() < 4000) {
    sensorLvalue = analogRead(pinL);
    sensorPvalue = analogRead(pinP);
    sensorHvalue = analogRead(pinH);

    // record the maximum sensor value(LPH)
    if (sensorLvalue > sensorLmax) {
      sensorLmax = sensorLvalue;
    }
    if (sensorPvalue > sensorPmax) {
      sensorPmax = sensorPvalue;
    }
    if (sensorHvalue > sensorHmax) {
      sensorHmax = sensorHvalue;
    }

    // record the minimum sensor value (LPH)
    if (sensorLvalue < sensorLmin) {
      sensorLmin = sensorLvalue;
    }
     if (sensorPvalue < sensorPmin) {
      sensorPmin = sensorPvalue;
    }
     if (sensorHvalue < sensorHmin) {
      sensorHmin = sensorHvalue;
    }

  }
  
  sensorLave = (sensorLmin + sensorLmax) / 2;
  sensorPave = (sensorPmin + sensorPmax) / 2;
  sensorHave = (sensorHmin + sensorHmax) / 2;
  
  sensorLaveT = 511;  // theoric valor
  sensorPaveT = 511;  // theoric valor
  sensorHaveT = 759;  // theoric valor
  
    Serial.print("tension arduino: ");
    Serial.print(voltageBOARD); // print board voltage
    Serial.println(""); // retour charriot
    Serial.print("tension de reference digitale (Aref: ");
    Serial.print(Aref); // print board voltage
    Serial.println(""); // retour charriot
    Serial.print("sensitivite VOLT: ");
    Serial.print(sensitivityV); // print voltage sensitivity
    Serial.println(""); // retour charriot
    Serial.print("sensitivite digitale: ");
    Serial.print(sensitivityD); // print digital sensitivity
    Serial.println(""); // retour charriot
    Serial.print("calibration laterale theorique: ");
    Serial.print(sensorLaveT); // print the average of L
    Serial.print("  mesurée: ");
    Serial.print(sensorLave); // print the average of L
    Serial.println(""); // retour charriot
    Serial.print("   "); // print nothing
    Serial.print("calibration Pitch theorique: ");
    Serial.print(sensorPaveT); // print the average of H
    Serial.print("  mesurée: ");
    Serial.print(sensorPave); // print the average of H
    Serial.println(""); // retour charriot
    Serial.print(" "); // print nothing
    Serial.print("calibration hauteur theorique: ");
    Serial.print(sensorHaveT); // print the average of P
    Serial.print("  emsurée: ");
    Serial.print(sensorHave); // print the average of P
    Serial.println(""); // retour charriot
       delay(1000);
    Serial.print("FIN CALIBRATION"); // calibration END
    delay(1000);
    Serial.println(""); // retour charriot
  // signal the end of the calibration period
  digitalWrite(13, LOW);
  
  
};//end "setup()"

void loop(){
  delay(wDelay);
  analogReference(EXTERNAL);

  // read the sensor:
  for (int i = 0; i < Nsamples; ++i) {
    sensorLvalue += analogRead (pinL);
    sensorPvalue += analogRead (pinP);
    sensorHvalue += analogRead (pinH);
    delay (10);
  }
  sensorLvalue /= Nsamples;
  sensorPvalue /= Nsamples;
  sensorHvalue /= Nsamples;
  
  
  accL = 100 * (sensorLvalue - sensorLave) / sensitivityD; // calcul acceleration laterale
  accP = 100 * (sensorPvalue - sensorPave) / sensitivityD; // calcul acceleration pitch
  accH = 100 * (sensorHvalue - sensorHave) / sensitivityD; // calcul acceleration verticale
 
 angle1dL= 180 * (acos(accL)) / pi * 100000000; //calcul avec la deviation de X uniquement
 angle2dL= 180 * (atan(accL / accH)) / pi * 100000000; //calcul sur 2 axes X et Z
 angle3dL= 180 * (atan(accL / sqrt(accH * accH + accP * accP))) / pi * 100000000; //calcul sur 3 axes
 
 Serial.print("         valeur laterale: "); 
 Serial.print(sensorLvalue);//write sensorValue
 Serial.print("         valeur pitch: ");
 Serial.print(sensorPvalue);//write sensorValue
 Serial.print("         valeur hauteur: ");
 Serial.print(sensorHvalue);//write sensorValue
   Serial.println("");//Send an "x" to turn a digit off
 Serial.print("    acceleration laterale: "); 
 Serial.print(accL);
 Serial.print("    acceleration pitch: "); 
 Serial.print(accP);
 Serial.print("    acceleration verticale: "); 
 Serial.print(accH);
   Serial.println("");//retour charriot
 Serial.print("    angle roulis(calcul 1D): ");
 Serial.println(angle1dL);
 Serial.print("    angle roulis(calcul 2D): ");
 Serial.println(angle2dL);  
 Serial.print("    angle roulis(calcul 3D): ");
 Serial.println(angle3dL);  
  delay(wDelay);
};

still have to work hard :smiley:

  delay(wDelay);
  analogReference(EXTERNAL);

I would swap the order of these.
In fact, I'd move them out of 'loop' altogether.

you are true, that's now 2 lines less! :smiley:

now at this point i have the acceleration correctly, but still with problems to get the right calculated angle.

on Excel (wich help me a lot to find bugs), i have the correct angle, but for the moment i'm still in trouble to calculate it on arduino.
i thinks it come from float() problems....

time will help me! :smiley: :smiley:

still in troubles.....

yesterday, i was getting negative value from the Z axis!!!! normally not possible because it's an average of 10 samples wich value is between 0 adn 1023!!!!!!....

really hard to understand what is going wrong....

i also change the way to define the calibration value: i took the choice to make an average of a number of samples (25),

can someone can plug a 3 axis accelerometer on a arduino board and try my code???

regards

const int pi = 314159265;

ints have a range of -32,768 to 32,767. You're trying to assign a value of 314,159,265 to one. Overflow.

angle1dL= 180 * (acos(accL)) / pi * 100000000; //calcul avec la deviation de X uniquement
angle2dL= 180 * (atan(accL / accH)) / pi * 100000000; //calcul sur 2 axes X et Z
angle3dL= 180 * (atan(accL / sqrt(accH * accH + accP * accP))) / pi * 100000000; //calcul sur 3 axes

In all likelihood, all these lines are overflowing as well, but even if they weren't, you're using an overflowed pi in them.

thanks....

i wil works on this way.....

if you get a look on the story of this thread, i was getting troubles to get a correct angle, so i think you put your finger on the right place!

i still don't know why i get negative value, but i will see i few minuts if it's go better!
thanks for your help!

regards

i still don't know why i get negative value

const int pi = 314159265;
is 0x12B9B0A1, or -20319 when truncated to 0xB0A1.
And 100000000 is 0x5F5E100, or -7936 when truncated.

ok, thanks,

but i get negative value before using pi or 100000.....

but arduino must be logic!!! so i will find my mistake, because it's sure there is one :smiley: :smiley:

regards

YESSSSSSS!!!!

it works much better!!!!

so to explain, what i add in mind, i was trying to calcul everything with integer numbers to keep speed and accuracy at the maximum,

anyway, for the moment, i found a solution, THANKS A LOT AWOL and jraskell :smiley: :smiley:

i was trying to calcul everything with integer numbers to keep speed

But you've got a 500ms delay in there!

yes i know,
but this 500ms are at the end of the loop, so when i'm doing it one more time to get a new mesure.
and to read on the serial monitor. (between 2 samples to get the average value, i need more speed (i know, there is a small ms value also :smiley: :smiley: )

and this is the first part of my project,
for the moment, with the "float" i have more than enough speed, but when i will add more things, i hope it will not be to slow!

thanks again for your big help

regards,

some news of my code.
i still have problems, but it's interesting!
the X ans Y 0g value are good (around 512)
but my 1G value on Z is strangely far from the theory: in theory i must have 512 - 248 (for 800mv/g) = 263

maybe my 3,3V is not so good??

have to work on it....