Bosch IMU under ROS on NVIDIA Jetson TK1

Robot Operating System (ROS) makes integration of sensors such as the Bosch BNO055 9 Degree of Freedom (DOF) IMU straightforward. Looky here:


When building a robot, designing, planning and selecting sensors and parts for the build are crucial to success of the overall project. In building our project robot, one of the sensors that is needed is an Inertial Measurement Unit (IMU) which will be used for odometery. Odometry will be discussed in another article in the future.

There are a multitude of IMUs on the market, in a three part article about IMUs, we discussed how to install a 10-DOF IMU. That particular IMU has discreet gyroscope and accelerometer/compass chips from which the host takes the readings and applies ‘fusion’ algorithms to produce three-axis orientation output. Also, the host generally has to provide a means to calibrate the sensor. Calibration usually means moving the IMU around each axis to hit the minimum and maximum values of the sensor, and place the information into a calibration file of some sort.

Bosch has recently come out with a new IMU, the BNO055. Adafruit has placed the BNO055 on a breakout board and added their usual niceties which allows the Jetson TK1 to access the BNO055 via I2C. The descriptive blurb from the Adafruit website:

If you’ve ever ordered and wire up a 9-DOF sensor, chances are you’ve also realized the challenge of turning the sensor data from an accelerometer, gyroscope and magnetometer into actual “3D space orientation”! Orientation is a hard problem to solve. The sensor fusion algorithms (the secret sauce that blends accelerometer, magnetometer and gyroscope data into stable three-axis orientation output) can be mind-numbingly difficult to get right and implement on low cost real time systems.

Bosch is the first company to get this right by taking a MEMS accelerometer, magnetometer and gyroscope and putting them on a single die with a high speed ARM Cortex-M0 based processor to digest all the sensor data, abstract the sensor fusion and real time requirements away, and spit out data you can use in quaternions, Euler angles or vectors.

Note: The BNO055 uses an I2C technique called ‘clock stretching’. The chip holds the SCL line low while taking a reading, basically saying “not ready yet”. This provides more flexibility on the I2C master side because it does not have to do a full command-and-response. However, be aware of this when connecting the BNO055 to things like I2C address multiplexers which may require you to work around this feature. For example, some people report that initializing the BNO055 twice may be required as a work around to get the sensor to work.

I like things that do the maths on their own and don’t bother me about it. Cool!

Install Strategy

The idea here is to install the Bosch IMU under ROS on the Jetson TK1. Following the path of least resistance, we’ll use RTIMULib (from richards-tech LLC on Github) as the low level interface library and rtimulib_ros (from Romain Reignier) as the interface to ROS. It should be noted that this is a trade off of convenience versus overhead. Because the Bosch IMU spits out fused sensor data, it should be straightforward to turn this directly into a producer that ROS can digest. However RTIMULib and rtimulib_ros already do this (though with added overhead), so for now this seems like a straightforward path to integration.

We will cover the software installation, how to wire up the Bosch IMU to the Jetson, and then how to run the visual demo.

Software Installation

Note: Before installing the IMU interface to ROS, ROS must be installed of course. Here’s an article on how to do that. Also you must have a catkin workspace installed. Here’s a video that covers that process.

Installing RTIMULib

Under the normal RTIMULib installation process, Qt is installed along with the library. If you are not using Qt on the robot, that is unnecessary. In this installation, the cmake file is modified to so that Qt is not installed.

First install ccmake, which will be used later to edit the cmake file.

sudo apt-get install cmake-curses-gui

Then get RTIMULib.

git clone

By default, I2C devices are owned by root. To change this, create a file /etc/udev/rules.d/90-i2c.rules and add the line:

sudo gedit /etc/udev/rules.d/90-i2c.rules


As an alternative, you can do this straight from a command line:

$ sudo bash -c 'echo KERNEL==\"i2c-[0-7]\",MODE=\"0666\" > /etc/udev/rules.d/90-i2c.rules'

Then prepare for compilation

$ cd RTIMULib
#Switch to the Linux directory
$ cd Linux
$ mkdir build
$ cd build
$ cmake ..

This generates the cmake file. The parameters can now be changed so that Qt is not required:

ccmake ..

Change the ‘GL’ related options to off, ‘c’ to configure and then ‘g’ to generate and exit. Then:

cmake ..
make -j4
sudo make install
sudo ldconfig

You will probably need to reboot the machine for the permission changes in the udev to take effect.

Install rtimulib_ros

Setup rtimulib next. “catkin_ws” is the example workspace name and should be replaced by your own catkin workspace name where you are doing development.

