Vyom Sawhney

← all portfolio

PosturePatch

A wearable posture sensor, from a TinyML classifier running on an IMU board to ESP32 firmware for a strain-gauge patch.

Built with
C++, TensorFlow Lite Micro, ESP-IDF

Real-time posture classification on a microcontroller. An Arduino Nano 33 BLE Sense Rev2 reads its onboard IMU, runs a small neural network with TensorFlow Lite Micro, and reports a smoothed good/bad posture percentage over serial. A small Python app turns that stream into a live, colour-coded display.

This is the first prototype of PosturePatch, a wearable posture sensor. The firmware for the next phase, which reads strain gauges through an HX711 on an ESP32-C3, is in PosturePatchFirmware.

How it works

BMI270 IMU ──> 6 features ──> MLP 6-16-8-1 ──> 25-sample ──> serial ──> Visualizer
(accel xyz,                   (TFLite Micro,   history       "Good: 72.4%
 gyro xyz)                     ~3 KB model)                   Bad: 27.6%"
  1. Sense. Each step reads acceleration and angular rate on three axes from the board's BMI270 IMU.
  2. Classify. The six values go into a three-layer fully connected network (6 → 16 → 8 → 1, ReLU then sigmoid) running on-device with TensorFlow Lite Micro from an 8 KB tensor arena. No data leaves the board to be classified.
  3. Smooth. Single readings are noisy, so the last 25 classifications are kept in a ring buffer and reported as a percentage. At one reading every 200 ms, that is a rolling window of about five seconds.
  4. Display. Visualizer/posture_gui.py parses the serial output and shows the good-posture percentage on a background that fades from red to green.

Repository layout

Path Purpose
Posture/ Main sketch: inference, 25-sample smoothing, serial output at 115200 baud
TensorML/TensorML.ino Earlier sketch: one classification every 500 ms, no smoothing
TensorML/model_data.* The trained model as a C array, used by TensorML.ino
TensorML/excess/ Data collection, preparation, training and conversion scripts, plus the datasets
posture model v1/ The trained model in Keras (.h5), TFLite and C-header form
Visualizer/ Tkinter app that displays the live posture percentage

Training pipeline

All of these live in TensorML/excess/.

  1. Stream. sketch_may13a.txt is an Arduino sketch that prints raw IMU readings as CSV at about 10 Hz.
  2. Record. data_creation.py reads that stream over serial and saves 300 labelled samples (about 30 seconds) per run. Set label to good_posture or bad_posture and the serial port to match your board.
  3. Prepare. prepare_data.py merges the two recordings, shuffles them, standardises each feature with scikit-learn's StandardScaler, and writes posture_train_data.npz. The combined set, posture_dataset.csv, holds 600 samples, 300 per class.
  4. Train. train_model.py trains the network with Adam and binary cross-entropy for 30 epochs, holding out 20% for validation, and saves posture_model.h5.
  5. Convert. convert_tflite.py converts it to TensorFlow Lite with the default optimisations, giving a 3,092-byte .tflite file. That file is then embedded in the sketch as a C array, for example with xxd -i.

Running it

Board. Arduino Nano 33 BLE Sense Rev2. Install the Arduino_BMI270_BMM150 library and a TensorFlow Lite Micro library for Arduino, then upload Posture/Posture.ino and open the serial monitor at 115200 baud.

Visualizer. Needs Python 3 with pyserial and Tkinter.

pip install pyserial
python Visualizer/posture_gui.py

It asks for the serial port, for example COM3 or /dev/ttyACM0.

Retraining. Needs pyserial, pandas, scikit-learn, numpy and tensorflow. Run the pipeline scripts above in order.

Known issues

  • Output indexing in Posture.ino. The model has a single sigmoid output, the probability of bad posture (labels are good_posture = 0 and bad_posture = 1). Posture.ino reads two outputs, [bad, good], so output->data.f[1] falls outside the output tensor. It should compare output->data.f[0] against 0.5, as TensorML.ino does.
  • Input scaling. Training standardises every feature, but both sketches feed the model raw IMU values. The scaler's mean and standard deviation need to be saved during preparation and applied on the board before inference.
  • Small dataset. 600 samples from short recording sessions. More sessions, positions and wearers would give a better sense of how well it generalises.

Next

PosturePatch moves from an IMU on a development board to strain gauges on a patch worn on the back. Phase 1 of that firmware, a raw HX711 data stream on an ESP32-C3, is in PosturePatchFirmware.