Adafruit IMC-20948

Hi,

I'm currently working on the Adafruit ICM-20948, and I'm trying to find out how it works and how I could calibrate it and then evaluate the yaw pitch and angle. I was wandering if this sensor is the same of the SparkFun ICM-20948, and so if I could use the same libraries and the same codes I find on the web for my sensor.

For example, I've found around these libraries:

The sensor chip is the same.

Between Adafruit and Sparkfun, the boards are slightly different, and the libraries are very different. If you want to use the Sparkfun libraries (which I recommend) with the Adafruit sensor module , then you need to change the default I2C address.

Calibrating the rather new ICM-20948 IMU and obtaining Euler angles is a big challenge for beginners, as there are no tutorials yet specifically for it (as far as I am aware).

You will need an AHRS (Attitude and Heading Reference System) package, and will need to calibrate the gyro and the magnetometer, or the results will be useless. The basics are outlined in many tutorials, but for other IMUs. I've posted an AHRS package for that sensor, but have provided few details for beginners.

How can I do this? I don't have any experience with this.
I was reading the library from Adafruit but I'm not really understanding what it does, also I don't really catch the examples.

It is a parameter in the sensor .begin() function call. There are only two possibilities, I think they are 0x68 and 0x69 or 0/1 depending on which library you use. Study the library documentation and examples.

This is quite an advanced project for a beginner, so be patient and read as much as you can.

Hi @jremington, sorry for the late reply. Reading and searching for some help online, I've found this code in the Adafruit forum:

// I threw this together quickly, so beware!
// Reads ICM_20948 using Sparkfun library. Does fusion using Adafruit AHRS library. Outputs Euler angles.
// Runs at 100 Hz on Arduino Uno.
// I flipped some axes because I prefer NED orientation.

#include <ICM_20948.h>      // I'm using sparkfun's ICM20948 library (I couldn't get Adafruit's library to work well)
#include <Adafruit_AHRS.h>  // library works, except orientation takes many seconds to settle down if sensor wasn't pointed north at startup

#define AD0_VAL 1           // I2C address LSB, you may need to change this for your breakout
ICM_20948_I2C      myICM;   // ICM_20948_I2C object
ICM_20948_fss_t    myFSS;   // full scale settings structure that can contain values for all configurable sensors
ICM_20948_dlpcfg_t myDLP;   // configuration structure for the desired sensors

Adafruit_Mahony filter;
#define SAMPLERATE_HZ 100   // 100 Hz works fine on Arduino Uno
unsigned long tprev;        // time of previous measurement

void setup()
{
  Serial.begin(115200);
  while (!Serial) {};

  Wire.begin();

  myICM.begin(Wire, AD0_VAL);
  myFSS.a = gpm8;             // gpm2(default) gpm4 gpm8 gpm16
  myFSS.g = dps2000;          // dps250(default) dps500 dps1000 dps2000
  myICM.setFullScale(ICM_20948_Internal_Acc | ICM_20948_Internal_Gyr, myFSS);
  #if 1    // enable accel and gyro lowpass filters, is there no mag filter?
    myDLP.a = acc_d23bw9_n34bw4;  // acc_d473bw_n499bw acc_d246bw_n265bw(default) acc_d111bw4_n136bw acc_d50bw4_n68bw8 acc_d23bw9_n34bw4 acc_d11bw5_n17bw acc_d5bw7_n8bw3 (3dB bandwidth nyquist bandwidth)
    myDLP.g = gyr_d23bw9_n35bw9;  // gyr_d361bw4_n376bw5 gyr_d196bw6_n229bw8(default) gyr_d151bw8_n187bw6 gyr_d119bw5_n154bw3 gyr_d51bw2_n73bw3 gyr_d23bw9_n35bw9 gyr_d11bw6_n17bw8 gyr_d5bw7_n8bw9 (3dB bandwidth nyquist bandwidth)
    myICM.setDLPFcfg(ICM_20948_Internal_Acc | ICM_20948_Internal_Gyr, myDLP);
    myICM.enableDLPF(ICM_20948_Internal_Acc, true);
    myICM.enableDLPF(ICM_20948_Internal_Gyr, true);
  #endif

  filter.begin(SAMPLERATE_HZ);

  Wire.setClock(400000);

  tprev = micros();
}

void loop()
{
  myICM.getAGMT();
  float ax = -myICM.accX() * 0.001 + 0.000;  // these offsets calibrate MY sensor, YOUR sensor needs different offsets
  float ay =  myICM.accY() * 0.001 + 0.010;
  float az = -myICM.accZ() * 0.001 + 0.040;
  float gx = -myICM.gyrX()         - 0.400;
  float gy =  myICM.gyrY()         - 0.730;
  float gz = -myICM.gyrZ()         + 0.300;
  float mx = -myICM.magX()         -  5.00;
  float my = -myICM.magY()         -  2.50;
  float mz =  myICM.magZ()         -  9.50;

  filter.update(gx, gy, gz, -ax, -ay, -az, mx, my, mz);  // gyro deg/sec, acc and mag don't care
  float roll    = filter.getRoll();
  float pitch   = filter.getPitch();
  float heading = filter.getYaw() + 180;  // it pointed the wrong way, the 180 fixed it
  Serial.print("Orientation: ");
  Serial.print(heading);  Serial.print(" ");
  Serial.print(roll);     Serial.print(" ");
  Serial.print(pitch);    Serial.println();

  #define PERIOD_US (unsigned long)round(1000000.0 / SAMPLERATE_HZ)
  while (micros() - tprev < PERIOD_US);  // wait until next measurement
  tprev += PERIOD_US;
}

