9740bc64c9
Firmware QEMU Tests (ADR-061) / QEMU Test (edge-tier1) (push) Has been skipped
Firmware QEMU Tests (ADR-061) / QEMU Test (full-adr060) (push) Has been skipped
Firmware QEMU Tests (ADR-061) / QEMU Test (tdm-3node) (push) Has been skipped
Firmware QEMU Tests (ADR-061) / Swarm Test (ADR-062) (push) Has been skipped
npm packages / tools/ruview-mcp (node 22) (push) Failing after 1s
nvsim-server → ghcr.io / build-and-publish (push) Failing after 1s
ruview-swarm CI guard / tests (full+train) (push) Failing after 2s
Bench Regression Guard / bench compile-verify (--no-run) (push) Failing after 0s
Bench Regression Guard / bench fast-run (informational, non-gating) (push) Has been skipped
Firmware CI / Verify version.txt matches release tag (push) Has been skipped
Dashboard a11y + cross-browser / a11y (push) Failing after 0s
nvsim Dashboard → GitHub Pages / build-and-deploy (push) Failing after 2s
Firmware CI / Build firmware (esp32s3 / 4mb) (push) Failing after 15s
Firmware QEMU Tests (ADR-061) / Build Espressif QEMU (push) Failing after 1s
Firmware QEMU Tests (ADR-061) / Fuzz Testing (ADR-061 Layer 6) (push) Failing after 1s
Continuous Deployment / Pre-deployment Checks (push) Has been skipped
Firmware CI / Build firmware (esp32c6 / c6-4mb) (push) Failing after 15s
Firmware CI / Build firmware (esp32s3 / 8mb) (push) Failing after 15s
Firmware QEMU Tests (ADR-061) / QEMU Test (boundary-max) (push) Has been skipped
Firmware QEMU Tests (ADR-061) / QEMU Test (boundary-min) (push) Has been skipped
Firmware QEMU Tests (ADR-061) / QEMU Test (default) (push) Has been skipped
Firmware QEMU Tests (ADR-061) / QEMU Test (edge-tier0) (push) Has been skipped
Firmware QEMU Tests (ADR-061) / NVS Matrix Generation (push) Failing after 1s
Security Scanning / Security Policy Compliance (push) Failing after 0s
Security Scanning / Dependency Vulnerability Scan (push) Failing after 0s
Security Scanning / Static Application Security Testing (push) Failing after 1s
Security Scanning / Infrastructure Security Scan (push) Failing after 1s
Security Scanning / Secret Scanning (push) Failing after 1s
npm packages / harness/ruview (node 22) (push) Failing after 17s
Security Scanning / License Compliance Scan (push) Failing after 1s
Security Scanning / Container Security Scan (push) Failing after 4s
three.js demos → GitHub Pages / build-and-deploy (push) Failing after 1s
Verify Pipeline Determinism / Verify Pipeline Determinism (3.11) (push) Failing after 1s
Fix-Marker Regression Guard / Verify fix markers (push) Failing after 1s
ADR-115 MQTT integration tests / mqtt-integration (push) Failing after 1s
npm packages / harness/ruview (node 20) (push) Failing after 1s
npm packages / tools/ruview-mcp (node 20) (push) Failing after 1s
npm packages / tools/ruview-cli (node 20) (push) Failing after 1s
npm packages / tools/ruview-cli (node 22) (push) Failing after 1s
BFLD MQTT Integration / cargo test --features mqtt (live mosquitto) (push) Failing after 29s
ruview-swarm CI guard / build train_marl bin (push) Failing after 2s
ruview-swarm CI guard / clippy (-D warnings, --no-deps) (push) Failing after 3s
ruview-swarm CI guard / tests (ruflo) (push) Failing after 1s
ruview-swarm CI guard / tests (train) (push) Failing after 2s
ruview-swarm CI guard / tests (default) (push) Failing after 2s
Point Cloud Viewer → GitHub Pages / build-and-deploy (push) Failing after 8s
ruview-swarm CI guard / ITAR / publish guard (push) Failing after 0s
wifi-densepose sensing-server → Docker Hub + ghcr.io / build · push · smoke-test (push) Failing after 1s
Continuous Deployment / Deploy to Production (push) Has been cancelled
Continuous Deployment / Rollback Deployment (push) Has been cancelled
Continuous Deployment / Post-deployment Monitoring (push) Has been cancelled
Continuous Deployment / Notify Deployment Status (push) Has been cancelled
Continuous Deployment / Deploy to Staging (push) Has been cancelled
Security Scanning / Security Report (push) Has been cancelled
108 lines
4.3 KiB
Python
108 lines
4.3 KiB
Python
"""WiFi-CSI pose model + LoRA adapter for the RuView calibration service.
|
||
|
||
Architecture matches the published flagship checkpoint
|
||
[`ruvnet/wifi-densepose-mmfi-pose`](https://huggingface.co/ruvnet/wifi-densepose-mmfi-pose)
|
||
(`pose_mmfi_best.pt`): transformer encoder + temporal attention pooling + skeleton-graph head.
|
||
|
||
The calibration service freezes this base and fits a tiny per-room **LoRA adapter** (rank 8 on the
|
||
input projection + pose head ≈ 11 KB) from ~100–200 labeled in-room samples. Empirically that lifts
|
||
cross-subject 64→72% and cross-environment 11→73% (ADR-150 §3.3–3.6).
|
||
"""
|
||
import numpy as np
|
||
import torch
|
||
import torch.nn as nn
|
||
|
||
# COCO-17 skeleton edges for the graph-refinement head.
|
||
EDGES = [(0, 1), (0, 2), (1, 3), (2, 4), (5, 6), (5, 7), (7, 9), (6, 8), (8, 10),
|
||
(5, 11), (6, 12), (11, 12), (11, 13), (13, 15), (12, 14), (14, 16)]
|
||
_A = np.eye(17, dtype=np.float32)
|
||
for _i, _j in EDGES:
|
||
_A[_i, _j] = _A[_j, _i] = 1.0
|
||
_A = _A / _A.sum(1, keepdims=True)
|
||
|
||
|
||
class LoRA(nn.Module):
|
||
"""Low-rank adapter wrapping a frozen Linear: y = W·x + (x·A·B)·(alpha/r)."""
|
||
|
||
def __init__(self, base: nn.Linear, r: int = 8, alpha: int = 16):
|
||
super().__init__()
|
||
self.base = base
|
||
for p in self.base.parameters():
|
||
p.requires_grad = False
|
||
self.A = nn.Parameter(torch.zeros(base.in_features, r))
|
||
self.B = nn.Parameter(torch.zeros(r, base.out_features))
|
||
nn.init.normal_(self.A, std=0.02)
|
||
self.scale = alpha / r
|
||
|
||
def forward(self, x):
|
||
return self.base(x) + (x @ self.A @ self.B) * self.scale
|
||
|
||
|
||
class GR(nn.Module):
|
||
"""Skeleton-graph refinement: nudges joints toward anatomically consistent positions."""
|
||
|
||
def __init__(self, d=256, h=96):
|
||
super().__init__()
|
||
self.je = nn.Parameter(torch.randn(17, 32) * 0.02)
|
||
self.inp = nn.Linear(d + 34, h)
|
||
self.g1 = nn.Linear(h, h)
|
||
self.g2 = nn.Linear(h, h)
|
||
self.out = nn.Linear(h, 2)
|
||
self.register_buffer("A", torch.tensor(_A))
|
||
|
||
def forward(self, z, kp0):
|
||
B = z.shape[0]
|
||
f = torch.relu(self.inp(torch.cat(
|
||
[z.unsqueeze(1).expand(-1, 17, -1), self.je.unsqueeze(0).expand(B, -1, -1), kp0], -1)))
|
||
f = torch.relu(self.g1(torch.einsum('ij,bjh->bih', self.A, f)))
|
||
f = torch.relu(self.g2(torch.einsum('ij,bjh->bih', self.A, f)))
|
||
return kp0 + 0.3 * torch.tanh(self.out(f))
|
||
|
||
|
||
class PoseNet(nn.Module):
|
||
"""Flagship pose model. Input [B,3,114,10] CSI amplitude (per-sample standardized) -> [B,34]."""
|
||
|
||
def __init__(self, na=3, nsc=114, nt=10, d=256, L=4, H=8):
|
||
super().__init__()
|
||
self.proj = nn.Linear(na * nsc, d)
|
||
self.pos = nn.Parameter(torch.randn(1, nt, d) * 0.02)
|
||
enc = nn.TransformerEncoderLayer(d, H, d * 2, dropout=0.2, batch_first=True, activation='gelu')
|
||
self.tf = nn.TransformerEncoder(enc, L)
|
||
self.att = nn.Linear(d, 1)
|
||
self.head = nn.Sequential(nn.Linear(d, 256), nn.GELU(), nn.Dropout(0.3), nn.Linear(256, 34))
|
||
self.gr = GR(d)
|
||
self.na, self.nsc, self.nt = na, nsc, nt
|
||
|
||
def forward(self, x):
|
||
B = x.shape[0]
|
||
t = x.permute(0, 3, 1, 2).reshape(B, self.nt, self.na * self.nsc)
|
||
h = self.tf(self.proj(t) + self.pos)
|
||
w = torch.softmax(self.att(h), 1)
|
||
z = (h * w).sum(1)
|
||
kp0 = torch.sigmoid(self.head(z)).reshape(B, 17, 2)
|
||
return self.gr(z, kp0).reshape(B, 34)
|
||
|
||
def add_lora(self, r=8, alpha=16):
|
||
"""Wrap the input projection + pose head with LoRA adapters (the ~11 KB calibration set)."""
|
||
self.proj = LoRA(self.proj, r, alpha)
|
||
self.head[0] = LoRA(self.head[0], r, alpha)
|
||
self.head[3] = LoRA(self.head[3], r, alpha)
|
||
return self
|
||
|
||
def lora_state(self) -> dict:
|
||
"""Extract just the LoRA A/B tensors (the per-room adapter to save)."""
|
||
return {k: v.detach().cpu().numpy() for k, v in self.state_dict().items()
|
||
if k.endswith(".A") or k.endswith(".B")}
|
||
|
||
def load_lora(self, adapter: dict):
|
||
sd = self.state_dict()
|
||
for k, v in adapter.items():
|
||
sd[k] = torch.tensor(v)
|
||
self.load_state_dict(sd)
|
||
return self
|
||
|
||
|
||
def standardize(x: torch.Tensor) -> torch.Tensor:
|
||
"""Per-sample standardization used in training/inference."""
|
||
return (x - x.mean((1, 2, 3), keepdim=True)) / (x.std((1, 2, 3), keepdim=True) + 1e-6)
|