Pre-clinical data and analysis code accompanying the paper:
"Embodied AI for Closed-Loop Pneumatic Posture Regulation: A Multimodal Smart Pillow Platform for Pre-Clinical Sleep Support Research"Author: Chalinee Chanprasert, Nutnaree Duangdee, Kirana Trakulmaykee, Waritsanant Rattanachaipong, Ratchatin Chancharoen
Affiliation: Robotics and AI, International School of Engineering, Chulalongkorn University, Bangkok, Thailand
This repository contains the dataset, preprocessing pipelines, AI inference models, and analysis scripts used in the pre-clinical validation of an embodied AI cyberβphysical system for real-time closed-loop posture regulation via a soft pneumatic smart pillow.
- Multimodal sensing β 512-point pressure-sensitive array + real-time physiological signals (HR, RR, SpOβ)
- AI inference β Hybrid TCN-Transformer model for sleep apnea event detection and localization
- Distributed pneumatic actuation β 10-channel independently controlled air chambers for targeted posture adjustment
- Closed-loop control β Real-time decision-making with 3β5 second system latency
- Edge-compatible β Quantized model (~0.35 MB) deployable on Raspberry Pi 4
- Pre-clinical engineering validation data collected under controlled conditions using a surrogate (mannequin) head
- No human subjects or clinical data β all experiments non-invasive and using synthetic loads
- Research-grade datasets (SHHS, MESA) for model training and validation
- Reproducible AI pipeline from raw signal to closed-loop decision
- Clinical efficacy claims or human-subject outcomes
- Real patient physiological data or medical records
- FDA/regulatory clearance documentation
ai-pillow-system/
β
βββ README.md # This file
βββ LICENSE # MIT (code) + CC-BY-4.0 (data)
βββ requirements.txt # Python dependencies
βββ environment.yml # Conda environment specification
β
βββ data/
β βββ garmin_sleep/ # Sleep physiological time series from Garmin wearable
β β βββ hrv # Heart rate variability (HRV) β 5-min intervals
β β βββ respiration # Respiration rate (brpm) β 1-min intervals
β β βββ spo2 # Blood oxygen saturation (%) β 1-min intervals
β β βββ sleep_stages # Sleep stage annotations (light/deep/REM/awake)
β β
β βββ ocr_realtime/ # Real-time OCR-extracted physiological signals
β β βββ realtime_log_S001.csv # Per-session: timestamp, HR, RR, SpOβ, OCR confidence
β β βββ realtime_log_S002.csv
β β βββ ...
β β
β βββ head_rotation/ # IMU-based head orientation & displacement
β β βββ imu_data_session_*.csv # Raw accelerometer + derived pitch/roll angles
β β
β βββ pressure_sensing/ # 512-point pressure mat spatial recordings
β β βββ session_A.csv # Time series: timestamp + p0βp511 pressure values
β β βββ session_B.csv
β β
β βββ shhs_processed/ # SHHS/MESA training datasets (processed)
β βββ all_processed.npz # Combined SHHS + MESA: X(N, 3, T), y(N, T)
β βββ simulation_samples.npz # Pre-selected windows for closed-loop simulation
β βββ simulation_results.csv # Model predictions on simulation samples
β
βββ scripts/
β β
β βββ preprocessing/
β β βββ preprocess_physiological.py # OCR signal β windowed, normalized tensors
β β βββ shhs_preprocessor.py # SHHS EDF download + preprocessing
β β βββ mesa_preprocessor.py # MESA local EDF + XML preprocessing
β β
β βββ model/
β β βββ sleep_apnea_model.py # Core: TCN, Transformer, classifier, quantizer
β β βββ tcn_transformer.py # Standalone hybrid TCN-Transformer architecture
β β βββ train_with_shhs.py # Full training pipeline with threshold sweep
β β βββ sample_for_simulation.py # Extract model-ready samples for testing
β β βββ combinebatch.py # Merge SHHS batches + MESA into single file
β β βββ best_model.pth # Pre-trained model weights (if available)
β β
β βββ control/
β β βββ closed_loop_main.py # (Placeholder) Main control loop orchestration
β β βββ pico_firmware.py # (Placeholder) Raspberry Pi Pico firmware
β β βββ pneumatic_control.py # (Placeholder) Valve actuation logic
β β
β βββ OCR/
β β βββ main2.py # Real-time screen OCR: HR, RR, SpOβ extraction
β β βββ README.md # OCR pipeline setup & troubleshooting
β β
β βββ visualization/
β βββ plot_all_figures.py # Reproduce paper figures from processed data
β
β
βββ results/ # Saved figures and inference outputs
β βββ fig9a_delta_theta_heatmap.png
β βββ fig9b_displacement_vs_orientation.png
β βββ fig9c_pressure_heatmap.png
β βββ simulation_results.csv
β
βββ docs/ # Extended technical documentation
β βββ DATA_COLLECTION.md # Experimental protocol & session types
β βββ DATA_DICTIONARY.md # Complete field definitions & units for all CSVs
β βββ HARDWARE_SETUP.md # System architecture, wiring, BOM
β βββ MODEL_ARCHITECTURE.md # TCN-Transformer design, training config
β
βββ assets/ # (Placeholder) System diagrams, photos
βββ system_diagram.png
All datasets are collected under controlled, non-human (surrogate head) pre-clinical conditions. For complete field definitions, units, and quality notes, see docs/DATA_DICTIONARY.md.
Physiological signals exported from a Garmin smartwatch via the Labfront research platform. Used as reference for real-time signal validation.
| File | Sampling | Description | Obs. range |
|---|---|---|---|
garmin-connect-hrv-values.csv |
5 min | Heart rate variability (RMSSD, ms) | 42β102 ms |
garmin-connect-sleep-respiration.csv |
1 min | Respiration rate (breaths/min) | 11.7β20.3 brpm |
garmin-connect-sleep-pulse-ox.csv |
1 min | Blood oxygen saturation (%) | 83β100% |
garmin-connect-sleep-stage.csv |
Variable | Sleep stage annotations (light/deep/REM/awake) | β |
Key apnea indicators:
- SpOβ shows gradual decrease β transient desaturation (83β88%)
- HR slight decrease β rapid compensatory increase
- RR cessation/reduction β rebound increase
See docs/DATA_DICTIONARY.md for Labfront CSV format & header parsing.
Physiological values extracted live via vision-based OCR pipeline from Garmin companion app display. These signals feed the AI inference module during closed-loop operation.
| File | Format | Sampling | Description |
|---|---|---|---|
realtime_log_<session_id>.csv |
CSV | ~1 Hz | Per-session: timestamp, HR, RR, SpOβ, OCR confidence |
CSV schema:
timestamp, heart_rate, respiration_rate, spo2, session_id, ocr_confidence
| Field | Unit | Range | Notes |
|---|---|---|---|
timestamp |
YYYY-MM-DD HH:MM:SS |
β | Wall-clock time |
heart_rate |
bpm | 30β200 | From Garmin wearable |
respiration |
brpm | 4β60 | From Garmin wearable |
spo2 |
% | 70β100 | From Garmin wearable |
OCR pipeline:
Smartwatch β Companion App β Screen Mirror (MSS) β
Frame Capture β ROI Extraction β Tesseract OCR β Value Parsing β CSV
Known issues:
- ~1 second latency from display refresh + OCR processing
- Rows with confidence < 0.5 should be excluded from model inputs
- Sessions with >20% low-confidence readings flagged for review
See scripts/OCR/main2.py for pipeline implementation.
IMU-based orientation angles and displacement measurements from a surrogate head with embedded 3-axis accelerometer during closed-loop actuation trials.
| File | Sampling | Description |
|---|---|---|
imu_data_<session_id>.csv |
~10 Hz | Raw accelerometer [X, Y, Z] + derived pitch/roll |
Observed ranges (pre-clinical):
| Metric | Min | Max | Notes |
|---|---|---|---|
| ΞΞΈ (orientation change) | 30Β° | 65Β° | Depends on initial contact position |
| |ΞX| (horizontal displacement) | 0 mm | 120 mm | Positive trend with ΞΞΈ |
| Pitch angle | -12.56Β° | +3.18Β° | Derived from accelerometer |
| Roll angle | -9.99Β° | -6.14Β° | Derived from accelerometer |
CSV schema β imu_data_<session_id>.csv:
Timestamp, Raw_X, Raw_Y, Raw_Z, Pitch_Deg, Roll_Deg
2026-05-07 17:16:46.123, -5.3, -9.8, -250.1, -2.34, -8.12
Spatial contact force distributions from the 512-point piezoresistive pressure mat (32 rows Γ 16 columns). Stored as both CSV time series and NumPy 2D arrays.
| File | Format | Sampling | Description |
|---|---|---|---|
session_<id>.csv |
CSV | ~5 Hz | Time series: timestamp + 512 pressure cell values |
Array shape: Each pressure map is a (32 Γ 16) grid, values in raw ADC units or normalized 0β1.
CSV schema β session_A.csv:
timestamp,p0,p1,p2,...,p511
2026-05-07 17:16:46.736,0,0,0,...,0
2026-05-07 17:16:46.839,0,0,0,...,0
Observed patterns:
- Most cells: 0 (no contact)
- Active cells during head contact: 0β255+ ADC counts
- Contact regions form coherent spatial clusters
- Temporal continuity expected within contact; isolated spikes = noise
Loading in Python:
import pandas as pd
import numpy as np
# Load CSV
df = pd.read_csv("data/pressure_sensing/session_A.csv")
# df.shape β (N_frames, 513) # timestamp + 512 cells
# Extract pressure matrix for frame i
frame_i = df.iloc[i, 1:].values.reshape(32, 16) # shape (32, 16)
# Or load pre-computed .npy arrays
pressure_array = np.load("data/pressure_sensing/pressure_maps/session_A.npy")
# pressure_array.shape β (N_frames, 32, 16)The decision-making module uses a hybrid Temporal Convolutional Network (TCN) + Transformer architecture for binary sleep apnea classification:
Input: (batch, 3 channels, 60 timesteps @ 1 Hz)
[Heart Rate, Respiration Rate, SpOβ]
β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β TCN Block (3 layers, dilation: 1, 2, 4) β
β - Receptive field: ~15 seconds β
β - Output channels: 32 β 64 β 128 β
β - Causal padding (no information leakage) β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Transformer Encoder (2 layers, 4-head attention) β
β - d_model=128, feedforward_dim=256 β
β - Captures long-range dependencies β
β - ~49 second receptive field from TCN + Transformerβ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Classification Head β
β - Global average pooling β FC(128β64) β FC(64β2) β
β - Softmax: [P(Normal), P(Apnea)] β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Output: [P(Normal), P(Apnea)]
| Component | Configuration | Value |
|---|---|---|
| Input | Channels / Timesteps / Rate | 3 / 60 / 1 Hz |
| TCN | Layers / Dilations / Channels | 3 / [1, 2, 4] / [32, 64, 128] |
| Transformer | Layers / Heads / d_model / FF | 2 / 4 / 128 / 256 |
| Dropout | All layers | 0.1 |
| Parameters | Total | ~360 K |
| Size | Full precision / Quantized | 1.4 MB / 0.35 MB |
| Parameter | Value | Notes |
|---|---|---|
| Loss | CrossEntropyLoss | Unweighted; class balance via sampler |
| Optimizer | AdamW | lr=2e-4, betas=(0.9, 0.999) |
| Learning rate schedule | CosineAnnealingLR | Warm down from lr to lr/100 |
| Batch size | 32 | With WeightedRandomSampler |
| Epochs | 50 | Early stopping if val loss plateaus (patience=10) |
| Gradient clipping | max_norm=1.0 | Prevent exploding gradients |
| Data imbalance | WeightedRandomSampler | Oversample apnea (~20% of sleep) |
from scripts.model.sleep_apnea_model import SleepApneaDetector
model = SleepApneaDetector(...)
model.load_state_dict(torch.load("best_model.pth"))
# Preprocess a 60-second window
window = preprocess(signal) # shape: (3, 60)
# Forward pass
with torch.no_grad():
logits = model(window.unsqueeze(0))
probs = torch.softmax(logits, dim=-1)
p_normal, p_apnea = probs[0, 0].item(), probs[0, 1].item()
# Classify with configurable threshold
threshold = 0.5 # Tunable for sensitivity/specificity trade-off
prediction = "APNEA" if p_apnea >= threshold else "NORMAL"Threshold guidance:
- High sensitivity (catch all events): threshold β 0.40
- Balanced: threshold β 0.50 (default)
- High specificity (fewer false alarms): threshold β 0.60
See docs/MODEL_ARCHITECTURE.md for full architecture details, training hyperparameters, and threshold sweep results.
git clone https://github.com/chaalee/ai-pillow-system.git
cd ai-pillow-system
# Using conda (recommended)
conda env create -f environment.yml
conda activate ai-pillow
# Or using pip
pip install -r requirements.txt# Start Jupyter and open notebooks
jupyter notebook
# Or run exploration script
python notebooks/01_data_exploration.ipynb# From real-time OCR logs
python scripts/preprocessing/preprocess_physiological.py \
--input data/ocr_realtime/ \
--output data/processed/ \
--window 60 \
--overlap 0.5 \
--confidence_threshold 0.5# Download and preprocess SHHS dataset (requires NSRR token)
python scripts/preprocessing/shhs_preprocessor.py \
--token YOUR_NSRR_TOKEN \
--max-subjects 150 \
--output-dir data/shhs_processed/
# Or preprocess local MESA EDF files
python scripts/preprocessing/mesa_preprocessor.py \
--raw-dir data/raw \
--out data/shhs_processed/mesa_processed.npz# Train on SHHS + MESA combined dataset
python scripts/model/train_with_shhs.py \
--processed-file data/shhs_processed/all_processed.npz \
--max-subjects None \
--epochs 50 \
--batch-size 32 \
--learning-rate 0.0002 \
--device mps # or cuda / cpu# Extract sample windows and run inference
python scripts/model/sample_for_simulation.py \
--npz data/shhs_processed/all_processed.npz \
--weights best_model.pth \
--n-apnea 5 \
--n-normal 5 \
--threshold 0.5 \
--device cpupython scripts/visualization/plot_all_figures.py \
--results results/ \
--data data/| Category | Metric | Observed Value | Notes |
|---|---|---|---|
| Pressure Sensing | Resolution | 512 points (32Γ16 grid) | ~17 mm spatial resolution |
| Orientation Estimation | ΞΞΈ range | 30β65Β° | Depends on initial head position |
| Pneumatic Actuation | Response time | 1β3 s | Proportional valve latency + physics |
| AI Inference | Model size (quantized) | 0.35 MB | 8-bit dynamic quantization |
| AI Inference | Latency (Raspberry Pi 4) | 40β60 ms | ONNX Runtime, int8 |
| AI Performance | F1-score (SHHS test set) | ~0.85 | Threshold-dependent |
| System Closed-Loop | Update period | 3β5 s | Physiological signal latency + compute |
docs/DATA_COLLECTION.mdβ Experimental protocol, session types, quality controldocs/DATA_DICTIONARY.mdβ Complete field definitions, units, value ranges for all CSVsdocs/HARDWARE_SETUP.mdβ System architecture, wiring diagrams, component list, pneumatic designdocs/MODEL_ARCHITECTURE.mdβ Detailed TCN-Transformer design, training config, threshold sweep, quantization
If you use this dataset or code in your research, please cite:
@article{chanprasert2025embodied,
title={Embodied AI for Closed-Loop Pneumatic Posture Regulation:
A Multimodal Smart Pillow Platform for Pre-Clinical Sleep Support Research},
author={Chanprasert, Chalinee and Duangdee, Nutnaree and Trakulmaykee, Kirana
and Rattanachaipong, Waritsanant and Chancharoen, Ratchatin},
journal={[Journal Name]},
year={2025},
note={Pre-clinical validation dataset. Pre-print / Under Review}
}- Code: MIT License β Free use, modification, distribution with attribution
- Data: Creative Commons Attribution 4.0 International (CC BY 4.0) β Free use with attribution; share-alike optional
- All experiments conducted on a surrogate (mannequin) head, not human subjects
- Controlled laboratory environment (no realistic sleep dynamics)
- Results do not generalize to clinical populations without further validation
| Issue | Impact | Mitigation |
|---|---|---|
| Surrogate vs. human anatomy | Limited generalizability | Clearly scoped as engineering validation |
| Memory foam attenuation | Reduced pressure spatial resolution | Region-based (not point-wise) strategy |
| OCR display lag (~1 s) | Temporal offset in physiological data | Documented in metadata; aligned via timestamps |
| Inter-channel pneumatic coupling | Variable pressure during simultaneous actuation | Sequential actuation enforced in control logic |
| Garmin wearable accuracy | Β±2β3% typical accuracy (SpOβ) | Used for relative variation, not absolute thresholds |
This research was supported by Robotics and AI, International School of Engineering, Bangkok, Thailand.
Data sources:
- SHHS: Sleep Heart Health Study (NSRR, NIH)
- MESA: Multi-Ethnic Study of Atherosclerosis (NSRR, NIH)
- Garmin: Garmin Connect companion app & Labfront research platform
Corresponding author: Prof. Ratchatin Chancharoen
Department of Mechanical Engineering, Faculty of Engineering, Chulalongkorn University
Bangkok 10330, Thailand
π§ Ratchatin.c@chula.ac.th
Contributions, issues, and feature requests are welcome! Please see the repository for guidelines.
Last updated: 2026-05-08
Repository: github.com/chaalee/ai-pillow-system