RuView 传感器布局优化器解析:从 ADR-308 到 ruview-placement 的实现与实践
RuView 通过普通 WiFi 信号实现空间感知、生命体征监测与存在检测,而真实部署中"传感器放哪"往往依赖经验猜测。本文围绕设计决策记录 ADR-308(sensor placement optimizer),讲解其在 RuView 感知子系统中承担的角色:以楼层平面图与硬件清单为输入,输出可落地的推荐布点,并在安装后用实测可观测性闭环校验。读者读完将掌握布局优化的输入/输出契约、预测与后安装比对的数据流、证据等级纪律,以及当前仓库中对应 Rust crate(ruview-placement)的源码级实现与测试验证方式。
背景:布局问题是 RuView 部署失败的常见根因
ADR-308 是 ADR-300(Perception Substrate Program) 规划蓝图下的子决策,归属于其 primitive #8「sensor placement optimizer」,处于 phase-3(研究前沿阶段),构建在融合世界状态之上。在 ADR-300 的 DAG 中,它与 ADR-315(digital RF twin) 紧密耦合:RF 孪生负责提供优化器规划所依赖的传播仿真;优化器本身读取 ADR-306(canonical spatial ontology) 描述的物理场景,并在安装后用自己的预测与 ADR-302 观测性与 ADR-318 能力证书做比对。
该问题被定义为 RuView 部署失败的单一最常见原因:传感器靠猜测摆放。房间能否被可靠感知,取决于 AP/传感器相对于墙体的几何位置、菲涅尔区净空(Fresnel-zone clearance)、多径结构以及人的真实活动区域。传统安装流程缺少原则性方法来回答"我手上只有两个节点,放在哪才能让厨房可观测",也无法在安装后判断现实是否与规划一致。优化器把 RuView 从"感知给定布点恰好能覆盖的范围"转变成"推荐能让目标感知需求可行的布点"。
方案选型:为什么不做静态指南或全电磁求解器
ADR-308 对比了三类候选方案,最终结论直接决定后续实现形态:
| 候选方案 | 结论 | 理由 |
|---|---|---|
| 文档静态布局指南(如"每房间一个节点、正对门") | 拒绝 | 忽略具体平面图、墙体材质与真实硬件清单,无不确定性量化,无安装后反馈 |
| 每站点全电磁求解器 | 默认路径拒绝 | 对安装者工作流过重,相对 RuView 现有的粗粒度 rf_attenuation_db 场景是过度设计;保留为 ADR-315 内部可选高保真后端 |
| 基于 ADR-306 场景 + ADR-315 RF 孪生做覆盖优化,并在安装后校验预测 vs 实测可观测性 | 采纳 | 消耗 RF 孪生而非重复实现第二个仿真器;可复用运行时同款观测性口径 |
可直接复用的既有资产
ADR-308 强调"构建而非重复造轮子",明确列出要复用的体系构件:
- worldgraph 场景模型:用
Room/Space+bounds_enu、Wall { rf_attenuation_db }(石膏墙约 3 dB、砖墙约 12 dB)、Doorway、Zone描述物理场景,足以播种一个粗粒度覆盖模型;同时包含作为候选位置的Sensor节点(ADR-306)。 - ADR-315(RF 孪生,phase-3):传播/多径仿真器。本优化器是孪生的消费者,而非第二个仿真器。
- ADR-302(OOD/可观测性)与 ADR-318(能力证书):定义"可靠感知目标现象"的度量口径,使优化器能针对运行时最终门控的同一种观测性度量做优化。
- ADR-029(多站感知)与 ADR-063(mmWave 融合):指示哪些链路几何对哪些现象有效。
决策内容与输入输出契约
决策定义明确:一个布局优化器,输入楼层平面图(ADR-306 场景)与硬件清单,输出推荐的传感器位置,并在安装后闭环。共分为三类输入、一步预测、一个安装后闭环和一套证据纪律。
三类输入
- ADR-306 规范场景:
Space/Zone边界、带rf_attenuation_db的Wall线段、Doorway拓扑,以及任何已部署的Sensor节点。 - 硬件清单:可用无线电的数量与类型(ESP32-S3/C6 节点、mmWave、网卡等),携带各自的能力包络——即每种硬件能感知什么,按 ADR-318 / ADR-320(sensor HAL)描述符定义。
- 感知目标(objective):哪种现象必须在哪个
Space/Zone可观测(存在检测 presence、生命体征 vitals、姿态 pose),用 ADR-302 观测性度量表述。
在源码中,这三类输入分别映射到 coverage.rs 的 Objective 与 Placement、inventory.rs 的 Inventory/RadioSpec,以及 lib.rs 中声明并重新导出的 SpaceId/ZoneId/SensorId/Container 等规范本体词表(ADR-300 rule 3 的体现——复用统一语义而不是发明新词)。
现象与感知需求强度
不同现象对菲涅尔净空的要求不同,coverage.rs 用 Phenomenon::demand() 给出一个乘数:
| 现象 | demand 乘数 | 含义 |
|---|---|---|
Presence(存在检测) |
1.0 | 基础覆盖阈值 |
Vitals(生命体征) |
1.6 | 需要更严格净空才计为可观测 |
Pose(姿态估计) |
2.2 | 最严格净空要求 |
预测:把 RF 孪生仿真折算成带不确定性的可观测性
对每个候选布局,优化器向 ADR-315 RF 孪生查询仿真 RF 覆盖:墙体 rf_attenuation_db 带来的路径损耗、链路端点间的菲涅尔区净空、粗粒度多径结构,由此推导出每个空间内每个目标的期望可观测性与不确定性——复用 ADR-302 定义,确保规划与运行时说同一种语言。
实现层面,ruview-placement crate 在 crate 文档字符串中把自身定位为 research-forward primitive / planning scaffold:给定粗粒度 FloorPlan(ADR-306 场景抽象)与硬件 Inventory,通过把候选布局对 ADR-315 RF 孪生的 SYNTHETIC(L0) 传播模型打分来推荐节点位置。
菲涅尔区几何:两个确定性纯函数
fresnel.rs 实现了两个无分配、确定性的解析函数:
fresnel_radius(wavelength_m, d1, d2):一阶菲涅尔半径,公式为F1 = sqrt(λ · d1 · d2 / (d1 + d2))。路径中点(d1 == d2 == L/2)退化为0.5·sqrt(λ·L),即测试中用于对照的已知解析极值。非有限或非物理输入一律返回0.0(绝不返回NaN/inf),调用方把零半径理解为"无净空信息",而不是除零。link_clearance(a, b, p, wavelength_m):返回[0, 1]的净空因子——点在链路上为1.0,线性衰减到一阶菲涅尔边界处的0.0,超出边界恒为0.0;投影不在两个端点之间的目标(处于链路感应走廊之外)以及退化链路(端点重合)也返回0.0。
其单元测试验证了关键解析情形:约 2.4 GHz(λ=0.125 m)、4 m 链路的中点半径直取解析式;链路中点净空为 1、端点之外与垂距远超菲涅尔半径处净空为 0;退化链路净空为 0。
覆盖打分与未知的一等公民语义
coverage.rs 说明了打分方式:候选布局用 RfTwin 前向模型在目标区域采样网格上打分——当采样点位于某条"预测良好链路"的一阶菲涅尔区内时即视为可观测。
每个目标输出一个 Observability 枚举,同时携带分数与不确定性,绝不给出模拟结果的单一自信数值:
pub enum Observability {
/// Modelled observability score in [0, 1] and its uncertainty in [0, 1].
Known {
score: f64, // 该区域模型感知值的均值
uncertainty: f64, // 分数的不确定性(越大越不确定)
},
Unknown { reason: ObservabilityUnknown }, // 模型无法评估:一等公民值,而非错误
}
Unknown 携带明确的类型化原因(ObservabilityUnknown):TargetNotInPlan(目标不在平面图中)、EmptyRegion(退化几何导致无采样点)、NoLinks(少于两个无线电,孪生没有可评估的链路)、NonFinite(出现非有限计算值)。这正是 ADR-300 rule 1 的落地——信息不足是返回值而非异常或自信默认值。
PlacementParams 提供模型参数与有界分配的硬上限,其 default_synthetic() 默认值如下(crate 明确声明这些默认值是教学性的,不对任何真实环境作断言):
| 参数 | 默认值 | 语义 |
|---|---|---|
wavelength_m |
0.1249 | 波长(米),约对应 2.4 GHz |
good_rssi_dbm |
-50.0 | RSSI ≥ 此值视为链路具备完整感知质量 |
floor_rssi_dbm |
-85.0 | RSSI ≤ 此值视为链路感知质量为零 |
coverage_threshold |
0.10 | 采样点被计为"已覆盖"的感知值下限(在现象 demand 乘数作用之前) |
blind_spot_fraction |
0.15 | 覆盖比例低于该值则把目标标记为盲区 |
grid_step_m / candidate_step_m |
0.5 / 1.0 | 采样网格 / 候选网格步长(米) |
uncertainty_variance_ref_db2 |
64.0 | 把模型化链路方差折算到 [0,1] 不确定性的参考方差 |
max_sample_points / max_candidates |
4096 / 512 | 每个目标采样点 / 生成的候选位置数量上限(有界分配) |
seed |
0 | 候选生成的显式种子(全 crate 无 RNG) |
propagation |
PropagationParams::default_indoor() |
RF 孪生传播模型参数(路径损耗指数、参考距离/损耗、阴影衰落 σ) |
边界校验在 validate() 中完成:非法矩形、非有限坐标、发射功率为无穷、wavelength_m = 0 等畸形输入返回类型化 PlacementError,从不 panic(见 lib.rs 的 boundary_validation_rejects_malformed_input_without_panic 测试)。几何边界常量在 geometry.rs:MAX_PLAN_WALLS = 4096、MAX_ZONES = 1024;清单上限 MAX_INVENTORY = 64 在 inventory.rs。
搜索:种子化候选网格上的贪心前向选择
optimize(见 plan.rs)在候选位置上做贪心前向选择:清单界定了无线电数量,场景界定了几何。每加入一个无线电只能让模型分数持平或上升,因此输出 PlacementPlan.score_trace 单调不减并最终在饱和处趋平。
配套的 rank_placements 用于对给定的候选布局集合排序(模型可观测性最高者优先),candidate_positions 生成种子化、确定性的候选网格——同一 seed 产生相同候选集,seed 变化则网格平移。crate 全链路确定性由 splitmix64 种子流驱动(lib.rs 的 synthetic_floorplan(seed)),不读时钟、无 I/O、无未种子随机性。
安装后闭环:预测 vs 实测对比与孪生残差
安装完成后,ADR-308 要求用来自新注册(ADR-305)且已校准(ADR-301) 传感器的 ADR-302 运行时观测性信号,比较预测 vs 实测可观测性。当实测与预测不一致时,给出调整建议(移动、重新对准、增加节点),并把残差回馈给 ADR-315 孪生以改进其场景参数(例如某面墙的有效衰减)。
源码中对应 compare.rs 的 compare_post_install / compare_post_install_with_tolerance(后者带默认容差 DEFAULT_COMPARE_TOLERANCE)。关键设计点是本 crate 从不测量:实测可观测性由调用方以 MeasuredObservability 提供,预测侧是 L0/SYNTHETIC。比对产出:
CompareVerdict:Match/Underperforming/Unknown(缺失实测即一等公民Unknown,而非错误);AdjustmentAction:MoveNode/ReAim/AddNode等建议动作;TwinResidual:含ResidualKind(如EffectiveAttenuationHigher)与magnitude_db,指示孪生应向哪个方向修正场景参数(例如墙体有效衰减更高)。
相关测试覆盖"实测显著低于预测 ⇒ 产出调整建议与正 dB 残差、判定 Underperforming"(lib.rs)以及"实测与预测一致 ⇒ 判定 Match、无需调整"(lib.rs)。
证据纪律:预测永不是感知声明
ADR-308 的证据纪律是该方案可信度的核心,与 ADR-282 的证据分级对齐:
- 预测覆盖属于仿真(ADR-282 中的证据等级 L0),必须标记为
SYNTHETIC,是建议而非感知声明。crate 用EvidenceLevel::L0显式标注在PlacementPlan上,且 serde 序列化会保留"evidence_level": "L0"到线上(见serde_round_trip_is_lossless_and_labels_evidence测试)。 - 预测 vs 实测的比对是唯一允许出现
MEASURED声明的位置,且必须附带复现步骤与真实芯片可观测性数据(遵循 CLAUDE.md 的硬件规则)。 - 优化器绝不把模拟覆盖图当作"该房间正在被感知"的证据;也不断言任何覆盖或精度数值。
源码 crate 文档同样在文件头强调:"Nothing here is a hardware, MEASURED, or accuracy claim, and the crate asserts no coverage or accuracy number — a twin/optimizer predicts, it does not measure."
结果与质量边界
正面结果:
- 安装者获得有原则、基于具体平面图的布局方案,关键是安装后还有一次"现实是否匹配规划"的校验——相对猜了再装的部署方式是差异化能力。
- 优化结果从不给出"推荐布点保证感知"的过度承诺:它只是在清单与几何约束下最大化模型化可观测性,且带显式不确定性。
质量边界:
- 质量受限于 ADR-315 RF 孪生的保真度与 worldgraph 场景的粗糙度(2D 墙、粗粒度衰减)。优化器选择"报告不确定性"而不是夸大粗模型,更高保真度属于 ADR-315 的范畴。
- 存在对 ADR-315(孪生)、ADR-302(观测性度量)、ADR-306(场景)的硬依赖;本 ADR 不自建仿真器也不自建观测性度量。
- 作为 phase-3 研究前沿 primitive,这是坐落在融合世界状态之上的设计意图,随 ADR-315 与 phase-1 主干落地后预期会被修订。
验证与测试矩阵
ADR-308 规划了四级验证,且均能在 ruview-placement crate 的 #[cfg(test)] 中找到对应实现:
| ADR 验证项 | 源码测试 |
|---|---|
| 覆盖/可观测性预测是 场景+布局+孪生参数 的确定性函数 | optimize_is_deterministic_and_seed_varies_the_scene(同 seed 逐位一致、不同 seed 场景不同、serde round-trip 无损) |
| 菲涅尔区与墙衰减数学对照已知解析情形 | fresnel_radius_matches_analytic_midpoint、clearance_is_one_on_the_line_and_zero_off_the_ends 等(fresnel.rs) |
| 搜索在小合成场景返回模型最优布局 | better_covering_placement_ranks_higher(链路横跨区域 vs 双节点扎堆角落,前者正确排第一) |
| 合成平面图上已知好/坏布局被正确排序,边缘情形不确定性更高 | blind_spot_zone_is_flagged(远端角区被标记盲区、覆盖比例更低)、marginal_case_reports_higher_uncertainty(菲涅尔边缘区域的不确定性高于链路正中区域) |
| 后安装闭环:注入预测-实测不一致产生合理调整建议与孪生参数残差 | predicted_vs_observed_delta_yields_adjustment_suggestion、matching_observation_needs_no_adjustment |
| 单调性与饱和度 | adding_a_node_improves_score_monotonically_until_saturation(分数单调不减、早期有实质提升、后期趋于饱和) |
尚未完成的是实硅片现场验证(ADR 中明确 deferred):在真实仪器化站点上报告 MEASURED 的预测 vs 实测可观测性并附复现步骤。在此之前,所有覆盖输出均为 SYNTHETIC/L0。
上手:一份可复现的最小示例
crate 文档字符串(lib.rs)提供了直接可运行的 API 示例——构建一个确定性的合成场景、一组清单与一个目标,调用 optimize 并断言证据等级与贪心轨迹单调性:
use ruview_placement::*;
// A reproducible SYNTHETIC scene, an inventory, and one objective.
let plan = synthetic_floorplan(7);
let inventory = Inventory::homogeneous("esp32-s3", 20.0, 4);
let objectives = vec![synthetic_objective(&plan)];
let params = PlacementParams::default_synthetic();
plan.validate().unwrap();
inventory.validate().unwrap();
let recommended = optimize(&plan, &inventory, &objectives, ¶ms);
assert_eq!(recommended.evidence_level, EvidenceLevel::L0); // SYNTHETIC
// The greedy score trace never decreases.
for w in recommended.score_trace.windows(2) {
assert!(w[1] + 1e-9 >= w[0]);
}
其中 Inventory::homogeneous("esp32-s3", 20.0, 4) 表示 4 台 ESP32-S3 类型无线电、每台 20 dBm 模型化发射功率;synthetic_floorplan(seed) 生成 5 m × 4 m 空间 + 一面衰减 6 dB 的内墙 + 中央 core 区与右上角 corner 区的确定性夹具场景。
小结
ADR-308 把"传感器放哪"从一个凭经验的问题转成一个可计算、可校验的问题:读取规范场景与硬件清单、消费 RF 孪生仿真、输出带不确定性的推荐布点,并在安装后以预测-实测比对闭环。RuView 当前仓库中的 ruview-placement crate 已落地这一设计的可运行骨架,严格贯彻 SYNTHETIC/L0 证据纪律与"信息不足是一等公民"的原则。若读者希望继续追溯,可依次深入 ADR-306(canonical spatial ontology)、ADR-315(digital RF twin) 与其对应 crate ruview-twin,以及 ADR-318(capability certificates)。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0631
MiniCPM5-2BMiniCPM5-2B 是一款面向端侧、本地部署和资源受限场景的 2B 稠密 Transformer,能够达到同尺寸开源模型 SOTA 水平。Markdown00
video-shotcraftAI宣传片skill,使用 Remotion 制作电影级产品视频:提供106 张镜头配方卡和可复用的视频魔板。适用于 Claude Code 与 Codex以及所有其他智能体Markdown00
HivisionIDPhotos⚡️HivisionIDPhotos: a lightweight and efficient AI ID photos tools. 一个轻量级的AI证件照制作算法。Python09
DragonOSDragonOS is an operating system developed from scratch using Rust, with Linux compatibility. It is designed for **Serverless** scenarios. 使用Rust从0自研内核,具有Linux兼容性的操作系统,面向云计算Serverless场景而设计。Rust00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00