5
\$\begingroup\$

I'm experimenting with an MPU6050 six-axis accelerometer/gyro reading data with a Raspberry Pi via I2C. The six-axis response makes sense in general as I rotate it by hand, rotating moves the gravity vector correctly per the accelerometer channels, and short rotations around each axis generate reasonable-looking gyroscope values.

But when I set it down on something that should isolate it from vibrations and just take data for a long time, I see strange non-statistical noise in some channels.

I wrote a short script (shown below) that samples all six channels for 400 iterations, then increments the low pass filter (seven levels from 0x00 to 0x06) and then increments the gain for both accel and gyro (four levels; 0x00, 0x08, 0x10, 0x18). So that's 28 tests with 400 samples each. The data rate is about 55 to 60 measurements of all six axes per second. The characteristic frequency for each setting of the digital low-pass filter from the register map pdf is:

enter image description here

and the data sheet pdf.

Below I show the data which ranges from -/+ 32,767. The only thing I've changed is to subtract the mean of the 400 measurements at each setting so just the noise is displayed.

As the seven low-pass filter settings increase, the frequency decreases and the noise decreases as expected. However, there are these sudden spikes of +/- 200 to 300 ADC counts. They are not exactly 256, but in that ballpark with seems slightly suspicious.

I also show the raw data and standard deviations per test in the second plot.

Question: Does this kind of behavior - this non-statistical, spiky noise - look familliar to anyone? I'm waiting on a second gyro to compare, but that will take many days, and I'd like to see if there is anything I can do in the mean time. Each of the six channels has a very different behavior wrt noise. Inside the IC there are six separate MEMs devices each connected to its own dedicated ADC.

enter image description here

enter image description here

Script for the Raspberry Pi to read the module via I2C:

#!/usr/bin/python

import web
import smbus
import math
import time

urls = ('/', 'index')

pwrmgmt_1 = 0x6b
pwrmgmt_2 = 0x6c

dlpf_set_addr    = 0x1a
gyro_scale_addr  = 0x1b
accel_scale_addr = 0x1c

scales = 0x00, 0x08, 0x10, 0x18
dlpfs  = 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06

pairs = ((0x3b, 0x3c), (0x3d, 0x3e), (0x3f, 0x40),
         (0x43, 0x44), (0x45, 0x46), (0x47, 0x48))

setups = []
for scale in scales:
    for dlpf in dlpfs:
        setups.append((scale, dlpf))
nsetups = len(setups)

bus = smbus.SMBus(1)

address = 0x68

ic       = 0
isetup   = 0
nsamples = 400

def read_six():
    vals = []
    for ahi, alo in pairs:
        high = bus.read_byte_data(address, ahi)
        low  = bus.read_byte_data(address, alo)
        val  = (high << 8) + low
        if val >= 0x8000:
            val = -((65535 - val) + 1)
        vals.append(val)
    return vals

class index:
    def GET(self):

        global ic, nsamples
        global isetup, nsetups, setups
        global accel_scale_addr, gyro_scale_addr, dlpf_set_addr

        six = read_six()
        six_str = str(six)[1:-1]

        ic += 1

        if not ic % nsamples:
            isetup += 1
            scale, dlpf = setups[isetup % nsetups]
            bus.write_byte_data(address, accel_scale_addr, scale)
            bus.write_byte_data(address, gyro_scale_addr,  scale)
            bus.write_byte_data(address, dlpf_set_addr,    dlpf)

        return six_str + ", " + str(isetup)

if __name__ == "__main__":

    # wake the 6050 up since it starts in sleep mode
    bus.write_byte_dta(address, pwrmgmt_1, 0)

    time.sleep(0.5)

    scale, dlpf = setups[isetup]

    bus.write_byte_data(address, accel_scale_addr, scale)
    bus.write_byte_data(address, gyro_scale_addr,  scale)
    bus.write_byte_data(address, dlpf_set_addr,    dlpf)

    print "setup scale: ", scale
    print "setup dlpf:  ", dlpf

    time.sleep(0.1)

    ic  = 0

    app = web.application(urls, globals())
    app.run()
\$\endgroup\$
3
\$\begingroup\$

Errors look like +/-256 +/- expected drift.

This suggests some kind of synchronising error - such as reading the LSByte of one sample and the MSByte of the next or vice versa. How does your gyro handle this, is there some way to temporarily put it on hold while you read data?

