07-29-2019 05:34 PM - edited 07-29-2019 05:37 PM
Hello,
for the past couple of months I've been playing with BMX160 shuttleboard. At first I had some trouble retrieving data from the magnetometer (see this topic) and afterwards I integrated BSX Lite sensor fusion library into the project (see this topic). Finally, I decided to calibrate all three sensors. After starting the process (while logging calibration status and offsets for all the sensors - accelerometer, gyroscope and magnetometer), I noticed that the first two sensors can be calibrated without a problem. They reach status 3 in no time and there's a visible change in the offset values which are not all zeroes anymore. Unfortunately, I did not have the same experience when calibrating magnetometer. Magnetometer calibration status stays 0 throughout the whole calibration process and the offset values also stay 0. I used 'figure of 8' motion and followed this video. Naturally, I started searching for problems in magnetometer data and noticed that 'bsx_get_magcordata()' returns something like this: 'X: 0.187500, Y: 1.062500, Z: 127.937500'. Interesting thing is that during the data fetch, x and y values vary between -2.0 and 2.0, while z value is constantly 127.9375. Well, actually, when rotating the shuttleboard in the air, x and y data varies between -2.0 and 2.0, while z data is either 127.9375 or between 0.0 and 2.0. It's like there's an imaginary line when rotating the device after which the z values become 127.9375. When logging the raw magnetometer data I get something like this: 'X: 14, Y : 24, Z : 2047'. Again, the values of x and y are somewhat between 0 and 50, while z is either 2047 or between 0 and 50. I think this phenomenon is not related to BSX library processing, but to acquisition of the data. BSX Lib documentation says that 'bsx_get_magcordata()' should return values in 'Micro Tesla', but I don't think that's the case here. I compared the data to magnetometer output on my mobile phone and the results are vastly different.
I also noticed that the heading information calculated from x and y values (atan2(y, x)), while ignoring the z axis, is actually quite good.
I just wanted to ask if the data retrieved from the sensor is good, or is there something wrong with it? My theory is that maybe the inaccurate data is causing the problem with calibration.
Kind regards,
Marko Njirjak
Solved! Go to Solution.
07-31-2019 04:05 AM
Hi Marko Njirjak,
The magnetic sensor value is too small especial on X and Y axis. Please check:
07-31-2019 09:41 PM - edited 07-31-2019 09:45 PM
Hi shellywang,
I checked everything you said. Here are the results:
1. Yes, I do use the API. Since BMX160 does not yet have a standalone API, I use BMI160 and BMM150 APIs (reference: this topic).
2. All communication with BMX160 is conducted through the APIs mentioned in the first answer. This is how I acquire data:
bmi160_read_aux_data_auto_mode(mag_data, &bmi);
bmm150_aux_mag_data(mag_data, &bmm);
where bmi and bmm are defined as:
struct bmi160_dev bmi;
struct bmm150_dev bmm;
3. The magnetometer is configured as follows:
bmm.settings.preset_mode = BMM150_PRESETMODE_REGULAR;
bmm150_set_presetmode(&bmm);
bmm.settings.pwr_mode = BMM150_FORCED_MODE;
bmm150_set_op_mode(&bmm);
bmi.aux_cfg.aux_odr = 6; // also tried an 8 here
bmi160_config_aux_mode(&bmi);
uint8_t aux_addr = 0x42;
bmi160_set_aux_auto_mode(&aux_addr, &bmi);
bmi160_set_sens_conf(&bmi);
According to BMX160 datasheet, bmi.aux_cfg.aux_odr of 6 represents 25Hz ODR, while 8 represents 100Hz ODR. I tried both.
4. I logged raw data from the sensor and filtered data from BSX Lib. The sensor was standing still.
- Raw data
ACC -> x: -12 y: -309 z: 17340
GYRO -> x: 31 y: 10 z: -10
MAG -> x: -2 y: 15 z: 2047
ACC -> x: 3 y: -317 z: 17329
GYRO -> x: 34 y: 11 z: -9
MAG -> x: -2 y: 15 z: 2047
ACC -> x: 7 y: -327 z: 17344
GYRO -> x: 29 y: 12 z: -10
MAG -> x: -2 y: 15 z: 2047
ACC -> x: -37 y: -296 z: 17380
GYRO -> x: 28 y: 12 z: -8
MAG -> x: -2 y: 15 z: 2047
ACC -> x: -29 y: -286 z: 17381
GYRO -> x: 26 y: 10 z: -11
MAG -> x: -3 y: 15 z: 2047
ACC -> x: -21 y: -300 z: 17356
GYRO -> x: 30 y: 9 z: -6
MAG -> x: -3 y: 15 z: 2047
ACC -> x: 5 y: -283 z: 17326
GYRO -> x: 32 y: 8 z: -8
MAG -> x: -3 y: 15 z: 2047
- BSX Lib data
ACC -> x: -0.005387 y: -0.184354 z: 10.382455
GYRO -> x: 0.000830 y: 0.000827 z: -0.000552
MAG -> x: -0.125000 y: 0.875000 z: 127.93750
ACC -> x: -0.011372 y: -0.176572 z: 10.369885
GYRO -> x: 0.001105 y: 0.001652 z: -0.000001
MAG -> x: -0.125000 y: 0.875000 z: 127.937500
ACC -> x: -0.017358 y: -0.175974 z: 10.382455
GYRO -> x: 0.001105 y: 0.001652 z: -0.000001
MAG -> x: -0.125000 y: 0.875000 z: 127.93750
ACC -> x: -0.005387 y: -0.185551 z: 10.375872
GYRO -> x: 0.000004 y: 0.002203 z: -0.000001
MAG -> x: -0.125000 y: 0.875000 z: 127.937500
ACC -> x: 0.004788 y: -0.188543 z: 10.370484
GYRO -> x: 0.000004 y: 0.002203 z: -0.000001
MAG -> x: -0.187500 y: 0.937500 z: 127.937500
ACC -> x: 0.004788 y: -0.178368 z: 10.380660
GYRO -> x: 0.000830 y: 0.000827 z: -0.001377
MAG -> x: -0.187500 y: 0.937500 z: 127.937500
ACC -> x: 0.004190 y: -0.173580 z: 10.366294
GYRO -> x: 0.000830 y: 0.000827 z: -0.001377
MAG -> x: -0.187500 y: 0.937500 z: 127.937500
In addition I will post all relevant code connected with BMX160 configuration and data acquistion.
a) Declaration
struct bmi160_dev bmi;
struct bmm150_dev bmm;
struct bmi160_sensor_data accel_data;
struct bmi160_sensor_data gyro_data;
uint8_t mag_data[8] = {0};
ts_dataxyz accdata, magdata, gyrodata;
libraryinput_t libraryInput_ts;
b) Aux interface helper functions
int8_t bmm150_aux_read(uint8_t id, uint8_t reg_addr, uint8_t *reg_data, uint16_t len) {
(void) id;
return bmi160_aux_read(reg_addr, reg_data, len, &bmi);
}
int8_t bmm150_aux_write(uint8_t id, uint8_t reg_addr, uint8_t *reg_data, uint16_t len) {
(void) id;
return bmi160_aux_write(reg_addr, reg_data, len, &bmi);
}
c) Sensor intialization
bmi.id = 0;
bmi.interface = BMI160_SPI_INTF;
bmi.read = spi_read_transfer;
bmi.write = spi_write_transfer;
bmi.delay_ms = delay;
bmm.dev_id = BMM150_DEFAULT_I2C_ADDRESS;
bmm.intf = BMM150_I2C_INTF;
bmm.read = bmm150_aux_read;
bmm.write = bmm150_aux_write;
bmm.delay_ms = delay;
bmi160_init(&bmi);
bmi.aux_cfg.aux_sensor_enable = BMI160_ENABLE;
bmi.aux_cfg.aux_i2c_addr = bmm.dev_id;
bmi.aux_cfg.manual_enable = BMI160_ENABLE;
bmi160_aux_init(&bmi);
bmm150_init(&bmm);
where spi_read_transfer, spi_write_transfer and delay are master chip specific functions related to SPI communication.
d) Turning on sensors
//Accelerometer
bmi.accel_cfg.power = BMI160_ACCEL_NORMAL_MODE;
bmi.accel_cfg.bw = BMI160_ACCEL_BW_NORMAL_AVG4;
bmi.accel_cfg.range = BMI160_ACCEL_RANGE_2G;
bmi.accel_cfg.odr = BMI160_ACCEL_ODR_100HZ;
//Gyroscope
bmi.gyro_cfg.odr = BMI160_GYRO_ODR_100HZ;
bmi.gyro_cfg.range = BMI160_GYRO_RANGE_500_DPS;
bmi.gyro_cfg.bw = BMI160_GYRO_BW_NORMAL_MODE;
bmi.gyro_cfg.power = BMI160_GYRO_NORMAL_MODE;
//Magnetometer
bmm.settings.preset_mode = BMM150_PRESETMODE_REGULAR;
bmm150_set_presetmode(&bmm);
bmm.settings.pwr_mode = BMM150_FORCED_MODE;
bmm150_set_op_mode(&bmm);
bmi.aux_cfg.aux_odr = 6;
bmi160_config_aux_mode(&bmi);
uint8_t aux_addr = 0x42;
bmi160_set_aux_auto_mode(&aux_addr, &bmi);
bmi160_set_sens_conf(&bmi);
e) Sensor data acquisition
bmi160_read_aux_data_auto_mode(mag_data, &bmi);
bmm150_aux_mag_data(mag_data, &bmm);
bmi160_get_sensor_data((BMI160_TIME_SEL | BMI160_ACCEL_SEL | BMI160_GYRO_SEL), &accel_data, &gyro_data, &bmi);
f) BSX Lib initialization
initParam_t s_input;
s_input.accelspec = (BSX_U8 *)&bsxLibConfAcc;
s_input.magspec = (BSX_U8 *)&bsxLibConfMag;
s_input.gyrospec = (BSX_U8 *)&bsxLibConfGyro;
s_input.usecase = (BSX_U8 *)&bsxLibConf;
bsx_init(&s_input);
where bsxLibConfAcc, bsxLibConfGyro, etc. are BSX_U8 arrays required for configuration (as BSX Lib documentation states).
g) Turning on BSX Lib
ts_workingModes s_workingmodes;
s_workingmodes.opMode = BSX_WORKINGMODE_NDOF_GEORV_FMC_OFF;
bsx_set_workingmode(&s_workingmodes);
h) Providing data to BSX Lib
libraryInput_ts.acc.data.x = accel_data.x;
libraryInput_ts.acc.data.y = accel_data.y;
libraryInput_ts.acc.data.z = accel_data.z;
libraryInput_ts.acc.time_stamp = accel_data.sensortime*39;
libraryInput_ts.gyro.data.x = gyro_data.x;
libraryInput_ts.gyro.data.y = gyro_data.y;
libraryInput_ts.gyro.data.z = gyro_data.z;
libraryInput_ts.gyro.time_stamp = gyro_data.sensortime*39;
libraryInput_ts.mag.data.x = bmm.data.x;
libraryInput_ts.mag.data.y = bmm.data.y;
libraryInput_ts.mag.data.z = bmm.data.z;
libraryInput_ts.mag.time_stamp = accel_data.sensortime*39; // accelerometer timestamp is used here, could that be the problem?
bsx_dostep(&libraryInput_ts);
Multiplying timestamps by 39 is conducted because BMX160 datasheet states that timestamp resolutions is 39us and BSX Lib documentation says that timestamps should be in us format.
i) BSX Lib data acquistion
ts_dataxyzf32 acc, gyro, mag;
bsx_get_acccordata(&acc);
bsx_get_gyrocordata_rps(&gyro);
bsx_get_magcordata(&mag);
printf("ACC -> x: %f y: %f z: %f \n", acc.x, acc.y, acc.z);
printf("GYRO -> x: %f y: %f z: %f \n", gyro.x, gyro.y, gyro.z);
printf("MAG -> x: %f y: %f z: %f \n", mag.x, mag.y, mag.z);
I used this as a reference when configuring the auxiliary sensor (magnetometer).
Kind regards,
Marko Njirjak
08-03-2019 02:28 PM - edited 08-03-2019 02:49 PM
Hello,
I think I figured it out. There were two things that were problematic:
1. BMM150 API returns the data in uT format, it does not return LSB, which is needed for the BSX library. This was solved by removing the division by 16 (since BMM150 resolution is 16 LSB/uT) in functions: compensate_x, compensate_y and compensate_z.
static int16_t compensate_x(int16_t mag_data_x, uint16_t data_rhall, const struct bmm150_dev *dev)
{
...
retval = (retval + (((int16_t)dev->trim_data.dig_x1) * 8));// / 16;
...
}
static int16_t compensate_y(int16_t mag_data_y, uint16_t data_rhall, const struct bmm150_dev *dev)
{
...
retval = (retval + (((int16_t)dev->trim_data.dig_y1) * 8));// / 16;
...
}
static int16_t compensate_z(int16_t mag_data_z, uint16_t data_rhall, const struct bmm150_dev *dev)
{
...
//retval = retval / 16;
...
}
This made the API return data in LSB format.
2. In BMM150 API, when performing the compensation for the z axis, unsigned and signed integers are compared.
if (retval > BMM150_POSITIVE_SATURATION_Z) {
retval = BMM150_POSITIVE_SATURATION_Z;
} else ...
Variable retval is of type int32_t, but BMM150_POSITIVE_SATURATION_Z is defined in bmm150_defs.h as UINT16_C(32767). I observed that the comparsion between retval = -916 and BMM150_POSITIVE_SATURATION_Z returns true, although it shouldn't. I believe this relates to comparing signed and unsigned integers. This was mitigated by defining BMM150_POSITIVE_SATURATION_Z as INT16_C(32767). Since the max value of INT16 is 32767, this should not be a problem. After this modification the former comparision between retval and BMM150_POSITIVE_SATURATION_Z returns false, as expected.
Magnetometer calibration status now successfully reaches 3 and the offsets are non-zero.
In addition, here's some information that might be useful:
a) BMM150 magnetometer data is most easily fetched by using this function:
bmm150_read_mag_data(&bmm150);
where bmm150 is of type 'struct bmm150_dev'.
b) After mag data fetch, the data (now in LSB values) is fed to the BSX library like this:
libraryInput_ts.mag.data.x = bmm150.data.x;
libraryInput_ts.mag.data.y = bmm150.data.y;
libraryInput_ts.mag.data.z = bmm150.data.z;
c) Here's a few samples of raw magnetometer data (fetched by the function mentioned in 'a') for testing purposes (the sensor was standing still):
x: -124, y: -11, z: -984
x: -124, y: -11, z: -984
x: -124, y: -11, z: -984
x: -124, y: -11, z: -984
x: -148, y: -35, z: -927
x: -148, y: -35, z: -927
x: -148, y: -35, z: -927
x: -148, y: -35, z: -927
x: -130, y: -35, z: -896
x: -130, y: -35, z: -896
x: -130, y: -35, z: -896
x: -130, y: -35, z: -895
x: -130, y: -17, z: -921
x: -130, y: -17, z: -921
x: -130, y: -17, z: -921
x: -130, y: -17, z: -921
x: -142, y: -23, z: -921
x: -142, y: -23, z: -921
x: -142, y: -23, z: -921
x: -142, y: -23, z: -921
x: -118, y: -23, z: -921
x: -118, y: -23, z: -921
d) Here's a few samples of BSX Lib magnetometer data fetched by the function 'bsx_get_magcordata' for testing purposes (the sensor was standing still):
x: -8.125000, y: -1.812500, z: -57.937500
x: -8.125000, y: -1.812500, z: -57.937500
x: -8.125000, y: -1.812500, z: -57.937500
x: -8.875000, y: -1.062500, z: -57.937500
x: -8.875000, y: -1.062500, z: -57.937500
x: -8.875000, y: -1.062500, z: -57.937500
x: -8.875000, y: -1.062500, z: -57.937500
x: -9.625000, y: -1.437500, z: -58.312500
x: -9.625000, y: -1.437500, z: -58.312500
x: -9.625000, y: -1.437500, z: -58.312500
x: -9.625000, y: -1.437500, z: -58.312500
x: -8.875000, y: -1.062500, z: -57.937500
x: -8.875000, y: -1.062500, z: -57.937500
x: -8.875000, y: -1.062500, z: -57.937500
x: -8.875000, y: -1.062500, z: -57.937500
x: -8.125000, y: -1.812500, z: -56.750000
x: -8.125000, y: -1.812500, z: -56.750000
x: -8.125000, y: -1.812500, z: -56.750000
x: -8.125000, y: -1.812500, z: -56.750000
x: -9.625000, y: -1.062500, z: -58.375000
x: -9.625000, y: -1.062500, z: -58.375000
x: -9.625000, y: -1.062500, z: -58.375000
Kind regards,
Marko Njirjak