//
you're reading...
How-to's

Sensor calibration: accelerometers

The previous post in this series discussed some of the background of sensor calibration. In this post we will actually calibrate a sensor.

As an example we will use a three axis accelerometer. Accelerometers are common sensors, most smartphones have one to orient the screen. Several third party vendors sell accelerometers for the EV3. Accelerometers measure acceleration, as such they also measure the force of gravity. This gives some fine reference values for calibrating these sensors. A motionless accelerometer should measure a force of 9.81 m/s^2 over an upward facing axis and 0 m/s^2 over any axis perpendicular to the upward axis.

Below you find the code for a sample program that lets you calibrate an accelerometer. I will reference to this code using line numbers in square brackets [11-13] of methods [doSomething()].

The program uses the LinearCalibrationFilter. The filter can determine and correct offset and scale errors. It assumes these errors to be constant. Correction for scale errors is optional using this filter [49]. During normal mode of operation [showSamples()] the filter corrects the samples that pass the filter using a set a set of known offset and scale errors. These errors can be determined by the filter when in calibration mode [calibrate()]. During calibration mode the filter collects maximum and minimum values from the samples that pass the filter. From these maxima and minima and with aid of a set of reference values [70-75] it calculates offset error and scale error.
The calibration process can be paused [84] and resumed [91], this is very useful if the sensor needs to be manipulated during the calibration process. While calibration is paused the filter will correct the samples that pass it using intermediate calibration results. This might, on occasion, lead to strange output.
After calibration [97] the calibration parameters can be stored to the file system [100] to be used in a later session or another program.

Now let us calibrate a triaxis accelerometer. I got one from Dexter Industries so I have taken that one as an example. You can easily adjust the example code below for your sensor [14, 42]. The graph shows the output from the sensor.beforeCalibration
As said we will use the force of gravity to calibrate the sensor. Zero gravity will be the reference for offset [71] and 1 G, or 9.81 m/s^2, is the reference for scale [75]. If only one reference value for scale is provided, as in this program, then the filter assumes the second reference value will be the negative of the provided value, -9.81 in our example.

The program starts by showing the current set of calibration parameters. As we did not yet calibrate the sensor these are the default values of 0 for offset error and 1 for scale error. These parameters belong to a perfect sensor. By pressing the Enter button the program goes into normal mode of operation. It shows an updated sample on the display at regular intervals. Pressing Enter again will start sensor calibration. This is indicated by a red LED on the brick.
To calibrate we will tumble the sensor to all six sides. Every axis of the sensor will thus be exposed to the force of gravity in two directions during the process. We let the sensor rest on each side for a while to give the filter a change to collect enough samples. I housed my sensor in a frame to make it easy to put it on all sides. When we tumble the sensor we should suspend calibration first by pressing the Up button, otherwise disturbances from moving and bouncing the sensor might cause bad calibration results. Suspended calibration is indicated by an orange LED. After the sensor is put on another side we should wait a few seconds before resuming calibration. This we do by pressing the down button, the status LED will be red again. After having collected samples from all six sides we can end the calibration process. We can end the process using the Escape button. Or by using the Enter button, the program will then save the calibration results to the file system. This is indicated by a green status LED. At the end of the calibration process the program shows the calculated offset and scale errors. After pressing Enter the program will display samples at a regular interval once again, only this time they are properly calibrated. This graph shows the output from my sensor after calibration.afterCalibration

There is one more thing to take notice of. Accelerometers have a lot of sensor noise. Ignoring this fact will result in too high values for maxima and too low values for minima. This might not affect the calculation of offset error too much, but it will affect the calculation for scale error badly. Therefore the calibration program uses a mean filter [46] to reduce sensor noise within the samples before using them in the calibration process.

import lejos.hardware.Button;
import lejos.hardware.LED;
import lejos.hardware.ev3.LocalEV3;
import lejos.hardware.port.I2CPort;
import lejos.hardware.port.Port;
import lejos.hardware.sensor.DexterIMUSensor;
import lejos.robotics.SampleProvider;
import lejos.robotics.filter.Dump;
import lejos.robotics.filter.LinearCalibrationFilter;
import lejos.robotics.filter.MeanFilter;
import lejos.utility.Delay;

