Skip to main content

WiFi-based human pose estimation, vital sign extraction, and ambient intelligence from Channel State Information (CSI). PyO3 bindings for the Rust core.

Project description

wifi-densepose

PyPI version Python License: MIT

Detect human presence, count people, read breathing and heart rate, and estimate skeletal pose — using only the WiFi signal already in your home.

No cameras. No wearables. Works through walls and in the dark.

wifi-densepose is the Python binding for the RuView sensing stack: a Rust core that turns the Channel State Information (CSI) emitted by ordinary WiFi chips into ambient-intelligence signals. The wheel ships compiled DSP for fast offline analysis, plus an opt-in Python client for talking to a live RuView sensing-server over WebSocket or MQTT.

Features

  • 17-keypoint pose — full-body skeletal estimate from WiFi CSI, no camera
  • Vital signs — respiratory rate (6–30 BPM) and heart rate (40–120 BPM) with a confidence score and clinical-grade / degraded / unreliable status
  • Presence, person count, fall detection, motion — fused outputs from the same CSI stream
  • 10 semantic primitives (HA-MIND) — someone-sleeping, possible-distress, room-active, bathroom-occupied, fall-risk-elevated, bed-exit, … — ready to wire into Home Assistant or Apple Home automations
  • Beamforming Feedback (BFLD) support — 802.11ac/ax/be compressed feedback matrices on top of the receiver-side CSI path
  • GIL-releasing DSP — extract loops run with the GIL released, so a tokio-backed web server can call into the pipeline without stalling its event loop
  • Tiny wheel — ~240 KB compiled (one binary per OS/arch covers Python 3.10+ via the stable ABI)

Install

pip install wifi-densepose                 # core DSP only
pip install "wifi-densepose[client]"       # + WebSocket/MQTT clients

Wheels are published for Linux (x86_64, aarch64), macOS (x86_64, arm64), and Windows (amd64).

Usage

Extract breathing rate from a CSI stream

from wifi_densepose import BreathingExtractor

br = BreathingExtractor.esp32_default()     # 56 subcarriers @ 100 Hz, 30s window

for residuals, weights in your_csi_source:  # one frame at a time
    est = br.extract(residuals=residuals, weights=weights)
    if est is not None:
        print(f"{est.value_bpm:.1f} BPM  (confidence={est.confidence:.2f})")

Heart rate is the same shape — HeartRateExtractor.esp32_default() with a 0.8–2.0 Hz band-pass and a 15-second window.

Subscribe to a live sensing-server

import asyncio
from wifi_densepose.client import SensingClient, EdgeVitalsMessage

async def main():
    async with SensingClient("ws://your-ruview-node:8765/ws/sensing") as c:
        async for msg in c.stream():
            if isinstance(msg, EdgeVitalsMessage):
                print(msg.presence, msg.breathing_rate_bpm, msg.heartrate_bpm)

asyncio.run(main())

React to Home Assistant semantic primitives

from wifi_densepose.client import (
    RuViewMqttClient, SemanticPrimitive, SemanticPrimitiveListener,
)

listener = SemanticPrimitiveListener()
listener.on(SemanticPrimitive.BedExit, lambda e: print("bed exit:", e.node_id))
listener.on(SemanticPrimitive.PossibleDistress, lambda e: alert(e))

client = RuViewMqttClient(broker_host="homeassistant.local")
client.on_message(
    "homeassistant/+/wifi_densepose_+/+/state",
    listener.handle_mqtt_message,
)
client.start()
client.wait_connected()

Decode 802.11ax beamforming feedback

import numpy as np
from wifi_densepose import BfldFrame, BfldKind

# Parse compressed BFR from a Wireshark capture into a Complex64 ndarray ...
fb = np.zeros((2, 1, 996), dtype=np.complex64)  # Nr=2 Nc=1 Nsc=996 for HE80

frame = BfldFrame.from_compressed_feedback(
    timestamp_ms=ts,
    sounding_index=seq,
    sta_mac="aa:bb:cc:dd:ee:ff",
    kind=BfldKind.CompressedHE80,
    feedback_matrix=fb,
)
print(frame.n_subcarriers, frame.mean_amplitude)

Hardware

Works with any WiFi chip that exposes CSI. Reference setups (ESP-IDF firmware, build scripts, witness-verified test bundles) are in the RuView repo:

Device Cost Role
ESP32-S3 (8MB flash) ~$9 WiFi CSI sensing node
ESP32-S3 SuperMini (4MB) ~$6 WiFi CSI (compact)
ESP32-C6 + Seeed MR60BHA2 ~$15 mmWave HR/BR/presence add-on

The legacy v1 line (Wi-Pose-style FastAPI server) is end-of-life; wifi-densepose==1.99.0 is a tombstone that raises ImportError pointing to v2 with a migration URL.

Links

License

MIT.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

wifi_densepose-2.0.0a1.tar.gz (161.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

wifi_densepose-2.0.0a1-cp310-abi3-win_amd64.whl (258.1 kB view details)

Uploaded CPython 3.10+Windows x86-64

File details

Details for the file wifi_densepose-2.0.0a1.tar.gz.

File metadata

  • Download URL: wifi_densepose-2.0.0a1.tar.gz
  • Upload date:
  • Size: 161.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.11

File hashes

Hashes for wifi_densepose-2.0.0a1.tar.gz
Algorithm Hash digest
SHA256 6000813e3c837d658b297026ff23828759fcad27df0f5f530c291e0f6747984c
MD5 14f3911907c4932a1af5a5923571c609
BLAKE2b-256 540ef661d67630a640b2a7c5d80319f03ceaf175aafdfdb7f027ef7e933ad294

See more details on using hashes here.

File details

Details for the file wifi_densepose-2.0.0a1-cp310-abi3-win_amd64.whl.

File metadata

File hashes

Hashes for wifi_densepose-2.0.0a1-cp310-abi3-win_amd64.whl
Algorithm Hash digest
SHA256 d4f7036f2b24e442b8ef1092dacbc092ba5c76e40d43ed25c7855cce7b98faa0
MD5 e69e94b1553f8967375bf6ca996c98a4
BLAKE2b-256 1c97bbeb3a2ea35bf7c0bce676685827de47dc3ed1051a8841b851878ad8486b

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page