Motion Sensor PoC: BNO055 and Raspberry Pi Subtleties

Abstract

The BNO055 is a capable IMU that has on-chip sensor fusion and filtering. Interfacing can be done using I²C and UART. When used with the Raspberry via I²C, you get erroneous measurements because of the I²C clock stretching bug of the Raspberry. Using the UART, results are correct.

To skip directly to the correct connection of the BNO055 Xplained Pro board, click here.

I need a motion sensor…

For my upcoming project (which is not a Quadcopter/Drone/Robot project!) I need to track motion, both rotational as well as linear motion, to a few mm/arcsecond precision, using a Raspberry Pi Zero. Looking around, you quickly stumble across the MPU 6050, for which you find cheap breakout boards all over the internet. Hooking up and reading out sensor measurements is a rather simple thing, had it up and running in less than an hour. This post series is a very good quick start guide, and there is at least one python library on github.

MPU 6050
The omnipresent MPU 6050 breakout board

But…

The values you get are everything but stable. Browsing the internet you learn about gyroscope drift, short term and long term fluctuations, nervous accelerometers and finally you arrive at the topic of sensor fusion, covariant filters and Kalman filters. This is deep dive stuff! For the curious: Some explanations can be found here. There is an Arduino implementation of a Kalman filter for the MPU 6050 which is supposedly bringing up nice and stable results, but I’d decided upon Raspberry for several reasons, and I could find no implementation for the Raspberry, and did not want to spend time porting code.

Finally, I found out that InvenSense, the manufacturer of the MPU 6050, “provides” ready made code for sensor fusion and filtering, that can run on the MPU 6050 itself, dubbed Digital Motion Processor (DMP). Registering with InvenSense you can download libraries and code examples for using the DMP, but all for different IDEs, not for Raspberries. And: this is proprietary code that is not well documented. It seems that the DMP is a binary code object that is uploaded to the MPU 6050 on start. There is the i2cdevlib which found a way to access and use the DMP (as far as I understand from porting the InvenSense code examples and extracting the firmware blobs), but again, only for Arduino. Certainly it would be possible to port this to the Raspberry – however: not done in a minute.

And there was a last catch: Obviously everyone using IMUs is only (or at least mainly) concerned about roll/pitch/yaw angles – linear acceleration just was not covered in nearly any guide, post or howto on the net. But I will badly need also a suffciently precise and robust linear motion measurement!

Since I want to go on with my project and not with the basics and mathmatics of inertial measurement units (IMUs), I needed to move on.

…and found Bosch BNO055.

Having learned about the term “sensor fusion”, I looked around for affordable IMU sensors that do the sensor fusion and filtering on-chip. I found the BNO055 Xplained Pro board from Atmel. Nearly a factor 3-10 more expensive as compared to MPU 6050 (depends if you order directly from the far east, or as I prefer local dealers), but still what I find reasonable. And it saves me days of work, and work of a kind I am not really craving for, having my project in mind. Finally, it offers nine “degrees of freedom” (9DOF), while MPU 6050 is 6DOF (not that I really need the magnetometer…).

BNO055 Xplained Pro Board
The BNO055 Xplained Pro board comes in a fancy box…

Just as a side remark, in the meantime I learned that the quadcopter/flight control community offers capable boards which from the white papers sound very useful and sophisticated – it seems that cc3d might do the trick at half the price. And perhaps I may still need to try this out – the BNO055 spits out the fused mesurements at about 100 Hz, which may turn out too slow for my project. cc3d claims about 500 Hz.

Interfacing with the Raspberry

For my Proof of Concept (PoC) with the BNO055 I used a Raspberry 3. Adafruit offers a comprehensive guide and a Python library that already covers nearly everything, no need to repeat this all here.

Setting up the Adafruit library

Just a few lines and you’re done:

Using I²C – works, but not OK!

The Xplained Pro board is by default set to I²C communication mode, so I started there.

Working out the connections to the Xplained Pro board is easy using Atmel’s datasheet. You only need to connect power and the I²C lines, but the Adafruit library also makes use of the reset pin, so I recommend to connect this also. Furthermore, you may connect the interrupt pin, and there is a RGB LED on board, where each color may be switched when connected to a Raspberry GPIO pin. However, interrupt and LED are not used by the library, so it’s mainly for future use.

Here’s what goes where:

PoC with I²C
I²C connections
Raspberry Pi Xplained Pro Remarks
Pin # Function Pin # Function
1 3.3 V 20 VCC
3 SDA 11 SDA
5 SCL 12 SCL
7 GPIO 4 9 INT optional – Interrupt may be configured to trigger e.g. on motion detection
9 GND 19 GND
11 GPIO 17 15 RESET optional, recommended – Allows to reset the BNO055 chip via GPIO
13 GPIO 27 7 LED B optional – The board has a RGB LED on it. Each color may be switched via GPIO. Low means the LED is on.
15 GPIO 22 8 LED R
16 GPIO 23 6 LED G
Board Hooked Up
…and how it looks in real life (no LED wires…)

In order to read data, in the files from Adafruit in Adafruit_Python_BNO055 you’ll find a file examples/simpletest.py. At the beginning of the script the sensor connection is defined, starting with bno=…. This line needs the following modification:

assuming that your reset pin is connected to GPIO 17. The I²C address 0x29 can be found in the BNO055 datasheet (and be modified using the ADDR0 pin), which can be found along with a lot of other documentation on the Bosch product page.

In addition, I am interested in the linear motion, so I added a few lines in the while True: loop at the end of the script to get these values also:

When you now start the code using

the readings come in. Now you need to follow the calibration procedure as described in the Adafruit guide. With the Raspberry 3 it seems to work very nice. But it only seems so! Looking a little bit closer, you’ll notice some oddities. Here are some example readings that show this:

Heading 367.5°? Strange… This goes up to about 370°, and then switches to 10° while turning the chip more and more.

Another one:

Looks totally sane – the only thing: The chip is completely at rest, lying on the table, no movement. But still acceleration in x direction? This comes only at certain positions, not everywhere.

And finally, once in a while a line like this turns up:

Obvious nonsense.

In the end it turned out that I did not read the Adafruit guide carefully enough. The Raspberry has a problem with I²C clock stretching, which is actually a hardware bug. Look for “clock stretching” in the Adafruit guide, it’s explained there and you’ll find additional links. Actually, had I used an older Raspberry version, most likely the chip would not have worked at all.

Fortunately, the chip can also be interfaced…

Using UART – OK!

The BNO055 datasheet states that the PS0 and PS1 pins allow the selection of the protocol:

PS0 PS1 Protocol
0 0 I²C
1 0 HID via I²C
0 1 UART
1 1 Reserved

The PS0 and PS1 pins are availabe at the lower right of the Xplained Pro board (J103 in the Circuit diagram), along with 3.3 V, so just a wire bridge switches the chip to UART mode, and you connect it to the Raspberry UART instead of the I²C pins:

PoC with UART
UART connections
Raspberry Pi Xplained Pro Remarks
Pin # Function Pin # Function
1 3.3 V 20 VCC
7 GPIO 4 9 INT optional – Interrupt may be configured to trigger e.g. on motion detection
8 TXD 12 RXD UART
9 GND 19 GND
10 RXD 11 TXD UART
11 GPIO 17 15 RESET optional, recommended – Allows to reset the BNO055 chip via GPIO
13 GPIO 27 7 LED B optional – The board has a RGB LED on it. Each color may be switched via GPIO. Low means the LED is on.
15 GPIO 22 8 LED R
16 GPIO 23 6 LED G

A few things need to be done on the Raspberry 3 using Jessie:

In /boot/config.txt add the line:

Then disable the console output to the UART:

And in /boot/cmdline.txt remove the part console=serial0,115200. A reboot is required.

For a very good and complete description of the serial interface and its configuration on Raspberry Pi 3 read this article.

And finally modify the simpletest.py bno=… line to now use the UART:

Now the readings after calibration come in stable and nice – here for the chip at rest:

And also the 360° → 0° switch is coming in correctly now:

Very nice! Project can continue!

Final remarks

The Raspberry SVG image used above I’ve “stolen” from the Fritzing parts library, along with a few more parts. I think I’ll have a closer look on this software in the future – seems like a really nice program!

Only recently I stumbled across this comparison of sensor fusion implementations – worth a read if you’re currently chasing for your solution.

Leave a Reply

Your email address will not be published. Required fields are marked *