public class CalibrateAccel {
  DexterIMUSensor         sensor;
  SampleProvider          accel;
  LinearCalibrationFilter calibrate;
  SampleProvider          dump;
  SampleProvider          mean;
  LED                     led      = LocalEV3.get().getLED();
  String                  filename = "DexterIMUAccel3";

  public static void main(String[] args) {
    CalibrateAccel foo = new CalibrateAccel();
    foo.showParameters();
    foo.showSamples();
    foo.calibrate();
    foo.showParameters();
    foo.showSamples();
    foo.sensor.close();
  }

  /**
   * Constructor. Initiate sensor and filters.
   */
  CalibrateAccel() {
    // Use High speed I2C
    Port port1 = LocalEV3.get().getPort("S1");
    I2CPort myPort = port1.open(I2CPort.class);
    myPort.setType(I2CPort.TYPE_HIGHSPEED);

    // Use the accelerometer from the Dexter IMU sensor
    sensor = new DexterIMUSensor(myPort);
    accel = sensor.getAccelerationMode();

    // Use a mean filter to remove some of the noise from the samples.
    mean = new MeanFilter(accel, 30);

    // Use the Linear calibration filter
    calibrate = new LinearCalibrationFilter(mean);

    // Calibrate for offset errors only.
    calibrate.setCalibrationType(LinearCalibrationFilter.OFFSET_AND_SCALE_CALIBRATION);

    // Use a Dump filter to display the samples on screen
    dump = new Dump(calibrate);
  }

  /**
   * Calibration routine (LED color is red to indicate calibration). Use buttons
   * to control calibration. Up suspends calibration (LED color is orange), Down
   * resumes calibration (LED color is red). Enter ends calibration and saves
   * parameters to file system (LED green). Esc ends calibration and does not
   * save the parameters (LED off).
   */
  void calibrate() {
    float[] sample = new float[dump.sampleSize()];

    led.setPattern(2);

    // Calibrate offset error against a real value of 0
    calibrate.setOffsetCalibration(0);

    // Calibrate scale error against a maximum value of 9.81 and a minimum value
    // of -9.81
    calibrate.setScaleCalibration(9.81f);

    calibrate.startCalibration();

    while (Button.ESCAPE.isUp() && Button.ENTER.isUp()) {
      dump.fetchSample(sample, 0);
      if (Button.UP.isDown()) {
        // Suspend calibration
        led.setPattern(3);
        calibrate.suspendCalibration();
        while (Button.UP.isDown())
          ;
      }
      if (Button.DOWN.isDown()) {
        // Resume calibration
        led.setPattern(2);
        calibrate.resumeCalibration();
        while (Button.DOWN.isDown())
          ;
      }
      Delay.msDelay(33);
    }
    calibrate.stopCalibration();
    if (Button.ENTER.isDown()) {
      led.setPattern(1);
      calibrate.save(filename);
    } else {
      led.setPattern(0);
    }
    while (Button.ESCAPE.isDown() || Button.ENTER.isDown())
      ;
  }

  /**
   * Periodically fetch a sample and show on screen
   */
  void showSamples() {
    float[] sample = new float[dump.sampleSize()];

    led.setPattern(0);

    while (Button.ESCAPE.isUp() && Button.ENTER.isUp()) {
      dump.fetchSample(sample, 0);
      Delay.msDelay(33);
    }
    while (Button.ESCAPE.isDown() || Button.ENTER.isDown())
      ;

  }

  /**
   * Show the calibration parameters in use.
   */
  void showParameters() {
    float[] offset = calibrate.getOffsetCorrection();
    float[] scale = calibrate.getScaleCorrection();
    System.out.format("Calibration: %d%n", calibrate.getCalibrationType());
    for (int i = 0; i < calibrate.sampleSize(); i++) {
      System.out.format("%f  %f%n", offset[i], scale[i]);
    }
    Button.ENTER.waitForPressAndRelease();
  }

}

Advertisements

Discussion

2 thoughts on “Sensor calibration: accelerometers

  1. Really Appreciate this post, how can I make is so that I receive an alert email when there is a new post?

    Posted by Dacia Cumberland | 2016/01/26, 11:40

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

About leJOS News

leJOS News keeps you up-to-date with leJOS. It features latest news, explains cool features, shows advanced techniques and highlights amazing projects. Be sure to subscribe to leJOS News and never miss an article again. Best of all, subscription is free!
Follow leJOS News on WordPress.com
%d bloggers like this: