LSM6DS33 IMU data not behaving as expected

This is my first project using an IMU and I don’t know what I am doing wrong.

I have a Romi 32U4 with the built-in LSM6DS33 and an onboard Raspberry Pi using I2C. The standard pololu-rpi-slave-arduino-library is installed on the 32U4.

When it is sitting still I would expect the gyro values to not change or at least have minimal changes and the accelerometer values to be zero. When it is moving, I would expect all the values to change appropriate to the motion. Instead, when it is sitting still the gyro values change wildly and constantly, and when it is moving the accelerometer values remain zero.

Here is my python code to setup the LSM6 and read the IMU values from the Raspberry Pi

import struct
import time
from smbus2 import SMBus

IMU_ADRS1 = 0x6b
CTRL1_XL = 0x10
CTRL2_G = 0x11
CTRL3_C = 0x12
CTRL4_C = 0x13
CTRL5_C = 0x14
CTRL6_C = 0x15
CTRL7_G = 0x16
OUTX_L_G = 0x22
XL_HM_MODE_MASK = 0b00010000
GY_HM_MODE_MASK = 0b10000000
ODR_MASK = 0b11110000
ODR_OFF = 0b00000000
ODR_166KHZ = 0b10000000


class IMUData:
    def __init__(self, tup):
        self.gy_x, self.gy_y, self.gy_z, self.xl_x, self.xl_y, self.xl_z = tup

    def __repr__(self):
        return f"IMUData(gy_x:{self.gy_x}, gy_y:{self.gy_y}, gy_z:{self.gy_z}, xl_x:{self.xl_x}, xl_y:{self.xl_y}, xl_z:{self.xl_z})"


class LSM6:

    def __init__(self, bus, adrs=IMU_ADRS1, xl_odr=ODR_OFF, xl_hp=False, gy_odr=ODR_OFF, gy_hp=False):
        self.bus = bus
        self.adrs = adrs
        self.xl_odr = xl_odr
        self.xl_hp = xl_hp
        self.gy_odr = gy_odr
        self.gy_hp = gy_hp

    @property
    def xl_odr(self):
        return read_byte_with_mask(self.bus, self.adrs, CTRL1_XL, ODR_MASK)

    @xl_odr.setter
    def xl_odr(self, value):
        write_byte_with_mask(self.bus, self.adrs, CTRL1_XL, value, ODR_MASK)

    @property
    def xl_hp(self):
        return read_byte_with_mask(self.bus, self.adrs, CTRL6_C, XL_HM_MODE_MASK) != 0

    @xl_hp.setter
    def xl_hp(self, value):
        write_byte_with_mask(self.bus, self.adrs, CTRL6_C, (1 if value else 0) << 4, XL_HM_MODE_MASK)

    @property
    def gy_odr(self):
        return read_byte_with_mask(self.bus, self.adrs, CTRL2_G, ODR_MASK)

    @gy_odr.setter
    def gy_odr(self, value):
        write_byte_with_mask(self.bus, self.adrs, CTRL2_G, value, ODR_MASK)

    @property
    def gy_hp(self):
        return read_byte_with_mask(self.bus, self.adrs, CTRL7_G, GY_HM_MODE_MASK) != 0

    @gy_hp.setter
    def gy_hp(self, value):
        write_byte_with_mask(self.bus, self.adrs, CTRL7_G, (1 if value else 0) << 7, GY_HM_MODE_MASK)

    @property
    def imu_data(self):
        t = read_unpack(self.bus, self.adrs, OUTX_L_G, 12, "HHHHHH")
        return IMUData(t)


def read_unpack(bus, adrs, register, size, fmt):
    bus.write_byte(adrs, register)  # no value, just moves the pointer to the specified address
    time.sleep(0.0001)  # pause to give the bus time to catch up
    byte_list = [bus.read_byte(adrs) for _ in range(size)]
    return struct.unpack(fmt, bytes(byte_list))


def write_pack(bus, adrs, register, fmt, *data):
    data_array = list(struct.pack(fmt, *data))
    bus.write_i2c_block_data(adrs, register, data_array)
    time.sleep(0.0001)  # pause to give the bus time to catch up


def read_byte_with_mask(bus, adrs, reg, mask):
    return read_unpack(bus, adrs, reg, 1, "B")[0] & mask


def write_byte_with_mask(bus, adrs, reg, value, mask):
    r = read_unpack(bus, adrs, reg, 1, "B")[0]
    b = (r & ~mask) | (value & mask)
    write_pack(bus, adrs, reg, "B", b)


def main():
    bus = SMBus(1)
    lsm = LSM6(bus, IMU_ADRS1, ODR_166KHZ, True, ODR_166KHZ, True)
    while True:
        print(lsm.imu_data)
        time.sleep(1)


if __name__ == "__main__":
    main()

Hello.

There are a couple immediate things to keep in mind when you first look at your IMU data. First, all gyroscopes drift, so the raw values from them will almost never be zero and some kind of calibration for that will be required. Second, the default configurations for the LSM6DS33 are to report measurements in some of the highest resolution modes, ±2 g for the accelerometer, and ±245 dps for the gyro, so measurement noise and gyro drift will have pronounced effects by default. (Your code does not appear to modify those default settings.) Have you tried converting the raw readings to units of dps and comparing the numbers to the datasheet’s zero rate specification?

To confirm that the LSM6DS33 is working, could you test it by uploading our InertialSensors.ino example program from the Romi 32U4 Arduino library to your control board? Please post a sample of the Serial Monitor output so we can see what is going on.

- Patrick

One possible issue I noticed with your code is that you’re interpreting all the sensor readings as unsigned shorts (format code ‘H’). If the sensors are varying slightly between small positive and negative numbers, you’ll see the values seemingly jumping between close to 0 and close to 65535. Changing the format code to use ‘h’ for signed shorts should fix that.

Kevin

1 Like

Changing to lowercase ‘h’ probably makes the numbers correct but didn’t help the problem. Here is a sample of the data I am getting with it sitting perfectly still

IMUData(gy_x:-17558, gy_y:-17263, gy_z:31807, xl_x:0, xl_y:0, xl_z:0)
IMUData(gy_x:11987, gy_y:13686, gy_z:24661, xl_x:0, xl_y:0, xl_z:0)
IMUData(gy_x:24575, gy_y:-17242, gy_z:-13248, xl_x:0, xl_y:0, xl_z:0)
IMUData(gy_x:23776, gy_y:30660, gy_z:-30953, xl_x:0, xl_y:0, xl_z:0)
IMUData(gy_x:26878, gy_y:18099, gy_z:31533, xl_x:0, xl_y:0, xl_z:0)
IMUData(gy_x:32477, gy_y:28366, gy_z:-13740, xl_x:0, xl_y:0, xl_z:0)
IMUData(gy_x:13525, gy_y:11704, gy_z:-20871, xl_x:0, xl_y:0, xl_z:0)
IMUData(gy_x:5629, gy_y:29595, gy_z:-23446, xl_x:0, xl_y:0, xl_z:0)
IMUData(gy_x:24571, gy_y:27779, gy_z:-31960, xl_x:0, xl_y:0, xl_z:0)
IMUData(gy_x:17037, gy_y:31946, gy_z:-22198, xl_x:0, xl_y:0, xl_z:0)
IMUData(gy_x:-24835, gy_y:-28948, gy_z:26638, xl_x:0, xl_y:0, xl_z:0)
IMUData(gy_x:23017, gy_y:24207, gy_z:-9210, xl_x:0, xl_y:0, xl_z:0)
IMUData(gy_x:17309, gy_y:25227, gy_z:-12499, xl_x:0, xl_y:0, xl_z:0)
IMUData(gy_x:12031, gy_y:30363, gy_z:26692, xl_x:0, xl_y:0, xl_z:0)
IMUData(gy_x:22746, gy_y:-32602, gy_z:20534, xl_x:0, xl_y:0, xl_z:0)
IMUData(gy_x:17390, gy_y:27063, gy_z:-21721, xl_x:0, xl_y:0, xl_z:0)
IMUData(gy_x:28142, gy_y:19663, gy_z:-32724, xl_x:0, xl_y:0, xl_z:0)
IMUData(gy_x:3491, gy_y:26082, gy_z:21403, xl_x:0, xl_y:0, xl_z:0)
IMUData(gy_x:12261, gy_y:24975, gy_z:21811, xl_x:0, xl_y:0, xl_z:0)
IMUData(gy_x:24812, gy_y:17058, gy_z:-928, xl_x:0, xl_y:0, xl_z:0)
IMUData(gy_x:22242, gy_y:25720, gy_z:19564, xl_x:0, xl_y:0, xl_z:0)
IMUData(gy_x:-27478, gy_y:21685, gy_z:31535, xl_x:0, xl_y:0, xl_z:0)
IMUData(gy_x:17919, gy_y:-25149, gy_z:21303, xl_x:0, xl_y:0, xl_z:0)
IMUData(gy_x:23024, gy_y:-21034, gy_z:-20379, xl_x:0, xl_y:0, xl_z:0)
IMUData(gy_x:25305, gy_y:24734, gy_z:27674, xl_x:0, xl_y:0, xl_z:0)
IMUData(gy_x:17934, gy_y:-26729, gy_z:-29881, xl_x:0, xl_y:0, xl_z:0)
IMUData(gy_x:27871, gy_y:-26740, gy_z:27483, xl_x:0, xl_y:0, xl_z:0)
IMUData(gy_x:30673, gy_y:-19552, gy_z:-26497, xl_x:0, xl_y:0, xl_z:0)
IMUData(gy_x:-28161, gy_y:-32048, gy_z:-19376, xl_x:0, xl_y:0, xl_z:0)
IMUData(gy_x:19394, gy_y:-26712, gy_z:-22499, xl_x:0, xl_y:0, xl_z:0)
IMUData(gy_x:22253, gy_y:25536, gy_z:-15291, xl_x:0, xl_y:0, xl_z:0)
IMUData(gy_x:4390, gy_y:26255, gy_z:-15274, xl_x:0, xl_y:0, xl_z:0)
IMUData(gy_x:13098, gy_y:-12156, gy_z:-15515, xl_x:0, xl_y:0, xl_z:0)

The accelerometer measurements all returning zero is immediately strange and suggests an issue with the way you are either configuring or reading the sensors. To first establish whether the LSM6DS33 itself is working, can you try reading it from the Romi 32U4’s AVR microcontroller with the InertialSensors.ino example from our library as suggested in my previous post?

If that works, then the next thing I suggest is trying out this Raspberry Pi code we have for the LSM6. That code was written for the Balboa 32U4, but the Romi 32U4 and Balboa 32U4 control boards are similar and both use the LSM6DS33, so it should work the same on a Romi.

- Patrick

1 Like

Thank you, PatrickM. That helped a lot. I had a lot of extra stuff going on in my LSM6 code that I picked up from an older example somewhere. I am using your LSM6 code and it is giving me much better numbers (see below). It still seems like a lot of variance in the accelerometer for something that is sitting still but the gyroscope is pretty settled. Now that it’s working I can figure out offsets and variances.

GY Vector(x=117, y=-178, z=-146) XL Vector(x=-1956, y=-891, z=23815)
GY Vector(x=121, y=-175, z=-147) XL Vector(x=589, y=-18, z=17019)
GY Vector(x=118, y=-177, z=-148) XL Vector(x=614, y=-16, z=16963)
GY Vector(x=122, y=-174, z=-146) XL Vector(x=629, y=-14, z=17060)
GY Vector(x=121, y=-173, z=-144) XL Vector(x=658, y=13, z=17079)
GY Vector(x=117, y=-180, z=-148) XL Vector(x=605, y=-22, z=17062)
GY Vector(x=120, y=-175, z=-148) XL Vector(x=607, y=-18, z=17048)
GY Vector(x=122, y=-179, z=-146) XL Vector(x=579, y=-40, z=17090)
GY Vector(x=123, y=-176, z=-149) XL Vector(x=642, y=1, z=17068)
GY Vector(x=122, y=-177, z=-149) XL Vector(x=637, y=6, z=17067)
GY Vector(x=119, y=-174, z=-150) XL Vector(x=592, y=-22, z=17063)
GY Vector(x=123, y=-177, z=-150) XL Vector(x=616, y=-39, z=17054)
GY Vector(x=123, y=-177, z=-145) XL Vector(x=605, y=-39, z=17085)
GY Vector(x=123, y=-178, z=-148) XL Vector(x=623, y=-10, z=17079)
GY Vector(x=117, y=-177, z=-148) XL Vector(x=641, y=10, z=17103)
GY Vector(x=117, y=-174, z=-147) XL Vector(x=655, y=19, z=17063)
GY Vector(x=122, y=-175, z=-145) XL Vector(x=574, y=-6, z=17063)
GY Vector(x=122, y=-173, z=-148) XL Vector(x=596, y=-43, z=17029)
GY Vector(x=119, y=-179, z=-148) XL Vector(x=621, y=12, z=17114)
GY Vector(x=120, y=-178, z=-150) XL Vector(x=661, y=-14, z=17097)
GY Vector(x=120, y=-176, z=-150) XL Vector(x=685, y=17, z=17121)
1 Like