Home Skip to the content.

Lab 2: IMU

Objective

The goal of this lab is to configure the IMU sensor on our robot and accurately capture its data.

Setup

The first thing we had to do was connect the IMU to the Artemis, as shown below.

IMU connected to Artemis

The software configuration for the IMU required the installation of the ICM_20948 library within the Arduino IDE, which allowed easy communication between the IMU and the Artemis platform. One important variable within the sample code is AD0_VAL, which represents the least significant bit of the IMU’s I2C address. This value should be set to 0 only if the ADR jumper is open. Since the ADR jumper is closed for us, AD0_VAL is set to 1.


Accelerometer

When printing the output, the accelerometer data presents the acceleration measurements for each coordinate axis. The IMU’s accelerometer captures these accelerations in micro-G units. To determine the angles relative to the x-axis (roll) and y-axis (pitch), the arctangent function is utilized:

Roll = atan2(a_x, a_z)
Pitch = atan2(a_y, a_z)

Accelerometer code snippet

Employing these equations, the values for pitch and roll were computed at each interval and graphically represented using the serial plotter. It was observed that upon rotating the pitch or roll to 90 degrees, the other one became noisy. For instance, during the initial rotation where the pitch approached 90 degrees, the roll measurement saw lots of noise and did not remain at 0.

Accelerometer output flat Accelerometer output not flat
Plot: flat Plot: ~90 degrees

The accelerometer has high accuracy when coupled with a low-pass filter, although it displays a slight deviation from the mean. Below you can see expected versus actual values for pitch and roll.

Expected vs actual pitch Expected vs actual roll

Below is the collected data and FFT with no large noise source around.

FFT of accelerometer data

Below is the code that was used for the low-pass filter.

Low-pass filter code

I believe this is due to the low magnitude of noise, since the IMU already has a low-pass filter according to its datasheet.


Gyroscope

The gyroscope uses the change in angle around each axis with this equation:

Gyroscope equation

Here you can see the code that was used:

Gyroscope code snippet

To minimize noise in the accelerometer data, it was necessary to incorporate gyroscope readings. However, as evident from the graph below, there is a noticeable drift in the gyroscope data over time, with the delay being set to 10 seconds.

Gyro drift over time

Here are the outputs from the serial plotter when it’s held flat and when lifted:

Serial plotter output flat Serial plotter output lifted

Complementary Filter

To address the issue of noise and drift from the accelerometer and the gyroscope, a complementary filter was implemented. This is demonstrated in the code below. This filter uses a weighting function, defined by the variable alpha, to mitigate noise from the accelerometer and drifting from the gyroscope. The optimal value of alpha was established through a series of tests and was determined to be 0.1.

Complementary filter code snippet

Sample Data

After eliminating all delay and print statements, and by storing time, accelerometer, and gyroscope data, the sampling rate became very fast. The average was around 4 ms. This could result in an excessive volume of data collected in a brief duration. Such a large dataset could quickly consume the Artemis’ storage capacity and make it challenging to analyze the robot’s long-term behavior.

To manage this, the data was stored in different string arrays before being transmitted to my laptop. Note that the IMU values are int16_t, and 100 values for each array was enough. This was about ~10 KB, and with the 384 KB internal data, the Artemis has plenty of memory for data collection. See my implementation below:

Sampling implementation code

To enable data transfer, I combined two files by creating a header file for the IMU sensor and including it in the BLE file, where I then executed the program.