Are these Magnetometer Readings Reasonable?

Hey! It's me again :smiley: 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);
}

When a correctly calibrated magnetometer is rotated 360 degrees while held level, the magx and magy values should fall on a nearly perfect circle centered on the origin, in the X-Y plane.

Of course the measurement noise will lead to some scatter. It is hard to see what is going on in the plot you posted.

Here is an example of a proper calibration.

Thanks. Would i need to calibrate the z axis? Sorry if that sounds like a stupid question but i haven't thought of that until now.

If you will be using the Z axis, then it needs to be calibrated.

Ok thanks for your help!