ADXL345 information/help/problem/guidance etc :)

Hello.

I’m trying to create an accelerometer logger for my R/C bikes (and cars, why not?).

The project will consists of an Arduino Nano, an ADXL345, a RTC, a GPS and a SD card module.
It will log the acceleration (in Gs) on X/Y/Z axis and the angle (in degrees) on X & Y axis (to see how much it leans at the corners, what was the maximum lean before crash etc). Also it will log the long/latt data from GPS and will be saved on SD card as KML (for Google Earth review). Power will be supplied either from the ESC’s BEC or from a channel of the RX (with a 7805 voltage regulator and a diode for protection).

So far it looks that works OK, but there are two problems related with the accelerometer.

First problem is that when I’m trying to move it left/right/front/back on a table (keeping the Z axis steady), the acceleration changes, but it changes the angle too. Is it normal?

Second problem is that I don’t know too much about accelerometers, and I’m not sure if it counts the G forces OK. It looks good, but when I’m accelerating for example, it changes the angle a lot, and I’m not leaning.

Here is my code so far. If I have any errors in calculations (I’m totally newbie to accelerometers), please help! Oh, and please contribute if you like :slight_smile:

#include <Time.h>  
#include "Wire.h"
#include "ADXL345.h"
#include <DS1307RTC.h>
#include <EEPROM.h>


#define GreenLed    13
#define RedLed      12
#define ButtonWrite 11

int x,y,z;
int xx, yy;
int defaultXX, defaultYY, defaultZZ;
double xyz[3];
double calibratedAccelXYZ[3];
double currentAccelX, currentAccelY, currentaccelz;
double differencexx, differenceyy, differencezz;
  
int CurrentFront = 0;
int CurrentBack = 0;
int CurrentLeft = 0;
int CurrentRight = 0;

double maxX = 0;
double maxY = 0;
double maxZ = 0;
int maxAngleLeft = 0;
int maxAngleRight = 0;
int maxAngleFront = 0;
int maxAngleBack = 0;

ADXL345 accel;

#define debug 1

void setup(void)
{
  pinMode(RedLed, OUTPUT);
  pinMode(GreenLed, OUTPUT);
  // Turn on the red led to define that calibration
  // process will be started
  pinMode(ButtonWrite, INPUT);
  digitalWrite(GreenLed, HIGH);
  digitalWrite(RedLed, HIGH);
  setSyncProvider(RTC.get);        // Get the time from RTC
  delay(100);
  if (debug==1)
  {
    Serial.begin(9600);
    Serial.println("Begin");
  }
  accel.powerOn();                 // Power On the Accelerator
  
  accel.setRangeSetting(8);        // Set maximum range to 8G
 
  digitalWrite(GreenLed, LOW);
  digitalWrite(RedLed, LOW);
  delay(100);
  digitalWrite(RedLed, HIGH);
  delay(100);
  digitalWrite(RedLed, LOW);
  delay(100);
  digitalWrite(RedLed, HIGH);
  delay(100);
  digitalWrite(RedLed, LOW);
  delay(100);
  digitalWrite(RedLed, HIGH);
  delay(100);
  
  // Read acceleration data and store them as default/inactive values
  accel.readAccel(&x, &y, &z);
  defaultXX = (atan2(y,z)+3.14)*RAD_TO_DEG;
  defaultYY = (atan2(x,z)+3.14)*RAD_TO_DEG;
  accel.get_Gxyz(xyz);
  calibratedAccelXYZ[0] = xyz[0];
  calibratedAccelXYZ[1] = xyz[1];
  calibratedAccelXYZ[2] = xyz[2];
  
  // Turn on the green led - ready to start
  delay(250);
  digitalWrite(RedLed, LOW);
  digitalWrite(GreenLed, HIGH);

  // Display previous records from EEPROM
  ReadFromEEPROM();
  
  // zero the variables to store the new maximum values
  maxX = 0;
  maxY = 0;
  maxZ = 0;
  maxAngleLeft = 0;
  maxAngleRight = 0;
  maxAngleFront = 0;
  maxAngleBack = 0;
  if (debug==1)  
    Serial.println("Ready!");
}




