首页
/ RuView 逐房间校准与专家小模型训练:用 4 分钟引导锚点把 WiFi CSI 调教成专属房间模型

RuView 逐房间校准与专家小模型训练:用 4 分钟引导锚点把 WiFi CSI 调教成专属房间模型

2026-09-07 16:44:57作者:曹令琨Iris

本文以 ADR-151(docs/adr/ADR-151-room-calibration-specialist-training.md)为核心,完整讲解 RuView 的"逐房间校准 + 专家化小模型训练"系统:一条本地优先的 baseline → enroll → extract → train 四阶段流水线,如何在已有 ADR-135 空房间指纹之上,用约 4 分钟的引导式人体锚点采集,训练出一组按 room_id 作用域版本化的小型专家模型(呼吸、心跳、睡眠躁动、姿态、在场、异常),并配合置信度门控与 STALE 失效机制在本地运行。读完本文,你能理解该系统的架构决策、逐阶段的数据契约与质量门控阈值,并能在 ESP32 CSI 数据源上复现 enroll / train-room / room-status / room-watch 的完整操作。

1. 为什么"先教房间,再教模型":问题背景与定位

ADR-151 开篇给出的论点是:RuView 的部署边界不在更强的通用模型,而在"先教房间,再教模型"(teach the room before you teach the model)。ADR-150(docs/adr/ADR-150-rf-foundation-encoder.md)记录了这堵墙的直接证据:MM-Fi 姿态头在域内取得 81.63% 的 torso-PCK@20,但跨主体(去泄漏后)只剩约 11.6%,而且更大的容量反而损害跨主体泛化(transformer 24.8% < conv 27.3%)。一个"理解世界"的超大模型会过拟合它见过的房间与人体。

因此系统取规模的相反方向作为杠杆:一个理解"某个特定房间 + 某个人"的小模型,几分钟内完成校准,本地运行,并按生物信号逐一特化。文档将 RuView 放在环境感知领域的两个既有范式之间:

  • 可穿戴设备——高保真,但人总会忘记佩戴,且只测量佩戴者本人;
  • 摄像头——能力强,但侵入性强、存储可识别视频,在黑暗/被覆盖场景失效。

RuView 的中间位置是:学习空间、学习,跟踪生物节律(呼吸、心跳、躁动、姿态、在场),而不看皮肤、不存视频。文档的关键判断是:心跳和呼吸不是视觉问题,而是射频场中微小、重复的扰动;把它们采得好是一个"校准问题",而不是"模型规模问题"。

1.1 能力盘点:缺的是"连接组织"

ADR 逐条盘点了流水线各环节的既有状态(下表为原文档 §1.2 的完整继承):

能力 现状 缺口
空房间基线(环境指纹) ADR-135 BaselineCalibration:每子载波幅度 + 圆相位统计,ruvcal NVS 命名空间 捕获的是房间,缺少在其上叠加引导式人体锚点的步骤
场本征结构 ADR-030 field_model.rs(SVD 房间本征模态) 消费校准结果,但未接入训练触发
共享不变骨干 ADR-150 RF Foundation Encoder(姿态保持、跨主体/房间/设备不变) 被定义为基础嵌入;没有东西把它蒸馏进逐房间专家
少样本适配 rapid_adapt.rs——测试时训练 → LoRA 权重增量(MERIDIAN P5) 只产出单个姿态适配增量,不产出按模态的专家库
特征提取器 ruvsense/{bvp,longitudinal,intention,gesture,pose_tracker,adversarial}.rstrain/src/signal_features.rs 各自输出信号,但没有一个被打包成带标注的训练源
小模型存储 wifi-densepose-ruvector(RVF 认知容器、HNSW、sketch) 没有"按 room_id 作用域的专家模型库"的 schema
HF 发布 ADR-149 AetherArena + from_pretrained 路径 发布/评估的是全局模型;没有"已发布骨干 + 私有本地头"的概念

缺失的系统就是"连接组织":一个引导式注册协议、一个"特征提取→标注"的桥接层、一个复用冻结 HF 骨干的专家库训练器、以及一个以置信度门控融合各专家的运行时。ADR-151 定义的就是这套系统。

2. 四步用户模型与两条设计不变量

系统面向操作者刻意呈现为四步普通操作,每步落到既有或新增代码上:

  1. 采集安静基线——无人,只有房间/路由器/反射/噪声/漂移 → 环境指纹。复用 ADR-135 BaselineCalibration + ADR-030 场本征模态,不写新采集代码;
  2. 采集引导样本——站立、坐下、躺下、慢呼吸与正常呼吸、小动作、睡姿。干净的锚点,而非小时级数据。新增 EnrollmentProtocol
  3. 提取有用信号——CSI 相位、幅度、多普勒移、微动、周期性、方差、时序。复用 signal_features.rs + ruvsense 提取器,打包成带标注的 AnchorFeature 记录;
  4. 把模式压缩成小 ruVector 模型——按信号特化:呼吸、心跳、睡眠躁动、姿态、在场、异常。新增 SpecialistBank,基于冻结骨干做 LoRA 头,以 RVF 存储。

