Integrating acceleration to get velocity

Hi,
I trying trying to calculate the speed of a model rocket using a MPU6050 sensor. After looking through forum archives I wrote some equations in Arduino that I thought would get velocity. But after testing it in a car the measurements don't go over 1 meter a second while the car is going down the 110 in "non traffic conditions". Does anyone know why my velocity measurements are so off, any help would be appreciated.
Code

// Basic demo for accelerometer readings from Adafruit MPU6050

#include <Adafruit_MPU6050.h>
#include <Adafruit_Sensor.h>
#include <Wire.h>
float Time_accellastz = 0;
float Time_accellasty = 0;
float Time_accellastx = 0;
float v_oldz = 0;
float v_oldy = 0;
float v_oldx = 0;
float offset = 0;
//File mysensordata;

Adafruit_MPU6050 mpu;

void setup(void) {
  Serial.begin(115200);
  while (!Serial)
    delay(10); // will pause Zero, Leonardo, etc until serial console opens

  Serial.println("Adafruit MPU6050 test!");

  // Try to initialize!
  if (!mpu.begin()) {
    Serial.println("Failed to find MPU6050 chip");
    while (1) {
      delay(10);
    }
  }
  Serial.println("MPU6050 Found!");

  mpu.setAccelerometerRange(MPU6050_RANGE_8_G);
  Serial.print("Accelerometer range set to: ");
  switch (mpu.getAccelerometerRange()) {
  case MPU6050_RANGE_2_G:
    Serial.println("+-2G");
    break;
  case MPU6050_RANGE_4_G:
    Serial.println("+-4G");
    break;
  case MPU6050_RANGE_8_G:
    Serial.println("+-8G");
    break;
  case MPU6050_RANGE_16_G:
    Serial.println("+-16G");
    break;
  }
  mpu.setGyroRange(MPU6050_RANGE_500_DEG);
  Serial.print("Gyro range set to: ");
  switch (mpu.getGyroRange()) {
  case MPU6050_RANGE_250_DEG:
    Serial.println("+- 250 deg/s");
    break;
  case MPU6050_RANGE_500_DEG:
    Serial.println("+- 500 deg/s");
    break;
  case MPU6050_RANGE_1000_DEG:
    Serial.println("+- 1000 deg/s");
    break;
  case MPU6050_RANGE_2000_DEG:
    Serial.println("+- 2000 deg/s");
    break;
  }

  mpu.setFilterBandwidth(MPU6050_BAND_21_HZ);
  Serial.print("Filter bandwidth set to: ");
  switch (mpu.getFilterBandwidth()) {
  case MPU6050_BAND_260_HZ:
    Serial.println("260 Hz");
    break;
  case MPU6050_BAND_184_HZ:
    Serial.println("184 Hz");
    break;
  case MPU6050_BAND_94_HZ:
    Serial.println("94 Hz");
    break;
  case MPU6050_BAND_44_HZ:
    Serial.println("44 Hz");
    break;
  case MPU6050_BAND_21_HZ:
    Serial.println("21 Hz");
    break;
  case MPU6050_BAND_10_HZ:
    Serial.println("10 Hz");
    break;
  case MPU6050_BAND_5_HZ:
    Serial.println("5 Hz");
    break;
  }

  Serial.println("");
  delay(100);
}

void loop() {
//mysensordata=SD.open("ttT.txt" , FILE_WRITE);


  /* Get new sensor events with the readings */
  sensors_event_t a, g, temp;
  mpu.getEvent(&a, &g, &temp);



float Dvz =( a.acceleration.z) * ( 0.1);
float Velocityz = v_oldz + a.acceleration.z - 10;
float V_oldz = a.acceleration.z - 10;


float Dvz =( a.acceleration.y) * ( 0.1);
float Velocityy = v_oldy + Dvy;
float V_oldy = Dvy;


  float Dvz =( a.acceleration.x) * ( 0.1);
float Velocityx = v_oldx + Dvx;
float V_oldx  = Dvx;                                            



  /* Print out the values */
  Serial.print("Acceleration X: ");
  Serial.print(a.acceleration.x);
  Serial.print(", Y: ");
  Serial.print(a.acceleration.y);
  Serial.print(", Z: ");
  Serial.print(a.acceleration.z);
  Serial.print(" m/s^2");
  Serial.print("Velocityx is");
  Serial.print(Velocityx);
  Serial.print ("velocityy is ");
  Serial.print(Velocityy);
 Serial.print ("velocityz is");
  Serial.print(Velocityz - 1.9);

  Serial.print("Rotation X: ");
  Serial.print(g.gyro.x);
  Serial.print(", Y: ");
  Serial.print(g.gyro.y);
  Serial.print(", Z: ");
  Serial.print(g.gyro.z);
  Serial.print(" rad/s");

  Serial.print("Temperature: ");
  Serial.print(temp.temperature);
  Serial.print(" degC");

  Serial.println("");
  delay(100);
}

Not sure I understand your code.

Integrating acceleration needs to have some element of time include ( delta speed = acceleration * delta time) but I see an arbitrary 0.1 in the code, not a real elapsed time (mills() - previousTime).

The basic idea, that you can integrate the acceleration over time to get velocity, is correct.

But it doesn't work with consumer grade accelerometers for several reasons. Here is a reasonable explanation of some of the more important problems: https://web.archive.org/web/20201022151347/chrobotics.com/library

Scroll down to "using accelerometers to estimate position and velocity".

In particular, you have probably noticed that the Z velocity is going crazy, because this attempt to subtract the acceleration due to gravity is wrong:

float V_oldz = a.acceleration.z - 10;

Thanks for the replies.
I rewrote my code using the millis function to tell time. However when I upload it the velocity seems really high. Just by moving the sensor by hand it reads out up to 20 meters a second which seems off. does anyone have any suggestions on how I can get relatively accurate measurements?
Thanks
Code

// Basic demo for accelerometer readings from Adafruit MPU6050

#include <Adafruit_MPU6050.h>
#include <Adafruit_Sensor.h>
#include <Wire.h>
float Time = 0;
float previous_time = 0;
float previous_velocity = 0;

Adafruit_MPU6050 mpu;

void setup(void) {
  Serial.begin(115200);
  while (!Serial)
    delay(10); // will pause Zero, Leonardo, etc until serial console opens

  Serial.println("Adafruit MPU6050 test!");

  // Try to initialize!
  if (!mpu.begin()) {
    Serial.println("Failed to find MPU6050 chip");
    while (1) {
      delay(10);
    }
  }
  Serial.println("MPU6050 Found!");

  mpu.setAccelerometerRange(MPU6050_RANGE_8_G);
  Serial.print("Accelerometer range set to: ");
  switch (mpu.getAccelerometerRange()) {
  case MPU6050_RANGE_2_G:
    Serial.println("+-2G");
    break;
  case MPU6050_RANGE_4_G:
    Serial.println("+-4G");
    break;
  case MPU6050_RANGE_8_G:
    Serial.println("+-8G");
    break;
  case MPU6050_RANGE_16_G:
    Serial.println("+-16G");
    break;
  }
  mpu.setGyroRange(MPU6050_RANGE_500_DEG);
  Serial.print("Gyro range set to: ");
  switch (mpu.getGyroRange()) {
  case MPU6050_RANGE_250_DEG:
    Serial.println("+- 250 deg/s");
    break;
  case MPU6050_RANGE_500_DEG:
    Serial.println("+- 500 deg/s");
    break;
  case MPU6050_RANGE_1000_DEG:
    Serial.println("+- 1000 deg/s");
    break;
  case MPU6050_RANGE_2000_DEG:
    Serial.println("+- 2000 deg/s");
    break;
  }

  mpu.setFilterBandwidth(MPU6050_BAND_21_HZ);
  Serial.print("Filter bandwidth set to: ");
  switch (mpu.getFilterBandwidth()) {
  case MPU6050_BAND_260_HZ:
    Serial.println("260 Hz");
    break;
  case MPU6050_BAND_184_HZ:
    Serial.println("184 Hz");
    break;
  case MPU6050_BAND_94_HZ:
    Serial.println("94 Hz");
    break;
  case MPU6050_BAND_44_HZ:
    Serial.println("44 Hz");
    break;
  case MPU6050_BAND_21_HZ:
    Serial.println("21 Hz");
    break;
  case MPU6050_BAND_10_HZ:
    Serial.println("10 Hz");
    break;
  case MPU6050_BAND_5_HZ:
    Serial.println("5 Hz");
    break;
  }

  Serial.println("");
  delay(100);
}

void loop() {
Time = millis();


  sensors_event_t a, g, temp;
  mpu.getEvent(&a, &g, &temp);

  float velocity_change = (Time/1000 - previous_time/1000) * (a.acceleration.x);
float velocity = (velocity_change) + previous_velocity;
Serial.print(Time/1000 - previous_time/1000);
  

Serial.print("velocity is");
  Serial.println (velocity);

  /* Print out the values */
  Serial.print("Acceleration X: ");
  Serial.print(a.acceleration.x);
  Serial.print(", Y: ");
  Serial.print(a.acceleration.y);
  Serial.print(", Z: ");
  Serial.print(a.acceleration.z);
  Serial.println(" m/s^2");

  Serial.print("Rotation X: ");
  Serial.print(g.gyro.x);
  Serial.print(", Y: ");
  Serial.print(g.gyro.y);
  Serial.print(", Z: ");
  Serial.print(g.gyro.z);
  Serial.println(" rad/s");

  Serial.print("Temperature: ");
  Serial.print(temp.temperature);
  Serial.println(" degC");

  Serial.println("");
  previous_time = millis();
  previous_velocity = (velocity_change);
  delay(200);
}