void loop()
{
  if (digitalRead(ButtonWrite) == HIGH)
    WriteToEEPROM();
  
  if (debug==1)  
  {
    Serial.print(TimeDate());
    Serial.print(" | ");
  }
  
  accel.readAccel(&x, &y, &z);
  xx = (atan2(y,z)+3.14)*RAD_TO_DEG;
  yy = (atan2(x,z)+3.14)*RAD_TO_DEG;
   
  accel.get_Gxyz(xyz);
  currentAccelX=abs(calibratedAccelXYZ[0]-xyz[0]);
  currentAccelY=abs(calibratedAccelXYZ[1]-xyz[1]);
  currentaccelz=abs(calibratedAccelXYZ[2]-xyz[2]); 
  
  if (currentAccelX>maxX)
    maxX = currentAccelX;
  if (currentAccelY>maxY)
    maxY = currentAccelY;
  if (currentaccelz>maxZ)
    maxZ = currentaccelz;
  
  if (debug==1)
  {
    Serial.print("Acceleration X: ");
    Serial.print(currentAccelX, 1);
    Serial.print(" - Y: ");
    Serial.print(currentAccelY, 1);
    Serial.print(" - Z: ");
    Serial.print(currentaccelz, 1);
    Serial.print(" | ");
  }

  
  differencexx = defaultXX-xx;
  if (differencexx < -180)
    differencexx += 180;
  if (differencexx > 180)
    differencexx -= 180;

  if (differencexx<0)
    CurrentLeft=abs(differencexx);
  else
    CurrentRight=differencexx;

  
  differenceyy = defaultYY-yy;
  if (differenceyy < -180)
    differenceyy += 180;
  if (differenceyy > 180)
    differenceyy -= 180;
    
  if (differenceyy<0)
    CurrentFront=abs(differenceyy);
  else
    CurrentBack=differenceyy;
    
  if (CurrentFront>maxAngleFront)
    maxAngleFront=CurrentFront;
  if (CurrentBack>maxAngleBack)
    maxAngleBack=CurrentBack;
  if (CurrentLeft>maxAngleLeft)
    maxAngleLeft=CurrentLeft;
  if (CurrentRight>maxAngleRight)
    maxAngleRight=CurrentRight;

  if (debug==1)
  {
    Serial.print("Front: ");
    Serial.print(CurrentFront);
    Serial.print(" - Back: ");
    Serial.print(CurrentBack);
    Serial.print(" - Left: ");
    Serial.print(CurrentLeft);
    Serial.print(" - Right: ");
    Serial.print(CurrentRight);
    Serial.println("");
    delay(50);
  }
}


String TimeDate()
{
  String datetime;
  
  if (hour()<10)
    datetime=String("0");
  else
    datetime=String(hour());
  if (minute()<10)
    datetime += String(":0");
  datetime += String(":")+String(minute())+String(":");
  if (second()<10)
    datetime += String("0");
  datetime += String(second())+String(" / ");
  if (day()<10)
    datetime += String("0");
  datetime += String(day())+String("/");
  if (month()<10)
    datetime += String("0");
  datetime += String(month())+String("/")+String(year());
  
  return datetime;
}


void WriteToEEPROM()
{
  digitalWrite(RedLed, HIGH);
  digitalWrite(GreenLed, LOW);
  Serial.println("");
  Serial.println("Write to EEPROM");
  int foo;
  foo = maxX*100;
  EEPROM.write(0,foo);
  foo = maxY*100;
  EEPROM.write(1,foo);
  foo = maxZ*100;
  EEPROM.write(2,foo);
  EEPROM.write(3,maxAngleFront);
  EEPROM.write(4,maxAngleBack);
  EEPROM.write(5,maxAngleLeft);
  EEPROM.write(6,maxAngleRight);
  delay(500);
  digitalWrite(RedLed, LOW);
  digitalWrite(GreenLed, HIGH);
  Serial.println("Done!");
}

