Making an odometer with an mpu6050 accelerometer

PUT IN CONTEXT

hi, i need to measure an aproximate short trip distance (less than a Km, scalar value) for an automotive application (alarm system) (a car immobilizer)

i considered various options like take lectures from ECU trip meter signal, use a gps module, and use an accelerometer to calculate velocity and distance... i choose the accelerometer method for being independent, cheaper, simpler, easier installation, no connection from ECU, no external signals, lower power consumtion, lighter and faster, etc

i made some tests with some libraries including the famous i2cdevlib... some libraries do many raw calculations to deal with vibrations and motion noise turning arduino potentially slower, but since i knew mpu6050 have an internal math coprocessor for these calculations (called "digital motion processor" or DMP) that i try to use it

i2cdevlib mpu6050 lib with dmp gave me problems, system freezes after few seconds... i tried almost everything i found and read but still freezes... when i found Simple_MPU6050 lib as an alternative, i test them running a whole day and run like a breeze

NOW THE QUESTIONS

  • so... how can i make an odometer, starting with accel data from mpu6050? (with sums?, averages?, integrals?, others?)
  • and what the heck mean all those xyz accel data variants?, with gravity?, without gravity?, orientation compensed?, no compensed? output in m/s2?, in newtons? etc
  • which would i choose, between easy implementation, light on resources and (some) accuracy?

thanks in advance

EDIT: i think low accuracy about 80-90% would be fine, car just needs to travel about 200-300m... theoretically at start the car is stopped and turned off

EDIT2: precision?, i don't care for now... just wanna try some formulas and see if they work for me

EDIT3: the application is acctually a car immobilizer, that travels a couple blocks and stops the car if you don't press the hidden button... read full context in post 33 below

What accuracy do you expect?
You need double integration.
Acceleration->speed
Speed->distance
You will suffer from small measurement errors that keep adding up (integral windup).

Why not measure tire revs...

So you want to build an INS (Inertial navigation system). It is a very complex subject and is used in cruise missiles, submarines, aviation..

I do not know the accuracy you get, using a fake mpu6050 china clone. Probably, not very good at all. But if you know the start velocity (probably zero), you can multiply acceleration with time and you get change of velocity, delta v. So you need to get hold of time, for example with the millis function. With velocity x time, you get displacement, "distance". I would go for a different solution, but it would be fun to try it out and see what you actually get of results. You should use a button for start and stop, the mcu do the calculations. The easy calculation is a stright line. Imagine the implications go in a curve....

GPS based can work if the walk is outdoors. 6050 will be like a lottery.

You can't do it with acceleration:
v = a * t + c0
s = a/2 * t^2 +c0 * t+c1
c0*t+c1 is your error (drift) term - as it's in its nature it will grow over time and it is (more or less) random.

expected acurracy: about 10% error would be fine for me (car moving between 200-300m)

i was thinking something like this pseudocode, please dont be so syntax freak and try to catch the idea:

// to calculate average accel later, in a try to minimize noise
sumAccel = 0;
iterations = 0;

deltaTime = 0;
sumDistance = 0;

loop {
  sumAccel += mpu.readAccelScalar();  // but mpu returns 3 accels for xyz, HOW TO GET THIS?
  ++iterations;
  deltaTime = updateTime();  // actually done with millis() but simplificated here

  // a second has passed
  if ( deltaTime >= 1000 ) {
    avgAccel = sumAccel / iterations;
    sumAccel = 0;
    iterations = 0;

    avgVelocity = 1000 * avgAccel / deltaTime;
    avgDistance = 1000 * avgVelocity / deltaTime;

    sumDistance += avgDistance;
    deltaTime = 0;
  }

  if ( sumDistance >= 200 )
    print("your car has traveled aprox. 200m");
}

i know mems like mpu have internal pendulums, masses, springs, tuning forks and so... so they also senses gravity force along position changes... so i think i should get the difference between current and past acceleration (delta)??... what represents all those different accelerations (compensated, etc) and what could be more useful for my purpose?

If you assume you start at 0 velocity, have a really good accelerometer and know time it can be done.
In theory it could be reasonably accurate. In practice, not so much.

Not possible with consumer grade IMUs. This blog article explains one of the major problems:
https://web.archive.org/web/20180112063505/http://www.chrobotics.com/library/accel-position-velocity

precision?, i don't care for now

i will just install a theoretical sketch and see how it behaves according my needs... i don't care if car travel 100m and sketch says it traveled 500m

everybody seem obsessed about precision, i just want to see it work... adjusts can be made on the run

It won't AT ALL, but have fun!

1 Like

thanks for cheering me xD .. i will have fun

You need to multiply by deltaTime.... .... not divide...
You may calculate this 3 times for 3 different directions.
Then distance follows from pythagoras.

dist = sqrt(x*x + y*y + z*z)

And you should remember the last calculated velocity and add that in your second equation....

interesting...

the division is to try to get an average between sensor samples taken during a second, in a silly attempt of mine to smoothen vibrations and noise :slight_smile:

so... if i have the sensor attached to some support, i think these are the lectures it reads (i will draw 2d due simplicity)

imagen
(i can't post more more than 1 image per post yet)

so if i think if i calculate the difference between those samples (the thin horizontal bottom green line) maybe i could remove the gravity?, or am i wrong?

.. or maybe there are another method to get gravity, that is take first samples with sensor resting static, then use that value to substract to all next samples?... maybe, that is why gyroscope is also, because when sensor rotates it anyway must ensure gravity is always pointing downwards (does that means "gravity compensated"?)

(the gravity is not actually "the gravity" but the force support make to keep sensor in its position, so the vector should be pointing upwards?... right?)

yes i know to calculate final vector i must use pythagoras in all 3 component axis... so maybe to get vector average should i calculate averages separately by each 3 axis, then use pythagoras with the 3 average axis components to get final average vector? ... or maybe there are other metter ways to calculate, don't think so because with this method i use square roots just once a second

maybe if i save all 3 xyz accel samples for future comparisions (difference) between iterations, i could isolate gravity (i dont care too much about gravity because i am only interested in scalar values)... maybe like this:

/*** PSEUDOCODE ***/
avgAccelOld = mpu.getAccelScalarValue();  // fill this variable first time
sumAccelX = sumAccelY = sumAccelZ = samples = travelDistance = 0;

loop {
  sumAccelX +=  mpu.getRawAccelX();  // accumulate these values for future use
  sumAccelY +=  mpu.getRawAccelY();
  sumAccelZ +=  mpu.getRawAccelZ();
  ++samples;

  if ( aSecondHasPassed() ) {
    avgAccelX = sumAccelX / samples;  // average components during a second
    avgAccelY = sumAccelY / samples;
    avgAccelZ = sumAccelZ / samples;
    sumAccelX = sumAccelY = sumAccelZ = samples = 0;  // reset values for next iteration

    avgAccelNow = sqrt(avgAccelX + avgAccelY + avgAccelZ);
    deltaAccel = avgAccelNow - avgAccelOld;
    avgAccelOld = avgAccelNow;  // save current accel for future use

    velocity = 1000 * deltaAccel / millisElapsed();
    travelDistance += 1000 * avgVelocity / millisElapsed();
  }

  if ( travelDistance >= 200 )
    print("your car has traveled aprox. 200m");
}

however i am not so sure about "delta accel"... maybe i am thinking just nonsenses xD

If you vehicle runs at a constant speed what will the output from the accelerator be ? Same as if it’s not moving , periods of constant speed will create increasing error.
If it goes over a bump in the road , the accelerometer will register it as an acceleration .
I’d suggest you just try reading the acceleration as the car runs , before worrying about coding distance to see if it looks as though it will work .
Don’t forget “ rubbish in rubbish out”

You are integrating here, not averaging...

i am starting to understand... to keep the system booting fast i thought to pre-calibrate it and store gravity vector in eeprom... but then i realize if the car is parked even in a small slope, then all next samples will be interpreted as if the car were pushed laterally, adding accel>speed in that direction exponentially :frowning:

@hammy : yes, if the car is moving at constant speed, acceleration is 0 because acceleration measures speed variation over a time (i still remember high school xD)

what do you mean? starting conditions? (calibration, etc)... once we get (ideal) accel and time the other magnitudes are easy just dividing by time

really?... i though just to approximate values

I mean take raw readings from the sensor during operation and see if meaningful calculations can be made from its output - particularly at constant speed where it’s output should be zero .
Acceleration is rate of change of velocity
A = dv/dt . Therefore velocity ( v) is the integral of acceleration (A).
Distance is the integral of velocity .
A*t+U is the result obtained by integrating acceleration to give speed , where t=time and U initial velocity .

after researching a little, now i understand this involve VERY complicated realtime calculations, even involving starting conditions, rotation of earth, distance to center, and many other variables

if i want to use dmp instead of raw data is precisely to prevent arduino doing these heavy calculations... because real calculations as i understand should be:

  1. starting with sensor in static position
  2. take some raw samples (accel, gyro) to deduce gravity with this data
  3. enter a loop, where gyro is constantly tracking orientation to update gravity direction
  4. go getting accel raw data and substract gravity vector everytime
  5. the resulting should be the REAL realtime accel, all these while dealing with motion noises
  6. in same loop, go integrating accel and velocity with time interval, to get velocity and distance respectively
  7. go summing these little distances to get travel distance

i want to use dmp to calculate at least up to step 5 (get real accel compensed to gravity, rotations and noises), while let arduino calculate the rest

arduino mpu6050 dmp libraries (i2cdevlib, simple_mpu6050 and few others) have functions to supposedly return these different types of accelerations (raw, compensed, etc), but i don't understand them because are poorly documented so i don't know if they will help me or which could do

moreover, i believe DMP is a kind of private math coprocessor with its own instruction set, to calculate geometry and integrals, somewhat secret because invensense (now tdk) don't care a damn to release good info, so nobody exactly knows how it works and the few advancements were made mainly by reverse engineering and binary editing

so please, go example sketches to catch the functions, how they work and what they mean... and tell us which functions can we use for inertial navigation systems and how :cry:

EDIT: after thinking and reading about help vampires (thanks @Juraj for your posts), i decided to do that tests by myself and share my results here after... but maybe it will take me a little time :slight_smile: ... however some advice will be helpful

I want to guide you to my favorite teacher, Joop Brokking. He explain the basics of MPU6050 for arduino. I try to avoid library where I can, as I need to learn how people wrote the library. And there is a lot of bad librarys. I`m better of learning C++ and simplify. => https://www.youtube.com/watch?v=4BoIE8YQwM8