Analysis of HRV Using Mobile Devices and Machine Learning

Research project for mobile ECG/PPG peak detection and HRV analysis. Compares 1D-CNN detectors against classical Pan–Tompkins/NeuroKit baselines using smartphone camera PPG and Polar H10 ECG. Peer-reviewed TASK Quarterly publication (March 2026).

Year
2025
Category
Research / Applied ML (biosignals)
Role
Co-author & primary codebase contributor (team of five)
Stack
PyTorch, Signal Processing, Android, Python, Research, HRV, PPG, ECG
Analysis of HRV Using Mobile Devices and Machine Learning cover
Term definitions18 terms
1D-CNN
One-dimensional convolutional network that slides along the time axis of a biosignal window; the shared pattern behind both the ECG and PPG peak scorers.
band-pass
Filter that keeps a frequency band and removes the rest before peak detection. ECG: 5th-order Butterworth 0.5–45 Hz; PPG: 4th-order 0.5–5 Hz – wrong passband quietly destroys peaks.
ECG
Electrocardiography – recording the heart’s electrical activity; in this project primarily via a Polar H10 chest strap.
F1
Harmonic mean of precision and recall for peak matching within a time tolerance; the primary detector quality metric in this work.
HRV
Heart rate variability – statistics of variation between successive beat intervals (e.g. SDNN, RMSSD), used as a physiology/research marker.
IBI
Inter-beat interval – time between successive PPG pulse peaks; PPG analogue of the RR interval.
LSTM
Long short-term memory recurrent layer – in the ECG network a unidirectional LSTM sits after the conv stack so per-sample logits can use QRS context across the 256-sample window.
match tolerance
Maximum allowed time offset when pairing a predicted peak with a reference peak. ECG evals here use ~10 ms; PPG often 150–250 ms – F1 is not comparable across protocols until this is published.
NeuroKit2
Open-source Python biosignal toolkit used as a classical peak-detection baseline alongside Pan–Tompkins.
Pan–Tompkins
A classical ECG QRS/R-peak detector (band-pass, derivative, square, integrate, threshold) used here as baseline and as a label source for the ECG network.
PPG
Photoplethysmography – estimating blood-volume pulses optically, here from a phone camera aimed at a fingertip lit by the flash, with a software green mute/filter applied in the acquisition app.
PTT
Pulse transit time – delay between an ECG R-peak and the corresponding PPG pulse peak after time alignment.
R-peak
The prominent upward spike of the ECG QRS complex that marks a ventricular beat; RR intervals and ECG peak F1 are defined from these timestamps.
RMSSD
Root mean square of successive differences of NN intervals; a short-term HRV measure computed in the GUI and analysis scripts.
RR interval
Time between consecutive ECG R-peaks; the classical beat-to-beat interval for HRV from ECG.
SDNN
Standard deviation of NN intervals over a window; an overall HRV dispersion measure shown alongside RMSSD.
SE block
Squeeze-and-Excitation – a channel-attention module that reweights convolutional feature maps; used inside the PPG peak detector.
WebSocket
A persistent TCP-based messaging channel; the Android companion app streams luma samples to the desktop listener on port 8765.

Introduction & Core Problem

This project presents a research pipeline for real-time and offline analysis of heart rate variability (HRV) using non-invasive mobile sensors: a camera-based smartphone app (PPG) and a Polar H10 chest strap (ECG).

The core challenge in mobile HRV analysis lies in beat timing accuracy, not high-level classification metrics. Standard machine learning models often report high sample-level accuracy or classification F1 scores for peak vs. non-peak frames, but frame-by-frame evaluation hides critical temporal errors.

In physiological monitoring, a detection is only valid if the predicted peak aligns with the actual heartbeat within a strict match tolerance window. Therefore, this project evaluates deep learning architectures (1D-CNNs) alongside classical baselines (Pan–Tompkins, NeuroKit2) strictly on beat-to-beat interval accuracy (RR intervals for ECG and IBI for PPG) rather than raw sample classification accuracy.

What we built, with whom, on what data

This research project, developed at Gdańsk University of Technology (2025) and published in TASK Quarterly (March 2026), centers on an end-to-end Python ML pipeline (research-project). It benchmarks custom 1D-CNNs (featuring SE blocks and LSTM layers) against classical algorithmic baselines (Pan–Tompkins, NeuroKit2) across dual-modality biosignals: Polar H10 chest-strap ECG and smartphone camera PPG (streamed via companion app: PPGbetter).

ML & analysis stack: Python 3, PyTorch (~2.6 CUDA), SciPy / NumPy / Pandas, NeuroKit2, scikit-learn, websockets. Pre-trained weights ship with the repo.