Aside from the fact that this won't ever work well, you could try using the correct scale factor (units) for acceleration.

Sorry but i don't understand what you are saying. The MPU6050 gets acceleration in meters a second squared. In that case would velocity be in meters a second as well?
Thanks for you help.

No, the sensor itself measures acceleration in different units. Have you verified that the reported value has been correctly converted to m/s^2?

For rapid movements, you need to numerically integrate the acceleration over very short time intervals. The sensor can't measure and report acceleration while you are printing out all that crap on the serial monitor, or much, much worse, while doing this:

  delay(200);

Hi,
I divided the calculated velocity by 10 and now the results seem reasonable. I shows readings between 3 and 8 meters a second with fast hand movements but still doesn't work in a car, luckily my final goal is to calculate the speed of a model rocket and the readings seem good enough to try and compare the readings with a professional altimeter. Thanks for all your help and if anyone has any suggestions they would be welcome.

Code

/ Basic demo for accelerometer readings from Adafruit MPU6050

#include <Adafruit_MPU6050.h>
#include <Adafruit_Sensor.h>
#include <Wire.h>
float Time = 0;
float previous_time = 0;
float previous_velocity = 0;
float velocity = 0;

Adafruit_MPU6050 mpu;

void setup(void) {
  Serial.begin(115200);
  while (!Serial)
    delay(10); // will pause Zero, Leonardo, etc until serial console opens

  Serial.println("Adafruit MPU6050 test!");

  // Try to initialize!
  if (!mpu.begin()) {
    Serial.println("Failed to find MPU6050 chip");
    while (1) {
      delay(10);
    }
  }
  Serial.println("MPU6050 Found!");

  mpu.setAccelerometerRange(MPU6050_RANGE_8_G);
  Serial.print("Accelerometer range set to: ");
  switch (mpu.getAccelerometerRange()) {
  case MPU6050_RANGE_2_G:
    Serial.println("+-2G");
    break;
  case MPU6050_RANGE_4_G:
    Serial.println("+-4G");
    break;
  case MPU6050_RANGE_8_G:
    Serial.println("+-8G");
    break;
  case MPU6050_RANGE_16_G:
    Serial.println("+-16G");
    break;
  }
  mpu.setGyroRange(MPU6050_RANGE_500_DEG);
  Serial.print("Gyro range set to: ");
  switch (mpu.getGyroRange()) {
  case MPU6050_RANGE_250_DEG:
    Serial.println("+- 250 deg/s");
    break;
  case MPU6050_RANGE_500_DEG:
    Serial.println("+- 500 deg/s");
    break;
  case MPU6050_RANGE_1000_DEG:
    Serial.println("+- 1000 deg/s");
    break;
  case MPU6050_RANGE_2000_DEG:
    Serial.println("+- 2000 deg/s");
    break;
  }

How do you plan to use the altimeter to estimate the rocket velocity?

Unless you have a way of calibrating it, using an altimeter inside a model rocket to estimate velocity seems doomed to failure, or at least gross inaccuracy, since I expect there will be local velocity and pressure differences along and inside the rocket body that will confound the altimeter readings.

I am using an accelerometer and I don't think pressure readings will mess with acceleration. I am expecting some inaccuracy due to the inherent noisiness of accelerometers though. In the future I plan to use a simple kalman filter to smooth out these readings. :slight_smile:

Oh. Your post said...

okay I have another question. I am working on using a mosfet as a switch to control 9 volts with a teensy 4.1. My problem is that the mosfet never switches off. My multimeters always show 9 volts as the output despite my code turning the turning the gate High and Low every 2 seconds. I have tried many different schematics and the mosfet still doesn't switch off. Can someone help? Thanks.
Mosfets

Schematic

Code

int mos = 2;

void setup() {
  // put your setup code here, to run once:
pinMode(mos, OUTPUT);
}

void loop() {
  // put your main code here, to run repeatedly:
digitalWrite(mos, HIGH);
delay(2000);
digitalWrite(mos, LOW);
delay(2000);
}

Could be because it is not suitable for 3.3v operation. Check the datasheet. Do some tests sans Arduino. Google 3.3v mosfet. Could be because you have a huge resistor on the gate (value not shown on your schematic). BTW, typically a pulldown resistor is used on the pin driving the gate. See R12 for Q3 in the following, courtesy of @LarryD

Hi, I got the circuit working on a breadboard and when I soldered it onto a protoboard the multimeters show the voltage switching from 9 to 0 as commanded. But when I attach some thin nichrome wire that will glow red with voltage the wire doesn't glow. Interestingly it will glow when used on a breadboard and I am confused. Does anyone have any ideas on why this is happening? Thanks.
Schematic

Code

int mos = 2;

void setup()
{
  
  pinMode(mos, OUTPUT);
  
 
  
}

void loop()
{
 
 
  
  digitalWrite(mos, HIGH);
  delay(500);
  digitalWrite(mos, LOW);
  delay(500);
    
    
    
    
  }

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.