KinetiClip™
Saim Hasan, Jacob Tang, Ingus Stegis, Katie Fourtner, Sam Doane
ACL Recovery
Gait Asymmetry
Toe-Out Angle
Strongly impacts ACL stress:
Addressing the Problem
Minimize athlete post-ACL tear reinjury risk through continuous, cost-effective monitoring of lower limb kinematics to inform physical therapy treatment decisions and recovery strategies.
Our Solution
Measuring Toe Angle
Measuring Gait Asymmetry
https://drive.google.com/drive/folders/1Mrfe90MjEbkb69NbPegqk9JYutBWu8KE?usp=sharing
Demo
Toe-out angle (top) and gait symmetry index (bottom) computed from synthetic IMU data, approximating expected gait variations.
Competitive Analysis
Consumer Factors | KinetiClip | Camera Based 3D Analysis System | Force Plate |
Manufacturability | Very few unique components (IMU, microcontroller, housing, battery) | Use high-tech expensive cameras, high speed computers, and lots of sensors | Measure ground forces for which person walks |
Assembly and usability | Place components on pants and shoes | Complex, requiring calibration and software setup | Correct placement of force plates, |
Adaptability | Clip on to any size pants and shoes | Fixed to specific setups with cameras | Less adaptable, focused force and not motion |
Cost | Low ($500-3,000) | High ($10,000 to $150,000) | High ($10,000 to $30,000) |
Portability | | | |
Future Direction
App
give real-time feedback, possibly pair with haptic
Housing
Improve housing by purchasing steel clip
Sensors
Wireless sensors to decrease clutter
Sports performance
Can be used by cyclists, runners, skiers, etc. to optimize performance
Other Medical Uses
post-stroke, hip labral tears, concussions, pigeon toes & Parkinson’s patients
Thank you!
Questions?
Ingus Stegis
Senior
BME
Saim Hasan
Sophomore
Mech E
Jacob Tang
Sophomore
Aero E
Sam Doane
Sophomore
Mech E
Katie Fourtner
Freshman
BME
Appendix
Use by Doctors & Physical Therapists
References
Price
Code
#include <Adafruit_FXOS8700.h>
/* Assign a unique ID to this sensor at the same time */
Adafruit_FXOS8700 accelmag = Adafruit_FXOS8700(0x8700A, 0x8700B);
const int gyroXPin = A0;
const int gyroYPin = A1;
// Configuration
const float Vcc = 3.3;
const int adcResolution = 1023;
const float sensitivity = 0.0333;
const float deadband = 0.5;
const unsigned long SAMPLE_INTERVAL = 50;
const int FILTER_SAMPLES = 5;
// Variables
float xZeroV, yZeroV;
unsigned long previousTime;
float angleX = 0, angleY = 0;
// Filter arrays
float xReadings[FILTER_SAMPLES] = {0};
float yReadings[FILTER_SAMPLES] = {0};
int readIndex = 0;
float smoothReading(float newReading, float readings[]) {
static float xTotal = 0;
xTotal = xTotal - readings[readIndex];
readings[readIndex] = newReading;
xTotal = xTotal + newReading;
return xTotal / FILTER_SAMPLES;
}
unsigned long getDeltaTime() {
unsigned long currentTime = millis();
unsigned long deltaTime;
if(currentTime < previousTime) {
deltaTime = (0xFFFFFFFF - previousTime) + currentTime;
} else {
deltaTime = currentTime - previousTime;
}
return deltaTime;
}
void calibrateZeroVoltage() {
long sumX = 0, sumY = 0;
const int samples = 100;
for(int i = 0; i < samples; i++) {
sumX += analogRead(gyroXPin);
sumY += analogRead(gyroYPin);
delay(10);
}
xZeroV = (sumX / samples * Vcc) / adcResolution;
yZeroV = (sumY / samples * Vcc) / adcResolution;
}
void displaySensorDetails(void) {
sensor_t accel, mag;
accelmag.getSensor(&accel, &mag);
Serial.println("------------------------------------");
Code
Serial.println("ACCELEROMETER");
Serial.println("------------------------------------");
Serial.print("Sensor: ");
Serial.println(accel.name);
Serial.print("Driver Ver: ");
Serial.println(accel.version);
Serial.print("Unique ID: 0x");
Serial.println(accel.sensor_id, HEX);
Serial.print("Min Delay: ");
Serial.print(accel.min_delay);
Serial.println(" s");
Serial.print("Max Value: ");
Serial.print(accel.max_value, 4);
Serial.println(" m/s^2");
Serial.print("Min Value: ");
Serial.print(accel.min_value, 4);
Serial.println(" m/s^2");
Serial.print("Resolution: ");
Serial.print(accel.resolution, 8);
Serial.println(" m/s^2");
Serial.println("------------------------------------");
Serial.println("");
Serial.println("------------------------------------");
Serial.println("MAGNETOMETER");
Serial.println("------------------------------------");
Serial.print("Sensor: ");
Serial.println(mag.name);
Serial.print("Driver Ver: ");
Serial.println(mag.version);
Serial.print("Unique ID: 0x");
Serial.println(mag.sensor_id, HEX);
Serial.print("Min Delay: ");
Serial.print(accel.min_delay);
Serial.println(" s");
Serial.print("Max Value: ");
Serial.print(mag.max_value);
Serial.println(" uT");
Serial.print("Min Value: ");
Serial.print(mag.min_value);
Serial.println(" uT");
Serial.print("Resolution: ");
Serial.print(mag.resolution);
Serial.println(" uT");
Serial.println("------------------------------------");
Serial.println("");
delay(500);
calibrateZeroVoltage();
previousTime = millis();
}
void setup(void) {
Serial.begin(9600);
/* Wait for the Serial Monitor */
while (!Serial) {
delay(1);
Code
}
Serial.println("FXOS8700 Test");
Serial.println("");
/* Initialise the sensor */
if (!accelmag.begin()) {
/* There was a problem detecting the FXOS8700 ... check your connections */
Serial.println("Ooops, no FXOS8700 detected ... Check your wiring!");
while (1)
;
}
/* Set accelerometer range (optional, default is 2G) */
// accelmag.setAccelRange(ACCEL_RANGE_8G);
/* Set the sensor mode (optional, default is hybrid mode) */
// accelmag.setSensorMode(ACCEL_ONLY_MODE);
/* Set the magnetometer's oversampling ratio (optional, default is 7) */
// accelmag.setMagOversamplingRatio(MAG_OSR_7);
/* Set the output data rate (optional, default is 100Hz) */
// accelmag.setOutputDataRate(ODR_400HZ);
/* Display some basic information on this sensor */
displaySensorDetails();
}
void loop(void) {
Serial.println("IMU");
sensors_event_t aevent, mevent;
/* Get a new sensor event */
accelmag.getEvent(&aevent, &mevent);
/* Display the accel results (acceleration is measured in m/s^2) */
Serial.print("A ");
Serial.print("X: ");
Serial.print(aevent.acceleration.x, 4);
Serial.print(" ");
Serial.print("Y: ");
Serial.print(aevent.acceleration.y, 4);
Serial.print(" ");
Serial.print("Z: ");
Serial.print(aevent.acceleration.z, 4);
Serial.print(" ");
Serial.println("m/s^2");
/* Display the mag results (mag data is in uTesla) */
Serial.print("M ");
Serial.print("X: ");
Code
Serial.print(mevent.magnetic.x, 1);
Serial.print(" ");
Serial.print("Y: ");
Serial.print(mevent.magnetic.y, 1);
Serial.print(" ");
Serial.print("Z: ");
Serial.print(mevent.magnetic.z, 1);
Serial.print(" ");
Serial.println("uT");
Serial.println("");
delay(500);
Serial.println("Gyro");
if (millis() - previousTime >= SAMPLE_INTERVAL) {
// Read and convert voltages
float voltageX = (analogRead(gyroXPin) * Vcc) / adcResolution;
float voltageY = (analogRead(gyroYPin) * Vcc) / adcResolution;
// Calculate angular velocities with deadband
float angularVelX = (voltageX - xZeroV) / sensitivity;
float angularVelY = (voltageY - yZeroV) / sensitivity;
if(abs(angularVelX) < deadband) angularVelX = 0;
if(abs(angularVelY) < deadband) angularVelY = 0;
// Smooth readings
angularVelX = smoothReading(angularVelX, xReadings);
angularVelY = smoothReading(angularVelY, yReadings);
// Calculate time delta and update previous time
float deltaTime = getDeltaTime() / 1000.0;
previousTime = millis();
// Integrate angles
angleX += angularVelX * deltaTime;
angleY += angularVelY * deltaTime;
// Output results
Serial.print("X-axis: ");
Serial.print(angularVelX);
Serial.print(" °/s, Angle: ");
Serial.print(angleX);
Serial.print(" deg | Y-axis: ");
Serial.print(angularVelY);
Serial.print(" °/s, Angle: ");
Serial.print(angleY);
Serial.println(" deg");
readIndex = (readIndex + 1) % FILTER_SAMPLES;
}
}