Skip to content
Edris-RahimiPublic

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

4 Commits

Folders and files

Repository files navigation

Wi-Fi CSI Motion Detection

Real-time, device-free human motion detection from Wi-Fi Channel State Information (CSI). A Raspberry Pi 4B running Nexmon CSI firmware captures CSI from a commodity 802.11ac radio (BCM43455); a logistic-regression classifier on the host turns frame-to-frame signal variation into a live motion / no-motion decision. No cameras, no wearables.

Overview

Movement in a room perturbs the multipath of Wi-Fi signals. By reading the per-subcarrier amplitude of each received frame and measuring how much it changes over time, the system separates "motion" from "no motion".

Pi (Nexmon CSI capture) -> UDP relay -> CSIKit decode -> preprocess -> features -> logistic regression -> smoothed live decision

CSI waterfall

How it works

  1. Capture. The Pi captures CSI frames into .pcap files (offline) or streams them live.
  2. Relay. A Pi-side relay (forward.py, documented in the setup doc) forwards raw Nexmon UDP CSI frames to the host on port 5500.
  3. Decode. CSIKit's NEXBeamformReader parses captures; amplitude is read directly from frame.csi_matrix to avoid a NumPy compatibility issue in the helper API.
  4. Preprocess. Drop 10 guard subcarriers each side (256 -> 236), clip outliers to the 1st/99th percentile, then apply a 5-frame moving-average smooth per subcarrier.
  5. Features. Per window of 150 frames (about 3 s at 50 Hz): per-subcarrier variance over time and mean frame-to-frame difference, concatenated.
  6. Classify. StandardScaler + LogisticRegression with balanced class weights. Sessions are split 70/15/15 by session, not by window, to avoid leakage between overlapping windows.
  7. Live decision. Probability is smoothed with an exponential moving average (alpha 0.15) and gated with hysteresis (on above 0.5, off below 0.3) for a stable readout.

Motion vs no-motion energy

Repository structure

.
|-- README.md
|-- requirements.txt
|-- .gitignore
|-- src/
|   |-- _paths.py             resolves project paths from any working directory
|   |-- preprocess.py         pcap -> processed .npz
|   |-- train.py              trains logistic regression, writes models/model.pkl
|   |-- realtime.py           terminal live inference
|   |-- visualize.py          PyQtGraph live waterfall + probability GUI
|   `-- tools/
|       |-- compare.py        motion vs no-motion energy comparison
|       |-- decode_test.py    sanity-check CSI decoding
|       |-- plot_waterfall.py render a CSI waterfall from a capture
|       |-- sample_rate.py    measure capture rate (Hz)
|       |-- receive_test.py   raw UDP receive test
|       `-- debug_stats.py    inspect features and model probabilities
|-- models/
|   `-- model.pkl             trained classifier (clf + scaler)
|-- data/
|   |-- samples/              small demo captures (committed)
|   `-- (raw session captures, gitignored)
|-- processed/                preprocessed .npz (gitignored)
|-- assets/                   figures used in this README
`-- docs/
    |-- setup_flashing_the_pi.docx
    |-- POTENTIAL_IMPACT_ASSESSMENT.md
    `-- project_proposal.docx

Hardware

  • Raspberry Pi 4B with the BCM43455 radio, running Nexmon CSI firmware
  • A host machine (Windows, Linux or macOS) on the same network as the Pi

Installation

python -m venv .venv
# Windows
.venv\Scripts\activate
# Linux / macOS
source .venv/bin/activate

pip install -r requirements.txt

Usage

All scripts resolve paths from the project root, so run them from anywhere.

Offline training pipeline:

# 1. Place captures in data/ named motion_*.pcap and no_motion_*.pcap
# 2. Decode and preprocess every capture into processed/*.npz
python src/preprocess.py

# 3. Train the classifier and write models/model.pkl
python src/train.py

Live detection (Pi capturing, forward.py relaying to UDP 5500):

python src/visualize.py   # GUI: live waterfall + probability bar
python src/realtime.py    # terminal: probability bar in the console

Utility scripts:

python src/tools/decode_test.py     # confirm CSI frames decode
python src/tools/plot_waterfall.py  # save a waterfall image from a sample capture
python src/tools/compare.py         # compare motion vs no-motion energy
python src/tools/sample_rate.py     # report capture rate in Hz
python src/tools/receive_test.py    # confirm UDP frames arrive on 5500
python src/tools/debug_stats.py     # print features and P(motion) for samples

Results

The logistic-regression classifier reaches roughly 97% test accuracy on held-out sessions. Run train.py to reproduce; it prints validation and test accuracy, a classification report and a confusion matrix. Because the split is by session, the figure reflects generalisation to unseen recordings rather than to neighbouring windows of the same recording.

Data

data/ and processed/ are gitignored because the raw and preprocessed captures are large. Small demo captures live in data/samples/ (committed) so the utility scripts run on a fresh clone.

Documentation

docs/ contains the Pi setup walkthrough (flashing the image and the per-reboot startup sequence), the potential impact assessment, and the project proposal.

Limitations

  • Single transmit/receive link in a single environment; the model is environment-specific and should be retrained per deployment.
  • The host scripts expect Nexmon CSI frames on UDP 5500; the Pi-side relay must be running.
  • The Pi needs a short startup sequence after every reboot (see the setup doc).

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages