I'm trying to calibrate my 3 axis accelerometer so that when I go through a calibration sequence of putting every axis at 0g and 1g it calculates a bias (to zero the readings at 0g, calculated using the difference between what the 0g reading is and should be) and a scaling factor so the selected ±1.5g range of the accelerometer fills the whole range and is accurate. But, it doesn't seem to work properly as it doesn't fill the scale properly, and the y-axis becomes erratic (though it is more sensitive to begin with than the other axes).
This also uses the Running Average (Arduino Playground - HomePage) library.
#include "RunningAverage.h"
#define xPin A0
#define yPin A1
#define zPin A2
#define gMode 1.5 //accelerometer has selectable ±1.5/±6g maximum
#define smoothCount 5 //how many data points should be used to average
int bias[3];
float scale[3] = {1.000, 1.000, 1.000};
RunningAverage xAxis(smoothCount);
RunningAverage yAxis(smoothCount);
RunningAverage zAxis(smoothCount);
////////////////////////////// SETUP /////////////////////////////
void setup() {
Serial.begin(9600);
}
///////////////////////////////// LOOP ///////////////////////
void loop() {
addCalibAvg();
Serial.println(xAxis.getAverage(), 3);
Serial.println(yAxis.getAverage(), 3);
Serial.println(zAxis.getAverage(), 3);
if (Serial.read() == 'c') {
calibrate();
}
}
/////////////////////////////// FUNCTIONS /////////////////////////
void calibrate() { // make sure accelerometer is where it should be calib'd
int zeroG[3], oneG[3]; //each axis' readings at 0 and 1g (x,y,z = 0,1,2)
digitalWrite(13, LOW);
clrAvg(); //clear all averages, even if it shouldn't be necessary with the next
// step as we will be filling the whole thing with new data anyway
//delay(2000); //give the user some time to get it right
for(byte i; i < smoothCount; i++) addRawAvg(); //fill with new data where calibration should be
zeroG[0] = xAxis.getAverage();
zeroG[1] = yAxis.getAverage();
oneG[2] = zAxis.getAverage();
digitalWrite(13, HIGH); // signal next stage y at +1g (pointing up)
delay(4000); //get ready for next position
digitalWrite(13, LOW);
delay(1000); //make the light turning off visible
clrAvg();
for(byte i; i < smoothCount; i++) addRawAvg();
zeroG[0] = (zeroG[0] + xAxis.getAverage()) / 2; // spread readings over more
oneG[1] = yAxis.getAverage();
zeroG[2] = zAxis.getAverage();
digitalWrite(13, HIGH); // now x pointing up
delay(4000);
digitalWrite(13, LOW);
delay(1000);
clrAvg();
for(byte i; i < smoothCount; i++) addRawAvg();
oneG[0] = xAxis.getAverage();
zeroG[1] = (zeroG[1] + yAxis.getAverage()) / 2;
zeroG[2] = (zeroG[2] + zAxis.getAverage()) / 2;
digitalWrite(13, HIGH); // let the user know it's finished
delay(600);
digitalWrite(13, LOW);
// Calculate bias and scale
//bias based off 0g and scale off difference between 0g - 512 and 1g
for(byte i = 0; i < 3; i++){ bias[i] = zeroG[i];}
//calculate scaling to make 1g on accelerometer= what 1g should be on a range of 0 - 1023 (341.3333333)
for(byte i = 0; i < 3; i++) {
scale[i] = 341 / (oneG[i] - bias[i]); //scaling factor calculation
}
Serial.print("Biases: ");
for(byte i; i< 3; i++) {
Serial.print(bias[i]);
Serial.print(", ");
}
Serial.print(" Scales:");
for(byte i; i < 3; i++) {
Serial.print(scale[i]);
Serial.print(" ");
}
Serial.println("");
}
void addCalibAvg() { //add to averages and convert to calibrated
xAxis.addValue((analogRead(xPin) - bias[0] - 512) * scale[0]);// take away the 512 extra as incoming values will be 0 - 1023
yAxis.addValue((analogRead(yPin) - bias[1] - 512) * scale[1]);
zAxis.addValue((analogRead(zPin) - bias[2] - 512) * scale[2]);
}
void addRawAvg() { //add to averages with raw -512 to 512 so centre is 0
xAxis.addValue(analogRead(xPin) - 512);
yAxis.addValue(analogRead(yPin) - 512);
zAxis.addValue(analogRead(zPin) - 512);
}
void clrAvg() {
xAxis.clear();
yAxis.clear();
zAxis.clear();
}
And when I look at the output of the biases and scales in the serial monitor from these lines:
Serial.print("Biases: ");
for(byte i; i< 3; i++) {
Serial.print(bias[i]);
Serial.print(", ");
}
Serial.print(" Scales:");
for(byte i; i < 3; i++) {
Serial.print(scale[i]);
Serial.print(" ");
}
Serial.println("");
it shows in the serial monitor:
Biases: -10, 21, 63, Scales:
or
Biases: -11, 22, -57, Scales:
etc. not showing the scales.
-
Is this best way to calibrate the accelerometer?
-
What’s going wrong with my code?