Bosch Sensortec Community

    cancel
    Showing results for 
    Search instead for 
    Did you mean: 
    SOLVED

    BMX160 magnetometer output data

    BMX160 magnetometer output data

    mnjirjak
    Established Member

    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

    3 REPLIES 3

    shellywang
    Occasional Contributor

    Hi Marko Njirjak,

    The magnetic sensor value is too small especial on X and Y axis. Please check:

    1. Use our BMX160 API or not.
    2. Cannot directly use the magnetic sensor register value in BMX160, need API do compensation.
    3. Mag data on Forced mode ? ODR
    4. Please provide the A/G/M logging data

    mnjirjak
    Established Member

    Hi ,

    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

    mnjirjak
    Established Member

    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

    Icon--AD-black-48x48Icon--address-consumer-data-black-48x48Icon--appointment-black-48x48Icon--back-left-black-48x48Icon--calendar-black-48x48Icon--center-alignedIcon--Checkbox-checkIcon--clock-black-48x48Icon--close-black-48x48Icon--compare-black-48x48Icon--confirmation-black-48x48Icon--dealer-details-black-48x48Icon--delete-black-48x48Icon--delivery-black-48x48Icon--down-black-48x48Icon--download-black-48x48Ic-OverlayAlertIcon--externallink-black-48x48Icon-Filledforward-right_adjustedIcon--grid-view-black-48x48IC_gd_Check-Circle170821_Icons_Community170823_Bosch_Icons170823_Bosch_Icons170821_Icons_CommunityIC-logout170821_Icons_Community170825_Bosch_Icons170821_Icons_CommunityIC-shopping-cart2170821_Icons_CommunityIC-upIC_UserIcon--imageIcon--info-i-black-48x48Icon--left-alignedIcon--Less-minimize-black-48x48Icon-FilledIcon--List-Check-grennIcon--List-Check-blackIcon--List-Cross-blackIcon--list-view-mobile-black-48x48Icon--list-view-black-48x48Icon--More-Maximize-black-48x48Icon--my-product-black-48x48Icon--newsletter-black-48x48Icon--payment-black-48x48Icon--print-black-48x48Icon--promotion-black-48x48Icon--registration-black-48x48Icon--Reset-black-48x48Icon--right-alignedshare-circle1Icon--share-black-48x48Icon--shopping-bag-black-48x48Icon-shopping-cartIcon--start-play-black-48x48Icon--store-locator-black-48x48Ic-OverlayAlertIcon--summary-black-48x48tumblrIcon-FilledvineIc-OverlayAlertwhishlist