
Mood change detection
Mood changes in young people often go unnoticed for years, and heart data is too private to pool in one place. I built a pipeline that estimates whether someone feels positive or negative from their heart rhythm, tested it on 74 people it had never seen, and trained it across six Raspberry Pis without sharing anyone's raw data.
Shared under CC BY, via arXiv.

The problem
Mood disorders in young people are usually caught through self-report and booked appointments, and the average gap between the first signs and treatment is eight to twelve years. Most heart-rhythm models are tested on the same people they learned from, which flatters them, and pooling everyone's heart data raises real privacy concerns.
What I did
I built the whole pipeline in Python, from the raw heart signal to cross-participant tests on three public datasets, and set up the six-node Raspberry Pi cluster for federated training. It is meant as a personal awareness tool, not a clinical one.
How it works
Record the heart
The heart's electrical signal comes from three public research datasets, 74 people in all, or from a chest strap.
Cut it up
Each recording is cut into 45-second pieces.
Measure the rhythm
Nine standard measures describe how the gap between heartbeats varies in each piece.
Guess, then vote
A model guesses the feeling for each piece, and the pieces vote on the whole clip.
Test on strangers
Each test leaves one person out of training, so the model is judged on someone it has never seen.
Train without pooling
Six Raspberry Pis train together and share only the model's numbers, never anyone's heart data.
Signal to features
01Acquire
ECG from DREAMER (23 people, 256 Hz), AMIGOS (15, 128 Hz) and EmoWear (36, 250 Hz), or live from a Polar H10 at 130 Hz.
- DREAMER
- AMIGOS
- EmoWear
- Polar H10
02Window
Non-overlapping 45-second windows, chosen over 15, 30, 60 and 90 s because they scored above the other lengths under both protocols.
45 s03Features
Nine HRV features per window. Nine standard features matched a 78-feature extended set under cross-participant evaluation.
- RMSSD
- SDNN
- pNN50
- MeanNN
- HF
- LF
- LF/HF
- SD1
- SD2
Training and evaluation
04Classify
Six classifiers under identical settings, a prediction per window and a majority vote per clip. Classes balanced by downsampling, not SMOTE, which would invent heart patterns between people.
- Logistic Regression
- Random Forest
- SVM
- XGBoost
- LDA
- KNN
05Evaluate
Leave-one-participant-out cross-validation is the main benchmark: the held-out person's data never touches training.
LOPO-CV
Weight vectors only, 9 × C
Federated cluster, six Raspberry Pi 4s
06Federate
One server and five clients, each holding four or five participants. Each trains Logistic Regression locally; FedAvg averages the weights. Random Forest cannot be averaged this way, so it stays centralised.
- Flower
- FedAvg
- Logistic Regression
In numbers
53.17%
Binary valence, leave-one-participant-out, DREAMER, Logistic Regression
Modest; 50% is chance
56.3%
Recall for the rarest class after balancing, up from 7.8%
Within participant, four class, DREAMER
2.7points
Largest gap between federated and centralised training (AMIGOS, 55.04% against 57.76%)
74
Participants across three public datasets
75%
Binary valence in the Polar H10 pilot, against 52.81% across participants
Pilot on one person, four clips
My part
Pipeline
I built every stage in Python: pre-processing, windowing, heart rate variability features, the classifiers and the evaluation.
All of it
Federated cluster
I deployed the federated training on six Raspberry Pi 4s with Flower.
All of it
Research
I did all of it: the coding, the experiments, the analysis and the writing. I am first author, and colleagues at the university are co-authors.
All of it
Built with
- Python
- The whole pipeline
- Flower
- Federated training
- Raspberry Pi 4
- Six-node cluster
- Logistic Regression and Random Forest
- The main classifiers
- XGBoost, SVM, LDA and KNN
- The comparison
- Polar H10
- The pilot recording
In pictures


From the work
These are pictures of the project's own code running on simulated input. They show how it works, not what it found.



Where it stands
Accepted on 1 June 2026 and presented at IEEE CIBCB 2026 in Athens.
What it found
The feeling closest to low mood, low energy and negative, was nearly invisible: the model found 7.8% of it. With the training data balanced, it found 56.3%. That test used the same people for training and testing.
Asked only whether someone felt positive or negative, on people it had never seen, it was right at most 53.17% of the time. That is modest. All six models I tried landed between 49.9% and 53.2%, which suggests the limit is in the signal, not the model.
Training across the six Raspberry Pis gave results comparable to training in one place, within 2.7 points on all three datasets.
In a pilot on one person with a Polar H10 chest strap, it was right 75% of the time for positive or negative, against 52.81% across participants. One person is not a study: it is promising, pending a wider one.
Class imbalance was the main thing holding back low-arousal, low-valence detection: 109 windows against 304 for the largest class. Downsampling every class to 109 raised its recall from 7.8% to 56.3% within participant, while the other classes moved by less.
Six classifiers converge between 49.9% and 53.2% on binary valence across participants. The paper reads that ceiling as physiological rather than algorithmic, which points to personalisation over time.
Federated against centralised Logistic Regression: DREAMER 53.99% against 53.17%, AMIGOS 55.04% against 57.76%, EmoWear 50.87% against 50.96%. Comparable, with no raw features shared; no significance test was run.
What went wrong
Too little data per person
One dataset had only about 24 pieces per person, so each test trained on very little, and four-way accuracy started at 28.82%.
AMIGOS: 15 participants, about 24 windows each, so each LOPO-CV fold trained on fewer than 336 examples against about 70 windows per participant in DREAMER; 28.82% four-class before equalisation.
Feelings did not follow the script
In the pilot, each clip was chosen to stir one feeling, but what the participant reported did not always match.
Self-reported valence and arousal did not always fall in the intended quadrant, so the pilot was scored against the self-reports.
The live validation is one participant, a study author, and four clips. Federated training used dataset partitions, not months of one person's data, and assumed a shared local network.
Four-class accuracy across participants is 24.02%. The contribution is the imbalance finding and private training, not accurate mood detection.
What comes next
Add EEG alongside the heart signal in the federated setup, and run live validation on a wider group of people, which needs ethics approval and informed consent first.
The paper is being extended into a journal paper.