决策部分给出两条统领所有设计的不变量(原文 §2 逐字继承):

(A) 特化优于规模。 每条生物信号一个小模型,而不是一个大一统模型。每个专家更快、更省、更私密——而且因为校准到了房间的真实指纹,往往比通用模型更准

(B) 本地优先、骨干共享。 冻结的跨房间/主体/设备不变骨干是唯一发布到 Hugging Face 的产物。逐房间基线与逐专家头除非操作者显式选择联邦(ADR-105),否则永不出设备。

3. 系统架构:一次加载的骨干,六个微头

原文档 §2.1 的架构图完整如下(保留原图):

                       HUGGING FACE HUB (public, room-agnostic)
                       ┌───────────────────────────────────────┐
                       │  RF Foundation Encoder (ADR-150)       │
                       │  pose-preserving · subject/room/device │
                       │  -invariant · frozen · safetensors     │
                       └───────────────┬───────────────────────┘
                                       │  from_pretrained() once, cached on device
                                       ▼
  STAGE 1 baseline        STAGE 2 enroll        STAGE 3 extract         STAGE 4 train (per room_id)
  ┌──────────────┐        ┌──────────────┐      ┌────────────────┐      ┌─────────────────────────┐
  │ ADR-135      │        │ Enrollment   │      │ signal_features│      │ SpecialistBank          │
  │ Baseline-    │──fp──► │ Protocol     │─clip►│ + ruvsense     │─AF──►│  frozen backbone        │
  │ Calibration  │        │ guided       │      │ extractors     │      │   │  ┌────────────────┐  │
  │ (env finger- │        │ anchors:     │      │ → AnchorFeature│      │   ├─►│ breathing head │  │
  │  print)      │        │ stand/sit/   │      │ (phase, amp,   │      │   ├─►│ heartbeat head │  │
  │ ADR-030      │        │ lie/breathe/ │      │  doppler,      │      │   ├─►│ restless head  │  │
  │ field eigen  │        │ move/sleep   │      │  micromotion,  │      │   ├─►│ posture head   │  │
  └──────────────┘        └──────────────┘      │  periodicity,  │      │   ├─►│ presence head  │  │
        │                                        │  variance,     │      │   └─►│ anomaly head   │  │
        │  baseline drift > τ → invalidate bank  │  timing)       │      │     (LoRA / ruVector    │
        └───────────────────────────────────────┴────────────────┴──────┤      small models)      │
                                                                          └───────────┬─────────────┘
                                                                                      │ RVF container
                                                                                      ▼
                                                              RUNTIME: Mixture-of-Specialists
                                                              each head emits {value, confidence};
                                                              coherence_gate (ADR-135) + anomaly
                                                              head veto → fused RoomState

共享骨干每设备只加载一次并冻结。每个专家都是其嵌入之上的一个小头——所以新增第六个专家的边际成本是几 KB 的 LoRA 权重,而不是又一个完整模型。

需要注意实现现状:当前落地版本的专家是统计型专家(阈值/原型/自相关),而非 LoRA 头——v2/crates/wifi-densepose-calibration/src/specialist.rs 的文件头注释明确说明:"ADR-151 的冻结 HF RF Foundation Encoder 骨干是计划中的升级路径:这些头届时将坐落在共享嵌入之上;这里的统计头让流水线今天就可运行、可验证。" 这一点在第 8 节"实现状态"中会完整交代。

4. Stage 2 — 引导式注册协议:8 个锚点与自适应质量门

EnrollmentProtocol 是一个 CLI 驱动的状态机,带着操作者走一遍固定的带标注锚点序列。设计规则来自用户愿景并写得很直白:要干净的锚点,不要小时级数据。每个锚点是一小段(默认 20 s @ 20 Hz = 400 帧)带标注的采集片段,叠加在已录好的基线之上。原文档 §2.2 的锚点表完整继承如下:

锚点 标注 时长 主要教会的信号 特征侧重
empty presence=0 (复用 ADR-135 基线) 缺席参照 幅度方差下限
stand_still posture=standing, presence=1 20 s 静态人体负载 幅度均值偏移、本征模态差
sit posture=sitting 20 s 较低的静态负载 幅度剖面
lie_down posture=lying 20 s 睡眠位置负载 幅度剖面、低多普勒
breathe_slow resp≈0.1–0.15 Hz 30 s 慢呼吸 周期性、微多普勒
breathe_normal resp≈0.2–0.3 Hz 30 s 正常呼吸 周期性、BVP 相位
small_move motion=1 20 s 肢体微动 多普勒展宽、方差
sleep_posture posture=lying, restless=0 30 s 安静睡眠基线 长窗方差、时序

协议是自适应的:锚点只有在其采集特征通过质量门(coherence_gate.rs 的一致性 ≥ 阈值、相对基线的足够 SNR、无饱和)时才被接受。失败的锚点会被重新提示而不是默默保留——文档的原话是"坏锚点毒化小模型的力度远超毒化大模型"。全部引导注册约 4 分钟墙钟时间,产出 8 个干净锚点。这刻意远低于从零训练所需的"小时级数据",因为骨干已经携带世界知识,注册只需要教会这个房间的偏置。

4.1 锚点在源码中的落点

八个锚点的枚举、顺序、提示语与时长在 v2/crates/wifi-densepose-calibration/src/anchor.rs 中实现:AnchorLabel::SEQUENCE 常量数组定义了规范的注册顺序(empty → stand_still → sit → lie_down → breathe_slow → breathe_normal → small_move → sleep_posture),duration_s() 给出呼吸/睡眠锚点 30 s、其余 20 s,prompt() 提供操作者提示(如 "Stand still, in view of the sensor…")。JSON 序列化固定为 snake_case 且与 as_str() 一致——label_serde_is_snake_case_matching_as_str 测试把这个线格式约定钉死为持久化契约。

会话是事件溯源的(EnrollmentSession,符合 CLAUDE.md 的状态规则):events 是追加式日志,事件包括 StartedAnchorAcceptedAnchorRejected(含人类可读原因)、Completed,以及 ADR-152 引入的 GeometryRecorded(天线阵列几何记录,最新事件生效,且是向后兼容的附加变体——旧会话 JSON 可干净地回放)。accepted_anchors() 从日志派生出"每个标签以最后一次接受为准"的锚点集合,next_anchor() / progress() / is_complete() / missing() 让重新注册天然增量且可审计:重跑只会补缺失的锚点。

4.2 质量门的真实阈值:AnchorQualityGate

质量门在 v2/crates/wifi-densepose-calibration/src/enrollment.rs 中落地为 AnchorQualityGate,其默认值与判定逻辑值得逐条看:

impl Default for AnchorQualityGate {
    fn default() -> Self {
        Self {
            min_presence_z: 1.5,   // 人均在场的最小平均幅度 z 分数
            empty_max_z: 1.0,      // empty 锚点:房间"真空"的最大平均 z
            max_still_motion: 0.6, // "静"锚点可容忍的最大运动帧占比
            min_move_motion: 0.3,  // "动"锚点所需的最低运动帧占比
            min_frames: 60,        // 评估一个锚点所需的最少帧数
        }
    }
}

evaluate() 按标签分支:要求在场的锚点若 presence_z < 1.5 会以 "no person detected … move closer / face the sensor" 拒绝;静锚若运动率 > 0.6 以 "too much motion … hold still" 拒绝;small_move 若运动率 < 0.3 以 "not enough motion — move a bit more" 拒绝;empty 锚点若 presence_z > 1.0 以 "room not empty … clear the room" 拒绝;帧数不足 60 直接报 "only N frames (need ≥60); is the ESP32 streaming?"。每个拒绝原因都会回传给操作者并重试,这正是文档 CLI 示例中 [✗ low SNR, retrying] 提示的来源。

一个关键的实现细节(ADR-152 发现,"z-band squeeze"):运动不是取自基线偏离分的 motion_flagged。该标志在 amplitude_z_median > 2.0 时触发,衡量的是相对空房间基线的偏离强度,与"是否在动"混淆——一个反射很强的静立者(每帧 z > 2)会被误判为"运动过多"而拒绝。修正后的 AnchorRecorder 从偏离序列的逐帧变化推导运动:|Δz| > Z_DELTA_MOTION(常量 0.5)或 |Δφ| > PHASE_DELTA_MOTION(π/6)才计一帧运动。该构造天然与在场强度无关:静止的强反射者 z 水平高但序列平坦;移动的人序列抖动。测试 still_anchor_with_strong_still_person_acceptsphase_delta_also_counts_as_motion 分别钉死了这两条路径。

5. Stage 3 — 特征提取:从锚点片段到带标注记录

每个被接受的锚点片段跑过既有提取器栈,按 ADR-135 做基线减除,然后打包成 AnchorFeature 记录。原文强调这一阶段是桥接,不是新算法("No new DSP is invented")。原文档 §2.3 的特征分组表完整继承:

特征组 来源模块 供哪些专家使用
CSI 幅度均值/方差 ADR-135 基线减除 + signal_features.rs presence, posture
CSI 相位(净化后、LO 对齐) phase_sanitizerphase_align posture, heartbeat
多普勒移/微多普勒 ruvsense/bvp.rsbreathing 路径 breathing, small-move
微动/意图前导 ruvsense/intention.rs restlessness, anomaly
周期性/谱峰 bvp.rs 自相关 + FFT breathing, heartbeat
长窗方差/漂移 ruvsense/longitudinal.rs(Welford) restlessness, presence
时序/帧间历元 c6_timesync epoch、帧 Δt 全部(节律对齐)
场本征模态系数 ADR-030 field_model.rs posture, presence

5.1 实际的 Features 向量与自动相关周期搜索

当前实现中,桥接层把每帧标量序列(基线减除后的 CSI 导出的每帧标量)压缩成一个紧凑的 Features 结构(v2/crates/wifi-densepose-calibration/src/extract.rs):

pub struct Features {
    pub mean: f32,             // 序列均值(在场/静态负载)
    pub variance: f32,         // 序列方差(运动/占用能量)
    pub motion: f32,           // 平均绝对一阶差分(即时运动代理)
    pub breathing_score: f32,  // 呼吸频带的周期性得分 [0,1]
    pub breathing_hz: f32,     // 主导呼吸频率 (Hz),无则 0
    pub heart_score: f32,      // 心率频带的周期性得分 [0,1]
    pub heart_hz: f32,         // 主导心率频率 (Hz),无则 0
}

周期性的估计用频带限定的自相关autocorr_dominant),与固件心率估计器(issue #987)用的是同一技术:呼吸带取 [0.1, 0.6] Hz,心率带取 [0.8, 3.0] Hz,返回带内最优峰值频率与归一化到 [0,1] 的峰得分。实现里有两处防泄漏的精细约束:

  1. 内部局部极大值约束(ADR-152 发现,"heart-band leakage"):胜出滞回必须是带内自相关的内部局部极大值,而不是带边缘值。带外强节律(呼吸泄漏进心率带)会形成单调斜坡,其带内最大值落在滞回下限——会把 heart_hz 钉在带顶并给出高得分。真正的带内周期在带内部达峰;边缘极大是泄漏,予以拒绝。测试 heart_band_rejects_breathing_leakage 用纯正弦 0.30 Hz 呼吸信号验证:心率带得分必须 < 0.25,而呼吸带本身仍要以 0.30 Hz 命中。
  2. 嵌入门控EMBED_MIN_SCORE = 0.25):供最近邻原型分类器使用的 5 维嵌入 [mean, variance, motion, breathing_hz, heart_hz] 中,两个 hz 分量只有在其周期性得分越过 0.25 才进入嵌入;否则置 0。原因是噪声窗口的带内峰值是随机频率,放进嵌入会让姿态/异常的原型距离变噪。原始的 breathing_hz/heart_hz 字段保持未门控——呼吸/心跳专家各自施加更严的 min_score 门。embedding_gates_hz_on_score 测试同时验证了"低分被门掉"与"高分原样通过"。

NaN 的 fail-closed 契约Features::from_series 在任何统计量计算之前丢弃非有限样本(NaN/±inf)——一个损坏的 CSI 帧被计为"没有帧",而不是传播 NaN 的帧;全非有限序列退化为全零的 Features::ZERO,与空序列同义。这不是防御性的摆设,而是修复过一个真实故障类别(见第 8 节)。回归测试 non_finite_samples_do_not_poison_features 还做了更强的等值断言:丢弃非有限样本后的结果必须与只喂有限样本的结果完全相等,以此证明"过滤"而非仅仅"有限"。

AnchorFeature 记录即 { room_id, label, features }——原文档 §2.3 设计的 { room_id, anchor_label, t_epoch_us, embedding: [f32; D], aux: {...} } 的当前实现形态:共享骨干嵌入字段是 P6 升级位(见后),aux 中的廉价手工特征(方差、运动、周期性得分、心率/呼吸频率)正是让小头特化而无需重学 DSP 的部分。

6. Stage 4 — 专家库:六个小模型按 room_id 版本化

SpecialistBank 是绑定到一个 room_id 的、版本化的小模型集合。原文档 §2.4 的专家表完整继承如下("Params" 列为 ADR 对 LoRA/RVF 形态的设计值):

专家 模型类型 参数量(典型) 标注来源 输出
breathing 1-D 时间头 + 周期性回归器 ~8 KB LoRA + aux breathe_slow/breathe_normal 呼吸率 (Hz) + 置信度
heartbeat 窄带相位头(谐波感知) ~12 KB 安静锚点 + 周期性 HR (bpm) + 置信度
sleep restlessness 方差/漂移分类器 ~4 KB sleep_posture vs small_move 躁动得分 [0,1]
posture k 路原型分类器(HNSW NN) 仅原型 stand/sit/lie 锚点 姿态类 + margin
presence 二值能量/本征模态门 ~2 KB empty vs 有人锚点 在场概率
anomaly 单类/物理不可能检测器(adversarial.rs ~6 KB 基线 + 全部锚点(新颖性) 异常得分 + 否决标志

由不变量 (A) 推出的设计性质:

  • 独立版本化、可替换。 重注册呼吸不会重训姿态。每个专家携带自己的 {trained_at, anchor_set_hash, baseline_hash, backbone_rev}
  • 分类器用 HNSW 原型存储。 姿态与在场是 RVF 索引中的最近原型查找——无推理引擎、微秒级延迟,新增姿态靠插入原型而非重训。
  • SONA 在线适配。 每个专家可携带一个 SONA/MicroLoRA 在线适配槽,以在全量重注册之间跟踪慢漂移(家具移动、季节性 RF 变化),由 ADR-135 基线漂移门控。
  • 教师-学生蒸馏(可选、离线)。 在存在标注公开语料(MM-Fi、Wi-Pose)处,ADR-150 骨干作为教师,在逐房间微调前对头做预塑形,改善冷启动。教师是全局/HF 的;学生头是本地的。

6.1 六个专家在 specialist.rs 中的实现细节

v2/crates/wifi-densepose-calibration/src/specialist.rs 把上表落地为六个类型,各自的关键参数已去魔法为命名常量(见第 8 节):

呼吸 / 心跳(无状态、频带周期性读取):BreathingSpecialistHeartbeatSpecialist 各自只有一个 min_score 字段。推理时若周期性得分低于门限或频率为 0 则返回 None(不报告);否则输出 value = hz × 60(BPM),confidence 即周期性得分。默认门限:DEFAULT_BREATHING_MIN_SCORE = 0.25DEFAULT_HEARTBEAT_MIN_SCORE = 0.30——心率门限更高是有文档依据的:亚毫米级胸部位移在 HR 频带逼近 CSI 噪声底(ADR-151 §3.2 风险条),心跳头要求更干净的峰才报告。测试 default_min_score_constants_match_prior_literals 钉死了边界语义:得分恰好等于门限时报告,低 1e-3 则不报告。

在场(双通道门):PresenceSpecialist::trainempty 锚点与所有有人锚点拟合。它同时维护两条互补通道(ADR-152 发现 "variance-only presence"):

  • 方差通道——运动/占用能量;能抓住移动的人,但对静止不动的人失明(静立者抬升标量均值——额外多径能量——却几乎不抬方差);
  • 均值偏移通道——|mean − empty_mean|;抓住方差通道漏掉的静立者。取绝对值是因为人体也可能遮蔽路径从而压低均值。

训练时方差通道阈值取"空房间方差与有人平均方差的中点",但有一个守卫(issue #1440):只有当有人方差真正超过空房间方差 VARIANCE_SEPARATION_MARGIN(0.05,即 5%)以上时通道才启用,否则阈值设为 +∞(通道失活)。否则中点阈值可能落在空房间基线之下,让真·空房间每帧都读"在场"——比没有信号更糟。测试 presence_inverted_variance_never_reports_empty_room_as_presentpresence_detects_motionless_person_via_mean_shift 分别覆盖这两种回归。旧版 JSON(无均值通道字段)通过 serde(default) 反序列化为失活通道,行为保持兼容(presence_old_bank_json_stays_variance_only)。

姿态(最近原型分类器):PostureSpecialist 从建立姿态的锚点(stand/sit/lie/sleep)收集 (Posture, [f32; 5]) 原型;推理取最近原型的姿态类,置信度由最近与次近原型的距离 margin 归一化得出:(second − best) / (second + best)

睡眠躁动RestlessnessSpecialist 把当前帧的 motion 在"平静"(sleep_posture,缺失时退到 lie_down)与"活跃"(small_move)两个锚点之间线性归一化到 [0,1]。训练要求 active > calm,否则拒绝成模型(返回 None,库保持部分可用)。

异常(新颖性检测):AnomalySpecialist 收集所有锚点嵌入为分布内流形,尺度取原型间平均最近邻距离(欧氏)。推理时取到流形的最近距离 best,得分 = best / (ANOMALY_OUTLIER_SPREADS × scale) 并截断到 [0,1],其中 ANOMALY_OUTLIER_SPREADS = 2.0 表示"超出典型散布 2 个尺度"即完全分布外;ANOMALY_LABEL_CUTOFF = 0.5 区分 "anomalous"/"normal" 的人类可读标签。训练要求至少 2 个锚点。

6.2 库的版本化与失效契约

v2/crates/wifi-densepose-calibration/src/bank.rs 中的 SpecialistBank 字段结构直接体现了失效契约:

pub struct SpecialistBank {
    pub room_id: String,          // 房间作用域
    pub baseline_id: String,      // 训练时所针对的 ADR-135 基线 id(漂移 → STALE)
    pub trained_at_unix_s: i64,  // 训练时间
    pub anchor_count: usize,     // 使用的锚点数
    pub geometry: Vec<NodeGeometry>, // ADR-152 天线几何快照(serde default, 空则不序列化)
    pub presence: Option<PresenceSpecialist>,
    pub posture: Option<PostureSpecialist>,
    pub breathing: BreathingSpecialist,   // 无状态,恒在
    pub heartbeat: HeartbeatSpecialist,   // 无状态,恒在
    pub restlessness: Option<RestlessnessSpecialist>,
    pub anomaly: Option<AnomalySpecialist>,
}

SpecialistBank::train 要求至少 1 个锚点(空锚点报 InsufficientSamples),各专家按"前置锚点缺失则留 None"的原则独立拟合——部分库仍然可用,能拟合的信号照常工作(例如仅有静态锚点时 presence + posture 即可运行,这正是文档 §3.2 风险缓解中"ship sane defaults so a partial bank works")。is_stale(current_baseline_id) 的比较就是文档所述的失效实现:

如实现的(As implemented),运行时只要当前基线 id 与训练时不同就标记库为 STALE——这是一个保守触发器,能抓住重校准(房间重摆、AP 移动、频带变更),因为这些动作中任何一个都会产生新基线。更细的漂移阈值触发器(在 ADR-135 每子载波偏离超过 τ 未整体重做基线时即标 STALE)是计划中的 P6 细化。无论如何,运行时会提示重新注册,而不是从失效模型静默地吐错误数字——这是 #954 DEGRADED 诚实规则的校准对应物:绝不从失效模型报告高置信数字。

check_fresh 把 STALE 变成 CalibrationError::StaleBaseline { trained, current } 错误;持久化走 to_json/from_json(serde 形状校验,畸形 → CalibrationError::Serde)。ADR-152 的几何字段遵循同一"附加字段 + serde default"的向后兼容模式:无几何的旧库 JSON 干净加载为空快照,且无几何的库序列化时不输出该字段。

7. 运行时:置信度门控的专家混合

推理时冻结骨干对每个 CSI 窗口嵌入一次;每个专家消费同一共享嵌入并输出 {value, confidence}v2/crates/wifi-densepose-calibration/src/runtime.rsMixtureOfSpecialists::infer 的融合规则与原文档 §2.5 一一对应:

// 异常先跑——它可以否决一切
state.anomaly = self.bank.anomaly.as_ref().and_then(|a| a.infer(f));
let vetoed = state.anomaly.as_ref()
    .map(|r| r.value >= self.veto_threshold)   // 默认 0.5
    .unwrap_or(false);

// 在场门
state.presence = self.bank.presence.as_ref().and_then(|p| p.infer(f));
let present = state.presence.as_ref().map(|r| r.value > 0.5).unwrap_or(true);
// 无在场专家 → 假定有人,让生命体征照常跑

state.restlessness = ...; // 无论在场与否都报告(运动隐含在场)

if present && !vetoed {
    state.posture   = ...; // 姿态
    state.breathing = ...; // 呼吸 (BPM)
    state.heartbeat = ...; // 心跳 (BPM)
}

三条规则:

  1. 异常专家持否决权:高异常得分(adversarial.rs 意义上的物理不可能信号)压制正向生命体征/姿态输出并置 vetoed 标志,而不是传播一个幻觉读数;
  2. presence=0 短路呼吸/心跳/姿态为 None(空房间里不可能有呼吸率);
  3. STALE 标记RoomState.stale 在库训练基线与当前基线不符时置位,所有读数随之不可信。

此外每条输出都携带专家置信度与 baseline_hash/backbone_rev 溯源(ADR 设计),使下游消费者(sensing-server、MQTT、Home Assistant)能按质量门控——与 ADR-135 一致性门语义一致。测试套件覆盖四条主路径:空房间抑制生命体征、有人房间报告呼吸、异常否决、STALE 库标记(empty_room_suppresses_vitals / present_room_reports_breathing / anomaly_vetoes_vitals / stale_bank_flagged)。

8. 操作员 CLI:从 calibrateroom-watch

原文档 §2.7 的 CLI 流程完整继承如下:

# Stage 1 — 环境指纹(ADR-135,既有)
wifi-densepose calibrate --room living-room --duration 60s     # 空房间

# Stage 2+3 — 引导注册(新增);提示走 8 个锚点,约 4 分钟
wifi-densepose enroll --room living-room
#   → "Stand still in view of the sensor…"  [✓ anchor accepted: coherence 0.91]
#   → "Sit down…"                            [✗ low SNR, retrying]
#   ...

# Stage 4 — 训练专家库(新增);复用已缓存的 HF 骨干
wifi-densepose train-room --room living-room \
    --specialists breathing,heartbeat,restlessness,posture,presence,anomaly

# 状态/失效
wifi-densepose room-status --room living-room
#   baseline: fresh (drift 0.04 < 0.20) · backbone: rf-foundation@1.2.0
#   breathing  ✓ trained 2026-06-09  conf p50 0.88
#   heartbeat  ✓ trained 2026-06-09  conf p50 0.71
#   posture    ✓ 3 prototypes (stand/sit/lie)
#   anomaly    ✓  · presence ✓  · restlessness ✓

这些子命令实现在 v2/crates/wifi-densepose-cli/src/room.rsenroll / train-room / room-status / room-watch),针对活的 ESP32 CSI 流工作(需要 edge_tier=0 的原始 CSI)。enroll 的实际参数面比 ADR 示例更完整:

参数 默认值 说明
--udp-port 5005 ESP32 CSI 帧的 UDP 端口(原始 CSI,用 --edge-tier 0 配置)
--bind 0.0.0.0 UDP 绑定地址
--baseline ./baseline.bin calibrate 产出的空房间基线
--tier ht20 PHY 层(ht20 / ht40 / he20 / he40)
--room-id default 房间标签
--output ./enrollment.json 注册产物(带标注特征 + 审计日志)
--fs-hz 15.0 周期性提取所用的 CSI 采样率
--attempts 2 每个锚点失败重试上限(自适应重提示)

采集循环本身值得注意:CLI 对每个锚点先做 3 秒倒计时提示,然后按 label.duration_s() 采满窗口;每帧取全子载波/全流的平均幅度作为标量(frame_scalar),其注释说明了实测依据——在 ESP32 上验证过,平均幅度能拾取呼吸调制,而"最大方差子载波"反而锁定在运动伪影上;相位载波(在稳定子载波上)是文档 §4 列出的高 SNR 后续改进项。enroll 的持久化结构 EnrollmentData 同时包含 anchors: Vec<AnchorFeature> 与完整 session: EnrollmentSession(审计日志),train-room 从该文件读入后调用 SpecialistBank::train

9. 实现状态与验证边界:如实的"已证明什么"

ADR §4 的阶段表完整继承如下:

阶段 范围 出口标准 状态
P1 脚手架 wifi-densepose-calibration crate;AnchorFeature schema;(骨干经 hf_hub 推迟) Crate + schema;单元测试 ✅ 完成(crate + Stage-1 基线经 calibrate/calibrate-serve;HF 骨干推迟)
P2 EnrollmentProtocol + anchor.rs(事件溯源会话)+ 带质量门的 CLI enroll 8 锚点注册;坏锚点重提示 ✅ 完成(anchor.rsenrollment.rs、CLI enroll
P3 extract.rs 桥接 → 带标注记录;基线减除(ADR-135) AnchorFeature 记录按 room_id 持久化 ✅ 完成(extract.rs;自相关周期性 + 方差/运动)
P4 SpecialistBank + presence/posture(原型)+ breathing(周期性);持久化 + 版本化 train-room 产库;room-status 读回 ✅ 完成(specialist.rsbank.rs、CLI train-room/room-status;JSON 持久化——RVF/HNSW 留待后续)
P5 heartbeat + restlessness + anomaly 专家;runtime.rs 混合 + 否决 + 置信门控 硬件端到端 RoomState;异常否决验证 ✅ 完成(runtime.rs、CLI room-watch;COM8 上的 ESP32 实测呼吸读数)
P6 基线漂移 STALE 失效;SONA 在线适配;可选 ADR-105 联邦;HF 教师-学生蒸馏 漂移标 STALE;AetherArena 入口 ◐ 部分(STALE 已完成;SONA/联邦/HF 骨干 = 后续)

当前状态(ADR 记录的 2026-06-10 节点)精确表述为:Stages 1–5 以统计专家(阈值/原型/自相关)实现。55 个测试(35 单元含多静态 + 1 全环集成 + 19 CLI)在 qemu-aarch64 下全部通过。验证范围被刻意说清:

  • 基线采集 + HTTP API + 鉴权在真实 CSI 上证明(Pi-5 nexmon,6,813 帧;以及 ESP32-S3);
  • 完整的 baseline → enroll → train-room → infer 循环在进程内于确定性合成 CSI 上证明——v2/crates/wifi-densepose-calibration/tests/full_loop.rs:干净基线零运动标志、8/8 锚点过质量门、训练 6 个专家、JSON 库往返、已训库推理 18±2 BPM 正 / 缺席负 / 外来基线 STALE、种子稳健;
  • 唯一的活运行时信号(呼吸约 16–31 BPM,经 room-watch)用的是无状态呼吸头,而已训库;干净的"空房间循环"尚未在目标硬件上跑通——剩余差距严格限于硬件会话(空房间 + 操作者锚点)。

验证纪律按 CLAUDE.md:cargo test --workspace --no-default-features 全绿;任何发布前在 ESP32-S3(当时 COM8)上做硬件验证;证明面变化时重新生成 witness bundle。

10. 正确性/安全评审发现:一次 NaN 投毒修复

ADR §6.1(2026-06-14 的 Beyond-SOTA 正确性+安全评审)记录了本 crate 一次真实的数值失效及其修复,值得作为"小模型的诚实性"案例完整阅读:

发现(已修复)——特征路径的 NaN 投毒(数值/fail-closed)Features::from_series 既是活推理、也是训练锚点提取的载体,原先对原始标量序列计算 mean/variance/motion 时无非有限守卫。单个 NaN/±inf 样本(损坏 CSI 帧)即产出 mean=NaN, variance=NaN 与全 NaN 原型嵌入;一旦在训练时写进 PresenceSpecialist::threshold/empty_meanNaN静默禁用在场检测直到该库寿命结束(对 NaN 的每个 >/|·| 比较都为 false → 永远读"缺席"、置信度 0),且无任何报错——还与严格做 NaN 防护的 geometry_embedding 形成不对称。修复落在生产边界:非有限样本被丢弃(坏帧计为没有帧),全非有限序列退化为 Features::ZERO;全有限输入下逐值相同(full-loop 与 extract 测试不变);由 non_finite_samples_do_not_poison_featuresall_non_finite_series_is_zero(两者在旧代码上都会失败)钉死。

干净维度(有证据、不虚构问题):crate 的 src/文件/路径 I/O(无 std::fs/Path/File/read/write,只有内存态 serde_json)——路径穿越/无界读取/工件路径处理完全在 wifi-densepose-cli 消费者(room.rs)一侧,在本 crate 边界之外;SpecialistBank::from_json 经 serde 形状校验(畸形 → CalibrationError::Serde),库本地优先、从不经网络接收,在本地优先威胁模型下可接受;crate 不发出任何哈希/回执/witness/签名,"无框架拼接"的 bug 类别在结构上不存在;geometry_embedding 净化每个输入并扫至有限;presence/restlessness/anomaly 的除法都有 .max(1e-3) 守卫;autocorr_dominant 守卫 r0、短信号与空带;train 拒绝空锚点;anomaly 要求 ≥2 锚点。

同次评审还把所有裸专家阈值字面量去魔法为命名文档化常量(呼吸/心跳默认 min-score、异常离群散布倍数 + 标签截断)——即第 6 节列出的 DEFAULT_BREATHING_MIN_SCORE 等——逐值不变,由常量相等测试钉死。评审后测试 58→62 单元 + 1 集成,0 失败,Python 确定性证明(不在信号证明路径上)不变。

11. 风险、被拒替代方案与小结

ADR §3 的风险与替代评估原文继承如下。

负面/风险(§3.2)

  • 骨干依赖。 每个专家都骑在 ADR-150 编码器上;其质量与修订兼容性(backbone_rev)是单点杠杆。缓解:每个专家钉住 backbone_rev;蒸馏冷启动降低敏感度。
  • 注册负担。 4 分钟虽小但非零,锚点质量取决于操作者跟随提示。缓解:自适应重提示 + 质量门;提供合理默认,使部分库(presence+posture)在只有静态锚点后就能工作。
  • 心跳很难。 HR 频带的亚毫米胸部位移逼近 ESP32-S3 噪声底;心跳专家的置信度会比呼吸更低、更波动。置信门控的运行时如实呈现这一点,而非假装。
  • 逐房间存储增殖。 每房间每人一个库;需要清晰的 RVF 生命周期(列出/修剪/导出)——由 bank.rs 版本化与 room-status CLI 处理。

被拒替代(§3.3)

替代 结论 理由
一个大通用模型管所有信号 拒绝 ADR-150 证据:规模过拟合房间/主体,跨域崩塌;且更慢、更贵、更不私密。直接违背不变量 (A)。
云训练逐房间模型 拒绝 违背不变量 (B):会把某人家庭/睡眠的原始 CSI 送上服务器。本地优先就是隐私承诺。联邦(ADR-105)是可选的共享改进路径,交换梯度/增量,永不交换原始 CSI。
跳过骨干;每个专家从零训练 拒绝 重新引入用户愿景明确拒绝的"小时级数据"要求,并丢失跨房间先验。
并入 ADR-135 拒绝 ADR-135 是房间校准(无人)。本 ADR 是在其之上的人体锚点注册 + 模型训练。生命周期不同、失效机制不同;保持为独立限界上下文。

小结(ADR §5 原句):

大模型理解世界。小 ruVector 模型理解你的房间。

ADR-151 把这句话工程化:一条本地优先的 baseline → enroll → extract → train 流水线,把约 4 分钟的干净人体锚点——叠加在 ADR-135 的空房间指纹与 ADR-150 的 Hugging Face 发布不变骨干之上——变成一个版本化的、微小的、特化的、隐私保全的模型库,覆盖呼吸、心跳、躁动、姿态、在场与异常。特化优于规模;本地头加共享基座;诚实的 STALE 退化优于自信的错误。 从源码结构看,这套系统的骨架(v2/crates/wifi-densepose-calibration/ 的 11 个源文件 + full_loop 集成测试)与 CLI 操作面(v2/crates/wifi-densepose-cli/src/room.rs)已完整落地为统计型专家;LoRA 头、RVF/HNSW 存储、SONA 在线适配与冻结 HF 骨干的蒸馏是文档中明确标注的 P6 后续工作——这也是当前仓库状态下复现该系统时应预期的边界。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

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