Also, examine the raw data around a few of these errors to see if it happens when a slowly drifting signal crosses an MSByte boundary. Obviously, focus on the Y Gyro, the errors are most clearly separated from the noise.

| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ This is exactly the kind of insight I was hoping for! I can try looking at the raw data and swapping bytes by hand for a few data points to see what affect is has. Since reading one byte at a time like I do in the Raspberry Pi script is totally asynchronous to the chip's internal sampling clock, I have a feeling this is not the right way to use the device, and I'd better learn more about synchronizing the two. \$\endgroup\$ – uhoh Oct 7 '17 at 12:07
  • \$\begingroup\$ update: It seems the RPi python package smbus has bus.read_word_data() and bus.read_i2c_block_data() methods, 1, 2. I'll try that and let you know what happens. \$\endgroup\$ – uhoh Oct 7 '17 at 12:36
  • 1
    \$\begingroup\$ I won't read datasheets or give details. But one approach is to discover (by polling a status reg) when a conversion has JUST started, giving you several ms to read the previous one. Another is to have the R-PI tell it to start, ... same effect. read_word_data may or may not be "atomic" (useful search term for background reading) \$\endgroup\$ – Brian Drummond Oct 7 '17 at 12:37
  • 1
    \$\begingroup\$ You've nailed it! The apparent noise was exactly what you suggested, asynchronous, incorrect pairing of low and high bytes. I've added a supplemental answer with the fix and the new data, looks much better. Thank you! \$\endgroup\$ – uhoh Oct 10 '17 at 10:20
3
\$\begingroup\$

@BrianDrummong identified the source of the apparent noise in the accepted answer. Reading one byte at a time over I2C resulted in asynchronous behavior so that the hi and lo bytes corresponded to different conversions. Using bus.read_i2c_block_data() solved the latching problem. By reading fourteen bytes (seven words) (three accelerometers, temperature, three gyroscopes) as a single I2C instruction, the "noise" has disappeared.

def read_sevenblock():

    # read fourteen bytes starting at address 0x3b. This will cycle through
    # ax, ay, az, temp, gx, gy, and gz

    fourteen_bytes = bus.read_i2c_block_data(0x68, 0x3b, 14)  # Read them all at once

    his, los       = fourteen[0::2], fourteen[1::2]  # high bytes are first

    values = []
    for hi, lo in zip(his, los):
        value  = (hi << 8) + lo            # combine high and low byte
        if value >= 0x8000:
            value = -((65535 - value) + 1)   # convert signed negative values 
        values.append(value)
    return values

enter image description here

| improve this answer | |
\$\endgroup\$
  • 1
    \$\begingroup\$ Thank you for the solution! what do we have in the list vals? is it in the following order: acc_x, acc_y, acc_z, temp, gyro_x, gyro_y, gyro_z? Can you please also explain why do we use 0x3b in the command? I thought 0x3b is only the register for acceleration in the x direction? \$\endgroup\$ – hoang tran Jan 7 '19 at 9:52
  • 1
    \$\begingroup\$ hi @hoangtran Here's a quick answer, I will update the post in a little while. The instruction bus.read_i2c_block_data(0x68, 0x3b, 14) says to read 14 bytes starting at 0x3b. The ADCs are 12-bit, so each measurement needs two bytes, to total is 14 bytes. The order is high bits first, then low bits, so you need to combine the two with val = (hi << 8) + lo (Aha! there is a typo in my script!) By the way, for higher speed constant reading, there is a FiFo available also. If you ask about that in a separate question I can post that script. \$\endgroup\$ – uhoh Jan 7 '19 at 10:00
  • \$\begingroup\$ great! thank you! I will ask a separate question. Hope I have enough reputation for that! \$\endgroup\$ – hoang tran Jan 7 '19 at 10:38
  • \$\begingroup\$ I asked this question yesterday: raspberrypi.stackexchange.com/questions/92631/… Maybe you can answer it with using FiFo? or should I ask a new question with specific topic of reading MPU data using FiFo? \$\endgroup\$ – hoang tran Jan 7 '19 at 11:34
  • \$\begingroup\$ @hoangtran I've posted something there now, have a look. \$\endgroup\$ – uhoh Jan 7 '19 at 12:23

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.