Guía de inicio de RuView: Estimación de postura humana sin contacto mediante WiFi CSI

March 27, 2026

30 minutos | Nivel Intermedio-Avanzado | Domine los principios de estimación de postura WiFi CSI, despliegue versiones en Python/Rust y conecte nodos de sensores ESP32 a un pipeline de extremo a extremo


Lectores objetivo

  • Desarrolladores interesados en la detección WiFi y computación ubicua.
  • Ingenieros con bases en Python que deseen explorar la inferencia de alto rendimiento con Rust.
  • Investigadores que busquen alternativas a la percepción humana basada en cámaras.

Requisitos mínimos: Capacidad para comprender clases en Python y sintaxis básica de Rust, contar con un entorno Linux/macOS o WSL2.


Dependencias principales y entorno

DependenciaVersión requeridaFunción
Python≥ 3.9Base de código principal v1
Rust≥ 1.75Puerto de inferencia de alto rendimiento
PyTorch≥ 2.1Inferencia de redes neuronales
NumPy + SciPyÚltima versión estableProcesamiento de señales CSI
OpenCV≥ 4.8Visualización de posturas
Placa ESP32-S38MB FlashNodo de hardware WiFi CSI (opcional)
Router con soporte AP802.11n o superiorFuente de datos CSI

WARNING

El ESP32 original y el ESP32-C3 no admiten la captura de CSI. El proyecto requiere explícitamente ESP32-S3 (Dual-core Xtensa) o ESP32-C6 + módulo mmWave de 60GHz.


Estructura completa del proyecto

RuView/
├── v1/                                 # Base de código principal en Python
│   ├── src/
│   │   ├── core/
│   │   │   ├── csi_processor.py       # Estructura de datos y procesamiento CSI
│   │   │   ├── phase_sanitizer.py     # Corrección de ruido de fase
│   │   │   └── router_interface.py    # Comunicación Router/ESP32
│   │   ├── hardware/
│   │   │   ├── csi_extractor.py       # Extracción de datos serie ESP32
│   │   │   └── router_interface.py    # Captura de tramas WiFi CSI
│   │   └── services/
│   │       └── ...                     # Pipeline de procesamiento de señales
│   ├── data/
│   │   └── proof/
│   │       ├── sample_csi_data.json   # 1000 tramas de CSI sintético (seed=42)
│   │       └── verify.py              # Script de verificación SHA-256 del pipeline
│   └── tests/                          # Suite de pruebas pytest
├── rust-port/wifi-densepose-rs/        # Adaptación a Rust
│   └── crates/
│       ├── wifi-densepose-core/        # Tipos principales, primitivas de tramas CSI
│       ├── wifi-densepose-signal/      # Procesamiento de señal SOTA RuvSense (14 módulos)
│       ├── wifi-densepose-nn/          # Backend de inferencia ONNX/PyTorch/Candle
│       ├── wifi-densepose-train/       # Pipeline de entrenamiento RuVector
│       ├── wifi-densepose-hardware/    # Protocolo TDM para ESP32
│       └── wifi-densepose-api/         # API REST con Axum
├── firmware/esp32-csi-node/            # Código fuente del firmware ESP32-S3
├── docs/adr/                           # 43 Registros de Decisiones de Arquitectura (ADR)
└── pyproject.toml                      # Definición de dependencias Python

Guía paso a paso

Paso 1: Comprender el principio de WiFi CSI (Simulación en Python sin hardware)

La esencia de WiFi CSI es: utilizar los cambios de amplitud y fase causados por el reflejo del cuerpo humano cuando las señales inalámbricas se propagan en el espacio para lograr una percepción sin contacto.

Cuando una persona se mueve entre un dispositivo WiFi (router) y el receptor, el reporte CSI registra los valores complejos de cada subportadora, incluyendo la amplitud y la fase. Aunque estos cambios son minúsculos, bastan para deducir la postura y el movimiento del cuerpo.

La genialidad de RuView reside en que convierte la señal CSI en una estimación de postura de 17 puntos clave (keypoints) a través de un pipeline completo (corrección de fase → supresión de trayectorias múltiples → inferencia de red neuronal).

Ejecute el proceso completo con datos sintéticos antes de conectar el hardware:

# Clonar el proyecto
git clone https://github.com/ruvnet/RuView.git
cd RuView

# Instalar dependencias de Python
cd v1
pip install -e ".[dev]"

# Ejecutar la verificación determinista del pipeline (sin hardware)
python data/proof/verify.py

Si todo es correcto, verá:

===== WiFi-DensePose Pipeline Verification =====
Generator: generate_reference_signal.py v1.0.0
Seed: 42
Frames: 1000 | Antennas: 3 | Subcarriers: 56
Loading sample CSI data... done
Running pipeline...
  [1/4] Phase sanitization ............ OK
  [2/4] Multistatic signal processing .. OK
  [3/4] Neural network inference ....... OK
  [4/4] Keypoint extraction ........... OK
Computing reference hash (SHA-256)...
VERDICT: PASS

TIP

Este script de verificación utiliza datos CSI sintéticos generados con seed=42, por lo que todos los resultados son reproducibles. Si supera este paso, su entorno Python está listo.


Paso 2: Preparación del entorno (Python + Rust + WSL2)

Entorno Python (Recomendado venv)

# Se recomienda Python 3.10 o 3.11 por compatibilidad
python3.10 -m venv .venv
source .venv/bin/activate  # Linux/macOS
# .venv\Scripts\activate   # Windows PowerShell

pip install --upgrade pip
pip install -e ".[gpu]"    # Si tiene GPU NVIDIA
pip install -e ".[dev]"    # Dependencias de desarrollo (incluye pytest)

Entorno Rust (Para puerto de inferencia de alto rendimiento)

# Instalar Rust (si no lo tiene)
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

# Verificar
rustc --version   # Debe ser 1.75+
cargo --version  # Debe ser 1.75+

# Soporte de toolchain Rust para desarrollo ESP32 (opcional)
rustup target add xtensa-esp32-elf riscv32imc-esp-elf

Usuarios de Windows: WSL2 es obligatorio

# Ejecutar en PowerShell como administrador
wsl --install
# Entrar en WSL tras reiniciar
wsl -d Ubuntu-22.04

WARNING

ESP-IDF tiene conflictos con Git Bash nativo de Windows (la variable MSYSTEM interfiere con ESP-IDF v5.4). Se recomienda usar WSL2 o PowerShell puro para compilar el firmware.


Paso 3: Ejecución del pipeline de estimación en Python

Ahora utilizaremos código real para procesar datos CSI en el pipeline de inferencia.

3.1 Estructura de datos CSI

# v1/src/core/csi_processor.py
from __future__ import annotations
import numpy as np
from dataclasses import dataclass
from typing import List

@dataclass
class CSIFrame:
    """Trama CSI individual — corresponde a una observación de canal WiFi"""
    timestamp_s: float
    amplitude: np.ndarray        # shape: (num_antennas, num_subcarriers)
    phase: np.ndarray            # shape: (num_antennas, num_subcarriers)
    subcarrier_indices: np.ndarray  # Índices de subportadora

    @classmethod
    def from_json(cls, data: dict) -> CSIFrame:
        return cls(
            timestamp_s=data["timestamp_s"],
            amplitude=np.array(data["amplitude"], dtype=np.float32),
            phase=np.zeros_like(data["amplitude"], dtype=np.float32),  # Inicialización manual
            subcarrier_indices=np.arange(56),  # 56 subportadoras por defecto
        )

