How to Implement Cross-Environment Domain Generalization with MERIDIAN for Zero-Shot Deployment in RuView

MERIDIAN (ADR-027) enables zero-shot cross-environment deployment in RuView by combining hardware-agnostic CSI normalization, geometry-aware FiLM conditioning, and adversarial domain factorization to eliminate the 40-70% accuracy loss typically seen when moving between rooms.

Cross-environment domain generalization with MERIDIAN allows a single WiFi-DensePose model trained in one room to deploy immediately in any new environment without retraining. This article explains how to implement MERIDIAN in the RuView repository using its Rust-based training and inference pipeline.

Understanding the MERIDIAN Architecture

MERIDIAN sits between the CSI encoder and the pose regression heads in wifi-densepose-rs. It consists of four tightly coupled modules that together enable zero-shot generalization.

Hardware Normalization Layer

The HardwareNormalizer in crates/wifi-densepose-signal/src/hardware_norm.rs resamples raw CSI from any supported chipset to a canonical 56-subcarrier grid. It Z-score normalizes amplitudes and sanitizes phase, ensuring that ESP32-S3, Intel 5300, and Atheros NICs produce identical input distributions.

use wifi_densepose_signal::hardware_norm::{
    HardwareNormalizer, HardwareType,
};

let normalizer = HardwareNormalizer::new();
let raw_amp: Vec<f64> = /* from ESP32 */;
let raw_phase: Vec<f64> = /* from ESP32 */;
let frame = normalizer
    .normalize(&raw_amp, &raw_phase, HardwareType::Esp32S3)
    .expect("normalisation failed");

Geometry Encoding and FiLM Conditioning

The GeometryEncoder in crates/wifi-densepose-train/src/geometry.rs converts physical AP positions into a 64-dimensional conditioning vector using Fourier positional encoding and DeepSets permutation-invariant pooling. This vector drives Feature-wise Linear Modulation (FiLM) layers that condition the pose features on room geometry.

use wifi_densepose_train::geometry::GeometryEncoder;

let ap_positions = vec![
    [0.0_f32, 0.0, 2.5],
    [3.5, 0.0, 2.5],
    [1.75, 4.0, 2.5],
];
let geom_enc = GeometryEncoder::new();
let g_vec = geom_enc.encode(&ap_positions);

// FiLM modulation
let gamma = linear_gamma(&g_vec);
let beta = linear_beta(&g_vec);
let h_pose_conditioned: Vec<_> = gamma.iter()
    .zip(h_pose.iter())
    .zip(beta.iter())
    .map(|((g, h), b)| g * h + b)
    .collect();

Domain Factorization with Gradient Reversal

The DomainFactorizer in crates/wifi-densepose-train/src/domain.rs splits features into pose-relevant (h_pose) and environment-specific (h_env) components. A GradientReversalLayer (GRL) forces h_pose to become invariant to the DomainClassifier's predictions, while the classifier tries to predict the source environment from h_pose.

Training with Adversarial Domain Generalization

During training (Phases 2-6), MERIDIAN adds adversarial and contrastive losses to the standard pose regression. The total loss function implemented in domain.rs is:


L_total = L_pose
        + λ_c·L_contrastive
        + λ_adv·L_domain
        + λ_env·L_env_recon

The adversarial weight follows the schedule λ(p) = 2/(1+exp(-10p)) - 1 implemented in AdversarialSchedule.

use wifi_densepose_train::domain::{
    DomainFactorizer, GradientReversalLayer,
    DomainClassifier, AdversarialSchedule,
};

let factorizer = DomainFactorizer::new(17, 64);
let classifier = DomainClassifier::new(17, 64, n_domains);
let schedule = AdversarialSchedule::new(max_epochs);

// Inside training loop
let (h_pose, h_env) = factorizer.factorize(&body_part_features);
let lambda = schedule.lambda(epoch);
let grl = GradientReversalLayer::new(lambda);
let domain_logits = classifier.classify(&grl.forward(&h_pose));
let l_domain = cross_entropy(&domain_logits, &domain_labels);

Zero-Shot Deployment Workflow

To deploy a MERIDIAN-trained model in a new room without retraining:

  1. Measure AP positions in the target room (meters from origin).
  2. Package the model with MERIDIAN segments (GEOM, DOMAIN, HWSTATS) in an RVF container.
  3. Run inference with geometry flags; the model conditions on the supplied layout automatically.

This eliminates the 40-70% accuracy degradation seen in naive WiFi-DensePose when moving between rooms.

Optional Few-Shot Rapid Adaptation

If 10 seconds of calibration data are available, RapidAdaptation in crates/wifi-densepose-train/src/rapid_adapt.rs builds a LoRA adapter using contrastive loss and entropy minimization—no pose labels required.

use wifi_densepose_train::rapid_adapt::RapidAdaptation;

let calibration_batch = collect_csi_batch(200); // ~10s @ 20Hz
let adapter = RapidAdaptation::default()
    .fit(&calibration_batch, &model)
    .expect("adaptation failed");

rvf_container.add_segment("DOMAIN", adapter.serialize());

Summary

Frequently Asked Questions

What hardware platforms does MERIDIAN support?

MERIDIAN supports ESP32-S3, Intel 5300, and Atheros chipsets through the HardwareNormalizer in hardware_norm.rs. It resamples raw CSI from any of these platforms to a canonical 56-subcarrier grid with Z-score normalized amplitudes and sanitized phase, ensuring consistent input distributions across hardware variants.

How does MERIDIAN handle different room geometries?

The GeometryEncoder in geometry.rs encodes physical AP positions into a 64-dimensional vector using Fourier positional encoding and DeepSets permutation-invariant pooling. This vector drives FiLM layers that condition the pose features on the specific room layout, allowing the model to adapt to new geometries without parameter updates.

What is the computational overhead of MERIDIAN?

MERIDIAN adds approximately 12,000 parameters to the base WiFi-DensePose model (totaling roughly 67,000 parameters), keeping it well within ESP32 and WebAssembly deployment budgets. The environment path (h_env) is discarded during inference, so only the pose-encoder path executes at runtime.

Can MERIDIAN adapt to new environments without labeled data?

Yes. The RapidAdaptation module in rapid_adapt.rs supports few-shot adaptation using only 10 seconds of unlabeled CSI data. It builds a LoRA adapter using contrastive loss and entropy minimization, requiring no ground-truth pose labels. The resulting adapter is stored in the RVF container's DOMAIN segment for persistent reuse.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →