Lab 2 report (IMU)

In the lab 2, I connected the IMU ICM20948 to the Artemis board and recorded and analyzed data from the IMU.

Task 1: Set up the IMU

I connected the IMU to my board's Qwiic connector.

Figure 1: Picture of the Artemis IMU connections

By installing the Arduino library 'SparkFun 9DOF IMU Breakout - ICM 20948', I can run the Example1_Basics. According to the Pimoromni, its I2C address can be 0x68/0x69(cut trace). The trace of the IMU I got is not cut, so the address should be 0x68. Since AD0_VAL defines as the value of the last bit of the I2C address, it should be 0 to makes it not be underflow.

As shown in the video, the built-in LED blinks 3 times slowly on start-up to indicate the board is running.

Figure 2: Picture of the Artemis IMU direction

When I rotate the board, the acceleration data changes mainly to show the direction of the gravity, as shown in the above video and figure 3. As I rotate the IMU along the x-axis, the degree of acc_z gradually decreases from +1g to 0g, while acc_y becomes +1g and acc_x stays the same. The gyroscope data changes to show the rotational velocity. As I rotate faster, the value of gyro gets bigger and vice versa.

Figure 3: Rotate IMU along X-axis

When I flip the board, the value of the accelerometer reverse along the axis of rotation. As shown in figure 4, when I flip the IMU along the x-axis, the degree of acc_z changes from +1g to -1g. The gyroscope shows rapid changes in angular rate. For example, the figure shows the rapid change in gyro_z.

Figure 4: Flip IMU along X-axis

When I accelerate along a certain axis, the accelerometer reading in that axis gets bigger. The gyroscope reading doesn't change much because there is no rotating IMU. As shown in figure 5, I accelerate the IMU along the '-x-axis' and the acc_x gets bigger, while the gyro value changes only slightly, probably due to vibration and noise.

Figure 5: Move IMU along X-axis

Task 2: Accelerometer

Convert to pitch and roll only from accelerometer data

By using the formula: Pitch= θ= atan2 (ay ,az) ×180π and Roll= ϕ= atan2 (-ax ,az) ×180π the pitch and roll can be calculated. As the lecture, I define the clockwise direction around the X-axis as the positive pitch direction; the clockwise direction around the Y-axis as the positive roll direction; and the clockwise direction around the Z-axis as the positive yaw direction, as shown in Figure 2. I placed the imu at -90,0,90 degrees along the pitch/roll direction, recorded 996 data for each placement, plotted the following plots and calculated the mean and accuracy.

As shown in the left plot in the figure 5, the pitch value at -90 degree are -86.76916. Accuracy=1 --90+86.76915747241726 -90×100% =96.41%

As shown in the middle plot in the figure 5, the pitch value at 0 degree are -0.00567. Absolute Error=|0- 0.005668001003009033|=0.0057

As shown in the right plot in the figure 5, the pitch value at +90 degree are 87.02103. Accuracy=1- 90-87.021033099297990×100 %=96.69%

Figure 5: Place IMU along Pitch direction at {-90, 0, 90} degrees

As shown in the left plot in the figure 6, the roll value at -90 degree are -87.15554. Accuracy=1- 90--87.155542168674790×100 %=97.16%

As shown in the middle plot in the figure 6, the roll value at 0 degree are -0.05848. Absolute Error=|0- -0.058475426278836506|=0.0585

As shown in the right plot in the figure 6, the roll value at +90 degree are 86.37310. Accuracy=1- 90-86.3730992978936790×100 %=96.37%

Figure 6: Place IMU along roll direction at {-90, 0, 90} degrees

The data and code for above calculation and plot is here.

I can use two-point calibration for pitch based on the points (-90, -86.76915747241726) and (90, 87.0210330992979). The result formula is pitch=1.03573164519733 *pitch_old-0.130437778707076.
For roll, the two points I used are (-90, -87.1555421686747) and (90, 86.37309929789367). The result formula is roll=1.03729274014214 *roll_old+0.405811154718554.

The following 6 plots in figure 7 show the pitch and roll value after two point calibration. The mean value of IMU's pitch and roll data are more closed to the correct value.

Figure 7: Pitch and roll value after two point calibration

Fourier Transform & complementary low pass filter

I collected 996 accelerometer data without large noise around, the data in time domain and in frequency domain plot are shown in the figure 8 and figure 9. Then, I collected 996 accelerometer data with large noise around(knock the table), the data in time domain and in frequency domain plot are shown in the figure 10 and figure 11. As you can see in the figure 11, the cutoff frequency should be around 40Hz. α=TT+RC= 1sample_rate 1sample_rate+ 12πfc =1153.9052268643466 1153.9052268643466+1 2π×40Hz =0.62021

Figure 8: Pitch and roll Time Domain Plot without vibrating the table
Figure 9: Pitch and roll Freq Domain Plot without vibrating the table
Figure 10: Pitch and roll Time Domain Plot with vibrating the table
Figure 11: Pitch and roll Freq Domain Plot with vibrating the table

However, after applying the low pass filter as following code, there's little change in pitch and roll in the frequency domain, as shown in figure 13.

Figure 12: Pitch and roll Time Domain Plot with lowpass filter and vibrating the table
Figure 13: Pitch and roll Freq Domain Plot with lowpass filter and vibrating the table

The code in python used to collect the serial data and analyze is shown in the following script.

Discussion on result

The reason the simple low-pass filter I wrote doesn't work is probably because there is a filter built into the chip, as shown in Figure 14. This image can be found in the ICM-20948 datasheet.

Figure 14: Built-in Low-pass Filter

Gyroscope

Compute pitch, roll, and yaw from gyroscope data and complementary filter

By using the following code burned into the board, I can get the yaw_g, pitch_g, roll_g form the integration of gyroscope data. I also can get pitch, roll from complementary filter using gyroscope data and accelerometer data.

To show the accuracy and range of pitch_g calculated using only gyroscope data and pitch calculated using the complementary filter, I rotated imu along the x-axis and showed their values at {-90,0,90} degrees, as seen in Figure 15. At the beginning (left plot), pitch_g and pitch are overlapped, and at a stable 90 degrees, pitch_g = 85.72 and pitch = 86.5, the accuracy rate are 95.24% and 96.11%. However, due to the cumulative error caused by the integration, pitch_g is shifted more and more, which can be seen in the middle and right plots, where the blue line and the red line are gradually separating from each other. At a steady 0 degrees, pitch_g = -6.51 and pitch = 0.11, the absolute error are 6.51% and 0.11%. At a steady -90 degrees, pitch_g = -97.86 and pitch = -87.55, the accuracy rate are 91.27% and 97.28%.

Figure 15: Time Domain Signal of pitch_g, roll_g, yaw_g, pitch, roll when rotate along x

To show the accuracy and range of roll_g calculated using only gyroscope data and roll calculated using the complementary filter, I rotated imu along the y-axis and showed their values at {-90,0,90} degrees, as seen in Figure 16. At a stable 90 degrees, roll_g = 105.72 and roll = 87.92, the accuracy rate are 72.53% and 97.69%. At a stable 0 degrees, roll_g = 16.06 and roll = 0.23, the absolute error are 16.06% and 0.23%. At a stable -90 degrees, roll_g = -72.26 and roll = -86.41, the accuracy rate are 80.29% and 96.01%.

Figure 16: Time Domain Signal of pitch_g, roll_g, yaw_g, pitch, roll when rotate along y

To show the accuracy and range of yaw calculated using only gyroscope data, I rotated imu along the z-axis and showed their values at {-90,0,90} degrees, as seen in Figure 17. At a stable 90 degrees, roll_g = 107.6, the accuracy rate are 80.44%. At a stable 0 degrees, roll_g = 19.69, the absolute error are 19.69%. At a stable -90 degrees, roll_g = -69.3, the accuracy rate are 77.00%.

Figure 17: Time Domain Signal of pitch_g, roll_g, yaw_g, pitch, roll when rotate along z

The pitch, roll, and yaw values from the integration of gyroscope data only are gradually shifted over time, even at rest, as seen in Figure 18. However, the pitch and roll from the complementary filter does not be affected. They are not susceptible to drift or quick vibrations.

Figure 17: Time Domain Signal of pitch_g, roll_g, yaw_g, pitch, roll at original position for 30 seconds

By using the following code, I changed the frequency from 83.33Hz to 20Hz. Then, I did the same operation of rotation to my IMU. As shwon in Figure 18, a lower frequency caused the drift be bigger, and the accuracy rate of pitch_g, roll_g, yaw_g, pitch, and roll be lower since the dy in the integral becomes larger, and each sampling of gyro and acce values had a greater impact on the results.

Figure 18: Time Domain Signal of pitch_g, roll_g, yaw_g, pitch, roll when frequency=20Hz

As shown in the following video, the range of the complementary filter is -180 degrees to 180 degrees since when I rotate the IMU along x/y axis more than 180 degrees, the value will change to -180 degrees. This brings up some design considerations, as we need to avoid applying this filter to devices with large steering ranges, or needing to do extra work on the formulas, or on the subsequent analysis of the data in this case.

Sample Data

Speed of sampling discussion

To speed up the execution of the main loop, I burned following code into board, I got following feedback:
First DataPoint - timestamp: 81, floats[0]: -9.765625
Last DataPoint - timestamp: 7417, floats[0]: -13.671875
Time elapsed: 7336
Data recorded: 3000
Frequency: 408.94
NOT READY TIMES: 42
The spped of the new way used to sample data is 408.94Hz. The main loop on the Artemis runs faster than the IMU produces new values since in the main loop, the times of myICM.dataReady() is false is 42.

Collect and store time-stamped IMU data in arrays

As shown in the above code, the size of the array 'dataPoints' stored the IMU data is the flag being used to stop the operation of store new IMU data. In the array used to store IMU data, each element in it is 9 floats and 1 unsigned int string, including 9 imu data and 1 time stamp. I choose to use this struct to stroe data to increase the sampling rate. Since the memory of the Artemis is 384KB, and I uses struct of 40 bytes = 4bytes * 9 floats + 4bytes*1 unsigned long. If the sampling rate is 408.94Hz, the Artemis can store 384 * 1024 / 40 / 408.94 = 24.04 seconds.
Then, I combined 1 IMU data (9 floats + 1 unsigned long) to be a string in csv format, and send to computer via BLE. Related codes are shown following.

Discussion

I use a large struct array to store the accelerometer and gyroscope data to get the highest sampling rate. I chose to use this way to store the data because most IMU data is of the floating point type, this structure makes reading the data easy to understand, and it uses the least amount of memory. When sending the data to the computer, I reconstructed the data into a string consisting of 9 IMU data + 1 timestamp. This way, it is sent data faster than if all the floating point type data was sent individually.

Collection and send via BLE

The following video shows the board collects 5s IMU data and sends to the computer via BLE.

Record a stunt

This is the video of playing the car without Artemis. Currently, since I don't have the Artemis connected to a battery, the Artemis is not able to run and collect IMU data without being connected to a computer.