DEV Community

Beck_Moulton
Beck_Moulton

Posted on

Decoding Stress: Building an HRV Anomaly Detector with VAE and InfluxDB

We are living in the era of the "Quantified Self." Between Oura rings, Apple Watches, and Whoop straps, we are drowning in data but starving for actionable insights. One of the most critical metrics for recovery and mental health is Heart Rate Variability (HRV). However, a raw HRV score doesn't tell the whole story. Is your HRV low because you crushed a leg day, or because your boss sent a "Can we talk?" email at 4:30 PM?

In this tutorial, we’ll build an end-to-end Physiological Stress Labeling Pipeline. We will use Heart Rate Variability (HRV) data, Variational Autoencoders (VAE) for unsupervised anomaly detection, and InfluxDB for time-series management. By the end, you’ll be able to automatically correlate physiological dips with external stressors like calendar events and inbox volume. This is cutting-edge Wearable Data Engineering designed to transform pixels and pulses into real-world meaning.

The Architecture 🏗️

To handle high-frequency wearable data, we need a robust pipeline that can ingest, process, and analyze signals in near real-time. Here is how our system flows:

graph TD
    A[Wearable Device / HealthKit] -->|Raw HRV Samples| B(InfluxDB)
    B --> C{Data Processor}
    D[Google Cal / Outlook API] --> C
    C -->|Feature Vector| E[VAE Model - Keras]
    E -->|Reconstruction Error| F{Anomaly Detected?}
    F -->|Yes| G[Automated Stress Labeling]
    F -->|No| H[Baseline Monitoring]
    G --> I[Plotly Dashboard]
Enter fullscreen mode Exit fullscreen mode

Prerequisites 🛠️

Before we dive into the code, ensure you have the following stack ready:

  • Storage: InfluxDB (perfect for time-series physiological data).
  • ML Framework: Keras/TensorFlow for building the VAE.
  • Preprocessing: Scikit-learn.
  • Visualization: Plotly.

Step 1: Ingesting Time-Series Data with InfluxDB

HRV data is inherently chronological. Unlike standard SQL databases, InfluxDB allows us to query "windows" of time efficiently. We'll pull our rmssd (Root Mean Square of Successive Differences) values—a common proxy for HRV.

from influxdb_client import InfluxDBClient

def fetch_hrv_data(bucket, org, token, url):
    client = InfluxDBClient(url=url, token=token, org=org)
    query = f'''
    from(bucket: "{bucket}")
      |> range(start: -7d)
      |> filter(fn: (r) => r["_measurement"] == "hrv_metrics")
      |> filter(fn: (r) => r["_field"] == "rmssd")
      |> aggregateWindow(every: 5m, fn: mean, createEmpty: false)
    '''
    df = client.query_api().query_data_frame(query)
    return df
Enter fullscreen mode Exit fullscreen mode

Step 2: The Brain — Building the VAE with Keras 🧠

Why a Variational Autoencoder (VAE)? Standard anomaly detection looks for outliers. But physiological data is "noisy." A VAE learns the latent distribution of your "normal" relaxed state. When the model fails to reconstruct a specific HRV pattern (high reconstruction error), it signals an anomaly.

import numpy as np
from tensorflow import keras
from tensorflow.keras import layers

def build_vae(input_dim, latent_dim=2):
    # Encoder
    inputs = layers.Input(shape=(input_dim,))
    h = layers.Dense(64, activation='relu')(inputs)
    h = layers.Dense(32, activation='relu')(h)

    z_mean = layers.Dense(latent_dim)(h)
    z_log_var = layers.Dense(latent_dim)(h)

    # Sampling layer
    def sampling(args):
        z_mean, z_log_var = args
        epsilon = keras.backend.random_normal(shape=(keras.backend.shape(z_mean)[0], latent_dim))
        return z_mean + keras.backend.exp(0.5 * z_log_var) * epsilon

    z = layers.Lambda(sampling)([z_mean, z_log_var])

    # Decoder
    decoder_h = layers.Dense(32, activation='relu')
    decoder_h2 = layers.Dense(64, activation='relu')
    decoder_outputs = layers.Dense(input_dim, activation='sigmoid')

    res_h = decoder_h(z)
    res_h2 = decoder_h2(res_h)
    outputs = decoder_outputs(res_h2)

    vae = keras.Model(inputs, outputs)
    return vae

# Training note: Normalize your data using Scikit-learn's MinMaxScaler first!
Enter fullscreen mode Exit fullscreen mode

Step 3: Contextual Correlation (The "Why")

Detecting an anomaly is only half the battle. To make it useful, we correlate the timestamps of "High Reconstruction Error" with external data. If your VAE flags 2:00 PM on Tuesday as "Anomalous" and your calendar shows "Quarterly Performance Review," the system automatically labels that physiological spike as Work-Related Stress.

For more production-ready patterns on integrating health data with LLMs for deeper semantic analysis, I highly recommend checking out the advanced guides at WellAlly Tech Blog. They cover some incredible nuances in bio-data engineering that go far beyond basic anomaly detection. 🥑

Step 4: Visualizing the Stress Landscape with Plotly

Static charts are boring. We need an interactive view to see where our body diverged from the norm.

import plotly.graph_objects as go

def plot_stress_anomalies(df, reconstruction_errors, threshold):
    fig = go.Figure()

    # Original HRV Signal
    fig.add_trace(go.Scatter(x=df['time'], y=df['rmssd'], mode='lines', name='HRV (RMSSD)'))

    # Highlight Anomalies
    anomalies = df[reconstruction_errors > threshold]
    fig.add_trace(go.Scatter(x=anomalies['time'], y=anomalies['rmssd'], 
                             mode='markers', name='Stress Event',
                             marker=dict(color='red', size=10)))

    fig.update_layout(title='Automated Physiological Stress Detection',
                      xaxis_title='Time', yaxis_title='ms')
    fig.show()
Enter fullscreen mode Exit fullscreen mode

Wrapping Up 🎁

By combining unsupervised learning (VAE) with time-series databases (InfluxDB), we’ve moved past simple "low heart rate" alerts. We've built a system that understands the context of your life.

What’s next?

  1. Fine-tuning: Use a LSTM-VAE to capture the temporal dependencies in the HRV signal more effectively.
  2. Edge Deployment: Try running a quantized version of this model directly on a mobile device.
  3. Deeper Insights: If you are looking for advanced architectural patterns for health tech, definitely head over to the WellAlly Tech Blog—it's a goldmine for developers in the health-wearable space.

How are you using your wearable data? Are you just looking at the charts, or are you building something with them? Let’s chat in the comments! 👇

Top comments (0)