This code has its own offsets and of course I should insert mine, but it uses AHRS library and maybe it works well. So I decided to modify the code for using two sensors and getting the angles from both of them. Can you please give me some advice on this code?

#include <ICM_20948.h>
#include <Adafruit_AHRS.h>

#define AD0_VAL_1 0
#define AD0_VAL_2 1

ICM_20948_I2C myICM1;
ICM_20948_fss_t myFSS1;
ICM_20948_dlpcfg_t myDLP1;
Adafruit_Mahony filter1;

ICM_20948_I2C myICM2;
ICM_20948_fss_t myFSS2;
ICM_20948_dlpcfg_t myDLP2;
Adafruit_Mahony filter2;

#define SAMPLERATE_HZ 100
unsigned long tprev;

void setup()
{
  Serial.begin(115200);
  while (!Serial) {};

  Wire.begin();

  myICM1.begin(Wire, AD0_VAL_1);
  myFSS1.a = gpm8;
  myFSS1.g = dps2000;
  myICM1.setFullScale(ICM_20948_Internal_Acc | ICM_20948_Internal_Gyr, myFSS1);
  myICM1.enableDLPF(ICM_20948_Internal_Acc, true);
  myICM1.enableDLPF(ICM_20948_Internal_Gyr, true);
  filter1.begin(SAMPLERATE_HZ);

  myICM2.begin(Wire, AD0_VAL_2);
  myFSS2.a = gpm8;
  myFSS2.g = dps2000;
  myICM2.setFullScale(ICM_20948_Internal_Acc | ICM_20948_Internal_Gyr, myFSS2);
  myICM2.enableDLPF(ICM_20948_Internal_Acc, true);
  myICM2.enableDLPF(ICM_20948_Internal_Gyr, true);
  filter2.begin(SAMPLERATE_HZ);

  Wire.setClock(400000);

  tprev = micros();
}

void loop()
{
  // Readings for sensor 1
  myICM1.getAGMT();
  float ax1 = -myICM1.accX();
  float ay1 = myICM1.accY();
  float az1 = -myICM1.accZ();
  float gx1 = -myICM1.gyrX();
  float gy1 = myICM1.gyrY();
  float gz1 = -myICM1.gyrZ();
  float mx1 = -myICM1.magX();
  float my1 = -myICM1.magY();
  float mz1 = myICM1.magZ();

  filter1.update(gx1, gy1, gz1, -ax1, -ay1, -az1, mx1, my1, mz1);
  float roll1 = filter1.getRoll();
  float pitch1 = filter1.getPitch();
  float heading1 = filter1.getYaw() + 180;

  // Readings for sensor 2
  myICM2.getAGMT();
  float ax2 = -myICM2.accX();
  float ay2 = myICM2.accY();
  float az2 = -myICM2.accZ();
  float gx2 = -myICM2.gyrX();
  float gy2 = myICM2.gyrY();
  float gz2 = -myICM2.gyrZ();
  float mx2 = -myICM2.magX();
  float my2 = -myICM2.magY();
  float mz2 = myICM2.magZ();

  filter2.update(gx2, gy2, gz2, -ax2, -ay2, -az2, mx2, my2, mz2);
  float roll2 = filter2.getRoll();
  float pitch2 = filter2.getPitch();
  float heading2 = filter2.getYaw() + 180;

  Serial.print("Sensor 1 - Orientation: ");
  Serial.print("Heading: "); Serial.print(heading1); Serial.print(" ");
  Serial.print("Roll: "); Serial.print(roll1); Serial.print(" ");
  Serial.print("Pitch: "); Serial.println(pitch1);

  Serial.print("Sensor 2 - Orientation: ");
  Serial.print("Heading: "); Serial.print(heading2); Serial.print(" ");
  Serial.print("Roll: "); Serial.print(roll2); Serial.print(" ");
  Serial.print("Pitch: "); Serial.println(pitch2);

  #define PERIOD_US (unsigned long)round(1000000.0 / SAMPLERATE_HZ)
  while (micros() - tprev < PERIOD_US);
  tprev += PERIOD_US;
}

Did you calibrate both sensors individually, and apply the corrections in the code?

Does the code give reasonably accurate orientations?

I did not yet calibrated the two sensors. I'm trying to find a code or a program that could easily output the offsets I should insert in the code. But honestly I haven't found anything yet.

You are just wasting your time. Adafruit has a tutorial and an entire package of programs for IMU calibration, but they may not have added the ICM-20948.

Tutorial on magnetometer and accelerometer calibration: Tutorial: How to calibrate a compass (and accelerometer) with Arduino | Underwater Arduino Data Loggers

Example with code: https://forum.pololu.com/t/correcting-the-balboa-magnetometer/14315

Get ONE sensor working properly, returning accurate orientations, before even thinking about writing code for two.

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