首页
/ RuView 时间演化感知解析:EvolutionTracker 跨链路变化检测与 VoxelMap 时间体素证据聚合(ADR-142)

RuView 时间演化感知解析:EvolutionTracker 跨链路变化检测与 VoxelMap 时间体素证据聚合(ADR-142)

2026-09-07 10:16:50作者:冯梦姬Eddie

本文以《ADR-142 Evolution Tracker and Temporal VoxelMap Evidence Aggregation》为骨架,结合仓库内 wifi-densepose-signal 源码 深入讲解:如何为 RuvSense 多径网格补上"跨链路协调器 + 时间体素占用记忆"两层缺失,实现环境变化的共识判定、体素级贝叶斯证据累积,以及经 BFLD 隐私门控(VoxelGate)后才可离开节点的输出路径。读完本文,你将掌握该 ADR 的完整数据结构、判定规则、隐私降级模型、测试验收方法与当前仓库的真实实现边界。


1. 背景与缺口:五个孤立模块之间没有"第二个时间导数"

RuvSense 的传感栈(ADR-029ADR-030)为"演化追踪器(Evolution Tracker)"所需的所有单个零件其实都已就位,但它们散落在五个互不通信的模块中,没有任何一个编排器把它们按时间、跨链路地串起来。对 v2/crates/wifi-densepose-signal/src/ruvsense/ 检索 EvolutionTrackerchange_pointVoxelMap 或跨模块驱动逻辑,得到的都是空结果。

模块 文件 它已经做了什么 它做不到什么
场模型 field_model.rs 每条链路的 Welford 基线(LinkBaselineStatsWelfordStats)、SVD 本征结构分解(finalize_calibration())、estimate_occupancy(&[Vec<f64>])、校准新鲜度 check_freshness 每个 FieldModel 都是"单房间单条链路"实例,无法把新鲜度跨链路聚合——它看不见兄弟实例
空房间基线 calibration.rsADR-135 BaselineCalibration、带 W 帧过期窗口的 CalibrationRecorder、逐帧 CalibrationDeviationScore(携带 drift_score 漂移信号只被单条链路自己的 recorder 消费;没有"3 条链路同时漂移 ⇒ 房间变了"这类跨链路规则
纵向漂移 longitudinal.rs 逐人 PersonalBaseline(5 个 Welford 指标)、EmbeddingHistory FIFO、输出每人的 DriftReport/MonitoringLevel 逐人独立,永不回填到产生该 embedding 的逐链路 RF 证据上
吸引子漂移 attractor_drift.rs 相空间状态分类 AttractorDriftAnalyzeranalyze() 返回 AttractorDriftReport { regime_changed, .. } 逐人逐指标,从不把 regime 变化升级到场/校准层
断层成像 tomography.rs 粗粒度 RF 断层 RfTomographer::reconstruct()(ISTA L1 求解)、无状态 OccupancyVolume OccupancyVolume 完全无记忆——每次 reconstruct() 都由单张衰减快照产生全新体积;占据 200 帧的体素与闪烁 1 帧的噪声体素无法区分,也没有逐体素置信度、last_update_ns、证据计数与多普勒

而在隐私侧,privacy_gate.rs 实现了单调的 PrivacyGate::demote(BfldFrame, PrivacyClass)ADR-120),按 Raw(0) → Derived(1) → Anonymous(2) → Restricted(3) 方向清零 payload 区段,并对任何"升档"尝试返回 BfldError::InvalidDemote。但它操作的是 BfldFrame 的 payload 区段(compressed_angle_matrixcsi_deltaamplitude_proxyphase_proxy)——对"体素网格"毫无概念。一旦断层成像产出 OccupancyVolume,它将处于"无人把门"的状态直接流出节点。

因此缺口是双重的:

  1. 缺少编排器。每条链路各自维护基线、漂移分、吸引子状态与占用估计;物理环境变化(挪家具、开墙)表现为多条链路的相关联漂移,但没有任何模块同时读取超过一条链路。跨链路变点检测——正是区分"世界变了"与"这条链路自己很吵"的信号——目前不存在。
  2. 缺少时间占用记忆RfTomographer::reconstruct() 无记忆,占用无法累积证据、无法赋置信度、无法在 20 Hz 重建节奏上做贝叶斯更新;而且产出物没有经过隐私门控。

ADR-030 定义了单房间场模型与 Tier-2 断层,却未提及多房间/多链路编排与时间体素状态。ADR-142 正是给 ADR-030 补上编排层时间体素层,并把两条路径都接进 BFLD 隐私门(ADR-120/ADR-141)。

"演化(Evolution)"在这里指什么

"演化"是二阶信号:不是场的瞬时状态,而是场的统计描述如何随时间变化、且这种变化在链路上是否一致。ADR-142 让下列三个此前没有模块能回答的问题变得可答:

  • 各链路基线作为"一个集合"还成立吗?(全网格新鲜度,而非单链路)
  • 环境刚刚变了,还是某条链路在闹脾气?(跨链路变点)
  • 模型的占用估计与原始 RF 人体扰动能量一致吗?(占用一致性——一种喂给 ADR-137 的内部矛盾自检)

它不是新断层求解器(只是包住现有 RfTomographer),不是新校准算法(读 ADR-135 的 BaselineCalibration 与 ADR-030 的 FieldModel),也不是新隐私模型(复用 BFLD PrivacyGate::demote 模式)。它只新增两样东西:一个协调器 EvolutionTracker,和一个有状态、经门控的占用记忆 VoxelMap + VoxelGate

