WEEK11 Input devices
WEEK11 Input devices
Group Assignment
probe an input device’s analog levels and digital signals
Individual Assignment
measure something: add a sensor to a microcontroller board that you have designed and read it
For this assignment, I would like to delve deeper into the use of the accelerometer sensor in the Final Project. In the Final Project, the goal is to attach an accelerometer sensor to a racket and display the swing speed and the number of practice swings on a connected smartphone via Bluetooth.
First, I will consider how to derive the speed from the accelerometer data (X, Y, Z acceleration) and gyro data (GX, GY, GZ). Next, I will think about how to detect the start and end points of a swing.
I will use the board I made in Session 8.
Creating sample data
First, I execute the sample code in the Arduino IDE to display the accelerometer and gyro data from the MPU6050 on the Serial Monitor.
Then, I will copy the data displayed on the Serial Monitor to create a sample data set.
The board was not recognized properly from the Arduino IDE, and the i2c_scanner program used in Assignment 8 helped solve the problem. The cause was a simple mistake of using the wrong board.
// MPU-6050 Short Example Sketch
// 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(0x3B); // starting with register 0x3B (ACCEL_XOUT_H)
Wire.endTransmission(false);
Wire.requestFrom(MPU_addr,14,true); // request a total of 14 registers
AcX=Wire.read()<<8|Wire.read(); // 0x3B (ACCEL_XOUT_H) & 0x3C (ACCEL_XOUT_L)
AcY=Wire.read()<<8|Wire.read(); // 0x3D (ACCEL_YOUT_H) & 0x3E (ACCEL_YOUT_L)
AcZ=Wire.read()<<8|Wire.read(); // 0x3F (ACCEL_ZOUT_H) & 0x40 (ACCEL_ZOUT_L)
Tmp=Wire.read()<<8|Wire.read(); // 0x41 (TEMP_OUT_H) & 0x42 (TEMP_OUT_L)
GyX=Wire.read()<<8|Wire.read(); // 0x43 (GYRO_XOUT_H) & 0x44 (GYRO_XOUT_L)
GyY=Wire.read()<<8|Wire.read(); // 0x45 (GYRO_YOUT_H) & 0x46 (GYRO_YOUT_L)
GyZ=Wire.read()<<8|Wire.read(); // 0x47 (GYRO_ZOUT_H) & 0x48 (GYRO_ZOUT_L)
Serial.print("AcX = "); Serial.print(AcX);
Serial.print(" , AcY = "); Serial.print(AcY);
Serial.print(" , AcZ = "); Serial.print(AcZ);
//Serial.print(" , Tmp = "); Serial.print(Tmp/340.00+36.53); //equation for temperature in degrees C from datasheet
Serial.print(" , Gx = "); Serial.print(GyX);
Serial.print(" , Gy = "); Serial.print(GyY);
Serial.print(" , Gz = "); Serial.println(GyZ);
delay(333);
}
Sample data
AcX = -1340, AcY = 16344, AcZ = -2480, Gx = -4480, Gy = 5981, Gz = 337
AcX = -1360, AcY = 16320, AcZ = -2564, Gx = -4480, Gy = 5977, Gz = 384
AcX = -1468, AcY = 16364, AcZ = -2372, Gx = -4448, Gy = 5974, Gz = 372
AcX = -1392, AcY = 16400, AcZ = -2460, Gx = -4448, Gy = 5985, Gz = 358
AcX = -1528, AcY = 16444, AcZ = -2468, Gx = -4432, Gy = 5973, Gz = 366
AcX = -1624, AcY = 16184, AcZ = -2280, Gx = -4400, Gy = 5837, Gz = 680
AcX = -1456, AcY = 16504, AcZ = -2772, Gx = -4416, Gy = 6145, Gz = 314
AcX = -1428, AcY = 16300, AcZ = -2388, Gx = -4400, Gy = 5972, Gz = 327
AcX = 840, AcY = 14964, AcZ = -4496, Gx = -4400, Gy = -396, Gz = 4395
AcX = 3860, AcY = 13236, AcZ = -5996, Gx = -4384, Gy = 5496, Gz = -35
AcX = -11640, AcY = -20580, AcZ = -16856, Gx = -4384, Gy = -32768, Gz = 18097
AcX = 2508, AcY = -2776, AcZ = -14324, Gx = -4368, Gy = 8152, Gz = -21
AcX = 1528, AcY = -1016, AcZ = -15804, Gx = -4336, Gy = 19600, Gz = 5028
AcX = -2628, AcY = -20, AcZ = -15544, Gx = -4336, Gy = -2376, Gz = -14080
AcX = -13568, AcY = 5092, AcZ = -5032, Gx = -4336, Gy = 5002, Gz = 1136
AcX = -13760, AcY = 8172, AcZ = -3764, Gx = -4352, Gy = 6979, Gz = 217
AcX = -12484, AcY = 8592, AcZ = -4688, Gx = -4304, Gy = 6213, Gz = 507
AcX = -12988, AcY = 8584, AcZ = -2780, Gx = -4336, Gy = 6077, Gz = -387
AcX = -13196, AcY = 8776, AcZ = -2772, Gx = -4288, Gy = 5948, Gz = 299
AcX = -12796, AcY = 9148, AcZ = -2796, Gx = -4304, Gy = 6088, Gz = 429
AcX = -32768, AcY = 32767, AcZ = -32768, Gx = -4272, Gy = 32767, Gz = -32768
AcX = 7804, AcY = -5236, AcZ = -14148, Gx = -4304, Gy = 834, Gz = -359
AcX = 5348, AcY = -5744, AcZ = -24352, Gx = -4288, Gy = 6183, Gz = 14962
AcX = -11584, AcY = 6648, AcZ = 2876, Gx = -4272, Gy = 4733, Gz = -7217
AcX = -13332, AcY = 8624, AcZ = 1364, Gx = -4272, Gy = 6619, Gz = 1104
AcX = -13196, AcY = 9528, AcZ = -5024, Gx = -4256, Gy = 3142, Gz = -362
AcX = -11168, AcY = 11260, AcZ = 2920, Gx = -4272, Gy = 4108, Gz = 373
A Python code that derives speed from sample data generated by ChatGPT
ChatGPT Prompt
Please write a Python code that calculates the speed in kilometers per hour from the data obtained from MPU-6050 (acceleration X, Y, Z & gyro Gx, Gy, Gz). The speed should be displayed as a single value by integrating all the data. Please use a Kalman filter.
Code
import numpy as np
def read_sensor_data(file_path):
sensor_data = []
with open(file_path, 'r') as file:
for line in file:
line = line.strip().replace(" ", "")
parts = line.split(",")
values = {
'ax': int(parts[0].split("=")[1]),
'ay': int(parts[1].split("=")[1]),
'az': int(parts[2].split("=")[1]),
'gx': int(parts[3].split("=")[1]),
'gy': int(parts[4].split("=")[1]),
'gz': int(parts[5].split("=")[1])
}
sensor_data.append(values)
return sensor_data
def kalman_filter(sensor_data, dt, accel_scale, gyro_scale, g):
# Calculate velocity and orientation from accelerometer and gyroscope data
velocity = np.array([0.0, 0.0, 0.0]) # Initial velocity
orientation = np.array([0.0, 0.0, 0.0]) # Orientation (roll, pitch, yaw)
covariance = np.eye(6) # Covariance matrix for estimating errors in velocity and orientation
process_noise = np.diag([0.1, 0.1, 0.1, 0.01, 0.01, 0.01]) # Process noise
measurement_noise = np.diag([20.0, 20.0, 20.0, 0.1, 0.1, 0.1]) # Measurement noise
I = np.eye(6) # Identity matrix
for data in sensor_data:
ax, ay, az = data['ax'] / accel_scale * g, data['ay'] / accel_scale * g, data['az'] / accel_scale * g
gx, gy, gz = data['gx'] / gyro_scale, data['gy'] / gyro_scale, data['gz'] / gyro_scale
# Update orientation based on gyroscope data
orientation += np.array([gx, gy, gz]) * dt
# Update state estimates
state = np.hstack((velocity, orientation))
state[0:3] += np.array([ax, ay, az]) * dt # Update velocity
# Prediction step of the Kalman filter
covariance = covariance + process_noise
# Update step
kalman_gain = covariance @ np.linalg.inv(covariance + measurement_noise)
state += kalman_gain @ (np.hstack([ax, ay, az, gx, gy, gz]) - state)
covariance = (I - kalman_gain) @ covariance
velocity = state[0:3] # Updated velocity
orientation = state[3:6] # Updated orientation
return velocity
def convert_velocity_to_kmph(velocity):
speed_ms = np.linalg.norm(velocity) # Velocity in m/s
speed_kmph = speed_ms * 3.6 # Convert m/s to km/h
return speed_kmph
def main():
file_path = "C:/Users/budob/Downloads/mpu6050.txt"
sensor_data = read_sensor_data(file_path)
accel_scale = 16384.0 # Accelerometer scale factor
gyro_scale = 131.0 # Gyroscope scale factor
g = 9.81 # Acceleration due to gravity in m/s^2
dt = 0.1 # Sampling interval in seconds
velocity = kalman_filter(sensor_data, dt, accel_scale, gyro_scale, g)
speed_kmph = convert_velocity_to_kmph(velocity)
print(f"Estimated speed: {speed_kmph:.2f} km/h")
if __name__ == "__main__":
main()
Execution Results
How to detect the start and end points of a swing
To measure accurate hourly speed and the number of practice swings, it is necessary to determine the starting and ending points of the swing from the data.
The following code is particularly organized to read the sensor data from a file, analyze the data to detect movements based on the acceleration changes, and then print out where these movements start and stop.
ChatGPT Prompt
Please write a Python code that attaches an MPU6050 accelerometer to a badminton racket to determine the starting and ending points of a swing. The data should be read from "C:\Users\budob\Downloads\mpu6050.txt" and the code should also use gyroscope data.
Code
import re
def read_sensor_data(file_path):
# Read data from the file line by line into a list
with open(file_path, 'r') as file:
lines = file.readlines()
return lines
def detect_swings(lines, accel_threshold=5000, gyro_threshold=2000):
previous_acx, previous_gz = None, None
swings = []
swing_active = False
start_line = None
for i, line in enumerate(lines):
# Use regular expression to extract the values of AcX and Gz
match = re.search(r"AcX = ([-\d]+),.*Gz = ([-\d]+)", line)
if match:
acx, gz = int(match.group(1)), int(match.group(2))
# Detect the start of a swing
if not swing_active and (abs(acx - (previous_acx if previous_acx is not None else 0)) > accel_threshold or
abs(gz - (previous_gz if previous_gz is not None else 0)) > gyro_threshold):
swing_active = True
start_line = i + 1 # Line numbers start from 1
# Detect the end of a swing
elif swing_active and abs(acx - previous_acx) <= accel_threshold and abs(gz - previous_gz) <= gyro_threshold:
swings.append((start_line, i))
swing_active = False
previous_acx, previous_gz = acx, gz
# Handle the last swing if it did not end within the data
if swing_active:
swings.append((start_line, len(lines)))
return swings
def main():
file_path = "C:\\Users\\budob\\Downloads\\mpu6050.txt"
lines = read_sensor_data(file_path)
swings = detect_swings(lines)
for swing in swings:
# Print the start and end line numbers of each detected swing
print(f"Swing started at line {swing[0]} and ended at line {swing[1]}")
if __name__ == "__main__":
main()
Execution Results
Going forward, it will be necessary to repeatedly test and adjust the threshold to improve the accuracy of the detection.