Using MPU-6050 to detect head movement

Hello Everyone, I am currently working on a project for my college assignment. I aim to make a head-mounted device with MPU6050 to detect wether the user is nodding or shaking their head. I am currently porting my data into Unreal Engine using the Serial Port Communication Plugin since part of my project will require the Unreal Engine. I currently run the calculations of nodding and shaking in Unreal but I feel like maybe I should do it in arduino. Anyways, I am very new to Aduino, so I have no clue what I should do. I researched Kalman filter but only got confused. Can anyone please tell me how to make it stable and actually detects the head movement correctly? I am not looking for precision just detecting two movements correctly. Thank you for taking time to read my problems.

Here is my current code:

//A god-forsaken code
#include <Kalman.h>
#include "Wire.h"       
#include "I2Cdev.h"     
#include "MPU6050.h"    

MPU6050 mpu;//declare mpu6050
Kalman kalmanX, kalmanY, kalmanZ; //declae kalman filter

int16_t ax, ay, az;//accelerometer value
int16_t gx, gy, gz;//gyroscope value
float filX, filY, filZ;//filtered value

uint32_t timer;

void setup()
{
  Serial.begin(9600);
  Wire.begin();
  mpu.initialize();

  kalmanX.setAngle(0);//Set Kalman filter value to 0
  kalmanY.setAngle(0);
  kalmanZ.setAngle(0);

  timer = micros();//Set timer

void loop()
{
  mpu.getMotion6(&ax, &ay, &az, &gx, &gy, &gz);//Get mpu6050 value

  //calculate the time between data
  double dt = (double)(micros() - timer) / 1000000;
  timer = micros();

  filX = kalmanX.getAngle(ax, gx, dt);//Input the accelerometer data(ax)and gyroscope data(gx)and time(dt)To the kalman fiter program
  filY = kalmanZ.getAngle(ay, gy, dt);
  filZ = kalmanZ.getAngle(az, gz, dt);

  byte mappedX = map(filX, -17000, 17000, 0, 255);//map the filtered data to 0-255 for easier transfer
  byte mappedY = map(filY, -17000, 17000, 0, 255);
  byte mappedZ = map(filZ, -17000, 17000, 0, 255);

  //data transfer
  Serial.write(mappedX);
  Serial.write(mappedY);
  Serial.write(mappedZ);

  //Serial.println(mappedX);
  //Serial.println(mappedY);
  //Serial.println(mappedZ);
  
  delay(50);
}

Thank you again for reading my codes.

Don't bother with the Kalman filter, it doesn't do anything useful.

You need to understand what the gyro actually measures, which is rate of rotation about three axes, not angles.

The rates of rotation alone are probably all you need to detect head nodding or shaking.

I suggest to just hold the sensor in your hand, while printing of the raw gyro measurements, and study the effect of rotating the sensor about different axes, at different rates.

This very simple code should get you started:

// MPU-6050 Short Example Sketch modified for gyro rates only
// By Arduino User JohnChi
// August 17, 2014
// Public Domain
#include<Wire.h>
const int MPU_addr = 0x68; // I2C address of the MPU-6050
int16_t AcX, AcY, AcZ, Tmp, GyX, GyY, GyZ;

void setup() {
  Wire.begin();
  Wire.beginTransmission(MPU_addr);
  Wire.write(0x6B);  // PWR_MGMT_1 register
  Wire.write(0);     // set to zero (wakes up the MPU-6050)
  Wire.endTransmission(true);
  Serial.begin(9600);
}

void loop() {
  Wire.beginTransmission(MPU_addr);
  Wire.write(0x43);  // starting with register 0x43 (GYRO_XOUT_H)
  Wire.endTransmission(false);
  Wire.requestFrom(MPU_addr, 6); // request a total of 6 registers
  int16_t t = Wire.read();
  GyX = (t << 8) | Wire.read(); // 0x43 (GYRO_XOUT_H) & 0x44 (GYRO_XOUT_L)
  t = Wire.read();
  GyY = (t << 8) | Wire.read(); // 0x45 (GYRO_YOUT_H) & 0x46 (GYRO_YOUT_L)
  t = Wire.read();
  GyZ = (t << 8) | Wire.read(); // 0x47 (GYRO_ZOUT_H) & 0x48 (GYRO_ZOUT_L)
  t = Wire.read();

  Serial.print("Gyro: "); Serial.print(GyX);
  Serial.print(", "); Serial.print(GyY);
  Serial.print(", "); Serial.println(GyZ);
  delay(333);
}

I will look into that, thank you so much!

I forgot to mention that the gyro has to be calibrated to remove the offsets, which is another reason the code you posted won't work as you might hope.

Here is a program that averages the first two hundred readings, then subtracts the offsets from the rest of the measurements.

// MPU-6050 Short Example Sketch modified for gyro rates only
// By Arduino User JohnChi
// August 17, 2014
// Public Domain
#include<Wire.h>
const int MPU_addr = 0x68; // I2C address of the MPU-6050
int16_t GyX, GyY, GyZ;

void setup() {
  Wire.begin();
  Wire.beginTransmission(MPU_addr);
  Wire.write(0x6B);  // PWR_MGMT_1 register
  Wire.write(0);     // set to zero (wakes up the MPU-6050)
  Wire.endTransmission(true);
  Serial.begin(9600);
}
int count = 0;
long sumx = 0, sumy = 0, sumz = 0;

void loop() {
  Wire.beginTransmission(MPU_addr);
  Wire.write(0x43);  // starting with register 0x43 (GYRO_XOUT_H)
  Wire.endTransmission(false);
  Wire.requestFrom(MPU_addr, 6); // request a total of 6 registers
  int16_t t = Wire.read();
  GyX = (t << 8) | Wire.read(); // 0x43 (GYRO_XOUT_H) & 0x44 (GYRO_XOUT_L)
  t = Wire.read();
  GyY = (t << 8) | Wire.read(); // 0x45 (GYRO_YOUT_H) & 0x46 (GYRO_YOUT_L)
  t = Wire.read();
  GyZ = (t << 8) | Wire.read(); // 0x47 (GYRO_ZOUT_H) & 0x48 (GYRO_ZOUT_L)

  if (count < 200) {  //add up gyro readins for offset calculations
    sumx += GyX;
    sumy += GyY;
    sumz += GyZ;
    count++;
  }
  else if (count == 200) { //calculate gyro offsets
    sumx = sumx / count;
    sumy = sumy / count;
    sumz = sumz / count;
    count++;
    Serial.print("Gyro offsets: ");
    Serial.print(sumx);
    Serial.print(", ");
    Serial.print(sumy);
    Serial.print(", ");
    Serial.println(sumz);
  }
  else { //subtract offsets and print results
    Serial.print("Gyro: "); Serial.print(GyX-sumx);
    Serial.print(", "); Serial.print(GyY-sumy);
    Serial.print(", "); Serial.println(GyZ-sumz);
    delay(333);
  }
}

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.