在流水线中的位置

Per-link CSI frame (baseline-subtracted, ADR-135)
  → CalibrationRecorder::record()      (ruvsense/calibration.rs)  → drift_score[link]
  → FieldModel::extract_perturbation() (ruvsense/field_model.rs)  → body_energy[link]
  → RfTomographer::reconstruct()       (ruvsense/tomography.rs)   → OccupancyVolume (snapshot)
        │                │                       │
        └────────────────┴───────────────────────┴──► EvolutionTracker::tick()   ← NEW
                                                          ├─ baseline freshness across mesh
                                                          ├─ cross-link change-point
                                                          ├─ occupancy-consistency check
                                                          └─ VoxelMap::ingest(volume)  ← NEW (temporal)
                                                                  │
                                                          VoxelGate::demote(map, mode) ← NEW (BFLD-gated)
                                                                  │
                            ┌─────────────────────────────────────┴───────────────────┐
                    ADR-137 contradiction flags                            ADR-139 WorldGraph nodes

EvolutionTracker::tick() 每个重建周期(20 Hz)运行一次:读取各链路漂移分与人体扰动能量、场模型占用估计、最新 OccupancyVolume,把体积折入持久的 VoxelMap;节点输出只能VoxelGate 离开。


2. 决策一:EvolutionTracker trait 与默认 MeshEvolutionTracker

EvolutionTracker 设计为 trait(生产聚合器与测试 harness 可注入不同的链路状态提供者),外加默认实现 MeshEvolutionTracker。它持有对既有模块已维护的逐链路状态的引用,而不复制它们的累加器。

use wifi_densepose_signal::ruvsense::calibration::{BaselineCalibration, CalibrationDeviationScore};
use wifi_densepose_signal::ruvsense::field_model::CalibrationStatus;
use wifi_densepose_signal::ruvsense::tomography::OccupancyVolume;

/// Stable identifier for one TX→RX link in the mesh.
pub type LinkId = usize;

/// Per-link evidence handed to the tracker each tick.
#[derive(Debug, Clone)]
pub struct LinkObservation {
    pub link_id: LinkId,
    /// ADR-135 per-frame deviation (carries drift_score + rms_amplitude_z).
    pub deviation: CalibrationDeviationScore,
    /// ADR-030 field-model freshness for this link's room.
    pub freshness: CalibrationStatus,
    /// Body-perturbation energy from FieldModel::extract_perturbation(),
    /// the residual after environmental modes are projected out.
    pub body_energy: f32,
    /// Capture timestamp, nanoseconds since the 802.15.4 epoch (ADR-110).
    pub timestamp_ns: u64,
}

/// Aggregate result of one evolution tick.
#[derive(Debug, Clone)]
pub struct EvolutionReport {
    /// Worst freshness observed across all links this tick.
    pub mesh_freshness: CalibrationStatus,
    /// Links currently Stale or Expired (drives CoherenceAlert).
    pub stale_links: Vec<LinkId>,
    /// True if a cross-link change-point fired this tick (§2.2).
    pub change_point: bool,
    /// Links that participated in the change-point (≥2σ this window).
    pub change_point_links: Vec<LinkId>,
    /// Occupancy as the field model sees it.
    pub model_occupancy: usize,
    /// Occupancy implied by summed per-link body-perturbation energy.
    pub perturbation_occupancy: usize,
    /// True when |model − perturbation| > 1 (drives AnomalyWarn, §2.3).
    pub occupancy_disagreement: bool,
    /// Alerts emitted this tick (typed, for the streaming engine ADR-136).
    pub alerts: Vec<EvolutionAlert>,
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum EvolutionAlert {
    /// One or more baselines are no longer fresh across the mesh.
    CoherenceAlert { stale_links: Vec<LinkId> },
    /// Cross-link change-point: the environment likely changed.
    ChangePoint { links: Vec<LinkId> },
    /// Model occupancy and RF-energy occupancy disagree by >1 person.
    AnomalyWarn { model: usize, perturbation: usize },
}

pub trait EvolutionTracker {
    /// Fold one tick of per-link observations + the latest occupancy
    /// snapshot into the tracker's persistent state. Updates the VoxelMap.
    fn tick(
        &mut self,
        observations: &[LinkObservation],
        volume: &OccupancyVolume,
        now_ns: u64,
    ) -> EvolutionReport;

    /// Borrow the temporal voxel map for gated output (§2.5).
    fn voxel_map(&self) -> &VoxelMap;

    /// Configuration knobs.
    fn config(&self) -> &EvolutionConfig;
}

MeshEvolutionTracker 持有既有模块所需的滚动窗口但不重新实现它们——只存分数(而非原始 CSI)的小型环形缓冲:

  • 每条链路的 VecDeque<f32>,保存最近 W = 300drift_score(与 ADR-135 CalibrationConfig.drift_window_frames 相同窗口);
  • 每条链路的 VecDeque<f32>,保存用于变点检验的 rms_amplitude_z
  • longitudinal.rsEmbeddingHistory FIFO 与 attractor_drift.rs 的相空间缓冲按句柄引用而非复制——tracker 按需调用它们既有的 analyze() / novelty()
#[derive(Debug, Clone)]
pub struct EvolutionConfig {
    /// Change-point window length in frames. Default: 30 (1.5 s @ 20 Hz).
    pub change_point_window: usize,
    /// Per-link z threshold counting toward a change-point. Default: 2.0σ.
    pub change_point_sigma: f32,
    /// Minimum links exceeding threshold to declare a change-point. Default: 3.
    pub change_point_min_links: usize,
    /// Occupancy disagreement tolerance, in persons. Default: 1.
    pub occupancy_tolerance: usize,
    /// Per-voxel minimum evidence count before a voxel is "confident". Default: 5.
    pub min_evidence_frames: u32,
}

impl Default for EvolutionConfig {
    fn default() -> Self {
        Self {
            change_point_window: 30,
            change_point_sigma: 2.0,
            change_point_min_links: 3,
            occupancy_tolerance: 1,
            min_evidence_frames: 5,
        }
    }
}

2.1 跨链路变点检测:把"单链路噪声"与"环境突变"分开

一条链路漂移只是噪声;整个环境改变会表现为相关联的漂移。每个 tick 评估的规则是:

在滚动 change_point_window(默认 30 帧 / 1.5 s)内,若 3 条及以上链路各自在 rms_amplitude_z 上超过 change_point_sigma(默认 2.0σ),则发出 ChangePoint 事件并点名这些链路。

fn detect_change_point(&self) -> Option<Vec<LinkId>> {
    let mut hot = Vec::new();
    for (link_id, window) in self.z_windows.iter() {
        // Count frames in the window above the sigma threshold.
        let n_hot = window.iter().filter(|&&z| z >= self.config.change_point_sigma).count();
        // A link "participates" if it was hot for a majority of the window.
        if n_hot * 2 > window.len() {
            hot.push(*link_id);
        }
    }
    (hot.len() >= self.config.change_point_min_links).then_some(hot)
}
  • 3 链路下限刻意与 ADR-135 的 drift_confirm_frames 确认逻辑同量级,但作用在空间而非时间上:ADR-135 用 45 s 确认单条链路陈旧;本规则用 1.5 s × 3 条链路确认环境变化。两者互补——ADR-135 回答"这条链路的基线是不是老了?",本规则回答"世界是不是刚刚动了?"。
  • 一个 ChangePoint 是让运维方(或启用 ADR-135 §2.6 的 recalibrate_on_drift 时由系统)对整个网格而非单条链路做重校准的上游触发信号。
  • 2.0σ 阈值复用了 ADR-135 的解读:单帧 rms_amplitude_z > 3.0 判为"可能有人占用",因此在 1.5 s 窗口内多条链路持续 2.0σ 属于结构性位移,而非一个人经过某条链路。
  • 防抖设计:单条链路要"占窗口多数帧"才算参与,杜绝单个热点帧误报。

2.2 网格级新鲜度聚合

独立于变点,tracker 把每条链路的 CalibrationStatus 按最坏情况序 Fresh < Stale < ExpiredUncalibrated/Collecting 视为比 Fresh 更差)规约成一个 mesh_freshness。任何落在 StaleExpired 的链路进入 stale_links 并产生 CoherenceAlert。这正是单靠 field_model.rs 的 check_freshness 做不到的跨网格新鲜度检查——它只认识一个房间。

在真实源码中,CalibrationStatus 枚举五个档位(field_model.rs)与这里的规约完全对应:

pub enum CalibrationStatus {
    Uncalibrated, // No calibration data yet.
    Collecting,   // Collecting calibration frames.
    Fresh,        // Calibration complete and fresh.
    Stale,        // Calibration older than half expiry.
    Expired,      // Calibration has expired.
}

新鲜度判定由 elapsed 时间与 baseline_expiry_s 的关系决定(field_model.rs):超过一半寿命即 Stale,超过全额寿命即 Expired。ADR-142 把这份逐房间状态提升为网格级告警。

2.3 占用一致性检查:两个独立估计互相对账

系统里有两个本应一致的独立占用估计:

  • 模型占用FieldModel::estimate_occupancy(recent_frames)field_model.rs)——由"非环境子空间"中的本征结构能量导出(真实实现里结合 Marcenko-Pastur 噪声估计、空房间残差能量边界与校准噪声地板,见 field_model.rs);
  • 扰动占用:对逐链路 body_energyextract_perturbation() 投影掉环境模式后的残差)求和,再用装机标定的每人大致能量刻度换算成人数。
fn occupancy_consistency(&self, model_occ: usize, body_energy_total: f32) -> (usize, bool) {
    let perturbation_occ = (body_energy_total / self.energy_per_person).round() as usize;
    let disagree = model_occ.abs_diff(perturbation_occ) > self.config.occupancy_tolerance;
    (perturbation_occ, disagree)
}

当两者相差超过 occupancy_tolerance(默认 1 人)时发出 AnomalyWarn { model, perturbation }。这正是 ADR-137 融合质量评分所消费的内部矛盾类型:下游产生的语义状态记录会把它作为矛盾标记,并携带两侧证据来源的引用。按项目规则,每条语义状态都要可回溯到信号证据LinkObservation 集合)、模型版本FieldModel SVD 代数)、校准版本(ADR-135 的 BaselineCalibration.captured_at_unix_s)与隐私决策VoxelGate 模式)。


3. 决策二:带贝叶斯证据累积的时间 VoxelMap

这是核心新状态。现有 OccupancyVolumetomography.rs)是无记忆快照;VoxelMap 是它的持久伴侣,跨 reconstruct() 调用累积证据。

/// One voxel of persistent, evidence-accumulating occupancy state.
#[derive(Debug, Clone)]
pub struct Voxel {
    /// Center position (metres), copied from OccupancyVolume::voxel_center().
    pub center_xyz: [f32; 3],
    /// Bayesian occupancy probability ∈ [0, 1].
    pub occupancy: f32,
    /// Confidence ∈ [0, 1]; rises with evidence_count, falls with staleness.
    pub confidence: f32,
    /// Nanoseconds (802.15.4 epoch) of the last frame that updated this voxel.
    pub last_update_ns: u64,
    /// Number of frames that have contributed evidence to this voxel.
    pub evidence_count: u32,
    /// Welford mean/variance of the density observations (variance flags noise).
    pub density_mean: f32,
    pub density_m2: f32,
    /// Radial Doppler velocity estimate (m/s), when CIR phase rate is available.
    pub doppler_velocity: f32,
}

/// Persistent occupancy grid shared across all reconstruct() calls.
#[derive(Debug, Clone)]
pub struct VoxelMap {
    pub voxels: Vec<Voxel>,
    pub nx: usize,
    pub ny: usize,
    pub nz: usize,
    pub bounds: [f64; 6],
    /// Half-life (frames) of the confidence decay for un-updated voxels.
    decay_half_life: f32,
}

impl VoxelMap {
    /// Allocate a VoxelMap matching an OccupancyVolume's geometry.
    pub fn from_geometry(volume: &OccupancyVolume) -> Self;

    /// Fold one fresh OccupancyVolume into the persistent map.
    ///
    /// For each voxel:
    /// 1. Bayesian log-odds update of `occupancy` from the new density
    ///    (density treated as a measurement likelihood via a logistic link).
    /// 2. Welford update of (density_mean, density_m2).
    /// 3. evidence_count += 1; last_update_ns = now_ns.
    /// 4. confidence ← logistic(evidence_count) × (1 − normalised_variance).
    /// Voxels NOT touched this frame decay confidence toward 0 with
    /// `decay_half_life`, but retain their last occupancy estimate.
    pub fn ingest(&mut self, volume: &OccupancyVolume, now_ns: u64, min_evidence: u32);

    /// Per-voxel Welford sample variance.
    pub fn density_variance(&self, idx: usize) -> f32;

    /// Voxels with evidence_count < min_evidence are LOW CONFIDENCE.
    pub fn low_confidence_indices(&self, min_evidence: u32) -> Vec<usize>;

    /// Occupancy histogram (counts per occupancy bucket) for Restricted mode.
    pub fn occupancy_histogram(&self, n_buckets: usize) -> Vec<u32>;
}

贝叶斯更新:每个体素的 occupancy 以 log-odds 形式维护,用逻辑斯蒂测量模型 p(occupied | density) = σ(k·(density − d₀)) 结合新密度观测更新。log-odds 累积是占用网格的标准做法(Moravec & Elfes, 1985;Thrun et al., 2005):可交换、数值稳定,让反复观测为占据的体素逼近 1.0,而单帧闪烁几乎不动估计。这直接解决无记忆快照问题:占据 200 帧与 1 帧尖峰现在可通过 evidence_count 与收敛的 log-odds 区分开

置信度与低置信度标记confidence = logistic(evidence_count / min_evidence) × (1 − clamp(normalised_density_variance))evidence_count < min_evidence_frames(默认 5)的体素由 low_confidence_indices() 返回,下游被打标,使融合引擎(ADR-137)绝不会把 4 帧的体素当置信检测。这与 tomography.rs 用 density > 0.01 计 occupied_count 的做法互补——后者只是快照级的二进制门限,前者补上了时间限定词

逐体素 Welford 方差:复用 field_model.rs WelfordStats 完全相同的 (mean, m2) 更新形式,因此"密度高但很吵(高方差)"的体素会被正确地怀疑,而不如"平稳、安静地占据"的体素可信。


4. 决策三:ADR-134 CIR 加权的可选距离先验

当 ADR-134 CIR 可用时,链路 Cirdominant_delay_sec() / dominant_tap_tof_s()cir.rs)给出主反射体的飞行时间,即距离。RfTomographer 的权重矩阵(tomography.rsweight_matrix: Vec<Vec<(usize, f64)>>)目前仅按菲涅尔半径邻近度加权整条链路路径上的每个体素(1.0 − dist/fresnel_radius)。有了 CIR 时延,tracker 提供一个距离先验:距离 TX 与 CIR 隐含距离吻合的体素权重被提升,使证据聚焦在反射体附近,而不是沿整条射线抹开。

/// Optional per-link CIR-derived distance prior, applied to the existing
/// Fresnel weights as a multiplicative Gaussian bump centred at the CIR range.
pub struct CirDistancePrior {
    pub link_id: LinkId,
    /// Reflector distance from TX (m), from Cir::dominant_distance_m().
    pub range_m: f64,
    /// Std-dev of the range bump (m), from tap_spacing → distance resolution.
    pub sigma_m: f64,
}

该先验是可选的:无 CIR 时(单天线回退,或 eigenvalue/CIR 特性关闭),tomographer 行为与今天完全一致,保持改动增量式,既有 tomography.rs 测试不受影响。每个 Voxeldoppler_velocity 也仅在 CIR 相位速率可用时才填充,否则保持 0.0。


5. 决策四:VoxelGate——BFLD 门控的体素输出

