Back to Ruview

ADR-020: Migrate AI/Model Inference to Rust with RuVector and ONNX Runtime

docs/adr/ADR-020-rust-ruvector-ai-model-migration.md

0.7.06.5 KB
Original Source

ADR-020: Migrate AI/Model Inference to Rust with RuVector and ONNX Runtime

FieldValue
StatusAccepted
Date2026-02-28
Decidersruv
Relates toADR-016 (RuVector Integration), ADR-017 (RuVector-Signal-MAT), ADR-019 (Sensing-Only UI)

Context

The current Python DensePose backend requires ~2GB+ of dependencies:

Python DependencySizePurpose
PyTorch~2.0 GBNeural network inference
torchvision~500 MBModel loading, transforms
OpenCV~100 MBImage processing
SQLAlchemy + asyncpg~20 MBDatabase
scikit-learn~50 MBClassification
Total~2.7 GB

This makes the DensePose backend impractical for edge deployments, CI pipelines, and developer laptops where users only need WiFi sensing + pose estimation.

Meanwhile, the Rust port at rust-port/wifi-densepose-rs/ already has:

  • 12 workspace crates covering core, signal, nn, api, db, config, hardware, wasm, cli, mat, train
  • 5 RuVector crates (v2.0.4, published on crates.io) integrated into signal, mat, and train crates
  • 3 NN backends: ONNX Runtime (default), tch (PyTorch C++), Candle (pure Rust)
  • Axum web framework with WebSocket support in the MAT crate
  • Signal processing pipeline: CSI processor, BVP, Fresnel geometry, spectrogram, subcarrier selection, motion detection, Hampel filter, phase sanitizer

Decision

Adopt the Rust workspace as the primary backend for AI/model inference and signal processing, replacing the Python FastAPI stack for production deployments.

Phase 1: ONNX Runtime Default (No libtorch)

Use the wifi-densepose-nn crate with default-features = ["onnx"] only. This avoids the libtorch C++ dependency entirely.

ComponentRust CrateReplaces Python
CSI processingwifi-densepose-signal::csi_processorv1/src/sensing/feature_extractor.py
Motion detectionwifi-densepose-signal::motionv1/src/sensing/classifier.py
BVP extractionwifi-densepose-signal::bvpN/A (new capability)
Fresnel geometrywifi-densepose-signal::fresnelN/A (new capability)
Subcarrier selectionwifi-densepose-signal::subcarrier_selectionN/A (new capability)
Spectrogramwifi-densepose-signal::spectrogramN/A (new capability)
Pose inferencewifi-densepose-nn::onnxPyTorch + torchvision
DensePose mappingwifi-densepose-nn::denseposePython DensePose
REST APIwifi-densepose-mat::api (Axum)FastAPI
WebSocket streamwifi-densepose-mat::api::websocketws_server.py
Survivor detectionwifi-densepose-mat::detectionN/A (new capability)
Vital signswifi-densepose-mat::mlN/A (new capability)

Phase 2: RuVector Signal Intelligence

The 5 RuVector crates provide subpolynomial algorithms already wired into the Rust signal pipeline:

CrateAlgorithmUse in Pipeline
ruvector-mincutSubpolynomial min-cutDynamic subcarrier partitioning (sensitive vs insensitive)
ruvector-attn-mincutAttention-gated min-cutNoise-suppressed spectrogram generation
ruvector-attentionSensitivity-weighted attentionBody velocity profile extraction
ruvector-solverSparse Fresnel solverTX-body-RX distance estimation
ruvector-temporal-tensorCompressed temporal buffersBreathing + heartbeat spectrogram storage

These replace the Python RssiFeatureExtractor with hardware-aware, subcarrier-level feature extraction.

Phase 3: Unified Axum Server

Replace both the Python FastAPI backend (port 8000) and the Python sensing WebSocket (port 8765) with a single Rust Axum server:

ESP32 (UDP :5005) ──▶ Rust Axum server (:8000) ──▶ UI (browser)
                          ├── /health/*          (health checks)
                          ├── /api/v1/pose/*     (pose estimation)
                          ├── /api/v1/stream/*   (WebSocket pose stream)
                          ├── /ws/sensing        (sensing WebSocket — replaces :8765)
                          └── /ws/mat/stream     (MAT domain events)

Build Configuration

toml
# Lightweight build — no libtorch, no OpenBLAS
cargo build --release -p wifi-densepose-mat --no-default-features --features "std,api,onnx"

# Full build with all backends
cargo build --release --features "all-backends"

Dependency Comparison

Python BackendRust Backend (ONNX only)
Install size~2.7 GB~50 MB binary
Runtime memory~500 MB~20 MB
Startup time3-5s<100ms
Dependencies30+ pip packagesSingle static binary
GPU supportCUDA via PyTorchCUDA via ONNX Runtime
Model format.pt/.pth (PyTorch).onnx (portable)
Cross-compileDifficultcargo build --target
WASM targetNoYes (wifi-densepose-wasm)

Model Conversion

Export existing PyTorch models to ONNX for the Rust backend:

python
# One-time conversion (Python)
import torch
model = torch.load("model.pth")
torch.onnx.export(model, dummy_input, "model.onnx", opset_version=17)

The wifi-densepose-nn::onnx module loads .onnx files directly.

Consequences

Positive

  • Single ~50MB static binary replaces ~2.7GB Python environment
  • ~20MB runtime memory vs ~500MB
  • Sub-100ms startup vs 3-5 seconds
  • Single port serves all endpoints (API, WebSocket sensing, WebSocket pose)
  • RuVector subpolynomial algorithms run natively (no FFI overhead)
  • WASM build target enables browser-side inference
  • Cross-compilation for ARM (Raspberry Pi), ESP32-S3, etc.

Negative

  • ONNX model conversion required (one-time step per model)
  • Developers need Rust toolchain for backend changes
  • Python sensing pipeline (ws_server.py) remains useful for rapid prototyping
  • ndarray-linalg requires OpenBLAS or system LAPACK for some signal crates

Migration Path

  1. Keep Python ws_server.py as fallback for development/prototyping
  2. Build Rust binary with cargo build --release -p wifi-densepose-mat
  3. UI detects which backend is running and adapts (existing sensingOnlyMode logic)
  4. Deprecate Python backend once Rust API reaches feature parity

Verification

bash
# Build the Rust workspace (ONNX-only, no libtorch)
cd rust-port/wifi-densepose-rs
cargo check --workspace 2>&1

# Build release binary
cargo build --release -p wifi-densepose-mat --no-default-features --features "std,api"

# Run tests
cargo test --workspace

# Binary size
ls -lh target/release/wifi-densepose-mat