1 of 21

KinetiClip™

Saim Hasan, Jacob Tang, Ingus Stegis, Katie Fourtner, Sam Doane

2 of 21

ACL Recovery

  • 20.5% of all knee injuries

  • 76% require surgical reconstruction

  • 18% of professional male football players experienced reinjury (15% retear)

3 of 21

Gait Asymmetry

  • Persist for 6 months-1 year after ACLR
  • Post-injury gait asymmetry can lead to…
  • Muscle imbalance
  • Joint strain or pain
  • Causes osteoarthritis
  • Increased reinjury risk

4 of 21

Toe-Out Angle

Strongly impacts ACL stress:

  • Toe-out significantly increases ACL strain�
  • Angles exceeding 30° are linked to ACL stress�
  • While running, peak anterior ACL force is 20% greater for toe-out

5 of 21

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.

6 of 21

Our Solution

  • Housing units protect IMUs and easily clip on clothing and shoes
  • 3 Gyroscopes placed on each pelvic bone and affected heel measures angular velocity
  • Calculates toe angle and gait asymmetry, sharing the data with PT

7 of 21

Measuring Toe Angle

  • Click button to calibrate initial angle

  • Heel and pelvis yaw rate difference

  • Numerical integration to determine toe angle

8 of 21

Measuring Gait Asymmetry

  • Symmetry index (SI)

  • Determine stride period T from pelvis IMU pitch rate sign change�
    • Hip joint changes rotation direction at stride peaks

9 of 21

https://drive.google.com/drive/folders/1Mrfe90MjEbkb69NbPegqk9JYutBWu8KE?usp=sharing

Demo

10 of 21

Toe-out angle (top) and gait symmetry index (bottom) computed from synthetic IMU data, approximating expected gait variations.

11 of 21

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

12 of 21

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

13 of 21

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

14 of 21

Appendix

15 of 21

Use by Doctors & Physical Therapists

  • Options include expensive sensors or visual assessments
  • Quantifies rehabilitation progress through precise gait tracking
  • Identifies asymmetry and maladaptive movement patterns early
  • Reduces reinjury rates and improves patient outcomes.

16 of 21

References

17 of 21

Price

18 of 21

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("------------------------------------");

19 of 21

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);

20 of 21

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: ");

21 of 21

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;

}

}