原始 VoxelMap身份泄漏的:高分辨率占用网格 + 逐体素多普勒可以重建一个人的轨迹与步态,绝不能未经门控离开节点。VoxelGate::demote 复用 privacy_gate.rs PrivacyGate::demote单调降级模式——接受 PrivacyClassbfld/src/lib.rsRaw(0) → Derived(1) → Anonymous(2) → Restricted(3)),拒绝任何"升档"(BfldError::InvalidDemote),产出逐级变粗的视图。与 BFLD 门一样,降级不可逆:字段一旦清零,字节就不在了。

use wifi_densepose_bfld::{BfldError, PrivacyClass};

/// Monotonic voxel-grid demotion, mirroring PrivacyGate::demote (ADR-120).
pub struct VoxelGate;

/// What actually leaves the node after gating.
#[derive(Debug, Clone)]
pub enum GatedVoxelOutput {
    /// Raw(0)/Derived(1): full VoxelMap (local-only by invariant; Raw never
    /// crosses a network sink — same structural rule as BFLD class 0).
    Full(VoxelMap),
    /// Anonymous(2): per-voxel doppler_velocity and confidence cleared to 0;
    /// occupancy retained but quantised. No trajectory reconstruction possible.
    Anonymous(VoxelMap),
    /// Restricted(3): NO voxel grid leaves the node — only an occupancy
    /// histogram (count of voxels per occupancy bucket).
    OccupancyHistogram(Vec<u32>),
}

impl VoxelGate {
    /// Demote the VoxelMap to the target class. Returns InvalidDemote if the
    /// target is a *lower* class number than `current` (i.e. would add info).
    pub fn demote(
        map: &VoxelMap,
        current: PrivacyClass,
        target: PrivacyClass,
    ) -> Result<GatedVoxelOutput, BfldError> {
        if target.as_u8() < current.as_u8() {
            return Err(BfldError::InvalidDemote {
                from: current.as_u8(),
                to: target.as_u8(),
            });
        }
        Ok(match target {
            PrivacyClass::Raw | PrivacyClass::Derived => GatedVoxelOutput::Full(map.clone()),
            PrivacyClass::Anonymous => {
                let mut m = map.clone();
                for v in m.voxels.iter_mut() {
                    v.doppler_velocity = 0.0;   // strip kinematic identity surface
                    v.confidence = 0.0;
                    v.occupancy = quantise(v.occupancy);
                }
                GatedVoxelOutput::Anonymous(m)
            }
            PrivacyClass::Restricted => {
                // The raw VoxelMap never leaves the node at Restricted.
                GatedVoxelOutput::OccupancyHistogram(map.occupancy_histogram(8))
            }
        })
    }
}

这与 privacy_gate.rs 的字段级降级一一对应:BFLD 在 Anonymous 清零 compressed_angle_matrix/csi_delta、在 Restricted 清零 amplitude_proxy/phase_proxyVoxelGate 在 Anonymous 清 doppler_velocity/confidence,在 Restricted 只放行直方图。哪种 class 适用由 ADR-141 的命名隐私模式与运行时证明控制平面决定,不由本 ADR 决定——VoxelGate 是机制,ADR-141 是策略。

异常路由EvolutionReport.alertsCoherenceAlert / ChangePoint / AnomalyWarn)不是体素数据、不参与体素降级——它们是类型化事件

  • ADR-137 融合矛盾标记:AnomalyWarn 变为语义状态记录上的矛盾引用(模型占用 vs 扰动占用),附带各自产生的模型版本与校准版本;
  • ADR-139 WorldGraph 节点:ChangePoint 更新环境数字孪生(如"家具被移动"边),CoherenceAlert 把受影响房间节点标记为待重校准。

接口边界总览

Boundary Direction Type Note
calibration.rs → tracker in CalibrationDeviationScore (per link) drift_score + rms_amplitude_z; no CSI crosses the boundary
field_model.rs → tracker in CalibrationStatus, body_energy: f32, estimate_occupancy mesh freshness + model occupancy
tomography.rs → tracker in &OccupancyVolume (snapshot) folded into VoxelMap::ingest
cir.rs → tracker in (optional) CirDistancePrior distance-weighted evidence; absent ⇒ unchanged behaviour
tracker → ADR-137 out EvolutionAlert (typed) contradiction flags, evidence references
tracker → ADR-139 out EvolutionAlert (typed) WorldGraph mutations
tracker → network sink out GatedVoxelOutput only never the raw VoxelMap; gated by VoxelGate

tracker 不持有原始 CSI、不持有 payload 字节——只有分数、占用估计与体素网格。通往网络的唯一路径是 VoxelGate::demote


6. 仓库实现现状:ADR 文档 vs 已落地的 building block

