Lab 2

Setup the IMU

1. Picture of your Artemis IMU connections

imu connection

2. Show that the IMU example code works

I add the LED blink when Artemis power on.

void setup()
{
  // Blink the LED 3 times slowly on start up
  int cnt = 0;
  while (cnt < 3) {
    digitalWrite(LED_BUILTIN, HIGH);  
    delay(800);
    digitalWrite(LED_BUILTIN, LOW);  
    delay(800);
    cnt += 1;
  }
  ...
}

3. AD0_VAL definition discussion

According to the description in the IMU_Example.ino : “On the SparkFun 9DoF IMU breakout the default is 1, and when the ADR jumper is closed the value becomes 0”. However, we can’t read the data from IMU when AD0_VAL is set to the default value 1, which throws the message:

data underflow

When modified that value to 0, the data from IMU can now be read normally.

4. Acceleration and gyroscope data discussion

The gyroscope data is close to zero when holding still. Gyro data for a given axis increases when rotating around the axis.

Accelerometer

1. Image of output at {-90, 0, 90} degrees for pitch and roll (include equations)

I add two functions:

convert2Attitude converts the raw accelerometer data to pitch and roll angle.

void convert2Attitude(ICM_20948_I2C *sensor, Attitude* att) {
  float pitch_rad = atan2(sensor->accX(), -sensor->accZ());
  float roll_rad = atan2(sensor->accY(), -sensor->accZ());
  att->pitch = pitch_rad * 180.0 / M_PI;
  att->roll = roll_rad * 180.0 / M_PI;
}

printAttitude prints formatted attitude values.

void printAttitude(Attitude *att) {
  SERIAL_PORT.print("Pitch [ ");
  printFormattedFloat(att->pitch, 3, 2);
  SERIAL_PORT.print(" ], Roll [ ");
  printFormattedFloat(att->roll, 3, 2);
  SERIAL_PORT.print(" ]");
  SERIAL_PORT.println();
}
  • 0 degree pitch & roll

0 pitch and roll

  • 90 degrees pitch

pitch 90

  • -90 degrees pitch

pitch -90

  • 90 degrees roll

roll 90

  • -90 degrees roll

roll -90

2. Accelerometer accuracy discussion

The pitch and roll data calculated by accelerometer data has large noise, it vibrates a lot even when the IMU is hold still.

serial plot for accel

3. Noise in the frequency spectrum analysis

(a) Include graphs for your fourier transform

pitch roll fft

(b) Discuss the results

There exists a lot of high frequency noise in the accelerometer data. However, we can only operate the car in a relatively low frequency, for example, no more than 10Hz. So a low pass filter is needed.

Gyroscope

1. Include documentation for pitch, roll, and yaw with images of the results of different IMU positions

void loop() {
    if (myICM.dataReady())
    {
        prev_time = curr_time;
        curr_time = millis();

        ...

        float gyr_x = myICM.gyrX();
        att.roll -= gyr_x * (curr_time - prev_time) / 1000.0;
        float gyr_y = myICM.gyrY();
        att.pitch -= gyr_y * (curr_time - prev_time) / 1000.0;
        float gyr_z = myICM.gyrZ();
        att.yaw -= gyr_z * (curr_time - prev_time) / 1000.0;

        printAttitude(&att);

    }
    ...
}

The serial plotter shows the curve of the angular data calculated by gyroscope.

seiral plot for gyro

The angular data calculated by gyroscope has less noise but will drift over time.

2. Demonstrate the accuracy and range of the complementary filter, and discuss any design choices

serial compl

Complementary filter has less noise than pure accelerometer data, and will not drift over time like pure gyroscope data.

att.roll = (1 - alpha) * (att.roll + gyr_x * dt) + alpha * acc_x;
att.pitch = (1 - alpha) * (att.pitch + gyr_y * dt) + alpha * acc_y;
att.yaw = att.yaw + gyr_z;

I choose the parameter alpha in complementary filter to be 0.01;

Sample Data

1. Speed of sampling discussion

By removing all the delay() and Serial.print() statements in the code, the code can run at about 250Hz. By storing time stamp and sensor data in a buffer, the code can run even faster, at a frequency of 330Hz.

This rapid sampling rate can lead to an accumulation of data within a short period of time. The data was organized as string arrays and transmitted to Jupyter through bluetooth.

2. Demonstrate collected and stored time-stamped IMU data in arrays

The data stored in Arduino in this form, with buffer size 2000;

struct Attitude {
  float roll = 0;
  float pitch = 0;
  float yaw = 0;
};

Attitude att;

const int buffer_size = 2000;
int end_pt = 0;
int time_stamps[buffer_size];
Attitude atts[buffer_size];

The data stored in Jupyter has this form:

def parse_to_time_data(ss):
    result = ss.split('|')
    time_ms = result[0]
    roll = result[1]
    pitch = result[2]
    yaw = result[3]
    time_stamps.append(time_ms)
    roll_arr.append(roll)
    pitch_arr.append(pitch)
    yaw_arr.append(yaw)

3. Demonstrate 5s of IMU data sent over Bluetooth

Each IMU data frame haws 3 float variable and 1 int variable, which takes 14B space, the sample rate was set to about 300Hz, which takes 4.2KB/s. The Artemis has 384KB of RAM, so there is enough space for 5s of IMU data to be transmitted through Bluetooth.

ble-ts-data