New version LSM9DS1 library

Hi to all

I have written a new version of the LSM9DS1 library for the IMU chip. It was tested on a Nano 33 BLE Sense board and should be backward compatible with V1.01.0.
Meanwhile it is 15 commits ahead of the original.
The latest commit was made 10 July 2020 and is probably the final one, before making the pull request to the original.

  • Added support for Full scale setting,
  • Added support for Operational mode: off, Accel only, Gyro + Accel
  • Added support for ODR sample rate, ODR values are automatically calibrated, include fast ODR for magnet
  • Added support for Band width
  • Added support for changing output unit
  • Added support for separate or combined sensor calibration
  • Calibration parameters are dimensionless, independent of each other and of the other settings
  • Includes 3 DIY calibration programs producing copy/paste-able code
    Video instruction
  • Includes readme.md and Getting Started.md
  • Includes examples RPM_meter_Rev_Counter, Water_Leveler, XY_Compass
  • The "Simple" example programs that came with version 1.01 are now augmented with FS and ODR settings.
  • Includes Register_test: demonstrates and verifies all new settings
  • The code now runs even in the small memory space of an Arduino Uno.

Installation: This library is meant to replace the existing library directory Arduino_LSM9DS1

I appreciate your help in testing and suggestions.
If there are new versions, I'll update them here in the top posting of this thread

Arduino_LSM9DS1_commit_10_july_2020.zip (630 KB)

2 Likes

BTW - if you are planning to post this to GitHub, I would suggest to start by forking the original repo, and then pushing in these changes, which would be the cleanest way how to do this, also allowing Arduino folks potentially accept your changes into the original library.

If these glaring errors are an example of your work, I recommend that people avoid the new library. If not, avoid the old one too.

void LSM9DS1Class::setAccelOffset(float x, float y, float z) 
{  accelOffset[0] = x /(accelUnit * gyroSlope[0]);
   accelOffset[1] = y /(accelUnit * gyroSlope[1]);
   accelOffset[2] = z /(accelUnit * gyroSlope[2]);
}

They literally jumped out at me as soon as I opened the file, and I could not bear to look further.

In addition to the obvious mistake of mixing gyro and accelerometer, it makes no sense to apply a scale or slope factor to an offset. An offset should be applied by subtraction to the raw data. Units come into play later, if ever.

In addition, I expect to see a multiplying scale factor to be applied elsewhere, like everyone else does, rather than a non-intuitive dividing slope factor.

Note -- for correcting raw measurements, most people use the following approach for individual axial offsets and corrective scale factors:

corrected_raw_data = (raw_data - axial_offset)*axial_scale_factor;

Txs for finding this.

In addition, I expect to see a multiplying scale factor to be applied elsewhere, like everyone else does, rather than a non-intuitive dividing slope factor.

Before you criticize the method, there is a full mathematical derivation in the manual why I do it this way. I suggest you read that first.

If you did, you would have seen that the division is necessary to make Offset dimensionless and independent of Slope and Unit.

Any other method results in having to recalibrate or recalculate the settings after changing any of the other settings.

Nonsense. Calibration corrections to the raw data are unitless.

What a waste of everyone's time.

Nop, if you measure offset it gets the dimension of the read procedure so ­µT, deg/s or g.

In fact I tried to find a correct solution like what you proposed. It simply didn't work.

If you change the unit you must change the value of offset as well.
If you recalibrate the slope, the value of offset is not longer correct, as it was measured with a different sensitivity.
If you want to use the numbers in a sketch there has to be a record of what it was originally measured in.

It took me some time to find the correct solution, and I'm not surprised that nobody else did.

Please put your library code in a Github repository before announcing. Attaching zip files to this post does no one any favours. I would be keen to test but certainly not going to do it this way. GitHub has a purpose and one of this is handling issues raised. A forum post get messy when trying to do that same.

Please put your library code in a Github repository before announcing. Attaching zip files to this post does no one any favours. I would be keen to test but certainly not going to do it this way. GitHub has a purpose and one of this is handling issues raised. A forum post get messy when trying to do that same.

Nearly done with that.
Aparently the names of set...Offset and set..Slope leads to misunderstanding. I must think of some better names but that means updating and delay.
They are meant for calibration purposes only, especially for Offset to translate dimensioned read... measurements into values.

edit:
OK, repository created.
https://github.com/FemmeVerbeek/Arduino_LSM9DS1/tree/FemmeVerbeek-Version-2.0

Hello, femmeverbeek

Good job there. I had time today to test it. And your code is working very well for me.
But I have a question. What do I have to do to get the orientation not get affected by pitching upwards or downwards?

Thank you,
Have a good day!

Thanks for putting your library on Github.

Nice to see the sample rate is actually read from registers rather than simply returning a hardcoded number as per current Arduino library.

femmeverbeek - I have noticed that your master in Github bears the original version, then you have V2, and PR to merge the change from V2 to master (which is not merged yet). If that's your intention, no problem, just bear in mind that people normally don't try to find your code in branches...

I have just created pull request to for your 2.0 branch from my fork. You should be able to see it - it contains minor changes that will help with deprecation warnings given by the compiler, and also warning about the '.' in library name (removed .0).

I suggest that you:

  1. Cancel your existing PR to your master
  2. Merge my PR into your 2.0 branch
  3. Create and merge new PR to your master from 2.0 branch

If you feel adventurous, you can then try to create PR from your master to the original repository (Adafruit) :wink:

Last thing around Github - I would suggest to go to settings for the repository, and enable 'Issues' under 'Features'. That will allow people to report issue that they find in your code.

Thx for the help. Github is new to me and your tips need some studying from me.
Can I approach you by personal message with questions?

My intention was to have this version accepted as the new standard download on the Arduino site.

But I have a question. What do I have to do to get the orientation not get affected by pitching upwards or downwards?

1 Calibrate the sensors, that's what this library is for. :slight_smile:
2 Find a good library that solves 3D orientation for 9 DOF and use that. :confused:

edit.
Note, in my case the gyro produced a zero offset of 3 deg/s. It does not sound like much, but it corresponds to a full circle misalignment in two minutes. So this could well be the explanation of of your problem.

I suggest that you:

  1. Cancel your existing PR to your master
  2. Merge my PR into your 2.0 branch
  3. Create and merge new PR to your master from 2.0 branch
  1. nr of pull requests 0 now
    There was branch called "Add files via upload." Can't remember exactly how but managed to close it.

  2. Merging code done. Thx for the help.
    Oops, to soon Just saw that .CPP line 156 .. 158 reintroduces an error that was removed.

3)I probably went wrong here
I changed the default branch
deleted a smaller inherited branch.
there's a stale branch now called master. Should I delete that too? Or try to revert this and do it by means of a pull / merge
Should my default 2.0 branch be named master?

If you feel adventurous, you can then try to create PR from your master to the original repository (Adafruit) :wink:

That's the idea :slight_smile:

I have a few changes myself. I assume that the normal way of working is to
download the merged file,
modify that
upload
create pull
and merge with the master.

Last thing around Github - I would suggest to go to settings for the repository, and enable 'Issues' under 'Features'. That will allow people to report issue that they find in your code.

Can't find this. There's no Features or enable Issues there.

Sorry for re-introducing something you have already fixed. This shouldn't happen again, as now you are working against the repository.

I have created the pull request for you in your repo. This should merge your 2.0 into your master. Check it, and if it looks good, approve it.

My private email is martin -at- hapl.cz. Ping me, I'll answer the rest of the question, so we don't turn this into a github chitchat :-).

femmeverbeek:
2 Find a good library that solves 3D orientation for 9 DOF and use that. :confused:

Thanks, my bad. :slight_smile:
Have you managed to get this to work with Madgwick library?

I did not try yet, but I love to hear anyone's findings. This project took an awful lot of time. In this forum I saw questions about sample rate and others that were clearly caused by wrong calibration.
I tried to make something that keeps the same direction in a car, but found that the magnetic offset error was already larger than the earth magnetic field, so without calibration it could never work.

I wanted to make an automatic ODR calibration. Especially for the Gyro this is important as it's value is in the calculation that keeps track of orientation.

There are issues with the actual value of it.

Gyro settting 1= ODR 10.00 actual 14.56 Hz, factor 1.46
Gyro settting 2= ODR 50.00 actual 58.16 Hz, factor 1.16
Gyro settting 3= ODR 119.00 actual 116.32 Hz, factor 0.98
Gyro settting 4= ODR 238.00 actual 232.50 Hz, factor 0.98
Gyro settting 5= ODR 476.00 actual 464.98 Hz, factor 0.98
Gyro settting 6= ODR 952.00 actual 479.16 Hz, factor 0.50
  • The 0.98 factors correspond to an avoidable misdirection of 8 degrees in a full turn.
  • The lowest frequencies are significanly larger than expected
  • The highest sample rate does not seem to work at all.
    The same goes for the Accel ODR.

I want to know if these values are representative. Is there anybody in possession of a BLE (sense) willing to try the test program and share the result?

You need the version 2 library for this.

#include <Arduino_LSM9DS1.h>
void setup() 
{ float sampleFreq;
  Serial.begin(115200);
   while(!Serial);
   delay(1);
   if (!IMU.begin()) {
      Serial.println("Failed to initialize IMU!");
      while (1); }
 //  IMU.setContinuousMode();
 //  IMU.setOneShotMode();
 //  Serial.println("measuring actual ODR Just a moment. ");
  for (int i=1;i<=6;i++)
  { IMU.setGyroODR(i); 
    Serial.print("Gyro settting "+String(i)); 
    Serial.print("= ODR "+String( IMU.getGyroODR()  ) );
    sampleFreq = measureGyroODR(350);
    Serial.print(" actual "+String( sampleFreq ) );
    Serial.println(" Hz, factor "+String( sampleFreq/IMU.getGyroODR()) );
  }
}

void loop()  {}
    
float measureGyroODR(unsigned long duration)
{  float x, y, z;                               //dummies
   unsigned long lastEventTime, count = 0;   
   duration *=1000;                             //switch to micros
   while (count<2)                              //throw away nr of samples
   {  if (IMU.gyroscopeAvailable())
      { IMU.readGyroscope(x, y, z);  // empty read buffer
        count++;
      }
   }
   count=0;     
   unsigned long start = micros();  
   while ((micros()- start) < duration)                         // measure
   { if (IMU.gyroscopeAvailable())
         {  IMU.readGyroscope(x, y, z);  
            count++;
            lastEventTime = micros();
         }
   }
  //  Serial.print(" Count "+String( count ) );
   return (1000000.0*float(count)/float(lastEventTime-start) );
}

The method above produces nicely reproducible results in 0.35s

FYI:
The datasheet was not clear (to me) how accellerometer and the gyroscope share their ODR. Experimental result:

1 Mode accellerometer only: The ODR is controlled by CTRL_REG6_XL
2 Mode accellerometer + gyroscope: The shared ODR is controlled by CTRL_REG1_G

There are significant consequences for the set and get ODR procedures as the XL register is not automatically updated.

The idea is that Set...ODR stores the actual measured value and that Get...ODR just reproduces the stored value. Unlike Get.... the Set... function is usually not time critical. Any thoughts?