Monitoring Motion with Accelerometer

Hi,

I am working on a project using an Arduino Duemilanove and a MMA7260QT 3 axis accelerometer, the project goal is to be able to monitor the motion of a person while they are laying down, for example the sensor could be positioned on the persons chest and I would like to be able to record the amount of motion in all axes. The ideal outcome of this project would be while the person was breathing it would be monitoring the motion in the z-axis and if they shifted their position it would record the data from the x and z axes as well.

Below is currently all my code for this project. To convert the acceleration to positon I have to integrate the acceleration twice, but how exactly would I do this? A little while ago I found a PDF that was similar to my project and it explained how they were getting position from the acceleration, it was kind of confusing because their code was pretty weird. This is what I got out of it:

//============================================================================
// analyzeAccelerometer - performs double integration on x, y, and z axes
//============================================================================

void analyzeAccelerometer()
{
  // acceleration[i][j]
  
  // acceleration[0][j] is x
  // acceleration[1][j] is y
  // acceleration[2][j] is z
  
  // let j = 0 be initial/previous, j = 1 be current
  
  // outer loop that handles: x, y, z;
  
  for (int j = 0; j < 3; j++)
  {
    // get the velocity
    velocity[j][1] = velocity[j][0] + acceleration[j][0] + ((acceleration[j][1] - acceleration[j][0]) / 2);
    
    // get the distance
    position[j][1] = position[j][0] + velocity[j][0] + ((velocity[j][1] - velocity[j][0]) / 2);
    
    // get ready for the next set of readings
    acceleration[j][0] = acceleration[j][1];
    velocity[j][0] = velocity[j][1];
    position[j][0] = position[j][1];
  }  
}

Is this correct? Right now if I run the program and just have the accelerometer sitting still, because of error the position just adds up, would I be able to compensate for that with having a logic statement where it would check the accelerometer readings and ignore minute changes therefore hopefully making cleaner data?

I was talking to someone else about it and they suggested using these two equations somehow:

x=v0 * t + 1/2 at**2

v=v0+at

Would this be better or more helpful?

Currently, I just have an accelerometer (MMA7260QT) and a gyro but I am only using the accelerometer at the moment, should I purchase a IMU would that make this process easier?

Here is my full code:

//============================================================================
// MMA7260QT Accelerometer
//============================================================================

#define ACCEL_X A2
#define ACCEL_Y A1
#define ACCEL_Z A0

double accelerometerOffsetX = 0.0;
double accelerometerOffsetY = 0.0;
double accelerometerOffsetZ = 0.0;

double acceleration[3][2] = {{0,0}, {0,0}, {0,0}};
double velocity[3][2] = {{0,0}, {0,0}, {0,0}};
double position[3][2] = {{0,0}, {0,0}, {0,0}};

//============================================================================
// setup - Arduino Setup Funciton
//============================================================================

void setup()
{
  Serial.begin(9600);
  initAccelerometer(5, 1);
}

//============================================================================
// loop
//============================================================================

void loop()
{
  acceleration[0][1] =    readAccelerometer(ACCEL_X) - accelerometerOffsetX;         // x-axis
  acceleration[1][1] =    readAccelerometer(ACCEL_Y) - accelerometerOffsetY;         // y-axis
  acceleration[2][1] = -( readAccelerometer(ACCEL_Z) - accelerometerOffsetZ) + 1.0;  // z-axis inverse, 1 g for earth gravity.

  analyzeAccelerometer();

//  Serial.print("X: "); 
//  Serial.print(acceleration[0][1]);
//
//  Serial.print(" Y: "); 
//  Serial.print(acceleration[1][1]);
//
//  Serial.print(" Z: "); 
//  Serial.print(acceleration[2][1]);

  Serial.print(position[0][1]);
  
  Serial.println();

  // delay 50 milliseconds
  delay(50);
}

//============================================================================
// initAccelerometer - Initializes everything for the accelerometer
//============================================================================

void initAccelerometer(int sleepPin, int sleepValue)
{

  // Set the sleepPin to output
  pinMode(sleepPin, OUTPUT);

  // This turns the sensor on or off
  // sleepValue = 1 the sensor is OFF
  // sleepValue = 0 the sensor is ON
  analogWrite(sleepPin, sleepValue);

  // delay 1 second to get the sensor in position
  delay(1000);

  calibrateAccelerometer();
}

//============================================================================
// calibrateAccelerometer - Handles all the calibration needed
//============================================================================

void calibrateAccelerometer()
{  
  
  double x_raw = 0;
  double y_raw = 0;
  double z_raw = 0;
  
  Serial.println("Calibrating...\n");

  // To calibrate we are going to take 1024 readings from the accelerometer
  // and average them to calculate the offset

  for (int i = 0; i < 1024; i++)
  {
    x_raw += analogRead(ACCEL_X);
    y_raw += analogRead(ACCEL_Y);
    z_raw += analogRead(ACCEL_Z);
  }
  
  x_raw = x_raw / 1024;
  y_raw = y_raw / 1024;
  z_raw = z_raw / 1024;
  
  // analogRead can be used without setting the pinMode.
  // 5.0 : The range of the ADC: 3.3V or 5V
  // 1023.0 :  1023 = 2^10 -1, the maximum raw value at the maximum voltage for a range of 0 - 1023
  // 0.8 : the number of mV/g. It is 800mv/G for 1.5g range.
  // 1.08 : the gain to compensate for a little less than 10% error

  x_raw = (double) x_raw * 5.0 / 1023.0 / 0.8;
  accelerometerOffsetX = x_raw * 1.08;
  

  y_raw = (double) y_raw * 5.0 / 1023.0 / 0.8;  
  accelerometerOffsetY = y_raw * 1.08;
  
  z_raw = (double) z_raw * 5.0 / 1023.0 / 0.8;  
  accelerometerOffsetZ = z_raw * 1.08;

}

//============================================================================
// readAccelerometer - reads the current output from the accelerometer
//============================================================================

double readAccelerometer(int thePin)
{

  // what we are returning
  double g;
  double data = 0;
  
  // To remove noise from accelerometer readings we are going to
  // sample the sensor 64 times and average the results.
  
  for (int i = 0; i < 64; i++)
  {
    data += analogRead(thePin);
  }

  // average it
  // since we took 64 readings, divide by 64;
  
  data /= 64;
  
  // analogRead can be used without setting the pinMode.
  // 5.0 : The range of the ADC: 3.3V or 5V
  // 1023.0 :  1023 = 2^10 -1, the maximum raw value at the maximum voltage for a range of 0 - 1023
  // 0.8 : the number of mV/g. It is 800mv/G for 1.5g range.
  // 1.08 : the gain to compensate for a little less than 10% error

  g = (double) data * 5.0 / 1023.0 / 0.8;
  g *= 1.08; 

  return (g);
}

//============================================================================
// analyzeAccelerometer - performs double integration on x, y, and z axes
//============================================================================

void analyzeAccelerometer()
{
  // acceleration[i][j]
  
  // acceleration[0][j] is x
  // acceleration[1][j] is y
  // acceleration[2][j] is z
  
  // let j = 0 be initial/previous, j = 1 be current
  
  // outer loop that handles: x, y, z;
  
  for (int j = 0; j < 3; j++)
  {
    // get the velocity
    velocity[j][1] = velocity[j][0] + acceleration[j][0] + ((acceleration[j][1] - acceleration[j][0]) / 2);
    
    // get the distance
    position[j][1] = position[j][0] + velocity[j][0] + ((velocity[j][1] - velocity[j][0]) / 2);
    
    // get ready for the next set of readings
    acceleration[j][0] = acceleration[j][1];
    velocity[j][0] = velocity[j][1];
    position[j][0] = position[j][1];
  }  
   
  
}

If someone could try to point me in the right direction that would be great. Thanks!
-Will

Integrating twice leads to a signal with extreme amounts of drift - you will need to high-pass filter the result to lose the drift and retain the short-time-scale information. From an accelerometer thats not moving fast you can derive orientation vector too (low pass filtering helps with this). This can then provide an estimate of the gravity component to subtract from the input to the integration steps.

Mark,

Thank you, I will look into them. Is it possible to use just an accelerometer for this project or will I need a gyro (I guess I would just upgrade to an IMU?)

Thanks,
-Will

Any improvement so far?