How to Create an Inclinometer using a Raspberry Pi and an IMU

This guide covers how to use an Inertial Measurement Unit (IMU) with a Raspberry Pi to create an inclinometer, just like the type you will find in a 4WD.

A prerequisite for this guide is to have a gyro and accelerometer from an IMU already up and running on your Raspberry Pi. A guide to interfacing an IMU with a Raspberry Pi can be found here.

We will be covering some basic SDL which will be used to produce our graphics.

 

The IMU used in this guide is the BerryIMU.  However, other IMUs or accelerometers and gyroscopes can be used.. Eg  Pololu MinIMU, Adafruit IMU and Sparkfun IMUs

Git repository here
The code can be pulled down to your Raspberry Pi with;

pi@raspberrypi ~ $ git clone https://github.com/mwilliams03/BerryIMU.git

The code for this guide can be found under the gyro_accelerometer_tutorial02_inclinometer directory.

 

BerryIMU Raspberry Pi Gyroscope Accelerometer

 Installing SDL

 

pi@raspberrypi ~ $ sudo apt-get install libsdl1.2-dev libsdl-image1.2-dev libsdl-gfx1.2-dev libi2c-dev

If you want to test to see if SDL installed correctly, you can create a file called test.c and copy in the code below;

#include <SDL/SDL.h>

int main(int argc, char** argv) {
        SDL_Init(SDL_INIT_EVERYTHING);
        SDL_Surface *screen;

        screen = SDL_SetVideoMode( 480, 320, 16, SDL_SWSURFACE );
        SDL_Rect rect;
        rect.x = 10;
        rect.y = 10;
        rect.w = 20;
        rect.h = 20;

        Uint32 color = SDL_MapRGB(screen->format, 0xff,0xff,0xff);
        Uint32 color2 = SDL_MapRGB(screen->format, 0,0,0);
        SDL_FillRect(screen, &rect, color);
        SDL_Flip(screen);
        getchar();

        SDL_Quit();
        return 0;
}

The above code can be compiled with;

pi@raspberrypi ~ $ gcc -o test test.c `sdl-config –cflags` `sdl-config –libs`

And to run the program;

pi@raspberrypi ~ $ sudo ./test

You should see your screen go white or a white square should be shown.

 PiScreen TFT Raspberry Pi

Setup SDL and Load Images

We will be adding to the code which was already created in the Guide to interfacing a Gyro and Accelerometer with a Raspberry Pi guide.

As we are using SDL, we will need to include the SDL header files in our program;

#include "SDL.h"
#include "SDL/SDL_image.h"

When using SDL, you first have to initialize SDL and setup your surfaces before you can start displaying information on the screen. The next section shows how this is done.

 

The two lines below initialize SDL and sets it to not display a cursor;

        SDL_Init(SDL_INIT_VIDEO);
        SDL_ShowCursor(SDL_DISABLE);

We then use the SDL_GetVideoInfo() function to get information about our display. E.g. Resolution and bits per pixel,etc. This is stored in the structure ‘videoinfo’. To view what height was returned, we would use ‘videoInfo->current_h’

We then use this information to create our screen, using the SDL_SetVideoMode() function. This function needs these parameters, X Resolution, Y resolution, bits per pixel and display surface type. In the code below we are using a software surface for the display type.

        videoInfo = SDL_GetVideoInfo();

        screen = SDL_SetVideoMode(videoInfo->current_w, videoInfo->current_h, videoInfo->vfmt->BitsPerPixel, SDL_SWSURFACE );
        if ( screen == NULL ) {
                fprintf(stderr, "Unable to setvideo: %s\n", SDL_GetError());
                exit(1);
        }

 

 

Once we have setup SDL, we can start loading our images.

        inclinometerOverlay =  IMG_Load("inclinometerOverlay.png");
                if (inclinometerOverlay == NULL){
                         printf("error loading Overlay image\n");
                        SDL_Quit();
                        exit(1);
                }

After we load the image, which is a PNG, we need to convert it to the correct format so it keeps its transparency

         compatibleInclinometerOverlay = SDL_DisplayFormatAlpha(inclinometerOverlay);

 

Rotate Images Based on Gyroscope and Accelerometer Angles

We will create a function to rotate our images as this will need to be done every time the main program loop is processed. We will pass two parameters to this function, carRoll(which is the X angle) and CarPitch(which is the Y angle).

int graphics(float carRoll, float carPitch)

 

 

The first task we want to do in the function is erase all information on the current surface. This can be done by using SDL_FillRect() and the value 0x000000, which is black.

        SDL_FillRect(screen,NULL,0x000000);

 

The next section of code will position the two jeep images correctly onto our surface. The formula for the ‘y’ position will place it in the middle of the surface.

        inclinometerJeepFrontPosition.x = 30;
        inclinometerJeepFrontPosition.y = (videoInfo->current_h/2)-(compatibleInclinometerJeepFront->h/2);
        inclinometerJeepSidePosition.x = 262;
        inclinometerJeepSidePosition.y = (videoInfo->current_h/2)-(compatibleInclinometerJeepFront->h/2);

 

We can now rotate our images based on the angle from the IMU. To do this we will use the rotozoomSurface() function. We will pass to it our compatible jeep images, the roll and pitch angles, the zoom factor(1.0 for no zoom) and smoothness (0 for no smooth).

        rotationInclinometerJeepSide = rotozoomSurface(compatibleInclinometerJeepSide, carPitch, 1.0, 0);
        rotationInclinometerJeepFront = rotozoomSurface(compatibleInclinometerJeepFront, carRoll, 1.0, 0);

 

