Files
ruvnet--RuView/docs/research/sota-2026-05-22/R12-pabs-implementation.md
T
rUv 9cd1b8ce2a research(R12 PABS): NEGATIVE -> POSITIVE — 1161x detection lift via R6.1 forward model (#722)
R12 (tick 5) was a NEGATIVE result: naive SVD-spectrum cosine distance
detected structure changes at 0.69x the natural drift floor (= undetectable).
R12 explicitly identified the revision: 'PABS over Fresnel basis'.

R6.1 (tick 18) shipped the multi-scatterer Fresnel forward operator.
This tick implements PABS on top of it.

PABS = ||y_observed - y_predicted||^2 / ||y_observed||^2

Benchmark (5 m link, 2.4 GHz, subject + 4 wall reflectors expected):

| Scenario                       | PABS / drift  | SVD (R12) / drift |
|--------------------------------|---------------:|------------------:|
| Empty room (subject missing)   |      7,362x   |               65x |
| Subject as expected (sanity)   |          0x   |                0x |
| +1 new furniture               |         84x   |               11x |
| +1 unexpected human            |      1,161x   |               11x |
| Subject moved 10 cm            |     21,966x   |               90x |
| Natural drift (5% wall shift)  |          1x   |                1x |

PABS detects unexpected human at 1161x natural drift; R12 SVD detected
at 11x. ~100x lift purely from physics-grounded prediction vs naive
statistical eigenshift.

R12 NEGATIVE -> POSITIVE. The meta-lesson: a research loop that catalogues
NEGATIVE results creates a backlog of revisitable work that pays off
when later tools become available. R12 -> R12 PABS is the worked example.

R13 cannot be similarly revisited -- its 5 dB shortfall is a hard
physics floor, not a missing model.

The subject-moved-10cm caveat: PABS detects ANY mismatch between
expected and observed scene. Real production PABS needs a pose-aware
forward model that updates from pose_tracker.rs in real-time. The
actual detection signal is PABS-after-pose-update. ~50-100 LOC Rust
glue, catalogued as R12.1 follow-up.

Composes:
- R6.1 unblocked this implementation
- R7 gets precise per-link consistency: residual small on all links =
  no structure; spike on one = local structure OR compromised link;
  mincut disambiguates
- R11 enables maritime container-tamper / hatch-seal apps
- R14 gets V0 security feature (intruder detection w/o biometric storage)
- ADR-029 needs to reference PABS as structure-detection primitive
- R10 PABS-vs-canopy works if forest modelled or learned

Honest scope:
- Pose-PABS closed loop not yet built
- Synthetic data only; real-world drift floor needs measurement
- Population-prior body; per-subject would tighten residual
- Single time-frame; real pipeline needs temporal averaging

Coordination: ticks/tick-19.md, no PROGRESS.md edit.
2026-05-22 03:49:41 -04:00

130 lines
8.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# R12 — Physics-Anchored Background Subtraction (PABS) implementation: NEGATIVE → POSITIVE
**Status:** working implementation, ~100× lift over R12 naive SVD baseline · **2026-05-22**
## What changed
R12 (tick 5 of this loop) was a **NEGATIVE result**: naive SVD-spectrum-cosine-distance failed because the eigenshift signal was **0.69×** the natural drift floor (signal-to-drift < 1 = undetectable). R12 explicitly identified the revision path: **PABS over a Fresnel-grounded basis**.
R6.1 (tick 18) shipped the multi-scatterer Fresnel forward operator. That made PABS implementable as a concrete experiment:
```
PABS = ||y_observed y_predicted||² / ||y_observed||²
```
where `y_predicted` is computed from R6.1's multi-scatterer model using a "what the scene should look like" prior (subject at known position + wall reflectors at known positions).
This tick implements PABS and benchmarks it against R12's naive SVD baseline on the same scenarios.
## Method
5 m link at 2.4 GHz; the "expected" scene is:
- 1 subject at (2.5, 2.75) — 25 cm off the LOS line (R6.1 said on-LOS is degenerate)
- 4 wall reflectors at the room corners with descending reflectivity
The forward operator computes `y_predicted` for this expected scene. Six observed scenarios are then tested:
| Scenario | Description |
|---|---|
| A | Empty room — no occupant (subject missing) |
| B | Subject exactly where expected (sanity check — PABS should be 0) |
| C | Subject + 1 new piece of furniture added |
| D | Subject + 1 unexpected second human |
| E | Subject + 5% wall reflectivity drift (the natural-drift floor) |
| F | Subject moved 10 cm from expected position |
## Results
| Scenario | PABS | SVD (R12 baseline) | **PABS / drift** | SVD / drift |
|---|---:|---:|---:|---:|
| A: no occupant | 4.17 | 0.60 | **7,362×** | 65× |
| B: subject as expected | 0.00 | 0.00 | 0× | 0× |
| C: +1 new structural element | 0.047 | 0.10 | **84×** | 11× |
| D: +1 unexpected human | 0.658 | 0.099 | **1,161×** | 11× |
| E: 5% wall drift (natural drift floor) | 0.0006 | 0.009 | 1× | 1× |
| F: subject moved 10 cm | 12.44 | 0.84 | 21,966× | 90× |
The headline contrast:
> **PABS detects an unexpected human at 1,161× the natural drift floor. R12's naive SVD detected the same at 11×.**
That's a **~100× lift**, achieved purely by using physics-grounded prediction instead of statistical eigenshift. The original R12 NEGATIVE finding (signal-to-drift 0.69× = undetectable) is now a positive 1,161× = trivially detectable.
## Why PABS works where SVD didn't
- **SVD on |y|** treats CSI as a generic 1-D vector and looks for statistical deviation from a learned baseline. It can't tell the difference between "wall drift" and "extra person" because both look like generic spectrum shifts.
- **PABS** compares against a forward-modelled "what should be there" prediction. New scatterers produce residuals **in the precise per-subcarrier signature** the forward model predicts is missing. Natural drift produces residuals in **diffuse, low-amplitude** patterns. The geometry separates them — and the separation is what gives the 100× ratio.
## The subject-moved-10cm scenario
Scenario F deserves a note. The subject moved only 10 cm from expected → PABS = 21,966× drift. That's not a bug; it's *exactly correct* behaviour:
- The forward model predicted "subject at (2.5, 2.75)"
- The observation has "subject at (2.5, 2.85)"
- The residual is the per-subcarrier signature of a scatterer moved by 10 cm — which is large
For a real "structure detection" pipeline, PABS must be coupled with a **pose tracker** that updates the expected scene model in real-time. The actual structure-detection signal is **PABS-after-pose-update** — i.e. residual that remains AFTER accounting for the subject's tracked position. New furniture / intruders cause residuals the pose tracker can't explain; subject motion does not.
The repo already ships pose tracking (`pose_tracker.rs`, ADR-079, ADR-101); the missing piece is the closed-loop coupling between pose updates and the PABS forward model. ~50-100 lines of Rust glue.
## R12 NEGATIVE → POSITIVE: what changed
| Aspect | R12 (NEGATIVE) | R12 PABS (POSITIVE) |
|---|---|---|
| Approach | SVD spectrum cosine distance | Forward-modelled residual norm |
| Required input | y_observed + y_baseline (no model) | y_observed + R6.1 forward model |
| Signal-to-drift on unexpected person | 0.69× | 1,161× |
| Signal-to-drift on new furniture | not measured | 84× |
| Dependence on temporal averaging | needed weeks of baseline | one-shot |
| What blocked it | no forward model | R6.1 unblocked it |
Two negative results in this loop (R12 + R13). R12 has now been **revisited and turned positive** — the kind of follow-up that makes a research loop's NEGATIVE entries productive rather than dead. R13 cannot be similarly revisited (its 5 dB shortfall is a hard physics floor, not a missing model).
## Composes with prior threads
- **R5** (saliency) — PABS's residual could itself be saliency-decomposed to localise *where* the structural change is (which body part / which voxel). Not implemented; natural next step.
- **R6** — single-scatterer Fresnel; provides the building block.
- **R6.1** — multi-scatterer forward operator; **the thing that unblocked this tick**.
- **R6.2 / R6.2.2** — placement that maximises Fresnel coverage maximises PABS sensitivity (residuals in covered zones are reliably detected).
- **R7** (mincut adversarial) — PABS residual against per-link forward models gives R7's multi-link consistency check a precise definition: residual norm should be small across all links simultaneously; spike on a single link = either local structure OR compromised link, R7 mincut disambiguates.
- **R10** (foliage / wildlife) — PABS-vs-forest-canopy works as long as the forest's static scatterers can be modelled or learned as a per-installation baseline.
- **R11** (maritime) — PABS in cabins detects "container tampered" by residual against the sealed-cabin scene model.
- **R12 NEGATIVE** — now POSITIVE.
- **R14 / ADR-105 / ADR-106** — PABS is a per-cog primitive that the federation protocol can ship; same privacy framework applies.
## Honest scope
- **PABS needs a pose-aware forward model in real-time** to avoid false alarms from subject motion (Scenario F). Without the closed-loop pose-PABS coupling, every subject move triggers a structural alarm.
- **The natural drift floor is geometry-specific.** The 5% wall reflectivity drift assumption is generic; specific installations may have higher (10-15%) drift floors from humidity / temperature cycles.
- **No multipath modelled here either.** Wall reflectors are static point scatterers; the model doesn't include floor / ceiling reflections.
- **No labelled real-world test.** The benchmark is on synthetic data. Real-world PABS on actual CSI captures is the next step.
- **Population-prior body assumption.** PABS uses a generic body model; per-subject body modelling would tighten the residual further (R3 + R15 give the embedding handle).
- **Single time-frame.** A real PABS pipeline should integrate over a temporal window for noise rejection; the current results are single-frame.
## What this DOES enable
1. **R12 NEGATIVE → POSITIVE.** The dead thread now has a working implementation with a 100× lift.
2. **Concrete next-step for the multistatic ADR-029 implementation**: PABS over per-link forward models is the structural-detection primitive.
3. **A worked-out example** of how negative-result + new-tool unblocking can convert dead research into shippable functionality.
## What this DOES NOT enable
- Production-ready structure detection (needs pose-PABS closed loop + temporal averaging + real-world calibration).
- Localisation of the structural change (residual norm gives detection; residual *direction* would give localisation — natural next step).
- Cross-room structure transfer (each installation has its own forward model; cross-installation transfer goes through ADR-105 / ADR-106).
## Next ticks (R12 PABS follow-ups)
- **R12.1 — Pose-PABS closed loop.** Couple `pose_tracker.rs` updates to the expected scene model. ~50-100 LOC Rust glue.
- **R12.2 — Localised residual decomposition.** Project residual onto a per-voxel basis to identify *where* the structural change is.
- **R12.3 — Real-world validation.** Run PABS on actual CSI captures from the bench ESP32; measure real-world drift floor and real intruder detection.
- **ADR amendment**: ADR-029 (multistatic sensing) should reference PABS as the structure-detection primitive.
## Connection back
- **R12 NEGATIVE** → POSITIVE (this tick).
- **R6.1** → enabled this implementation.
- **R7** → gets a precise per-link consistency definition.
- **R11** → enables maritime container-tamper / hatch-seal applications.
- **R14** → security feature (intruder detection) becomes a V0 vertical: "alert me if someone unexpected enters". The privacy framework allows this without storing biometrics (just the *existence* of a residual, not who).