void ReadFromEEPROM()
{
  Serial.println("");
  Serial.println("");  
  Serial.println("Reading EEPROM");  
  maxX = EEPROM.read(0);
  maxY = EEPROM.read(1);
  maxZ = EEPROM.read(2);
  maxAngleFront = EEPROM.read(3);
  maxAngleBack = EEPROM.read(4);
  maxAngleLeft = EEPROM.read(5);
  maxAngleRight = EEPROM.read(6);
  Serial.println("");
  double foo = 0;
  Serial.print("Max X Acceleration: ");
  foo = maxX/100;  
  Serial.println(foo, 2);
  Serial.print("Max Y Acceleration: ");
  foo = maxY/100;  
  Serial.println(foo, 2);
  Serial.print("Max Z Acceleration: ");  
  foo = maxZ/100;  
  Serial.println(foo, 2);
  Serial.println("");
  Serial.print("Max Angle Front: ");
  Serial.println(maxAngleFront);
  Serial.print("Max Angle Back: ");
  Serial.println(maxAngleBack);  
  Serial.print("Max Angle Left: ");
  Serial.println(maxAngleLeft);  
  Serial.print("Max Angle Right: ");
  Serial.println(maxAngleRight);  
  Serial.println("");
  Serial.println("");
  delay(2000);
}

PS 1: I haven’t receive the GPS & the SD card module yet, so it only displays the data on screen (and saves the max values @ EEPROM when button is pressed).

PS 2: I want to log the crashes (don’t forget it’s a RC Bike, it has lot of crashes). I guess that’s possible with the tap detection. Any ideas how to do that?

Thank you,

Antonis.

you are not considering the fast that the acceleration and the gravity vector sum together. That's why when you move your vector grow so much: it's the acceleration!

if you will let the module fall on the Z axis, you will read exactly 0 on all axes (well 0 in theory :grin:)

don't bother with tap detection, you are more flexible if you just log all the data on SD and post-process it with your computer; because GPS has a precision of +-5 meters (due to some physic and software limitation), using accelerometer data you can estimate your position. But you'll need a gyroscope. take a look at quadcopeter's IMU/MARG code to understand the math behind this. Your system is easier only if you remove some "freedom", like no lateral or backward movement, no pitch change, etc. but this will be missing data in post processing because when you crash (or jump) you will probably break this rule.

sorry if i was confusing, but it is not easy as it seems, but using some "pre-coocked" math and patience, it is possible for everyone.

I did this with my cars some time back, as Lesto says you are much better of capturing the raw data and doing you post processing in excel.

You can find some of my excel charts here, I was mostly interested in the traction circle -

Ultimatley I found that I could not interpret the data in a way that I could translate to faster lap timers so I build a lap timer instead - you can quickly gain a few seconds around the track with the instant feedback a lap timer provides.

Anyway, its all in the link

Duane B

rcarduino.blogspot.com

It will log the acceleration (in Gs) on X/Y/Z axis and the angle (in degrees) on X & Y axis

They are one and the same. No point in storing the angles.

Is it normal?

Yes. Your acceleration on X/Y is being combined with the gravity (-1g on Z).

dhenry:

It will log the acceleration (in Gs) on X/Y/Z axis and the angle (in degrees) on X & Y axis

They are one and the same. No point in storing the angles.

Is it normal?

Yes. Your acceleration on X/Y is being combined with the gravity (-1g on Z).

Now I get it, thanks.

So the problem now is how to seperate the leaning (on one axis, left/right) of a bike from total acceleration? With a second accelerator or something else?

DuaneB:
Ultimatley I found that I could not interpret the data in a way that I could translate to faster lap timers so I build a lap timer instead - you can quickly gain a few seconds around the track with the instant feedback a lap timer provides.

Anyway, its all in the link

Duane B

rcarduino.blogspot.com

I have already found your site some days ago. I don't want to get lap times right now. What I need is to find out how much acceleration in G forces can do (when accelerating, on brakes, on turns) and how much the rc bike can lean (in degrees) before it starts to slide/crash/etc :slight_smile:

BUT I have already bookmark your site, as a lap timer is not a bad idea, at least for the local parking track we race with some friends :slight_smile:

you know that gravity is a fixed lenght vector, but you still need to know your orientation to the floor to subtract the gravity vector and get your real acceleration vector. (well vice-versa in your case)

without a gyro you can only know your inclination on lateral axes, because (hopefully) they are not influenced by acceleration

lesto:
without a gyro you can only know your inclination on lateral axes, because (hopefully) they are not influenced by acceleration

Thats the axis I want to know for bike leaning. Just left & right degrees.
And the Gs on acceleration, brakes, left leaning, right leaning. So I can see what forces and what angle the bike has..

You cannot measure bike leaning with only an accelerometer. The vector will always be straight down with respect to the bikes axis. A cheap gyro cannot do much better in the real world on a full size bike. Because of all the vibration. Using GPS only you can measure G-forces because you know the radius and speed. The code for this is complex. Questions?

