Hey! It's me again
The same guy from 'Are these Gyroscope readings correct?' here. After calibrating my gyroscope and accelerometer i had to try and calibrate the magnetometer. After many days, i finally managed to calibrate it but i am unsure if the calibration is good enough.
I did not compensate for tilt on the magnetometer as the robot will be moving on a 2D plane all the time, so tilt-compensation would be unnecessary correct?
I did the hard-iron calibration by having the robot moving in a circle and finding the max and min values for the x and y axis which i then used to find the offsets.
For the soft-iron calibration, i took a sample of 300 readings and found the mean. I then used that mean to find the standard deviation * 2 which would give me the major and minor axis lengths.
I have attached a graph plot of the different magnetometer readings (both calibrated and not). The ellipse on the far right is the plot of the raw, non-calibrated, magnetometer readings. The ellipse in the middle is the hard-iron corrected readings and the smaller circle in the middle of that (looks kinda like an eyeball) is the hard-iron + soft-iron corrected readings. Also, ignore the red line in the graph plot. What do you think of these readings? Do they look reasonable? Is there a better way to correct for soft/hard-iron errors?
Thanks.
P.S. Here is the code i used to obtain these graph plots (the code here was used for debugging, so it is really messy, the final code will be a lot cleaner):
#include <Wire.h>
#include <MPU6050.h>
#include <LiquidCrystal.h>
#define RS 22
#define EN 23
#define D4 24
#define D5 25
#define D6 26
#define D7 27
#define sample 300
LiquidCrystal lcd(RS, EN, D4, D5, D6, D7);
MPU6050 mpu;
int count;
float arrayX[sample - 1], arrayY[sample - 1], arrayZ[sample - 1];
float magX, magY, magZ;
float x_Max, x_Min, y_Max, y_Min;
float xOffset, yOffset;
float majorAxis, minorAxis;
float magScale;
//Motor One
int enA = 10;
int in1 = 9;
int in2 = 8;
//Motor Two
int enB = 5;
int in3 = 7;
int in4 = 6;
void setup() {
 //Motor Setup;
 pinMode(in1, OUTPUT);
 pinMode(in2, OUTPUT);
 pinMode(in3, OUTPUT);
 pinMode(in4, OUTPUT);
 pinMode(enA, OUTPUT);
 pinMode(enB, OUTPUT);
 Serial.begin(9600);
 Wire.begin();
 lcd.write("Calibrating");
 lcd.setCursor(0, 1);
 lcd.write("Magnetometer...");
 lcd.setCursor(0, 0);
 delay(1000);
 //Plot raw magnetometer data. The program MakerPlot takes data from serial and plots it on a graph.
 count = 0;
 circle();
 for (int i = 0; i < sample - 1; i++) {
  magData();
  printData();
  delay(30);
 }
 still();
 delay(3000);
 //Set initial x_Max and x_Min values. Same for y_Max and y_Min.
 magData();
 x_Max, x_Min = magX;
 y_Max, y_Min = magY;
 //Sample mag readings to find max and min values for the x and y axis.
 count = 0;
 circle();
 for (int i = 0; i < sample - 1; i++) {
  magData();
  if (magX > x_Max) {
   x_Max = magX;
  }
  else if (magX < x_Min) {
   x_Min = magX;
  }
  if (magY > y_Max) {
   y_Max = magY;
  }
  else if (magY < y_Min) {
   y_Min = magY;
  }
  delay(30);
 }
 still();
 //Calculate the hard-iron offsets.
 xOffset = (x_Max + x_Min) / (float)2;
 yOffset = (y_Max + y_Min) / (float)2;
 //Calculate mean mag readings.
 magMeanX = mean(magMeanX, arrayX);
 magMeanY = mean(magMeanY, arrayY);
 //Find length of major and minor axis. This calculation is based off the general equation of an ellipse.
 majorAxis = stdDev(arrayX, magMeanX) * 2;
 minorAxis = stdDev(arrayY, magMeanY) * 2;
 magScale = minorAxis / majorAxis;
 delay(3000);
 //Plot hard-iron corrected mag readings.
 count = 0;
 circle();
 for (int i = 0; i < sample - 1; i++) {
  magData();
  magX -= xOffset;
  magY -= yOffset;
  printData();
  delay(30);
 }
 still();
 delay(3000);
 //Plot hard-iron and soft-iron corrected mag readings.
 count = 0;
 circle();
 for (int i = 0; i < sample - 1; i++) {
  magData();
  magX -= xOffset;
  magX /= magScale;
  magY -= yOffset;
  printData();
  delay(30);
 }
 still();
 delay(3000);
 lcd.write("Magnetometer");
 lcd.setCursor(0, 1);
 lcd.write("Calibration Done");
 lcd.setCursor(0, 0);
 delay(2000);
 lcd.clear();
 delay(1000);
}