After we rotate the image, we need to recenter the pivot point.

        inclinometerJeepFrontPosition.x -= rotationInclinometerJeepFront->w/2-compatibleInclinometerJeepFront->w/2;
        inclinometerJeepFrontPosition.y -= rotationInclinometerJeepFront->h/2-compatibleInclinometerJeepFront->h/2;
        inclinometerJeepSidePosition.x -= rotationInclinometerJeepSide->w/2-compatibleInclinometerJeepSide->w/2;
        inclinometerJeepSidePosition.y -= rotationInclinometerJeepSide->h/2-compatibleInclinometerJeepSide->h/2;

 

We can now flip our surface so we can see it on our display

        SDL_Flip(screen);

 

And finally, we need to free our surfaces for the next time the graphic() function is called. This prevents a memory leak.

        SDL_FreeSurface(screen);
        SDL_FreeSurface(rotationInclinometerJeepFront);
        SDL_FreeSurface(rotationInclinometerJeepSide);

 

Update Main Loop

You can now call the graphics() function from your main loop and pass the angles CFangleX & CFangleY

  graphics(CFangleX,CFangleY);

Compile and Run

As there are a number of libraries we are using to create this program, we have to include them when compiling. You can use this to compile;

pi@raspberrypi ~ $ gcc -o gyro_accelerometer_tutorial02 -lm gyro_accelerometer_tutorial02.c `sdl-config –cflags` `sdl-config –libs` -lSDL_image -lSDL_gfx

To run;

pi@raspberrypi ~ $ sudo ./gyro_accelerometer_tutorial02

 

Displaying the Output on a Small TFT

Inclinometer raspberry pi
If you are using a small TFT connected to your Raspberry Pi, you can have the output shown on this TFT just like in the video above.

To do this, you fist need to specify /dev/fb1 as the framebuffer device.

putenv("SDL_FBDEV=/dev/fb1");

The above command needs to be place just before SDL_Init();

You will also need to increase the time that each pass of the main loop takes. This is because writing to the TFT will cause some delays in loop and the loop needs to run at a consistent time so that the gyro tracking will work. The bellow snipped forces the main loop to take at least 130ms to run.

#define DT 0.13

        while(mymillis() - startInt < (DT*1000))
        {
            usleep(100);
        }

.

14 thoughts on “How to Create an Inclinometer using a Raspberry Pi and an IMU”

  1. I know there’s gyros, imu, magnetometers, and accelerometers, sometimes single eval boards that can provide up to 9dof!

    But just for comparison, what type of visualization can be achieved by each of these sensors on it’s own? For example I have an accelerometer (not imu) that also outputs vlalues based on tilt. How would the display look different than the imu based project?

    I’ve seen some neat projects that rotate a 3d object based on sensor data, but that must work using gyro + accelerometer, no?

    1. The problem with just using an accelerometer is that the accelerometer is very noisy. You pictures would bounce around. You could use a filter to try and remove some of this noise.

      Here is why a gyro a IMU is recommended;

      Gyros – A gyro measures the rate of rotation, which has to be tracked over time to calculate the current angle. This tracking causes the gyro to drift. However, gyros are good at measuring quick sharp movements.

      Accelerometers – Accelerometers are used to sense both static (e.g. gravity) and dynamic (e.g. sudden starts/stops) acceleration. They don’t need to be tracked like a gyro and can measure the current angle at any given time. Accelerometers however are very noisy and are only useful for tracking angles over a long period of time.

      Combining both readings from these sensors removes the drift and the noise.

      Regarding the 3D object, it would be a gyro + accelerometer and maybe a magnetometer.

      1. Thanks! I’ll give it a try by heavily (fir) filtering the readings.. it’ll introduce a delay for sure but slowly rotating the object, and seeing the model move in 3d sounds like too much fun to pass-up!

  2. Any tips for porting to SDL2?

    For example, I got it to at least dispay the png, however it’s in black & white, most likely because I had to omit the call to ‘SDL_DisplayFormatAlpha’. That function no longer exists in SDL2.

    Also, SDL_Flip(screen); has been replaced by SDL_RenderPresent(renderer) however couldn’t get that to work. Instead I used SDL_UpdateWindowSurface( gWindow ); Not sure the consequences of doing that.

    Is the theory that all 3 surfaces are the same size, but inclinometerOverlay makes transparent the areas occupied by compatibleInclinometerJeepFront and compatibleInclinometerJeepSide? While the JeepFront and JeepSide images make transparent everything except it’s own area within the circle?

    Also, is inclinometerJeepFrontPosition & inclinometerJeepSidePosition ONLY used to determine the rectangle of interest to update? Couldn’t all three images be updated wholly (at the expense of performance)?

    Not sure why it displays as black and white, but if I get it working I’ll post it here.

    1. I haven’t used SDL2 yet.
      However, for SDL_DisplayFormatAlpha, try SDL_ConvertSurfaceFormat.
      Have you looked through this?
      https://wiki.libsdl.org/MigrationGuide

      All three images are different sizes. Overlay has not transparency, the two jeep images have transparency.
      Place the overlay first and then place the two jeep images. If transparency is working, you should see the overlay through the transparent sections of the jeep images.

      inclinometerJeepFrontPosition and the other rects are used to place the images in the correct location on the screen. They are also used to move the pivot point every time the image is rotated, as the pivot point by default is at the top left hand corner of the image.

      Please let us know if you get it working… good luck!

  3. hello
    im making a inclinometer for a 4wd
    i have a android based app witch is the same design i want
    im using a ADXL335 chip
    happy to pay someone to help me
    #noob

  4. Great documentation and info, thanks so much! I’ve got this stuff all working here, and I’m just wanting to push it a little further, but I’m sort of stuck here still. I see the python examples, and see the readouts of the X,Y,Z rotations, if it’s able to output in python, I would think you’d be able to control the rotations of a 3d model in something like pi3d since that’s all python based no?

Leave a Reply

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