sbright33:
You cannot measure bike leaning with only an accelerometer. The vector will always be straight down with respect to the bikes axis. A cheap gyro cannot do much better in the real world on a full size bike. Because of all the vibration. Using GPS only you can measure G-forces because you know the radius and speed. The code for this is complex. Questions?

I get it :slight_smile:
So I have to keep the accelerometer for the G forces, and a 3ple axis gyro for the leaning...
The vibrations are not too much (I guess). Running on a track, with a "street" bike...

Thanks.

Let me try again. You only need GPS to measure G-forces. Accel, braking, left, right. 5-10hz sampling would be best. For a motorcycle there is no left or right G-forces. Only down with respect to the bikes axis.

You cannot measure leaning directly with any cheap hardware. Perhaps you could calculate it from GPS data. Many people have spent years on this problem. There is too much vibration to use a cheap Gyro over any length of time. If there are many straight sections you could assume it is upright between every turn. Even that is difficult without spending a lot of money.

OK, now I get it.
So, no low-cost gyro can provide what I need.
So, bye bye angle-meter :slight_smile:

gps have 2 meter radius precision AND normally 1 or 5 Hz output... i don't think it is enough.

lesto:
gps have 2 meter radius precision AND normally 1 or 5 Hz output… i don’t think it is enough.

Low-cost GPS have up to 10Hz sampling rate. The precision is about 2 meters (WAAS/EGNOS-enabled), but -if I’m not wrong- that’s not affecting the results. If it’s 2m, it will be also 2m here (sample #1) and 2m there (sample #2). So the results will be -almost- the same…

no way, take a look at sparkfun GPS test (can't find it, but here you can see and image of result GPS Tracking Tutorial - News - SparkFun Electronics), and you will see how things around you will ruin signal.
Also pay attention to REAL output and interpolated output. i'm not expert in this, but many times i've seen this alert :wink:

@vegos- That's right! You can measure the radius of a curve very accurately. The error from 1 second to the next is minimal. It's an absolute error, not relative. Speed is also very accurate. These 2 give you G-force.

Question:

To measure G forces, I'm using that code:

#include "ADXL345.h"
ADXL345 accel;

...

  accel.powerOn();
  accel.setFullResBit(1);
  accel.setRangeSetting(8);
  accel.get_Gxyz(xyz);

Is it correct? When I'm changing from 8 to 2 or to 4 for example, I get different readings. I just want the upper limit to be more than 4 Gs.

with setRangeSetting you are setting the upper (and lowest) limit. because the value are 10bit (form 0 to 1024), changing the limit also change the precision of the reading…

with limit of 2, a read of 512 (zero)+(512/2=256) is 1G. BUT at limit 8, 1G it is 512+(512/8=64)

(value are just example, they can be wrong. for example, al 16G the sensor can use 16bit precision.

lesto:
with setRangeSetting you are setting the upper (and lowest) limit. because the value are 10bit (form 0 to 1024), changing the limit also change the precision of the reading…

with limit of 2, a read of 512 (zero)+(512/2=256) is 1G. BUT at limit 8, 1G it is 512+(512/8=64)

(value are just example, they can be wrong. for example, al 16G the sensor can use 16bit precision.

That means after reading the values (I’m using this library) I have to convert them?

Here is reading procedure of the library:

void ADXL345::get_Gxyz(double *xyz){
  int i;
  int xyz_int[3];
  readAccel(xyz_int);
  for(i=0; i<3; i++){
    xyz[i] = xyz_int[i] * gains[i];
  }
}

It looks like it multiplies the result with gains (for the 3 axis). I need to do further calculations, like the ones you suggest?

yes, but technically the library is wrong.
every time you change the range, the array gains should change accordingly..

but it is not implemented, so you can make the gain calculation by yourself and use the setGain function.

(opened an issue about this :slight_smile: )

How sensitive is this sensor in recording tilt.
I've had a look at the data sheets, and it just says less than 1 degree.
Does anyone know if there's anything more specific than this. Like 0.5 degrees.
Or would 1 degree be enough to go with.
The reason I'm asking is because I need to choose a motor for a self leveling platform, and the sensitivity of the sensor will dictate the motor I choose.

Thanks :slight_smile: