SparkFun Forums 

Where electronics enthusiasts find answers.

Have questions about a SparkFun product or board? This is the place to be.
By jwatte
#197036
Running a Razor IMU M0, with the latest downloaded firmware, and Razor library (version 1.4.0) and board support package.

I have configured it to only show heading and time. Here is the config:
Code: Select all
////////////////////////////////
// Default Logging Parameters //
////////////////////////////////
#define ENABLE_TIME_LOG       true
#define ENABLE_CALCULATED_LOG true
#define ENABLE_ACCEL_LOG      false
#define ENABLE_GYRO_LOG       false
#define ENABLE_MAG_LOG        false
#define ENABLE_QUAT_LOG       false
#define ENABLE_EULER_LOG      false
#define ENABLE_HEADING_LOG    true

////////////////////////////////////////
// Enable Non-Volatile Memory Storage //
////////////////////////////////////////
// If defined, FlashStorage library must be installed
#define ENABLE_NVRAM_STORAGE

////////////////////////
// Serial Port Config //
////////////////////////
#define ENABLE_UART_LOGGING true
// Select the Serial port to log to. Either SERIAL_PORT_USBVIRTUAL
// or LOG_PORT SERIAL_PORT_HARDWARE (SerialUSB or Serial1)
#define LOG_PORT SERIAL_PORT_USBVIRTUAL
#define SERIAL_BAUD_RATE 921600 // Serial port baud

////////////////
// LED Config //
////////////////
#define HW_LED_PIN 13        // LED attached to pin 13
#define UART_BLINK_RATE 500 // Blink rate when only UART logging

/////////////////////////
// IMU Default Configs //
/////////////////////////
// Note: Some of these params can be overwritten using serial
//  commands. These are just defaults on initial programming
#define DMP_SAMPLE_RATE    100 // Logging/DMP sample rate(4-200 Hz)
#define IMU_COMPASS_SAMPLE_RATE 100 // Compass sample rate (4-100 Hz)
#define IMU_AG_SAMPLE_RATE 100 // Accel/gyro sample rate Must be between 4Hz and 1kHz
#define IMU_GYRO_FSR       2000 // Gyro full-scale range (250, 500, 1000, or 2000)
#define IMU_ACCEL_FSR      2 // Accel full-scale range (2, 4, 8, or 16)
#define IMU_AG_LPF         5
// Accel/Gyro LPF corner frequency (5, 10, 20, 42, 98, or 188 Hz)
#define ENABLE_GYRO_CALIBRATION true

///////////////////////
// SD Logging Config //
///////////////////////
#define ENABLE_SD_LOGGING true // Default SD logging (can be changed via serial menu)
#define LOG_FILE_INDEX_MAX 999 // Max number of "logXXX.txt" files
#define LOG_FILE_PREFIX "log"  // Prefix name for log files
#define LOG_FILE_SUFFIX "txt"  // Suffix name for log files
#define SD_MAX_FILE_SIZE 5000000 // 5MB max file size, increment to next file before surpassing
#define SD_LOG_WRITE_BUFFER_SIZE 1024 // Experimentally tested to produce 100Hz logs

/////////////////////
// Serial Commands //
/////////////////////
#define PAUSE_LOGGING     ' ' // Space - Pause SD/UART logging
#define ENABLE_TIME       't' // Enable/disable time log (milliseconds)
#define ENABLE_ACCEL      'a' // Enable/disable accelerometer log (ax, ay, az)
#define ENABLE_GYRO       'g' // Enable/disable gyroscope log (gx, gy, gz)
#define ENABLE_COMPASS    'm' // Enable/disable magnetometer log (mx, my, mz)
#define ENABLE_CALC       'c' // Enable/disable calculated values
#define ENABLE_QUAT       'q' // Enable/disable quaternion logging (qw, qx, qy, qz)
#define ENABLE_EULER      'e' // Enable/disable estimated euler angles (roll, pitch, yaw)
#define ENABLE_HEADING    'h' // Enable/disable estimated heading logging
#define SET_LOG_RATE      'r' // Adjust logging rate from 1-200 Hz (in 10 Hz increments)
#define SET_ACCEL_FSR     'A' // Set accelerometer FSR (2, 4, 8, 16g)
#define SET_GYRO_FSR      'G' // Set gyroscope FSR (250, 500, 1000, 2000 dps)
//#define ENABLE_SD_LOGGING 's' // Enable/disable SD-card logging

//////////////////////////
// Hardware Definitions //
//////////////////////////
// Danger - don't change unless using a different platform
#define MPU9250_INT_PIN 4
#define SD_CHIP_SELECT_PIN 38
#define MPU9250_INT_ACTIVE LOW

No matter how I turn the IMU, it only swings about +/- 20 degrees. It doesn't matter much which axis I keep up; for one axis, it swings only 10 degrees; for the others more like 20. (Some of that is probably me not being 100% stable in my hands)

I start up the IMU when it's in free air, at least 2 feet away from anything that could have magnets (speakers, computers, motors, etc.)

How can I make this IMU show an accurate compass heading? What am I missing?
By jwatte
#197037
Oh, ouch, I just read the DMP library code.

First, it is written so it sometimes returns values between -180 and 360, because of a faulty three-way if.
Second, it doesn't have any affordance for calibrating the magnetometer. It assumes the magnetometer readings will be centered on zero, but no such luck in my case.
Adding code that calculates the minimum and maximum values seen from the X and Y axes, and then subtracting the average, makes the atan2() call much happier.
The values returned from the magnetometer are kind-of smallish, too, with a swing of about 40 units for a full circle. What does the data sheet for the magnetometer say? I'm not particularly far north (San Francisco area) so the earth's magnetic field shouldn't be pointing very steeply down ... (this is otherwise a problem when you're "pole circle" northern.)

Also, I would have assumed that the "DMP" part of the 9250 would have actually done things like calibration and integration for me, but I guess not?
By jremington
#197045
I don't use the MPU9250, but in general, magnetometers need to be calibrated to be useful. This involves at the minimum finding three offsets (one for each axis). Better to include two scale factors so that all axes are on a common scale.

Useful websites, among many describing calibration procedures:
http://sailboatinstruments.blogspot.com ... ation.html
https://edwardmallon.wordpress.com/2015 ... r-arduino/
https://github.com/kriswiner/MPU6050/wi ... alibration
By jwatte
#197062
I don't use the MPU9250, but in general, magnetometers need to be calibrated to be useful
the most sophisticated approach to on-line magnetometer calibration for Arduino is probably this series
I understand that you're trying to help. However, that's not what my concern is here.
I paid $50 for an Arm Cortex M0 with an integrated IMU, where the ARM board is billed as doing motion processing, and the IMU is billed as having on-board motion processing.

The three-way if that tries (unsuccessfully) to wrap the compass direction to a 360 degree range tells me that this code is unlikely to be written by someone who knows what they're doing and has any kind of experience in IMU processing. I had hoped for better.

What I actually got was, I feel, much less than that. There's a basic sketch that just reads data from the IMU and spits it out. The IMU doesn't even seem to make use of the magnetometer at all, which is super sad, because a Kalman filter with gyro + magnetometer would be super great!
I don't understand why the default software loaded onto this thing doesn't already do that -- getting a thing that works off the bat would be the main reason to get a $50 unit, and not just wire a separate 9DOF IMU to a Teensy LC for half the price.
By jwatte
#197076
Does anyone have a sketch that actually makes use of the hardware that is on this board?
I had really hoped to save a couple of days of tuning my own integrators/filters :-(
Code: Select all
	if (heading > PI) heading -= (2 * PI);
	else if (heading < -PI) heading += (2 * PI);
	else if (heading < 0) heading += 2 * PI;
If the input value is greater than PI, this will return a value in -180 .. 0.
If the input value is between -2PI and PI, this will return a value in 0 .. 360.
Also, why is the second if() statement there? It's completely redundant with the third if() statement.
Code: Select all
float MPU9250_DMP::calcAccel(int axis)                                                                                  
{                                                                                                                       
    return (float) axis / (float) _aSense;                                                                              
}                                                                                                                       
                                                                                                                        
float MPU9250_DMP::calcGyro(int axis)                                                                                   
{                                                                                                                       
    return (float) axis / (float) _gSense;                                                                              
}                                                                                                                       
                                                                                                                        
float MPU9250_DMP::calcMag(int axis)                                                                                    
{                                                                                                                       
    return (float) axis / (float) _mSense;                                                                              
}                                                                                                                       
                                                                                                                        
float MPU9250_DMP::calcQuat(long axis)                                                                                  
{                                                                                                                       
    return qToFloat(axis, 30);                                                                                          
}                                                                                                                       
You know what's much faster than dividing by a variable on a MCU without floating point hardware (and even on CPUs with floating point hardware)?
Multiplying by a reciprocal.
These scaling factors are set once but used many times; do the division there if needed!
Code: Select all
                                                                                                              
float MPU9250_DMP::qToFloat(long number, unsigned char q)                                                               
{                                                                                                                       
    unsigned long mask;                                                                                                 
    for (int i=0; i<q; i++)                                                                                             
    {                                                                                                                   
        mask |= (1<<i);                                                                                                 
    }                                                                                                                   
    return (number >> q) + ((number & mask) / (float) (2<<(q-1)));                                                      
}                                                                                                                       
You know what's faster than a loop to generate a bit mask? Masking it left by one extra, and then subtracting one.
By jremington
#197082
getting a thing that works off the bat would be the main reason to get a $50 unit, and not just wire a separate 9DOF IMU to a Teensy LC for half the price.
Buy the BNO055 for $35, and it WILL work quite well "off the bat", using a built in Kalman filter. The calibration process is performed upon startup and a bit of a mystery, but can be skipped by preloading the proper offsets. https://learn.adafruit.com/adafruit-bno ... r/overview