@dataclass
class PoseKeypoints:
    """Resultados de postura de 17 puntos clave COCO"""
    keypoints: List[np.ndarray]  # (x, y, confianza) para cada punto
    pose_id: int

    def get_skeleton(self) -> np.ndarray:
        """Retorna array de coordenadas (N, 2) para dibujo"""
        return np.array([p[:2] for p in self.keypoints])

3.2 Corrección de fase (Eliminación de desviación LO)

La fase CSI original está muy contaminada por el ruido del oscilador local (LO) y debe corregirse:

# v1/src/core/phase_sanitizer.py
import numpy as np

def sanitize_phase(csi: np.ndarray) -> np.ndarray:
    """
    Estimación iterativa de desviación de fase LO + corrección de media circular
    Ref: ADR-014 SOTA Signal Processing
    """
    num_antennas, num_subcarriers = csi.shape
    phase = np.angle(csi)

    for _ in range(3):
        phase_offset = np.zeros(num_antennas)
        for a in range(1, num_antennas):
            delta = phase[a] - phase[0]
            delta_mean = np.arctan2(
                np.mean(np.sin(delta)),
                np.mean(np.cos(delta))
            )
            phase_offset[a] = delta_mean
            phase[a] -= phase_offset[a]
    return phase

3.3 Flujo de inferencia de extremo a extremo

Ejecute el script de demostración:

python v1/demo_pipeline.py
# Ejemplo de salida:
# Loaded 1000 CSI frames
# Frame 0: 17 keypoints, avg confidence=0.901
# ...
# Pipeline run complete.

Paso 4: Compilación y pruebas en Rust (Inferencia ultrarrápida)

La versión de Rust es el camino de producción capaz de alcanzar latencias de milisegundos.

cd rust-port/wifi-densepose-rs

# Verificar compilación (sin GPU)
cargo check -p wifi-densepose-core --no-default-features

# Ejecutar suite de pruebas (más de 1,000 pruebas)
cargo test --workspace --no-default-features

Paso 5: Grabación de firmware ESP32-S3 y conexión WiFi

5.1 Compilación de firmware

cd firmware/esp32-csi-node
cp sdkconfig.defaults.8mb sdkconfig.defaults

# Activar entorno ESP-IDF y compilar
idf.py build

5.2 Grabación y provisión

idf.py -p /dev/ttyUSB0 flash monitor

# Configurar WiFi y destino de datos
python provision.py --ssid "SuSSID" --password "SuPassword" --target-ip 192.168.1.20

Paso 6: Verificación final — De CSI a Postura

Conecte el flujo de datos del ESP32 con el extractor y la API de Rust:

# Terminal 1: Extractor CSI
python -m src.hardware.csi_extractor --port /dev/ttyUSB0

# Terminal 2: API REST (Servicio Axum en Rust)
cd rust-port/wifi-densepose-rs
cargo run -p wifi-densepose-api

Consulte los resultados mediante curl:

curl http://localhost:8080/api/v1/poses/current

Solución de problemas comunes

P1: El ESP32 no envía datos CSI Verifique que tanto el router como el ESP32 soporten 802.11n. Algunos routers requieren activar manualmente la captura de CSI.

P2: Error numpy.linalg.LinAlgError Suele deberse a incompatibilidad de versiones. Asegúrese de usar numpy >= 1.24 y scipy >= 1.11.

P3: La postura salta mucho o hay mucho ruido Es común por interferencia de trayectorias múltiples. Intente rotar las antenas 45° o aumente los parámetros de supresión en field_model.rs.


Lecturas adicionales / Direcciones avanzadas

  1. Fusión de atención multi-vista RuvSense: Utilice el mecanismo CrossViewpointAttention para combinar perspectivas de múltiples nodos ESP32.
  2. Arquitectura ADR: Lea ADR-014 para entender la selección del procesamiento de señales SOTA.
  3. Entrenamiento con RuVector: Use el crate wifi-densepose-train para ajustar el modelo ONNX con sus propios conjuntos de datos CSI.
Updated March 27, 2026