Double integration - Zero result when summing

I was wondering if anyone can help me. I am using an accelerometer, the ADXL345 to measure displacement using double integration. I took a lot of information for reading the accelerometer data from a very useful tutorial page at http://www.den-uijl.nl/gyro.html so thanks to him. This is my first post but reading this forum is very interesting and helpful. I should also mention that this is an algorithm I'm writing for heave compensation.

As I am integrating , the results will obviously drift. To counteract this, I have set up a regression line fit to zero the results about the time axis. I found a relatively simple calculation for it from here; http://www.revisesmart.co.uk/maths/statistics-2/the-least-squares-regression-line.html. Before, I was using time averaging but this was not satisfactory as it was not responsive enough. After the first integration to get velocity and plotting the new offset velocity (offsetVelZ), the plot looked good. The problem is when I integrate again this value always goes to zero. I am baffled and equally frustrated!! I can't understand why it isn't summing up the values, I have checked and there are individual values there but whenever I ask it to sum them up it is always zero.

Also, this code is working fine when I put through the unfiltered velocity data VelZ_2 into it.

The gyro data is not used in this part of the code so you can ignore that.
Thanks to anyone who can help. Any questions, let me know.
Dan.

#include <Wire.h>

int gyroResult[3], accResult[3];
float biasGyroX, biasGyroY, biasGyroZ, biasAccX, biasAccY, biasAccZ;
float timeStep = 0.02;
float t = 0;
float VelZ_2 = 0;
float e = 0;
int S2 = 100;            //Number of samples taken for the time averaged velocity to find zero mean
float arrayVelZ[100];    //Time averaging array for the y input
float T = 100;
float arrayTime[100];
int S1 = 20;              //Number of samples taken for the time averaged acceleration
float arrayAccZ[20];
unsigned long timer;
float sum1 = 0;
float AccZ_smooth = 0; // averaged accelaration of acc in heave direction
float offsetVelZ = 0;
float sumS2Time;
float sumS2Time_squared;
float S2Time_ave = 0;
float sumArrayVelZ = 0;
float sumXY = 0;
float VelZ_ave = 0;
float distZ;
float slopeVelZ = 0;
float slopeVelZ2 = 0;
float offsetVelZ_regression = 0;
int c, d, f, j;
int m = 1, n = 1, p = 1;
float SampleSize = 0;
float accZ = 0;
float y;

void writeTo(byte device, byte toAddress, byte val) {
  Wire.beginTransmission(device);  
  Wire.write(toAddress);        
  Wire.write(val);        
  Wire.endTransmission();
}
void readFrom(byte device, byte fromAddress, int num, byte result[]) 
{
  Wire.beginTransmission(device);
  Wire.write(fromAddress);
  Wire.endTransmission();
  Wire.requestFrom((int)device, num);
  int i = 0;
  while(Wire.available()) {
    result[i] = Wire.read();
    i++;
  }
}
void getGyroscopeReadings(int gyroResult[]) {
  byte buffer[6];
  readFrom(0x68,0x1D,6,buffer);
  gyroResult[0] = (((int)buffer[0]) << 8 ) | buffer[1]; //Combine two bytes into one int
  gyroResult[1] = (((int)buffer[2]) << 8 ) | buffer[3];
  gyroResult[2] = (((int)buffer[4]) << 8 ) | buffer[5];
} 
void getAccelerometerReadings(int accResult[]) {
  byte buffer[6];
  readFrom(0x53,0x32,6,buffer);
  accResult[0] = (((int)buffer[1]) << 8 ) | buffer[0]; //Note, byte order different from gyros'
  accResult[1] = (((int)buffer[3]) << 8 ) | buffer[2];
  accResult[2] = (((int)buffer[5]) << 8 ) | buffer[4];
}

void setup() {
  int totalGyroXValues = 0;
  int totalGyroYValues = 0;
  int totalGyroZValues = 0;
  int totalAccXValues = 0;
  int totalAccYValues = 0;
  int totalAccZValues = 0;
  int i;

  Wire.begin();            //Open I2C communications as master
  Serial.begin(115200);    //Open serial communications to the PC to see what's happening
  
  writeTo(0x53,0x31,0x01); //Set accelerometer to 10bit, +/-2g  writeTo(0x53,0x31,0x09)Set accelerometer to 11bit, +/-4g 
  writeTo(0x53,0x2D,0x08); //Set accelerometer to measure mode
  writeTo(0x68,0x16,0x1A); //Set gyro to +/-2000deg/sec and 98Hz low pass filter should it be writeTo(0x68,0x16,0x20)?
  writeTo(0x68,0x15,0x09); //Set gyro to 100Hz sample rate
  delay(100); //wait for gyro to "spin" up
  for (i = 0; i < 50; i += 1) {
    getGyroscopeReadings(gyroResult);
    getAccelerometerReadings(accResult);
    totalGyroXValues += gyroResult[0];
    totalGyroYValues += gyroResult[1];
    totalGyroZValues += gyroResult[2];
    totalAccXValues += accResult[0];
    totalAccYValues += accResult[1];
    totalAccZValues += accResult[2];
    delay(50);
    }
  biasGyroX = totalGyroXValues / 50;
  biasGyroY = totalGyroYValues / 50;
  biasGyroZ = totalGyroZValues / 50;
  biasAccX = totalAccXValues / 50;
  biasAccY = totalAccYValues / 50;
  biasAccZ = (totalAccZValues / 50) - 256; //To include gravity in after zeroing
}

void loop() {
  timer = millis();
  getGyroscopeReadings(gyroResult);
  getAccelerometerReadings(accResult);
  accZ = (accResult[2] - biasAccZ - 256) / 256 * 9.81; //  Accelration in m/s^2
  SampleSize += 1;
    
  arrayAccZ[m - 1] = accZ; // Creates an array of the Z axis acceleration for running average smoothing
  m = m + 1;
  if (m == (S1+1)){ // Puts the value of n back to zero when the matrix size + 1 is reached so that the array is continuously filled with new data
    m = 1;
  }
  
  for (j = 0; j < S1; ++ j){  //Sums up all the values of the array
    sum1 += arrayAccZ[j];
  }
  AccZ_smooth = sum1 / SampleSize;
  
  VelZ_2 = VelZ_2 + AccZ_smooth * timeStep;    //Integrate the accelerometer averaged results to give velocity

  if (SampleSize == (S2 + 1)) {
    SampleSize = S2;
  }

  if (SampleSize < S2) {
    arrayVelZ[n - 1] = VelZ_2; // Creates an array of the y values for the zero mean
    arrayTime[n - 1] = t; // Create a time array 1,2,3,..
    n = n + 1;
  }
  else {
    arrayVelZ[S2 - 1] = VelZ_2; // Creates an array of the y values for the zero mean
    arrayTime[S2 - 1] = t;
  }
  
  for (d = 0; d < SampleSize; ++ d){  //Sums up all the values of S2
    sumS2Time += arrayTime[d];
    sumS2Time_squared += pow(arrayTime[d],2);
  }
  S2Time_ave = sumS2Time / SampleSize;

  for (c = 0; c < SampleSize; ++ c){  //Sums up all the values of the array for the least squares regression line
    sumArrayVelZ += arrayVelZ[c];
    sumXY += arrayVelZ[c] * arrayTime[c];
  }
  
  VelZ_ave = sumArrayVelZ / SampleSize;
  
  slopeVelZ = ((sumXY / SampleSize) -  S2Time_ave * VelZ_ave) / ((sumS2Time_squared / SampleSize) - pow(S2Time_ave, 2));
  offsetVelZ_regression = slopeVelZ * t - slopeVelZ * S2Time_ave + VelZ_ave;
  offsetVelZ = VelZ_2 - offsetVelZ_regression;
  
  distZ += offsetVelZ * timeStep;
  
  Serial.print(accZ);
  Serial.print("\t");
  Serial.print(AccZ_smooth);
  Serial.print("\t");
  Serial.print(VelZ_2);
  Serial.print("\t");
  Serial.print(offsetVelZ);
  Serial.print("\t");
  Serial.print(distZ,8);
  Serial.print("\n");
  
  sum1 = 0;
  sumArrayVelZ = 0;
  sumXY = 0;
  sumS2Time = 0;
  sumS2Time_squared = 0;
  t += 1;
  
  if (SampleSize == S2) {
    for (f = 0; f < S2; f++) {
      arrayVelZ[f] = arrayVelZ[f + 1];
      arrayTime[f] = arrayTime[f + 1];
    }
  }

  timer = millis() - timer;            
  timer = (timeStep * 1000) - timer; 
  delay(timer);
}
1 Like
int c, d, f, j;
int m = 1, n = 1, p = 1;
float y;