cd ~/catkin_ws/src
source devel/setup.bash
git clone

To recreate the demonstration, the ROS Topic needs to be “imu”. In the source file, it is listed as “imu/data”. The Topic name is probably related to the consumer viewing the Topic, you may need to change this appropriately later. For now, we’ll just get this working for the demo.

cd rtimulib_ros/src
$ gedit rtimulib_ros.cpp

Change the line:

// ros::Publisher imu_pub = n.advertise(“imu/data”, 1);
ros::Publisher imu_pub = n.advertise(“imu”, 1);

Then build the node:

$ cd ..
$ cd ..
$ catkin_make

For the demo, install the IMU visualizer:

$ sudo apt-get install ros-indigo-razor-imu-9dof
$ sudo apt-get install python-visual
$ sudo apt-get install python-wxtools

Installing the Bosch IMU

There are several ways to connect the Inertial Measurement Unit (IMU) to the Jetson TK1 Development Kit. Here’s a demonstration of a very simple way. The IMU is not the Bosch, but is very similar and the installation method is the same. Looky here:

Here’s a couple of pictures of the Bosch breakout board from the Adafruit website:
BNO055 top

BNO055 bottom

Solder the supplied header pins onto the IMU breakout board, then wire the IMU header pins to the Jetson J3A1 connector as follows:

GND J3A1-14 (-)
Vin J3A1-16 (+)
SCL J3A1-18 (C)
SDA J3A1-20 (D)

Here are some pictures from the Jetson Wiki for reference :


There are several ways to actually connect the headers to the Jetson J3A1 connector, in this case we use simple female to female jumper wires (0.1″ spacing, 2.54mm pitch – this is standard Arduino size) to attach to the IMU header, and then connect the jumper wire to a machine pin jumper wire, which is then connected to the Jetson.

For the demo, I used Adafruit Premium Female/Female Jumper Wires – 40×6″ and 20 CM Machine Pin Wire Kit/10 Pack. This approach may be adequate for some projects, but for more rugged projects you may want to consider actually soldering wires to the header pins approach or making a breakout board for the Jetsons’ J3A1 header. Remember that the J3A1 header is 2mm pitch (0.08″) which is slightly smaller than the more standard DIY 2.54mm pitch that something like an Arduino uses. You’ll also want to physically mount the device some where also, fortunately there are mounting holes for that purpose on the breakout board.

Running the demo

After installing the software and wiring the IMU to the Jetson, you should be ready to start robotin’.

# Open a new terminal
$ roscore
# Open a new Terminal
$ cd ~/catkin_ws
$ source devel/setup.bash
$ roslaunch rtimulib_ros rtimulib_ros.launch
# Open a new Terminal
$ cd ~/catkin_ws
$ source devel/setup.bash
$ roslaunch razor_imu_9dof razor-display.launch

At this point, you should be up and running and you should be receiving data from the IMU.


