Respiration and heart rate from Wi-Fi CSI: what works, what does not
2026-10-11 · 2 min read
The idea is simple: chest movement changes multipath, and that shows up in CSI amplitude/phase (see the CSI sensing survey [1]). Engineering it is hard, because you must extract 0.1–0.5 Hz (breathing) and 0.8–2.0 Hz (heart rate) from noisy data.
Sampling is usually not the bottleneck
Our measured ~95 records/s across 8 chains is far above the Nyquist needs of a 2 Hz signal. The real limits are:
- phase noise (CFO/SFO, hardware drift),
- body motion (talking, shifting) swamping micro-motion,
- too-short observation windows (spectral resolution).
Runnable pipeline
import numpy as np
from numpy.fft import rfft, rfftfreq
def bandpass(x, fs, lo, hi):
n = len(x); X = rfft(x - x.mean()); f = rfftfreq(n, 1 / fs)
X[(f < lo) | (f > hi)] = 0
return np.fft.irfft(X, n)
def estimate(signal, fs=95.0, band=(0.1, 0.5)):
n = len(signal); x = bandpass(signal, fs, *band)
spec = np.abs(rfft(x * np.hanning(n))); freqs = rfftfreq(n, 1 / fs)
mask = (freqs >= band[0]) & (freqs <= band[1])
return freqs[mask][np.argmax(spec[mask])] * 60.0 # bpm
Order matters: unwrap → remove linear CFO/SFO drift → band-pass → window → peak — the canonical phase-based vital-signs work is PhaseBeat [3]. Resample by timestamp; never assume a constant frame rate.
When it can work
| Condition | Why |
|---|---|
| subject seated / lying still | motion dominates breathing otherwise |
| stable reflection path | line-of-sight or stable multipath |
| window ≥ 20 s | 30 s ≈ 0.033 Hz resolution (~2 bpm) |
| stable frame rate, timestamp-resampled | jitter creates fake peaks |
| single subject | multiple people blend in phase |
Honest limits
- Not a medical device — no diagnostics, no medical claims;
- heart rate is much harder than breathing (smaller amplitude, harmonics interference);
- parameters must be re-calibrated per room/pose;
- our platform's vitals module is gated (returns
Unavailablewhen motion/SNR conditions fail) — that is more honest than printing a number anyway.
Published work reports the same distance/environment dependence: FarSense extended the respiration range using the CSI ratio of two antennas [2] — "it can sense" and "it can sense in any room" are different claims.
Related: Python pipeline · datasets · Wi-Fi CSI explained
References and data sources
Every external claim carries a source and an evidence grade (grading scheme). Grade E marks first-party, reproducible measurements from this project.
- Grade A · standard/review
WiFi Sensing with Channel State Information: A Survey
W. Ma, W. Zhou, X. Wang · ACM Computing Surveys · 2019
doi:10.1145/3310194 · citations 857 · verified: CrossRef API, 2026-10-11 · The field's most-cited CSI sensing survey (857 citations, verified via CrossRef). - Grade B · peer-reviewed
FarSense: Pushing the Range Limit of WiFi-based Respiration Sensing with CSI Ratio of Two Antennas
Y. Zeng, D. Wu, J. Xiong, et al. · Proceedings of the ACM on Interactive, Mobile, Wearable and Ubiquitous Technologies (IMWUT) · 2019
doi:10.1145/3351279 · citations 461 · verified: CrossRef API, 2026-10-11 · Range limits for respiration sensing and the two-antenna CSI ratio method (461 citations). - Grade B · peer-reviewed
PhaseBeat: Exploiting CSI Phase Data for Vital Sign Monitoring with Commodity WiFi Devices
X. Wang, C. Yang, S. Mao · IEEE ICDCS 2017 · 2017
doi:10.1109/icdcs.2017.206 · citations 325 · verified: CrossRef API, 2026-10-11 · A representative work on respiration/heart-rate monitoring from CSI phase (325 citations).