值得强调的是,ADR-142 的标题状态是 Accepted — partial:截至 2026-05-29,已构建并测试的构建块(commit 1f8e180d6,issue #846)包括 EvolutionTracker(跨链路变点)、TemporalVoxel(贝叶斯 log-odds 占用 + 置信度地板)与 VoxelGate(隐私降级到直方图);但field_model.estimate_occupancy() 一致性检查与 CIR 峰值时延距离加权接到实时信号上、把异常路由到 ADR-137 矛盾标记,属于尚未接上实时路径的集成胶水(详见 ADR-136 实现状态 系列框架)。

真实代码落在 evolution.rs,并通过 ruvsense/mod.rspub mod evolution; 与 re-export(ChangePoint, EvolutionTracker, TemporalVoxel, TemporalVoxelMap, VoxelGate, VoxelPrivacy)进入 crate 公开面。实现相对 ADR 文档有几处可对照的工程化取舍:

  • 体素级别(TemporalVoxelobserve()钳制的 log-odds 累积(上下限 ±20,防止单帧坏数据饱和后验)、confidence = 1 − exp(−count/5)(等效 ADR 的 5 帧规则,evidence_count < 5is_low_confidence()),并在更新时可选地写入 doppler_velocityevolution.rs):
pub fn observe(&mut self, p: f64, doppler: Option<f64>, ns: u64) {
    let p = p.clamp(1e-4, 1.0 - 1e-4);
    let evidence_logit = (p / (1.0 - p)).ln();
    // Clamp the running log-odds so a single bad frame cannot saturate.
    self.log_odds = (self.log_odds + evidence_logit).clamp(-20.0, 20.0);
    self.occupancy = 1.0 / (1.0 + (-self.log_odds).exp());
    self.welford.update(p);
    self.evidence_count += 1;
    self.confidence = 1.0 - (-(self.evidence_count as f64) / 5.0).exp();
    if doppler.is_some() {
        self.doppler_velocity = doppler;
    }
    self.last_update_ns = ns;
}
  • 隐私降级没有引入对 BFLD crate 的依赖:实现定义了本地的 VoxelPrivacy { Full, Anonymous, Restricted } 枚举(注释明确"mirrors the BFLD demotion ladder of ADR-120/141 without taking a crate dependency"),VoxelGate::demote(&mut TemporalVoxelMap, posture, bins) 原地修改网格并只在 Restricted 返回聚合直方图、同时清空每个体素的 occupancy/doppler/confidence(evolution.rs)。从接口看,这比 ADR 文档中"clone 后逐字段清零"的 GatedVoxelOutput 更省、更贴合 Rust 的所有权风格,且同样满足"限制级下原始网格不出节点"的不变量。

  • 变点检测器实现为 EvolutionTracker struct(非 trait):每条链路一个 WelfordStatsobserve_window(&[f64]) 先对本轮样本折入前的基线判定发散(要求该链路 ≥2 次先验观测且标准差 > 1e-9),统计同时发散链路数,达到 min_links 即返回 ChangePoint { diverging_links, sigma_threshold }with_defaults(n_links) 即 2.0σ + ≥3 链路。实现用"瞬时窗口采样中 ≥3 链路同时超基线 2σ"来近似 ADR 文档"30 帧窗口多数帧发热"的规则。

evolution.rs 内测试 覆盖了贝叶斯更新收敛、5 帧置信地板、低置信度索引、Anonymous 清多普勒保留占用、Restricted 出直方图并清空、单链路发散不触发变点、三链路同时漂移触发变点、以及一条贯通 ADR-142 验收路径的端到端测试(acceptance_drift_to_histogram_with_contradiction:quiet baseline → 三链路同步漂移触发 ChangePoint → VoxelMap 累积证据 → 低置信体素被抑制 → Restricted 只出直方图 → 记录 ADR-137 ContradictionFlag::DriftProfileConflict)。

从代码结构看,还可以推断:ADR-142 文档中 tick() 一次性折叠多路 LinkObservation + OccupancyVolume 的"完整编排器",在实现中更偏向分件使用——调用方按需用 EvolutionTracker 做逐链路滚动统计、用 TemporalVoxelMap::observe() 按体素喂证据、最后用 VoxelGate 决定对外口径;把三者组织成单一 tick() 的胶水属于上文所述的"集成胶水 pending"部分。ADR 中"tracker 引用而非复制 EmbeddingHistory/相空间缓冲"的编排约定,需要等实时路径接线后才能真正兑现。


7. 后果、成本与风险取舍

正面收益

  • 单一编排点:五个此前孤立的模块(calibrationfield_modellongitudinalattractor_drifttomography)有了协同读取的协调器;跨链路变点检测首次成为可能。
  • 时间占用记忆:200 帧的持续占用与单帧噪声尖峰可借 evidence_count 与收敛贝叶斯 log-odds 区分;融合引擎(ADR-137)获得逐体素置信度,而非二值快照门限。
  • 网格级新鲜度FieldModel::check_freshness 只认识一个房间;EvolutionTracker 把逐链路新鲜度规约为网格 CoherenceAlert,补上了 ADR-135 逐链路漂移分留下的运维缺口。
  • 内部矛盾检测:占用一致性检查把两个独立估计(本征结构 vs 体扰动能量)变成 ADR-137 可评分的 AnomalyWarn——一条流水线从未有过的内置自检。
  • 结构性隐私:除 VoxelGate::demote 外没有任何体素网格能到达网络 sink;多普勒(体素网格中最强的步态身份面)在 Anonymous 即被清除;网格本身在 Restricted 下绝不外发。
  • 增量式 CIR 集成CirDistancePrior 可选;缺 CIR 时 tomography.rs 行为不变、既有测试不受影响。

负面成本

  • 新增持久状态VoxelMap 长寿命(每监测体积一份)。8×8×4 网格 = 256 体素 × ~40 字节 ≈ 10 KB,可忽略;但更细的 16×16×8 网格约 2,048 体素,且衰减循环每 tick 遍历全部体素。有限且廉价,但它是 20 Hz 下新增的常开工作。
  • 每人大致能量是装机常量:一致性检查的 energy_per_person 与环境相关,必须在校准时设定;数值错误会引发伪 AnomalyWarn。它由与 ADR-135 基线相同的空房会话导出。
  • 变点窗口需调参:30 帧 / 3 链路 / 2σ 默认值是从 ADR-135 阈值推得的,尚未在真实多房间硬件上验证;嘈杂网格可能过触发 ChangePoint。缓解:每条链路需占窗口多数帧"热"(§2.1),而非单个热帧。
  • 多普勒过早被门掉:有用的运动学信息在 Anonymous 即被清除。这是有意的(它是身份面),但意味着轨迹分析必须在门之前、可信节点边界之内完成,不能依赖门控输出。

风险登记

Risk Probability Impact Mitigation
噪声网格(HVAC、日照)上 ChangePoint 过触发 Medium 伪网格重校准提示 逐链路多数窗口热 + 3 链路下限;ADR-135 漂移确认仍门控自动重校准
人离开后贝叶斯体素收敛到陈旧占用 Medium 空置体素数秒内仍显示占用 未更新体素的 decay_half_life 置信衰减;后续低密度观测把 log-odds 拉回"空"
Anonymous 量化仍泄漏粗粒度轨迹 Low 随时间从粗网格重识别 不可信 sink 用 Restricted(仅直方图);ADR-141 控制平面按 sink 选 class
CIR 距离先验在主径(而非人体)时错置证据 Medium 证据聚集在墙壁而非人身上 先验是对既有菲涅尔权重的乘性增强(射线不经过处不会凭空产生证据);体扰动能量仍门控体素是否"被占"
错误的 energy_per_person 造成伪 AnomalyWarn Medium 污染 ADR-137 矛盾流 默认容忍 1 人;空房会话标定该值并在 ChangePoint 时重新推导

8. 备选方案与被否决策

  • 就地给 OccupancyVolume 加状态(否):把 confidence/last_update_ns/evidence_count 直接加进 tomography.rsOccupancyVolume,让 reconstruct() 原地修改保留实例。被否:OccupancyVolume 目前是 reconstruct() 的纯输出,测试(如 test_zero_attenuation_empty_room 断言新体积 occupied_count == 0)假设它是快照;把快照与持久状态混在一起会破坏该契约,并把时间策略缠进求解器。VoxelMap 让求解器保持纯净、时间状态独立。
  • 每条链路一个 tracker(否):这恰恰是现状,正是它让跨链路变点与网格新鲜度无法实现。演化追踪器的全部价值就在于跨链路视角
  • 逐体素 Kalman/粒子滤波(暂缓):能联合建模占用与速度,但对当前分辨率下的粗 8×8×4 网格属于过度设计;log-odds 占用网格是标准、廉价、可交换的选择(Thrun et al., 2005),且与既有 ISTA 输出无缝集成。带运动模型的滤波器属于姿态追踪器(pose_tracker.rs 已有 17 关键点 Kalman),而非粗占用网格。若体素分辨率显著提升可重新考虑。
  • 裸发 VoxelMap、下游再门控(否):与 BFLD class 0(Raw 依不变量 I1 仅限本地,见 bfld/src/lib.rs)同样的结构性理由——身份泄漏的原始体素数据一旦跨过网络边界就无法"反泄漏"。门控必须在 sink 之前、节点内部完成,正是 VoxelGate::demote 强制的事。
  • 为体素自造新隐私机制(否)privacy_gate.rs 的单调降级不变量已被证明且过审计(ADR-120),ADR-141 已定义命名模式控制平面。复用 PrivacyClassdemote 模式意味着全系统只有一个隐私模型、一套证明测试、无需第二个待审计机制

9. 测试与验收标准

单元测试

  • T1 — 网格新鲜度聚合:喂入混合 CalibrationStatusFresh/Stale/Expired)的 LinkObservation,断言 mesh_freshness 取最坏情况、stale_links 恰好列出非 Fresh 链路,且当且仅当有链路 Stale/Expired 时发出 CoherenceAlert
  • T2 — 3 链路触发跨链路变点:推入 30 帧 z 窗口,恰好 2 条链路超过 2.0σ 且占多数帧:断言无 ChangePoint;加入第 3 条:断言 ChangePoint { links } 触发并点名全部三条。
  • T3 — 单条持续链路不触发变点:一条链路整窗口发热、其余安静:断言无 ChangePoint(这是 ADR-135 单链路陈旧域,不是环境变化)。
  • T4 — 占用一致性model_occupancy = 1,喂入暗示 1 人的体能量:断言无 AnomalyWarn;喂入暗示 3 人的体能量:断言 AnomalyWarn { model: 1, perturbation: 3 }occupancy_disagreement == true
  • T5 — VoxelMap 证据累积:对一个体素 ingest 200 个相同的占据体积、对另一个只 ingest 1 个。断言 200 帧体素 evidence_count == 200occupancy > 0.95、不在 low_confidence_indices(5);1 帧体素 low_confidence_indices(5) 中且 occupancy 远离 1.0。
  • T6 — 门限处低置信标记:某体素恰好 ingest 4 帧:断言低置信;ingest 第 5 帧:断言它离开 low_confidence_indices(5)
  • T7 — 置信衰减:把体素 ingest 到高置信,然后 ingest decay_half_life 个该体素未被触及的 tick:断言 confidence 减半而 occupancy(最后估计)被保留。
  • T8 — 逐体素 Welford 方差:喂入 [0.9, 0.1, 0.9, 0.1, ...](吵)与 [0.5, 0.5, ...](稳)两组等均值密度:断言噪声体素 density_variance() 更高、confidence 相应更低。
  • T9 — VoxelGate 单调性demote(map, Anonymous, Derived) 返回 BfldError::InvalidDemote { from: 2, to: 1 }demote(map, Derived, Anonymous) 成功且返回的 VoxelMap 每个 doppler_velocity == 0.0confidence == 0.0
  • T10 — Restricted 不出网格demote(map, Anonymous, Restricted) 返回 GatedVoxelOutput::OccupancyHistogram 而绝非 VoxelMap——断言变体是直方图且长度等于请求桶数。
  • T11 — CIR 先验增量式:带与不带 CirDistancePrior 各跑一次 RfTomographer::reconstruct();断言无先验路径与当前 tomography.rs 输出逐位一致(既有测试不变),带先验路径密度更集中于 CIR 距离附近。

集成测试(门控,#[cfg(feature = "hardware-test")]

  • T12 — 真实多基地网格(COM9 + cognitum-seed-1):空房跑 30 s,断言无 ChangePointmesh_freshness == FreshVoxelMap 所有体素 occupancy < 0.2。有人走过:沿路径占据体素升至 0.8 以上、evidence_count 增长、人走开后置信衰减。搬走一把椅子离开:断言 1.5 s 内触发 ChangePoint 且点名受影响链路。

确定性 / Witness(CI 兼容,扩展 ADR-028)

  • T13 — 确定性 VoxelMap 哈希:以 seed=42 构建固定 600-tick 合成占用流,ingest 进 VoxelMap,对序列化体素状态做 SHA-256,记为 voxelmap_evidence_v1;由 verify.py 重新生成并断言哈希一致。方法学镜像 ADR-135 的 calibration_nvs_baseline_v1 证明(archive/v1/data/proof 目录是 ADR-028 证明链的既有载体)。

验收标准

  1. EvolutionTracker::tick() 对 8×8×4 网格与 12 条链路运行 < 1 ms(20 Hz 预算 50 ms,余量充足)。
  2. 变点当且仅当 ≥ change_point_min_links 条链路在窗口多数时间内超过 change_point_sigma 时触发(T2、T3)。
  3. 低于 min_evidence_frames 的体素始终报低置信(T5、T6)。
  4. 没有任何代码路径绕过 VoxelGate::demote 向网络 sink 裸发 VoxelMap(由 §2.7 接口边界强制;VoxelGateGatedVoxelOutput 的唯一公开构造者)。
  5. VoxelGate::demote 单调:升档尝试必返回 BfldError::InvalidDemote(T9)。
  6. 每条发出的语义状态(占用 + 告警)携带信号证据(LinkObservation 集)、模型版本(FieldModel SVD 代数)、校准版本(BaselineCalibration.captured_at_unix_s)与隐私决策(VoxelGate 目标 class)的引用。
  7. CIR 距离先验可证增量式——无先验重建不变(T11)。

10. 相关 ADR 关系网

ADR Relationship
ADR-030 被扩展:补上它未定义的跨链路编排器与时间体素层;消费 FieldModel::estimate_occupancyCalibrationStatus
ADR-134 被集成(可选)Cir::dominant_distance_m()CirDistancePrior 进入断层权重矩阵做距离加权
ADR-135 前置/消费者:读取 CalibrationDeviationScore.drift_score;跨链路变点是其单链路陈旧判断的空间互补;共享 W=300 窗口与重校准触发
ADR-120 被复用VoxelGate::demotePrivacyGate::demote 单调不变量与 PrivacyClass 枚举的直接应用
ADR-141 策略提供者:决定每个 sink 适用哪个 PrivacyClass 并运行时证明;本 ADR 提供体素机制
ADR-137 消费者AnomalyWarn(占用不一致)成为带证据引用的矛盾标记进入语义状态记录
ADR-139 消费者ChangePointCoherenceAlert 修改环境数字孪生(移动家具边、房间重校准标记)
ADR-136 基座EvolutionReport/EvolutionAlert 是流经流式引擎帧契约的类型化阶段输出
ADR-084 / ADR-118 相关:tracker 引用的逐人基线的纵向漂移与持久化上下文

11. 核心参考源码清单

  • evolution.rs — ADR-142 的落地实现:TemporalVoxel / TemporalVoxelMap / VoxelGate / VoxelPrivacy / EvolutionTracker 及其测试
  • ruvsense/mod.rsevolution 模块声明与类型 re-export(ChangePointEvolutionTrackerTemporalVoxelTemporalVoxelMapVoxelGateVoxelPrivacy
  • field_model.rsWelfordStats(逐体素方差复用)、CalibrationStatusestimate_occupancyextract_perturbationcheck_freshness
  • calibration.rs — 逐链路 CalibrationDeviationScore.drift_score 消费方(ADR-135)
  • tomography.rsRfTomographer / OccupancyVolume / weight_matrixVoxelMap 的时间伴侣
  • longitudinal.rsPersonalBaselineEmbeddingHistory(按句柄引用,不复制)
  • attractor_drift.rsAttractorDriftAnalyzer::analyze regime 变化折入演化状态
  • privacy_gate.rsbfld/src/lib.rsPrivacyGate::demote 单调降级模式与 PrivacyClass(Raw/Derived/Anonymous/Restricted)、BfldError::InvalidDemote
  • archive/v1/data/proof — ADR-028 确定性证明链载体,voxelmap_evidence_v1 哈希将落于此处
登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.14 K
2.75 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
857
1.35 K
docsdocs
暂无描述
Markdown
898
5.82 K
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
921
1.84 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.8 K
1.02 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
531
596
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
1.02 K
519
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.36 K
1.46 K
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
548
391