The Bosch BNO055 is a good choice for many applications where the host system does not want to bother with having to generate the IMU sensor fusion data. The Bosch is straightforward to use, and does not require the calibration gymnastics that most other IMUs require. Recommended.


  1. With a drawer full of -other- 6 and 9 degree IMU’s, I hesitated to buy this.

    I am glad I did. The MEMS gyros and accelerometers in the other IMU’s all work well. I could never get the magnetometer component of 9DOF units to determine ‘magnetic north’ well enough to use. A lot of robot odometry routines fall back to using just the rotation gyro for this reason I suspect.

    This Bosch IMU does repeatably return heading values in relation to ‘north’ while mounted on my robot.
    To give the automatic calibration software the best chance, I did mount it as far away from the motors as possible.

  2. Diving a little deeper into the Bosch BNO055 and the ROS navigation stack…

    I am still a big fan of this device. Repeatable absolute magnetic bearing readings are nothing to be sneezed at for mobile ground robot applications.

    For those using the ROS navigation stack, this may be of interest.
    No solution yet, just interesting data points…

    From the ROS REP 103 Standard Units of Measure and Coordinate Conventions:
    “By the right hand rule, the yaw component of orientation increases as the child frame rotates counter-clockwise, and for geographic poses, yaw is zero when pointing east.

    This requires special mention only because it differs from a traditional compass bearing, which is zero when pointing north and increments clockwise. Hardware drivers should make the appropriate transformations before publishing standard ROS messages.”

    The navigation stack of ROS expects some fairly bizarre sensor outputs vs intuitive compass behavior. When using just gyro readings for yaw values not referenced to magnetic north, this is easily overlooked.

    The Bosch IMU has two data output modes, both increment yaw turning clockwise.

    The BNO055 allows inverting the sign of the Z axis in register settings. We will try that first before doing conversions further downstream.

    As usual, rvis will be the easiest debug tool for verification.

    • That’s interesting information. Coincidentally I’ve been playing with the BNO055 and found in the RTIMULib library BNO055 driver that the Euler angles had their axes remapped before being stored as a fusionPose in IMURead, and were then converted to a separate quaternion (fusionQPose). rtimulib_ros then publishes the quaternion (fusionQPose) angles to the “imu/data” topic. I’m certainly interested in hearing about what you learn.

  3. After resorting to painters tape on the floor to create a compass rose with both NED and ENU coordinate systems, the solution was actually fairly simple.

    1) I physically rotated the IMU to report zero Euler yaw with the robot facing east.
    2) Subtract the reported fused Euler yaw from 360 before sending it off to ROS.

    The Euler yaw to quaternion conversion is done in a ROS node on the TK1.

    My robot is doing all the odometry calculations on a mbed enabled nucleo STM32 development board. The Bosch IMU is being read by the mbed board in my case.

    X_odometry increases as the robot travels east.
    Y_odometry increases as the robot travels north.
    Euler yaw value increases as the robot turns counter-clockwise.

    The ROS navigation stack seems OK with this setup.
    It is strange to have the robot moving due east on a zero degree heading.

  4. How about the entire odometry code? x_odom, y_odom and yaw_enu are all that ROS needs to publish the robot pose. Things do get easier with faster microcontrollers. Most of the current odometry examples seem to be geared for legacy (slow) 8-bit microcontroller limitations.

    I will probably circle back around to this one day to see if fiddling with the IMU registers can accomplish the same thing. I have probably hosed the pitch and roll reporting, but yaw is really the only thing I am after from the IMU.

    imu.get_Euler_Angles(&euler_angles); // From Bosch BNO055 IMU
    yaw_enu = 360 – euler_angles.h;

    left_pulses_odom = left_enc.getPulses();
    right_pulses_odom = right_enc.getPulses();

    left_delta_odom = left_pulses_odom – prev_left_pulses_odom;
    right_delta_odom = right_pulses_odom – prev_right_pulses_odom;

    distance_delta_odom = (0.5 * (double)(left_delta_odom + right_delta_odom)) * MetersPerCount;
    distance_total_odom += distance_delta_odom;
    x_delta_odom = distance_delta_odom * (double)cos(yaw_enu*0.017453292);
    y_delta_odom = distance_delta_odom * (double)sin(yaw_enu*0.017453292);

    x_odom += x_delta_odom;
    y_odom += y_delta_odom;
    prev_left_pulses_odom = left_pulses_odom;
    prev_right_pulses_odom = right_pulses_odom;

  5. A followup, by using volume 1 of “ROS By Example” by R. Patrick Goebel as a step by step guide for standards checking of my scratch built ‘turtlebot cousin’, this second iteration of the build works well.

    Xbox Kinects have a narrow field of view to generate ‘fake’ laser scan data. My previous build with gyro only yaw was not accurate enough for ROS move_base and amcl to work as robustly as I knew they could.

    The Bosch IMU allows such tight odometry calculations, the ROS navigation stack now works like it should. Even with a less than optimum ‘laser’, the robot keeps localization within a map during ‘patrols’ very well.

    Where the Jetson will shine is adding some vision applications to the mix.
    Running ROS does not tax the JetsonTK1 capabilities at all.

    Only one minor gripe with the Bosch IMU. I do not think there is a way to store calibration data on the device between power cycles. This means on every power-up of the IMU, I have to pick up the robot and do the ‘figure eight’ maneuver to calibrate the IMU. This is not a problem on a small robot. Larger robots may need a way to unmount the IMU to wave it around and remount in the same position.

    • My understanding is that once you have the IMU calibrated, you can read the calibration information from the chip, save it somewhere, and then on startup set the calibration for the IMU from the saved data. The IMU will still calibrate itself, but it should be pretty close.

      The registers that hold the calibration data have names like:

      MAG_RADIUS …

  6. Its the visualization stuff. Going to rviz or rqt its good to go. Tried recompiling those three python packages and discovered one of them is what is keeping me from building ros-desktop. Wxtools. Gets a seg fault when trying to build ros as well. But the separate package compiles no problem. So finally I have everything hooked up to the tx1 I had on the tk1’s.

    • Hi Cactus,
      Thanks for your kind words. A couple of my friends and I get together and jam every couple of months, and I throw an audio recorder on. Since I’ve been doing this for more than ten years, I have a good backlog of background music to select from. Most of the songs are several minutes long, so they tend to match the length of the videos I do for this channel. Thanks for reading and watching (and listening!)

    • Where you able to see the BNO055 on the I2C bus? There’s not enough information in your question to give you any help.

      Once the board is wired up, turn the Jetson TK1 on.
      In order to be able inspect the i2c bus, you will find it useful to install the i2c tools:

      $ sudo apt-get install libi2c-dev i2c-tools
      After installation, in a Terminal execute:

      $ sudo i2cdetect -y -r 1
      You should see an entry of 0x28, which is the default address of the IMU.

      • Hi kangalow,
        I have same problem,

        run -> roslaunch rtimulib_ros rtimulib_ros.launch
        showing this error:
        Failed to open I2C port – Failed to read BNO055 data
        Failed to open I2C bus1

        I check i2c entry of 0x28 is OK.
        Any ideas on this ? Thanks~

        • My first guess would be that the udev permissions are messed up. What is the content of the file /etc/udev/rules.d/90-i2c.rules

          There should be a line


          • [Possible Fix] I had this issue using the tx1 and the BNO055 IMU. I edited the RTIMULib.ini file under catkin_ws/src/rtimulib_ros/config. For IMUType the default was set to 10. I changed it to 0 and it started working for me. I now look at the .ini file after running once and it says 10 again but its working repeatably now.

      • I’m using SparkFun 9DoF Razor IMU M0 (MP9250)… facing with the same issue. The i2c address is 8×60.

        Any fix that I should do?

  7. I am trying to get my IMU talking to ROS and I am running into a permissions issue. I have tried adding the rules file you describe on two different Jetson TK1s. The first is a vanilla 21.4 install, and the other is a grinch kernel. On both of them I added the rules file and rebooted, but the permissions for the i2c lines are still set to only be for root user. I used the single line command you provide, and I verified the file was created in the correct spot. Any ideas on what I could be doing wrong here?

    • I figured it out. When I went back and looked at the entry in the rules file I noticed that it had quote marks around the whole statement. When I took those out and rebooted it worked fine. I believe your command should look like this instead: sudo bash -c ‘echo KERNEL==\”i2c-[0-7]\”,MODE=\”0666\” > /etc/udev/rules.d/90-i2c.rules’

      • I apologize, I had placed the command in an HTML blockquote block which messed up the intent. I placed the command into a HTML code block which should clear things up. I’m sorry for the inconvenience.

  8. Long time viewer first time poster. Thanks for the most excellent toturials! My question : Any caveats for doing this with a JTX1 instead of a JTK1?

  9. Hi,

    Can this package be used standalone with normal pc running ROS. I do not have a jetson to play with right now but would like to test the Bosch BNO055 IMU. I plan to use it on the turtlebot2 with a laptop PC. Any suggestions or pointers?

  10. Hi,
    Has anyone checked if their Barometer Pressure Sensor Works? My sensor is MS5611 and it is supported by the library but i have no way of checking if it working properly or not?
    Any ideas?

  11. This is an old post but maybe someone will see a new comment…

    Does anyone know if it is possible to use the this package with two IMU units of the same make/model on the same machine? I am using a Pololu MinIMUv3 with the L3GD20H and LSM303D that has worked great with this package, but I have to add a second one on my robot. The module provides a way to set different slave addresses for the units, but in the RTIMU .ini file it only seems to specialize which i2c bus is used, not the address.

    As always, thanks for the help. JetsonHacks has helped me go from completely clueless to being able to BS my way through quite a few situations 🙂

  12. This turorial was super helpful! Thank you very much for the time put into it. I have one question about the output rate: how can I change the publishing rate so that I’m only getting 5 or 10 data points a second.l? For my application, I need to save all the data to a .bag or .txt file for 45-60 minutes and don’t want to have to work through hundreds of thousands of data points at the end.

  13. Thanks for the tuturial. I had to make some changes as some instructions seem out of date.

    The package ros-indigo-razor-imu-9dof did not seem available to my TX2 board so I used the package ros-kinetic-razor-imu-9dof.

    However, this did not seem to install properly as no new package was found in catkin_ws/src. So I had to install from source using:

    cd catkin_ws/src
    git clone
    cd ..

    • The Jetson TX2 runs a version of Ubuntu 16.04, which uses ROS Kinetic. The TK1 uses an earlier version of Ubuntu, and requires ROS Indigo.
      A package that you install via apt-get is a binary, it does not get installed in the src directory. The catkin_ws/src directory is for packages that you are compiling from source files. Thanks for reading!

Leave a Reply

Your email address will not be published.