Gyroscope vs accelerometer

What a gyroscope and an accelerometer each measure, why adding a turn rate up drifts while an absolute reading only jitters, and what a complementary filter buys you. Four demos run on a live robot: change A, press Run and watch the drift come back.

Guidefree, runs in your browser

A gyroscope measures how fast something is turning. An accelerometer measures how hard it is being pushed, including the steady pull of gravity. They are different sensors answering different questions, and they sit next to each other on one chip in every phone, drone, games controller and robot. That chip is an IMU, an inertial measurement unit. Neither sensor can say which way a robot is facing on its own: the gyroscope's answer slides away over a minute or two, and the accelerometer's answer is buried in noise. Used together they do the job, which is why they are almost never sold apart. On this page a small robot stands still on a 2 metre mat while both are read, and each demo below is a real program you can change and run.

What each one measures

A gyroscope reports a rate: degrees per second of turn. It says nothing about which way you are pointing. To get an angle you have to add the rate up over time, and that is where the trouble starts.

An accelerometer reports an acceleration in each of three directions, in metres per second squared or, on this robot, centimetres per second squared. Gravity counts. A sensor lying flat and completely still reads about 9.8 m/s² straight down and nothing sideways, so the direction of that reading tells you which way is down. Tip it over and the reading tips with it. That is an absolute measurement: it is made afresh every time, against something outside the robot that never moves.

The BugBot carries a BNO055 on its odometry board. imu() hands back four numbers: the heading the chip itself believes, the turn rate from the gyroscope, and the two accelerations the accelerometer feels along the robot's own right and forward axes.

A gyroscope measures the rate of turn and has to be added up; an accelerometer measures acceleration including gravity, which says which way is downgyroscopeaccelerometerfrom abovefrom the side, tipped 20 degreesgravitythe robot's downhow fast it is turningdegrees per secondadd it up to get an anglestanding still: -0.18 deg/show hard it is being pushedgravity includedwhich way is down, from nothingstanding still: -0.13 cm/s/s
The two sensors answer different questions. The gyroscope's answer has to be added up, and the -0.18 deg/s it reads while standing still is added up with it. The accelerometer's answer is made again from nothing every reading, so its error never builds, but it is noisy: the readings in the first demo ran from -10.8 to +9.8 cm/s/s.

Two readings from a robot that is not moving

The robot sits still for 20 seconds. Both sensors should read zero. Neither does.

Standing still for 20 s: the turn rate wanders between -2.5 and +2.0 deg/s with an average of -0.18, and the sideways acceleration between -10.8 and +9.8 cm/s/s with an average of -0.13.
The program
from bugbot import *
connect()

# the robot never moves, so both readings should be zero
rates, sides = [], []
for tick in range(200):              # 20 seconds
    fused, rate, right, ahead = imu()
    plot("turn rate deg/s", rate)
    plot("sideways cm/s/s", right)
    rates.append(rate)
    sides.append(right)
    wait(0.1)
stop()
n = len(rates)
print("turn rate: average", round(sum(rates) / n, 2), "deg/s, biggest", round(max(rates), 1))
print("sideways:  average", round(sum(sides) / n, 2), "cm/s/s, biggest", round(max(sides), 1))
Put this demo on your own site

Paste it into a school website, Moodle, Google Sites or a blog. More options on the embed page.

Both charts are a fuzz around zero, and that fuzz is noise: a different wrong number every reading, as likely to be high as low. Average enough of it and it cancels.

The averages are the interesting part. Run the same loop for 5, 10, 20, 40 and 55 seconds and the accelerometer's average comes out as 0.02, 0.13, -0.13, -0.03 and 0.07 cm/s/s: it hops about near zero and gets no worse. The gyroscope's average comes out as -0.17, -0.22, -0.18, -0.17 and -0.17 deg/s. It is not settling on zero. It is settling on -0.17, and it will still be -0.17 in an hour.

That is a bias: a fixed amount the sensor is out by, the same every reading, for as long as the chip stays at the same temperature. Every gyroscope has one. It is small enough to be invisible in a single reading, and it is the whole problem.

Why a gyroscope drifts

A rate on its own is not much use to a robot that wants to know which way it is facing, so the program adds it up. Every tenth of a second it takes the rate and adds rate × 0.1 to a running total. That is numerical integration, and it adds the bias up too. A bias of one degree per second is one degree of error after a second, sixty after a minute, and it never comes back.

This demo adds the total up for 55 seconds while the robot stands still, and draws it against the heading the BNO055 itself reports. BIAS is an extra error added to every reading, so the effect is easy to see on a short run. The BugBot's own gyroscope is about six times better than this.

Standing still for 55 s with a 1 deg/s bias: the added-up turn rate climbs a straight line to 45.6 degrees, while the chip's own fused heading stays between -1.7 and +9.9.
The program
from bugbot import *
connect()

# change this number and press Run
BIAS = 1.0      # deg/s the gyroscope reads while the robot is still

gyro = 0.0                           # the gyroscope's running total
for tick in range(550):              # 55 seconds, standing still
    fused, rate, right, ahead = imu()
    rate = rate + BIAS
    gyro = gyro + rate * 0.1         # add the turn rate up
    plot("gyro only", gyro)
    plot("fused heading", (fused + 180) % 360 - 180)
    plot("truth", heading())
    if tick % 100 == 0:
        print(round(clock()), "s  gyro", round(gyro, 1), " fused", round((fused + 180) % 360 - 180, 1))
    wait(0.1)
stop()
Put this demo on your own site

Paste it into a school website, Moodle, Google Sites or a blog. More options on the embed page.

The gyro only line is almost perfectly straight: 8.0 degrees after 10 seconds, 16.7 after 20, 25.4 after 30, 33.1 after 40 and 45.6 at the end. The robot has not moved a millimetre. The slope is the bias, 0.83 degrees per second once the robot's own -0.17 is taken off the 1.0 that was added.

The fused heading line is the BNO055's own answer, and it behaves differently. It is noisier from reading to reading, it wanders a few degrees over the minute, and it stays between -1.7 and +9.9. It does not grow. Set BIAS = 0 and the robot's own gyroscope still drifts, to -9.4 degrees after 55 seconds, which is roughly the same size as the wander in the fused reading. Over ten minutes it would be 100 degrees, and the fused reading would still be within about ten.

That is the difference that matters. The gyroscope's error grows with time. The absolute measurement's error does not.

The error of the added-up gyroscope grows in a straight line, while the error of the absolute reading stays inside a band0102030405001020304050time standing still, secondserror, degrees45.6 after 55 sgyroscope, added upslope = the bias, 0.83 deg/sthe chip's own heading stays between -1.7 and +9.9, for ever
Both lines are errors, on a robot that never moves. The added-up turn rate gains 0.83 degrees every second and reaches 45.6 after 55 seconds, and it would keep going. The absolute reading is noisier from tick to tick but stays in the green band, about 11 degrees wide, however long the run is.

An accelerometer does not drift, but adding it up does

It is tempting to conclude that accelerometers are the safe ones. They are not, if you treat them the same way. Adding up an acceleration gives a speed, and adding that up again gives a position, and each addition carries the noise with it.

This demo adds up the forward acceleration of a robot that never moves, and draws the answer against the optical flow sensor underneath, which measures speed directly by watching the mat slide past.

Standing still for 55 s: the speed worked out from the accelerometer wanders to 4.9 cm/s and ends at 2.6, while the flow sensor, which measures speed itself, stays within 2.3 cm/s of zero.
The program
from bugbot import *
connect()

speed = 0.0
for tick in range(550):              # 55 seconds, standing still
    fused, rate, right, ahead = imu()
    speed = speed + ahead * 0.1      # add the acceleration up
    plot("speed added up cm/s", speed)
    plot("flow sensor cm/s", flow()[1])
    if tick % 100 == 0:
        print(round(clock()), "s  from the accelerometer", round(speed, 1),
              "cm/s, from the flow sensor", flow()[1])
    wait(0.1)
stop()
print("it never moved")
Put this demo on your own site

Paste it into a school website, Moodle, Google Sites or a blog. More options on the embed page.

The added-up line is a different shape from the gyroscope's. It does not march off in one direction: it staggers, up to 4.9 cm/s and back down, because the noise it is adding has no bias behind it. That staggering is a random walk, and it grows with the square root of the time rather than with the time. It is slower than a bias, and it still gets away in the end.

This is why nothing works out its position from an accelerometer alone for more than a few seconds. A phone dropped into a pocket would think it had walked off down the street. Aircraft and submarines do navigate this way, with gyroscopes and accelerometers costing more than a car, and even those are corrected every so often by something that looks outside.

The lesson is not that one sensor is good and the other bad. It is that adding up is what causes drift, and that the sensor to trust is the one measuring the thing you actually want. The flow sensor measures speed, so its speed does not drift. Its position, added up, does.

Fusing them: the complementary filter

The gyroscope is right about what just changed and wrong about where you started. The absolute measurement is right about where you are on average and wrong from one reading to the next. So use each for the part it is good at.

The complementary filter is the smallest way of writing that down:

angle = A × (angle + rate × dt) + (1 - A) × absolute reading

Take the estimate you already had, move it by what the gyroscope says just happened, and then pull it a little of the way towards the absolute reading. A is a number just under 1, usually between 0.9 and 0.99. The demo writes the same sum in a form that copes with the reading crossing 360 degrees.

10 s standing still, a 95 degree turn, then still again: the gyro-only line ends 45.7 degrees out, the fused reading 5.3 out, and the two combined 6.5 out and far smoother.
The program
from bugbot import *
connect()

# change these two numbers and press Run
A = 0.95        # how much of the gyroscope the filter keeps each tick
BIAS = 1.0      # deg/s the gyroscope reads while the robot is still

gyro = 0.0      # the gyroscope's running total
both = 0.0      # the two sensors combined
for tick in range(550):              # 55 seconds
    fused, rate, right, ahead = imu()
    rate = rate + BIAS
    gyro = gyro + rate * 0.1
    step = (fused - both - rate * 0.1 + 180) % 360 - 180
    both = both + rate * 0.1 + (1 - A) * step
    plot("gyro only", gyro)
    plot("fused heading", (fused + 180) % 360 - 180)
    plot("combined", both)
    plot("truth", heading())
    if tick == 100:                  # turn for three seconds
        drive(0, 0, 25)
    if tick == 130:
        stop()
    wait(0.1)
stop()
print("truth", round(heading(), 1), " gyro only", round(gyro, 1),
      " fused", round(imu()[0], 1), " combined", round(both, 1))
Put this demo on your own site

Paste it into a school website, Moodle, Google Sites or a blog. More options on the embed page.

The robot stands still for 10 seconds, turns for 3, and then stands still again. The truth is 95.5 degrees. The gyroscope alone says 141.2, out by 45.7 and getting worse. The chip's fused heading says 100.8. The combined estimate says 102.0, and on the chart it goes round the turn as cleanly as the gyroscope line and then stays put.

Notice what fusing does not do. The combined estimate does not beat the absolute reading in the long run, because that is what is holding it in place. If the absolute reading sits 5 degrees off, so will the filter. What fusion buys is a smooth, fast estimate that does not run away.

The complementary filter: the gyroscope moves the estimate on, and the absolute reading pulls it backgyroscoperate, deg/s× 0.1 show far it turned+estimatethe answerabsolute readingnoisy, does not drift-× (1 - A)pull it a little of the waythe estimate so farA = 0.95leans on thegyroscope for 1.9 s
One line of arithmetic drawn out. The gyroscope moves the estimate on by what just happened, and the absolute reading pulls it back by (1 - A) of the difference. With A = 0.95 and a tick of 0.1 s, the filter leans on the gyroscope for 1.9 s before the absolute reading wins, which is short enough to keep the drift out and long enough to ride over the noise.

Choosing A

A decides how long the filter leans on the gyroscope before the absolute reading pulls it back. The time it takes to be pulled most of the way is A × dt / (1 - A), which with dt = 0.1 gives:

A leans on the gyroscope for combined heading at the end
0 (no gyroscope) 0 s 103.3
0.5 0.1 s 101.9
0.8 0.4 s 101.1
0.95 1.9 s 102.0
0.98 4.9 s 104.7
0.995 19.9 s 115.7

The truth is 95.5 and the fused reading it is being pulled towards ends at 100.8. Above about 0.98 the bias starts to show: a filter that leans on the gyroscope for 19.9 seconds keeps 19.9 seconds' worth of drift, which at 0.83 deg/s is the 15 degrees the last row is out by.

Small values pay in noise instead. Standing still for 30 seconds, the fused reading on its own jumps 1.79 degrees from one tick to the next. The combined estimate jumps 0.74 degrees at A = 0.5, 0.28 at 0.8, 0.11 at 0.95 and 0.08 at 0.98. The higher A is, the smoother the answer.

So the choice is drift against noise, and the usual answer is a second or two. There is one more reason to keep A high on a real robot that this simulator does not show: an accelerometer measures every knock and shove as well as gravity, so while the robot is accelerating its idea of which way is down is wrong. A filter that leans on the gyroscope for a second or two rides straight over that.

What this looks like on a real robot

Reading which way is down and reading which way you are facing are two different jobs, and the accelerometer can only help with the first.

Tilt is easy. Gravity points down, the accelerometer sees it, and the angle falls out of the numbers with atan2. The gyroscope fills in the fast movement between readings, and a complementary filter does the joining. This is what balances a self-balancing robot and what keeps a drone level, and a 6-axis IMU, which is three gyroscope axes and three accelerometer axes, is all it needs.

Heading, also called yaw, is the hard one. Spin a robot on a flat floor and gravity does not change at all, so the accelerometer has nothing to say. The absolute reference has to come from somewhere else: a magnetometer reading the earth's field, which makes it a 9-axis IMU, or a camera, or wheel odometry against a map, or the tags on a wall. The filter is the same shape whichever it is. The BugBot's BNO055 has all nine axes and does the fusing inside the chip, which is why imu()[0] already has a heading in it.

Two other names worth knowing. Madgwick and Mahony filters do this job in three dimensions at once using quaternions, and they are what most flight controllers actually run. A Kalman filter does the same job with an explicit account of how much it trusts each sensor, and it can estimate the gyroscope's bias while it runs rather than assuming it away.

Where this is taught

Questions

What is the difference between a gyroscope and an accelerometer?

A gyroscope measures the rate of turn, in degrees per second. An accelerometer measures acceleration, including gravity, so it can tell which way is down. A gyroscope has to be added up to give an angle, and the small errors add up with it; an accelerometer gives an absolute reading every time, but a noisy one.

Why does a gyroscope drift?

Because a program has to add the turn rate up to get an angle, and the sensor's bias, the fixed amount it reads when standing still, is added up too. On this page a bias of 0.83 degrees per second became 45.6 degrees of error in 55 seconds without the robot moving at all.

Can an accelerometer measure heading?

No, not on a flat floor. Turning on the spot does not change the direction of gravity, so the accelerometer reads the same all the way round. It can measure tilt, which is roll and pitch. Heading needs a magnetometer, a camera or something else that looks outside the robot.

Can you get position from an accelerometer?

Only for a few seconds. Position means adding the acceleration up twice, and the noise is added up twice with it. In the third demo on this page the robot stood perfectly still and the speed worked out from its accelerometer wandered to 4.9 cm/s, which would be metres of position error within a minute.

What is sensor fusion?

Combining two or more sensors so that the answer is better than either alone. Here the gyroscope is trusted for the fast changes and the absolute reading for the slow truth, which is exactly what each is good at.

What is a complementary filter?

The simplest fusion rule: angle = A × (angle + rate × dt) + (1 - A) × absolute, with A a little under 1. It is a high pass filter on the gyroscope and a low pass filter on the absolute reading, and the two halves add to 1, which is where the name comes from.

How do I choose A in a complementary filter?

A × dt / (1 - A) is how long the filter leans on the gyroscope. Pick a second or two, then check both ends: too high and the gyroscope's drift shows in the answer, too low and the noise of the absolute reading does. The table on this page shows both failures on the same robot.

What is an IMU, and what do 6-axis and 9-axis mean?

An inertial measurement unit: one chip carrying a gyroscope and an accelerometer, and often a magnetometer. 6-axis means three gyroscope axes and three accelerometer axes, which is enough for tilt. 9-axis adds three magnetometer axes, which gives heading as well.

Is a Kalman filter better than a complementary filter?

It does more. A Kalman filter keeps track of how uncertain it is and works out how much to trust each sensor from that, and it can estimate the gyroscope's bias as it runs. It costs more code and more arithmetic. For tilt on a small robot a complementary filter is usually enough, and many flight controllers use Madgwick or Mahony, which sit between the two.

Do gyroscopes and accelerometers appear on the GCSE or A level specification?

Not by name. No GCSE or A level Computer Science specification (AQA, OCR, Edexcel, Eduqas) names them. They turn up in Physics as examples of sensors, and in any robotics or electronics project. The programming on this page is a loop, a running total and one line of arithmetic, which is GCSE material.

Learn it step by step

These lessons build the same ideas one at a time, each with tasks the simulator marks.

  1. 2.5 Drift and correction Sensing, Robot club
  2. U1.3 Truth and belief The robot as a system, University
  3. U3.1 Integrating velocity Odometry and drift, University
  4. U3.3 How the error grows Odometry and drift, University
  5. U3.4 Calibration Odometry and drift, University
  6. U4.1 A reading is a distribution Noise and filtering, University
  7. U4.3 The low pass filter Noise and filtering, University
  8. U6.1 Two sources, one state State estimation, University
  9. U6.2 Predict and correct State estimation, University
Open the lessons