One letter variables are generally throw away values, use as loop indices, etc. Global variables with one letter names are generally a bad idea.

Some of these clearly need to be local variables.

for (int j = 0; j < S1; ++ j){ //Sums up all the values of the array
is actually, in my opinion, better than
int j;
for (j = 0; j < S1; ++ j){ //Sums up all the values of the array
Either, though, is better than having j be a global variable.

With that out of the way, one would need to be familiar with the process you are using to match the code to the problem description. One the other hand, one need not be familiar with the technique to spot a coding error, if one knows that all the variables contain the expected values here, but here they do not. Something like "At point B, the value in variable X is bad/wrong/not what I expect..." would make helping you much easier.

Since you already know that the hardware is working and that you're getting valid data, this is really a math programming problem rather than an Arduino-specific problem, yes?

Ok, you got your data and you did a linear least squares fit which provided you with the coefficients for y = A*x + B. Knowing that, we don't have to care what your data looked like - but we do need the values of A and B and the minimum and maximum values of x as a starting point.

Thank you for your replies. Thanks for that suggestion Paul; it also reduces the rather large amount of variables I have declared at the top!

Sorry for being vague. My specific problem relates to the integration on line 155; distZ += offsetVelZ * timeStep;

I am getting values for "offsetVelZ * timeStep;" on its own but when I try to sum them up it is always zero. For these values I was previously getting in the order of about 0.05. I have printed out to 8 decimal places to check if the value was very small but no luck, all zeroes. In the line equation y = A * x + B, A is slopeVelZ and B is offsetVelZ_regression.

The maths isn't a problem as I have done this exact same integration but using a different offset on previous versions and gotten results out.

Focusing on that section of code then:

   offsetVelZ_regression = slopeVelZ * t - slopeVelZ * S2Time_ave + VelZ_ave;
   offsetVelZ = VelZ_2 - offsetVelZ_regression;
  
   distZ += offsetVelZ * timeStep;

What is the value of the variable t ?

*** Never mind, I'd missed the subsequent increment.

The original post asserted that the hardware was working properly, and the latest post that the math/software works properly, so there's not much left to debug except the final result. :grin:

Thanks for your help again. That final result debugging is what's bugging me! Basically when I sum up offsetVelZ * timeStep it stays as zero all the time even though it show me that it is getting individual values for it. Logically the programme seems fine so maybe the issue lies somewhere else. Could it be that there is no free memory so it cannot add more results? I'm very stuck, maybe it's time to get back to the drawing board!

Dan_Kyle:
Thanks for your help again. That final result debugging is what's bugging me! Basically when I sum up offsetVelZ * timeStep it stays as zero all the time even though it show me that it is getting individual values for it. Logically the programme seems fine so maybe the issue lies somewhere else.

I'm too lazy to try to figure out how that sketch is intended to work or where the problem is, but from your post it sounds as if you have a calculation which is not producing the values you expect.

In that case I suggest that you find out exactly what values you are putting in to the calculation and what value you are getting out of it. Then create a new sketch which does nothing but execute that same calculation on the same values and report the result. If your assumptions are all valid, it should produce the same result in your test sketch that the full sketch produced. In that case, post the test sketch and tell us what you think the output should have been and what it actually was.

Hi Peter, yes the final integration is the problem. I have re-written the code and the input is now a sin wave so is much simpler. Essentially the thing I am having problems are on lines 35 and 37. The programme prints off the offsetY value (in the diagram offsetY is number 2, red and the integration is line 3, the green one) correctly but it won't sum them up on line 37.

I have attached a diagram of the results plotted in Excel. The original sin wave, Yvalue is line 1; the corrected value about zero offsetY, line 2; and then yIntegrated line 3.

Hope this code makes it all clearer. I couldn't make it any simpler without it not working!

Thanks again, Dan.

unsigned long timer;
float timeStep = 0.02;
float time = 0;
float slope = 0.01;
float yValue = 0;
int arraySize = 20;
float arrayY[20];
int arrayTime[20];
int n = 1;
float sumTime = 0;
float sumTimeSquared = 0;
float sumArrayY = 0;
float sumTY = 0;
float timeAve = 0;
float slopeY = 0;
float yAve = 0;
float regressionOffset_calc = 0;
float regressionOffset = 0;
float offsetY;
float yIntegrated = 0;


void setup() {
  Serial.begin(115200);

}

void loop() {
  
  timer = millis();
  
  yValue = sin(PI * time / 90 * 4) + slope * time;
  
  regressionOffset = calculateRegression();
  offsetY = yValue - regressionOffset; //inputs ( t, a, y = time, ArraySize, yValue) do we need array size? (already declared in initial variables.
  
  yIntegrated += offsetY * timeStep;
  
  Serial.print(time);
  Serial.print("\t");
  Serial.print(yValue);
  Serial.print("\t");
  Serial.print(offsetY);
  Serial.print("\t");
  Serial.print(yIntegrated,4);
  Serial.print("\n");

  time +=1;
  
  timer = millis() - timer;
  timer = (timeStep * 1000) - timer;
  delay (timer);
}


float calculateRegression() {

  sumArrayY = 0;
  sumTY = 0;
  sumTime = 0;
  sumTimeSquared = 0;
 
  arrayY[n - 1] = yValue;
  arrayTime[n - 1] = time;
  
  n += 1;
  if (n == arraySize) {
    n = 1;
  }
    
  for (int i = 0; i < arraySize; ++i) {
   sumTime += arrayTime[i];
   sumTimeSquared += pow(arrayTime[i],2);
   sumArrayY += arrayY[i];
   sumTY += arrayY[i] * arrayTime[i];
  }
  
  timeAve = sumTime / arraySize;
  yAve = sumArrayY / arraySize;
  slopeY = ((sumTY / arraySize) - timeAve * yAve) / ((sumTimeSquared / arraySize) - pow(timeAve, 2));
  regressionOffset_calc = slopeY * time - slopeY * timeAve +  yAve;
 
  return regressionOffset_calc;
 
}

Not bad! :grin:

Integration of a function, in simple terms, amounts to finding the area between the curve and the axis of integration, where the area(s) above the axis are positive and the area(s) below the axis are negative...

One of the reasons I asked the questions I did was because there was a possibility that your computed result might just be correct.

Hi Morris, I finally figured out what was happening. The time was always starting at zero and in the equation the denominator was time so it was always tending to infinity, hence no results. I should have spotted it before ,doh!

Thanks all for your help,
Dan.

Hey Dan,
Can I know the final debugged code for my understanding...!!! Actually I am also looking for a code for displacement measurement from accelerometer data in my project. If you able to share the code with me, It will be helpful to me... Thanks in advance buddy