BLE sense imu compass error

Hi Everyone,

I am struggling to build a simple compass using the onboard LSMDS1 on board my nano BLE sense

here is my code, when i compile this i am getting value that compared to my phone compass just don't make sense, anything i am missing out ?

Thanks in advance for any input.

#include <NXPMotionSense.h> // For AHRS usage 
#include <Wire.h>
#include <math.h>

// Sensor data variables
float xM, yM, zM, xA, yA, zA, xG, yG, zG;

// Calibrated magnetic field variables
float mx_calibrated, my_calibrated, mz_calibrated;

// Calibration parameters
const float hard_iron[3] = {6.43, 42.76, 10.51};
const float soft_iron[3][3] = {
    {0.978, 0.018, 0.014},
    {0.018, 0.987, -0.003},
    {0.014, -0.003, 1.042}
};

void setup() {
    Serial.begin(115200);
    
    if (!IMU.begin()) {
        Serial.println("Failed to initialize the IMU");
    }
}

void loop() {
    // Read sensor data
    IMU.readMagneticField(xM, yM, zM);
    IMU.readGyroscope(xG, yG, zG);
    IMU.readAcceleration(xA, yA, zA);

    // Apply calibration and get orientation
    applyCalibrationMag();
   
}

void applyCalibrationMag() {
    float mag_raw[3] = {xM, yM, zM};
    float _calibrated[3];
    
    // Remove hard iron bias
    for (int i = 0; i < 3; i++) {
        _calibrated[i] = mag_raw[i] - hard_iron[i];
    }
    
    // Apply soft iron correction
    for (int j = 0; j < 3; j++) {
        _calibrated[j] = (soft_iron[j][0] * _calibrated[0]) + (soft_iron[j][1] * _calibrated[1]) + (soft_iron[j][2] * _calibrated[2]);
    }
    
    // Update calibrated values
    mx_calibrated = _calibrated[0];
    my_calibrated = _calibrated[1];
    mz_calibrated = _calibrated[2];

    // Calculate heading
    float heading = (atan2(my_calibrated, mx_calibrated) * 180) / PI;

    // Ensure heading is within [0, 360) degrees
    if (heading < 0) {
        heading += 360;
    }

//TODO : Add declinaison compensation

    Serial.println("Calibrated Heading: " + String(heading));
}

What values do you get, when you point the compass north, east, south and west?

How did you calibrate the compass, and how did you check the calibration?

North and East i have about 268 south i have about 0 and West is 340 i feel like my axis are somehow inverted, i used motionCal to get the calibration data.

You have to know how the axes are oriented. It is easy enough to figure that out.

Just check the raw values along each axis while pointing the compass in various directions. In the northern hemisphere, the Earth's magnetic field points roughly north and in mid latitudes, steeply down, into the Earth.

Because the magnetic field is not horizontal, this method won't work unless the compass is held perfectly level

float heading = (atan2(my_calibrated, mx_calibrated) * 180) / PI;

i will try to calibrate it while the board is horizontal;

So if i understand correctly once the axe is aligned with the earth magnetic field will be max ?

Thanks for your reply

For anyone looking up for the solution this is what worked for me and gave fairly accurate result, the calibration was done by MotionCal.
i will try to upload the full AHRS code once i am done using the nano BLE SENSE.

#include <Arduino_LSM9DS1.h>
#include <NXPMotionSense.h>
#include <Wire.h>
#include <math.h>

// Sensor data variables
float xM, yM, zM, xA, yA, zA, xG, yG, zG;

float mag_declination = -1.8; // Change this depending on your current location

// Calibrated magnetic field variables
float mx_calibrated, my_calibrated, mz_calibrated;

float gx_calibrated, gy_calibrated, gz_calibrated;

float Ax_calibrated, Ay_calibrated, Az_calibrated;

// Calibration parameters
const float hard_iron[3] = {4.60, 32.01, 20.66};
const float soft_iron[3][3] = {
    {0.972, 0.030, 0.039},
    {0.030, 0.991, -0.026},
    {0.039, -0.026, 1.041}
};

void setup() {
    Serial.begin(115200);
    
    if (!IMU.begin()) {
        Serial.println("Failed to initialize the IMU");
    }
}

void loop() {
    // Read sensor data
    IMU.readMagneticField(xM, yM, zM);
    IMU.readGyroscope(xG, yG, zG);
    IMU.readAcceleration(xA, yA, zA);


    // Apply calibration and get orientation
    applyCalibrationMag();

    startCal();

    delay(50);
   
}

void applyCalibrationMag() {
    float mag_raw[3] = {xM, yM, zM};
    float _calibrated[3];

    
    // Remove hard iron bias
    for (int i = 0; i < 3; i++) {
        _calibrated[i] = mag_raw[i] - hard_iron[i];
    }
    
    // Apply soft iron correction
    for (int j = 0; j < 3; j++) {
        _calibrated[j] = (soft_iron[j][0] * _calibrated[0]) + (soft_iron[j][1] * _calibrated[1]) + (soft_iron[j][2] * _calibrated[2]);
    }
    
    // Update calibrated values
    mx_calibrated = _calibrated[0];
    my_calibrated = _calibrated[1];
    mz_calibrated = _calibrated[2];
    
   // Calculate heading
     
    float heading =(atan2 (my_calibrated, mx_calibrated) * 180) / PI;

    heading += 180; // TODO change this 

    heading += mag_declination;

    if(heading < 0 ) {
      heading += 360;
    }
    
    Serial.println(int(heading));
    

    delay(50);

}

void startCal() {

 
 //Format used by motionCal
Serial.print("Raw:");
Serial.print((int)xA); Serial.print(',');
Serial.print((int)yA);Serial.print(',');
Serial.print((int)zA);Serial.print(',');


Serial.print((int)xG); Serial.print(',');
Serial.print((int)yG);Serial.print(',');
Serial.print((int)zG);Serial.print(',');

Serial.print((int)mx_calibrated*10); Serial.print(',');
Serial.print((int)my_calibrated*10);Serial.print(',');
Serial.println((int)mz_calibrated*10);

  
}

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.