Authors: Jan Bancerewicz, Julian Kotłowski, Mateusz Rzęsa, Julia Morawska, and Ostap Lozovyy under the academic supervision of Ph.D. Julian Szymański.

In scope: ML architecture design and offline neural network training/evaluation, automated beat-matching logic within strict time tolerances, comparison against classical peak detectors, end-to-end signal filtering, HRV (RMSSD/SDNN) and PTT computation, and desktop GUI for live/replay signal inspection.

Out of scope: clinical certification, on-device mobile ML execution, full cohort raw dataset distribution (privacy/size constraints), production CI/CD, hospital integration.

Why Standard Mobile Pulse Estimation Fails for HRV

Phone health APIs and raw brightness thresholds return a calm BPM curve. Research-grade HRV needs every beat timed on both modalities, then compared.

Camera PPG fights you in three concrete ways: the pulse can sit inverted (aligned PPG-vs-ECG validation multiplies the optical trace by -1 before filtering), motion swamps the optical band, and the phone wall clock is not the Polar clock. Polar ECG at FS ≈ 130 Hz is the easier modality – but even there, “we trained a CNN” is meaningless unless you publish match tolerance and label source.

Without modality-specific band-pass filters, a refractory gap between accepted peaks, and an explicit classical or NeuroKit2 reference, you cannot tell whether an RMSSD mismatch is physiology or a missed peak. That is the failure mode we designed against.

From camera capture app to side-by-side RMSSD

How the system grew

Start: Polar BLE connect scripts and an early ECG CNN that plotted R-peaks – detect chest-strap beats, little else.

Then: live plots → HRV GUI metrics → PPG CNN branch → timestamp/alignment fixes → classical Pan–Tompkins baselines → redesigned PPG residual + SE block network → cleanup for publication.

End: Replay (or stream) fingertip PPG next to Polar/file ECG, mark peaks classically and with CNNs, show RMSSD / SDNN / RR interval side by side, export aligned CSV, reproduce detector comparisons. Under controlled recording, mobile PPG can approximate ECG-based HRV while staying more artifact-sensitive.

End-to-end HRV Mobile ML pipeline from PPG/ECG ingestion through filtering, peak detection, alignment, and desktop GUI

Pipeline overview: smartphone PPG (WebSocket) and Polar H10 ECG (BLE / CSV replay) → modality-specific band-pass → classical vs 1D-CNN / LSTM beat detection (IBI / RR interval) → alignment, RMSSD / SDNN / PTT → Tkinter GUI.

Acquisition path

Desktop entry point: HR Monitor. Two producer threads feed queues:

  • PPG – listens on 0.0.0.0:8765, protocol timestamp value (async WebSocket from the phone).
  • ECG – Polar over BLE, or CSV replay for demos.

Windowed processors (256 samples for ECG, ~100 for PPG) filter, detect peaks, and drive three tabs: PPG + ECG, HRV (RMSSD / SDNN / RR for EKG vs PPG), Peaks Compare. Save buttons write ECG, PPG, or both aligned; HRV plots export as PNG at 300 dpi.

HR Monitor PPG + ECG tab with raw traces and detected peaks for both modalities

HR Monitor — PPG + ECG tab. Polar ECG with marked R-peaks above; camera PPG pulse peaks below. Same session, dual-modality replay.

Signal chain (same idea, different numbers)

  1. band-passECG: 5th-order Butterworth 0.5–45 Hz. PPG: 4th-order 0.5–5 Hz. Wrong passband quietly destroys peaks before any network runs.
  2. Classical peaksECG: Pan–Tompkins (band-pass → derivative → square → integrate → threshold). PPG: local maxima with refractory spacing (~300–600 ms depending on the eval; comparison path often ~350 ms).
  3. Neural peaks
    • ECG – input length 256. Conv1D channels 1 → 16 → 32 → 64 → 128 (kernels 5 / 5 / 3 / 3), BatchNorm + LeakyReLU, max-pool, unidirectional LSTM, FC head → per-sample logits. Training: labels from Pan–Tompkins on filtered Polar recordings; BCE-with-logits; Adam 1e-4. Two held-out recordings stay out of training.
    • PPG – segments 50–100 samples (paths disagree – real friction). Initial Conv1D 1→32 (k=7, GELU + BN); residual blocks 32→64 (k=9), 64→128 (k=5), 128→128 (k=3), 128→128 (k=7) with dropout; SE block on 128 channels; avg+max pool; FC → sigmoid per-sample peak probability. Validation uses a held-out multi-minute fingertip recording.
  4. Postprocess – threshold probabilities (often 0.5; probabilistic PPG eval also uses height 0.25 on the probability trace), enforce minimum inter-peak distance, map to unix timestamps for pairing.
  5. HRV / PTTRR interval from ECG, IBI from PPG → mean / SDNN / RMSSD (rolling window ~60 s in the paper narrative). After absolute-time alignment, pair each R-peak with the next chronological PPG peak:
PTT=t(PPG)t(ECG)PTT = t_(PPG) - t_(ECG)
HR Monitor HRV tab comparing RMSSD, SDNN, and RR side by side for EKG versus PPG

HR Monitor — HRV tab. Left: EKG RMSSD / SDNN / RR. Right: the same from PPG. Clean chest-strap metrics vs artifact-sensitive optical column — why beat timing, not BPM curves, is the product.

Offline evaluation compares detectors head-to-head:

ComparisonWhat it answers
ECG CNN vs Pan–Tompkins vs NeuroKit2Beat-match on Polar recordings; match tolerance ~10 ms at 130 Hz
PPG CNN vs reference peaksTolerance often 150–250 ms (one frozen dump: 264 ms)
PPG model vs ECG NeuroKit2 peaksAligned dual-modality pair; invert + band-pass; 250 ms tolerance
Dual-trace visualizationAmplitude-aligned PPG and ECG for qualitative checks

ECG and PPG F1 numbers are not directly comparable until you normalize match tolerance. That is intentional honesty, not a bug in the write-up.

Terminal F1 scores and plot of ECG CNN peaks versus NeuroKit reference on Polar recording

Polar ECG detector bake-off: CNN vs Pan–Tompkins vs NeuroKit2 (56 peaks each). CNN vs NeuroKit F10.973 at ~10 ms match tolerance; red AI markers sit on blue NeuroKit crosses.

Decisions worth defending

  1. Classical + neural side by side. ECG training labels partly come from Pan–Tompkins. Shipping the classical detector next to the CNN makes label circularity visible instead of one glossy neural score.
  2. Separate architectures per sensor. Polar QRS context at 130 Hz and camera pulse morphology are not one problem. Forcing a shared “universal beats model” would hide that.
  3. Thin phone, heavy desktop. PPGbetter only acquires. Tkinter + CUDA/CPU PyTorch owns ML and paper figures. A plain WebSocket line protocol on :8765 stays trivial on Android – at the cost of no auth on 0.0.0.0. Fine for a lab LAN.
  4. Explicit held-out recordings. Not a full MLOps story, but an anti-leakage gesture that is written down rather than hoped for.

What the numbers say – and what we refuse to inflate

MetricValueConditionsTakeaway
ECG R-peak F10.98Polar H10; neural vs NeuroKit2 / Pan–TompkinsCNN+LSTM viable for this strap/FS; keep classical baseline
ECG accuracy97%same Polar setupTracks F1 here; easier to inflate alone
PPG sample accuracy / F198.17% / 0.9774frame-level confusion matrixStrong frame-level separation
PPG AI vs reference peaksPrec 0.901, Rec 0.889, F1 0.895tolerance ≈ 264 msBeat-match is the HRV-relevant metric
PPG F1 span~0.824–0.977Peak-match @ ~264 ms → sample-level classificationProtocol dominates the headline
ECG match tolerance~10 msFS = 130 HzStricter than PPG evals
PPG band-pass0.5–5 Hz, order 4vs ECG 0.5–45 HzWrong filter destroys optical peaks
ECG window / FS256 samples @ 130 HzECG network input≈ 2 s of QRS context
Cohort6 subjectsAuthor-confirmedKeep generalization claims narrow
Paper RMSSD/SDNN error tablesNot quoted in this write-up
F1 @ 50/100/150 ms, median PTT, plot latencynot frozen herepipelines existRe-measure if needed

Transferable rule: publish match tolerance and label source next to every peak F1. Otherwise mobile-sensor results are not comparable across blogs or papers.

What we would do differently next time

  1. Improve the companion app before collecting data. We limited ourselves to a 30 Hz sample rate, which gives us a hard limit on how heart rate can be measured.
  2. One shared eval config for sampling rate, window lengths, tolerances, and model paths.
  3. Commit a frozen metrics snapshot from re-runnable comparison scripts.
  4. Auth-bind the WebSocket to localhost or a token.
  5. Smoke tests on synthetic sine / QRS fixtures.
  6. Document file replay as the supported demo path; treat BLE + phone as optional.

We started with Polar R-peaks on a plot. We ended with a pipeline that can replay fingertip PPG beside Polar ECG, defend detector scores against classical and NeuroKit2 references, and show side-by-side HRV without pretending sample accuracy is beat accuracy. Consumer sensors can support controlled HRV research. They do not forgive motion, clock skew, or optimistic metrics.

Working on something like this?

If any of this is close to a problem on your team, I would like to hear about it. LinkedIn is the fastest way to reach me.