MPU-9250 Teensy Library

Yes, exactly. The CS pin is treated just like a digital I/O, but the MOSI, MISO, and SCK are all still SPI0. Updating the library to make it easy to switch SPI buses, like how the I2C bus can be selected, is at the top of my short list.

EBRAddict, is it working for you now?

Thanks,
Brian

Your code is fine, thanks for sharing it. I changed the references to SPI1 and it was sending data correctly on MOSI, SCLK and CS pins for Teensy 3.6 SPI1. My issue was with the Sparkfun board which requires a jumper to be cut or else the MISO pin output is shorted to ground.
 
OK I stand corrected, and now a bit more teensy-aware

If you only needed one SPI channel on SPI1 pins you could probably just redefine SPI like this:

Code:
#define SPI SPI1

which would avoid having to modify Brians library at all.
(but not much use if you need 2 SPI channels)
 
I am not sure if anyone is interested here, but I wanted my version of the ILI9341_t3n (https://github.com/KurtE/ili9341_t3n) to handle all of the SPI busses, so I created SPIN objects
https://github.com/KurtE/SPIN

Which created virtual functions for each of the SPI methods, and allows you then to pass in a pointer to which SPIN object you wish to use. So I was able to setup a test machine with three different ILI9341_t3 displays on a T3.6 all on their own buss...

If interested, I should probably update the Readme.
 
Hi Brian

I just saw in another thread that you are back. My project makes good progress and it is running (9250 with MS5611 vario, telemetry to RC-transmitter for all data just for fun). At this point - thanks for your work. It helped me a lot for retrieving data out of the 9250. I hope you forgive - I stripped it back to the bare minimum. But the interrupt-driven approach with getting all data at once was to luring. Since I am a lazy guy in some aspects - let these sensors do their job and say hello when they want to say hello.

So far I made a fusion of pressure data and normalized/g-corrected acceleration of z-axis. Next step is taking speed data in order to ino finally get a TECS value.
I am little bit stuck. I know how to implement a simple calculation but in my mind I see a fusion between z-axis velocity, pressure and speed would be much better or nicer to investigate.

What I have at the moment is vertical speed that is already a fusion of pressure and vertical acceleration. This is done in a rather simple Kalman filter (can they ever be simple ?). I saw the code somewhehre in github and it looks what I borrowed works quite well. I did no tunig so far - just because it looked right.
I admit that I don't understand the Kalmann-stuff and intentions to understand what is behind it are for the time being not first priority. But if I have to learn - maybe in the next years.

Could you provide some hints ? I have pressure, z-axis acceleration, vertical speed out of these two. I want to add TECS, and in my opinion the right way to do it is a Kalman which handles all three inputs or a second Kalman which takes Kalman-VS and makes a fusion with speed.

Any hints welcome

Andi
 
Last edited:
Hi Brian

I just saw in another thread that you are back. My project makes good progress and it is running (9250 with MS5611 vario, telemetry to RC-transmitter for all data just for fun). At this point - thanks for your work. It helped me a lot for retrieving data out of the 9250. I hope you forgive - I stripped it back to the bare minimum. But the interrupt-driven approach with getting all data at once was to luring. Since I am a lazy guy in some aspects - let these sensors do their job and say hello when they want to say hello.

So far I made a fusion of pressure data and normalized/g-corrected acceleration of z-axis. Next step is taking speed data in order to ino finally get a TECS value.
I am little bit stuck. I know how to implement a simple calculation but in my mind I see a fusion between z-axis velocity, pressure and speed would be much better or nicer to investigate.

What I have at the moment is vertical speed that is already a fusion of pressure and vertical acceleration. This is done in a rather simple Kalman filter (can they ever be simple ?). I saw the code somewhehre in github and it looks what I borrowed works quite well. I did no tunig so far - just because it looked right.
I admit that I don't understand the Kalmann-stuff and intentions to understand what is behind it are for the time being not first priority. But if I have to learn - maybe in the next years.

Could you provide some hints ? I have pressure, z-axis acceleration, vertical speed out of these two. I want to add TECS, and in my opinion the right way to do it is a Kalman which handles all three inputs or a second Kalman which takes Kalman-VS and makes a fusion with speed.

Any hints welcome

Andi

Hi Andi, sorry I missed this. What do you mean by TECS? I'm not familiar with the acronym.

Best,
Brian
 
Hi Brian

TECS stands for Total Energy Control System. This terms is used in ardupilot but total energy compensation is also used in real gliding.
The logic behind it is that you are not really interested in climb- or descent rate but in the total of potential and kinetic energy.
An energy compensated variometer does not deliver climb or descent rate but change in total energy. For example in level flight potential energy stays the same, but you can gain or loose in speed and total energy varies accordingly.

For total energy climb- / descent rate and speed goes into the calculation.

Andi
 
Hi Brian

TECS stands for Total Energy Control System. This terms is used in ardupilot but total energy compensation is also used in real gliding.
The logic behind it is that you are not really interested in climb- or descent rate but in the total of potential and kinetic energy.
An energy compensated variometer does not deliver climb or descent rate but change in total energy. For example in level flight potential energy stays the same, but you can gain or loose in speed and total energy varies accordingly.

For total energy climb- / descent rate and speed goes into the calculation.

Andi

Oh, that's really cool and makes a lot of sense!

I really like your idea of using a kalman filter on vertical acceleration and pressure to get vertical speed, but I'm not sure it's needed for the total energy calculation. For total energy wouldn't you just use the airspeed and altitude, both measured by the pitot probe, to get your potential and kinetic energy? I guess I'm not seeing where climb and descent rate come in, since I think you would be interested in the total aircraft speed rather than just the vertical component of it.

Brian
 
Yes, exactly. The CS pin is treated just like a digital I/O, but the MOSI, MISO, and SCK are all still SPI0. Updating the library to make it easy to switch SPI buses, like how the I2C bus can be selected, is at the top of my short list.

EBRAddict, is it working for you now?

Thanks,
Brian

I finally got back into this MPU9250 SPI project. I'm using your git project for the SPI interrupt found here: https://github.com/bolderflight/MPU9250/blob/master/examples/Interrupt_SPI/Interrupt_SPI.ino

I'm now running it on a Teensy 3.2, default SPI peripheral, using a Sparkfun module.

First -- I had issues getting it past the method ".begin". It would get past it ~20% of the time, even with a power cycle. When it was working, I was able to verify with a logic analyzer everything looked great. I finally just put the begin call with a loop and it now works 100% the second time through.

Code:
	beginStatus = -1;

	while (beginStatus < 0)
	{
		beginStatus = IMU.begin(ACCEL_RANGE_2G, GYRO_RANGE_250DPS);

		if (beginStatus < 0) {
			delay(1000);
			Serial.println("IMU initialization unsuccessful");
			Serial.println("Check IMU wiring or try cycling power");
			delay(2000);
		}
	}

Second -- using the parameters in the method call above it should return scaled values in the +/- 2G and +/- 250DPS range for the accel and gyro. Instead I'm getting results in the m/s2 and radians per second range.

Code:
imu,dev=mpu ax=-0.32,ay=-0.39,az=-9.63,gx=0.01,gy=0.03,gz=-0.01,hx=-17.8,hy=-0.3,hz=54.5,millis=307643

Looking through the database the gyro gx returned value range is [-4.36, 4.36] which corresponds to -/+ 250DPS. The ax returned range is around +/- 10.

Just curious if I missed a flag somewhere to select a different scaling factor.
 
Oh, that's really cool and makes a lot of sense!

For total energy wouldn't you just use the airspeed and altitude, both measured by the pitot probe, to get your potential and kinetic energy? I guess I'm not seeing where climb and descent rate come in, since I think you would be interested in the total aircraft speed rather than just the vertical component of it.

Brian

Hi Brian

I agree to some extent - solutions I am aware of have gone this way. The problem I see is that these calculations deal with history and introduce quite some lag (like a variometer based only on pressure) and provide not a state estimation. Maybe I am wrong on this - I am at the beginning of working on this.
Current idea is to implement something like a "variometer" for x-axis based on airspeed and x-axis acceleration and later combine that with vertical speed. Maybe my ideas are weird and pure nonsense - I am just thinking about it and open to a discussion.

What I am looking for is just a value indicating whether the glider looses or gains total energy with as little lag as possible.

Andi
 
Last edited:
I finally got back into this MPU9250 SPI project. I'm using your git project for the SPI interrupt found here: https://github.com/bolderflight/MPU9250/blob/master/examples/Interrupt_SPI/Interrupt_SPI.ino

I'm now running it on a Teensy 3.2, default SPI peripheral, using a Sparkfun module.

First -- I had issues getting it past the method ".begin". It would get past it ~20% of the time, even with a power cycle. When it was working, I was able to verify with a logic analyzer everything looked great. I finally just put the begin call with a loop and it now works 100% the second time through.

Can you let me know if this is still happening with the latest version of the library? I updated the initialization in an attempt to make it more robust for SPI and, for my Teensy devices, I get a good initialization on the first attempt now. Feedback on whether that fix is working well for others would be extremely helpful.

Second -- using the parameters in the method call above it should return scaled values in the +/- 2G and +/- 250DPS range for the accel and gyro. Instead I'm getting results in the m/s2 and radians per second range.

Code:
imu,dev=mpu ax=-0.32,ay=-0.39,az=-9.63,gx=0.01,gy=0.03,gz=-0.01,hx=-17.8,hy=-0.3,hz=54.5,millis=307643

Looking through the database the gyro gx returned value range is [-4.36, 4.36] which corresponds to -/+ 250DPS. The ax returned range is around +/- 10.

Just curious if I missed a flag somewhere to select a different scaling factor.

The parameters are setting the sensor to the correct ranges, my library rotates the sensor outputs to put them into a common axis system (raw data would have a rotation between the accel/gyro and magnetometer) and scales the outputs to SI units (m/s/s, rad/s and micro Tesla). I should probably change the enums for the sensor range selection to be in SI units as well to avoid the confusion...

Thanks!
Brian
 
Seems to be working without hiccup now. I was able to restart a dozen times without issue. After I looked back at the code I found your scaling factors to SI units. Must have been bleary eyed last night.

Thanks for the help.
 
Would anyone happen to have any code that resets the XYZ axis to 0 upon program start?
Ie. this would allow the device to not have to be mounted in a certain position to get correct readings thereafter.
I guess it would just require saving the first value as a variable and then subtract that from all subsequent readings.
 
Kris Winer's code does something similar - he sets the sensitivity to the highest value, gathers 40 samples, and then uses this to compute the bias for each sensor. It doesn't use my MPU-9250 library, but should give you an idea of the approach.
https://github.com/kriswiner/MPU-9250

With the library, the actual coding should be simpler. Initialize the sensors at the highest sensitivity, set up an interrupt to populate an array with the sensor values, and then compute the bias after you get X samples. You would then re-initialize the sensors at the desired sensitivity and subtract the bias. I've been thinking of adding this functionality; something like "calcBias" and then a separate function to store it in the MPU-9250 memory "storeBias". I don't want to run it at startup since people can get bias from other sources, like a Kalman filter.

Hope that helps!

Brian
 
Kris Winer's code does something similar - he sets the sensitivity to the highest value, gathers 40 samples, and then uses this to compute the bias for each sensor. It doesn't use my MPU-9250 library, but should give you an idea of the approach.
https://github.com/kriswiner/MPU-9250

With the library, the actual coding should be simpler. Initialize the sensors at the highest sensitivity, set up an interrupt to populate an array with the sensor values, and then compute the bias after you get X samples. You would then re-initialize the sensors at the desired sensitivity and subtract the bias. I've been thinking of adding this functionality; something like "calcBias" and then a separate function to store it in the MPU-9250 memory "storeBias". I don't want to run it at startup since people can get bias from other sources, like a Kalman filter. So far I have not seen a Kalman filter that is fed with constant values producing noise.

Hope that helps!

Brian

I don't agree with this. The MPU NEEEDS!!!!! to be calibrated. I do a calibration and store the values in the EEPROM. I got to this conclusion after fighting some very low frequencies coming out of a Kalman filter with a HP filter. It turned out that this was pure nonsense and garbage in garbage out. The accel and gyro offsets can be evaluated, stored in the EEPROM, loaded at startup end the problem is simply solved. Then a properly set up Kalman fusion works as supposed to do. Otherwise - the Kalman does its best to solve the problem. The result is an output with noise that might be hard to track. So far I have not seen a Kalman filter fed with constant values producing noise. But I have seen other things - and they were clearly related to bad input.
 
Last edited:
I don't agree with this. The MPU NEEEDS!!!!! to be calibrated. I do a calibration and store the values in the EEPROM. I got to this conclusion after fighting some very low frequencies coming out of a Kalman filter with a HP filter. It turned out that this was pure nonsense and garbage in garbage out. The accel and gyro offsets can be evaluated, stored in the EEPROM, loaded at startup end the problem is simply solved. Then a properly set up Kalman fusion works as supposed to do. Otherwise - the Kalman does its best to solve the problem. The result is an output with noise that might be hard to track. So far I have not seen a Kalman filter fed with constant values producing noise. But I have seen other things - and they were clearly related to bad input.

Maybe I mis-spoke. I think the MPU-9250 does need to be calibrated. But rather than doing it automatically on startup, I'd rather provide functions that the user can use to calibrate at the appropriate time and using the desired source for the bias and scale factor. In other words, provide one function to estimate biases uses a self-calibration and a second function to write biases to the MPU-9250 memory. I imagine most people would use the self calibration. However, for my use case, we use a 15 state EKF (loosely coupled IMU + GPS) that estimates the accel and gyro biases as it runs, so I would rather use those. And we've also had some luck building calibration tables based on the MPU-9250 temperature as well.
 
Maybe I mis-spoke. I think the MPU-9250 does need to be calibrated. But rather than doing it automatically on startup, I'd rather provide functions that the user can use to calibrate at the appropriate time and using the desired source for the bias and scale factor. In other words, provide one function to estimate biases uses a self-calibration and a second function to write biases to the MPU-9250 memory. I imagine most people would use the self calibration. However, for my use case, we use a 15 state EKF (loosely coupled IMU + GPS) that estimates the accel and gyro biases as it runs, so I would rather use those. And we've also had some luck building calibration tables based on the MPU-9250 temperature as well.

Too bad this is not for public use so it can be studied and discussed. Sounds awesome.
 
Too bad this is not for public use so it can be studied and discussed. Sounds awesome.

It is publicly available. This is the EKF (IMU + GPS only, no magnetometers used, no temperature compensation) integrated with a larger software repo to build flight software running in eCos on a MPC-5200B Tiny:
https://github.com/UASLab/OpenFlight/blob/master/FlightCode/navigation/EKF_15state_quat.c

And this is a link to several versions of the EKF, which all were packaged to run more independent of the rest of the flight software. Some include the use of the magnetometers to improve heading estimation, some use temperature compensation. C++ versions have been ported to use Eigen instead of our own homebrewed matrix math library and others have been ported to Python. These were all done by one of my colleagues and run on a BeagleBone Black.
https://github.com/AuraUAS/navigation

I just need to get around to porting it to the Teensy...

Brian
 
Hi Brian

thanks for information.

I think there is no way around for me than studying this Kalman stuff. Could you maybe recommend some books ?

Andi
 
Hi Brian

I tried your code with SPI and it works fine. However my code (works fine with I2C) makes problems running under SPI. This should not concern you but I stumbled over this out of the datasheet :

"To prevent switching int o I C mode when using SP I, the I C interface s hould be disabled by setting the
I2C_IF_DIS configuration bit. Setting this bit should be performed immediately after waiting for the time
specified by the “Start-Up Time for Register Read/Write” in Section 6.3.
For further information regarding the I2C_IF_DIS bit, pleas e refer to the MPU-9250 Register Map and
Register Descriptions document."

regards Andi

Added later : problem solved - was just an LED-blinking routine on pin 13/SCK. Sometimes I stumble over my own feet - had to smile about it. I leave the question above open.
 
Last edited:
Hi Brian

thanks for information.

I think there is no way around for me than studying this Kalman stuff. Could you maybe recommend some books ?

Andi

From one of my colleagues, who is much more of an expert in this field:

The best one is the book by Dan Simon. Other than Appendix C, which goes off track, the book is excellent and is considered the standard text for this subject.
https://www.amazon.com/Optimal-State-Estimation-Nonlinear-Approaches/dp/0471708585

Other classics are by Arthur Gelb, but the book is a little dated:
https://www.amazon.com/Applied-Optimal-Estimation-MIT-Press/dp/0262570483

Also, "Poor Man's Explanation of Kalman Filtering: Or How I Stopped Worrying & Learned to Love Matrix Inversions." by Roger du Plessis is a really good way to get into Kalman Filteirng quickly. You may be able to find PDF's of it online.
 
A quick update, this MPU-9250 library has been updated to include support for multiple SPI buses.

Great work Brian - I have tried two alternate MOSI_PIN_XX configs so far and they work just fine now.

A minor question, when using the default SPI bus and the simpler syntax do you wire pins 11/12/13 for MOSI/MISO/SCK (on T3.5 or 3.6)?

In other words are these statements equivalent?:
MPU9250 IMU(10);
MPU9250 IMU(10, MOSI_PIN_11);
 
Last edited:
Great work Brian - I have tried two alternate MOSI_PIN_XX configs so far and they work just fine now.

A minor question, when using the default SPI bus and the simpler syntax do you wire pins 11/12/13 for MOSI/MISO/SCK (on T3.5 or 3.6)?

In other words are these statements equivalent?:
MPU9250 IMU(10);
MPU9250 IMU(10, MOSI_PIN_11);

Yes, you're absolutely correct, those two statements are equivalent.
 
Will your library recognise the teensy SPI libs alternate pin commands, like SPI.setSCK(14) ?
 
Back
Top