RuView Sensor HAL(ADR-320):用一套 `SensorHal` trait 与统一 `Observation` 类型抽象全部感知硬件
导读
本文围绕 ADR-320-sensor-hal.md(ADR-300 感知基座程序的第 20 号原语、phase-2 集成原语)展开,讲解 RuView 如何通过一个 SensorHal trait 和唯一的 Observation 类型,把 {CSI、802.11bf、BLE、UWB、mmWave、声学、相机、激光雷达、IMU、自定义} 等任意传感硬件收拢进同一条摄入通道,使下游世界模型永远只看到携带来源与证据标注的观测,而不再面对某一模态专有的帧格式。读完本文,你将理解该 HAL 的 trait 契约、Modality/SensorDescriptor/HalObservation 等核心类型的设计逻辑、边界校验与“诚实证据”纪律,以及它如何为 ADR-311 的真实传感器融合提供可实现的前提,并能对照 ruview-hal crate 的源码与测试亲自验证。
背景:为什么需要一层“传感器 HAL”
RuView 的战略天花板长期由它与 WiFi CSI 的耦合程度决定。每一个新模态进入系统,历史上都会变成一条定制摄入链路:自己的帧形状、自己的一套 provenance 处理、自己在管线中的专属位置。这样的路线使 RuView 停留在“一个 WiFi-DensePose 项目”的范畴;而让任意传感硬件——{CSI, 802.11bf, BLE, UWB, mmWave, acoustic, camera, lidar, IMU, custom}——经由同一个抽象进入、变成同一个 Observation 并喂给同一个世界模型,才会让它演化为“开放的空间智能操作系统层”。二者分野的判据就是:换一个传感品类是否意味着重写一条管线。
必须强调的是,这是一次统一(unification),而不是绿地重写。现有的适配器必须复用而非重建:
- ADR-279 的原生 RF 帧契约(
RfFrameV2)已经统一了 ESP32、Intel、Atheros、PicoScenes、Realtek 雷达以及 320 MHz 802.11bk 生产者,全部收敛进一个共享隐空间——“轻量、按设备定制的适配器进共享隐空间,而不是进共享张量”。HAL 把这条经验推广到 RF 之外。 - 既有 CSI 适配器(ESP32/Nexmon/FeitCSI 各路径)、ADR-063 的 mmWave 融合路径、ADR-029 的多站 WiFi 路径,都是要被纳入同一 trait 的具体生产者。
- ADR-305 已经完成了
Sensor/DeviceId的认证;ADR-306 已把Sensor、Observation、Track、Event定义为一等节点类型。HAL 正是那个把异构设备变成“已认证Sensor、持续发出Observation”的 trait。
缺口的答案是一个 SensorHal trait 与一个 Observation 类型:每种模态都实现该 trait,世界模型永远不接触模态专有的帧,只看到携带 provenance、带证据标签的 Observation。
备选方案回顾(Options considered)
ADR-320 在做出决策前比较了三条路线,理解取舍有助于把握 trait 形态为何如此:
- 继续增加按模态定制的摄入路径——否决。代价是 O(模态数) 条专用管线,每条都在重复实现 provenance 与证据逻辑,每条都是一处可被攻破的“梯子”;且它让 RuView 在类别上始终是 WiFi 项目。
- 把所有模态强行塞进 ADR-274/279 的 RF 张量/帧——否决。ADR-279 的经验恰恰是:过早规范化会丢弃信息(带宽、天线结构、相位)。相机、激光雷达、IMU 根本没有有意义的
RfFrameV2投影;强行投射只是把同一个错误放大到更大的尺度。 - 定义产出单一
Observation类型的SensorHaltrait,以既有适配器为实现,喂给共享隐空间与 ADR-306 本体——采纳。
决策:一条 trait + 一个 Observation
决策本身很克制:引入 SensorHal trait 与唯一的 Observation 类型;每种传感模态都是该 trait 的一个实现;世界模型只消费 Observation。
SensorHal trait:描述能力、保留原生帧、提升为观测
一个 SensorHal 承担三件事:
- 能力描述:说明设备能感知哪些现象,复用 ADR-305/ADR-141 的能力证明(
CapabilityAttestation)思路; - 保留原生帧:设备原生的帧保持原生形态,不做规范化(遵循 ADR-279 的共享隐空间教训——过早规范化会丢弃带宽、天线结构、相位等信息);
- 提供“提升”方法:把一个原生帧提升(lift)成一个
Observation。
在 ruview-hal crate 中,该契约被编码为一个小而精确的 trait(adapter.rs):
pub trait SensorHal {
/// 该适配器消费的、模态专有的原生采样类型。
/// 保持原生(不规范化),遵循 ADR-279 共享隐空间经验。
type Raw;
/// 用规范术语描述这台设备。
fn describe(&self) -> SensorDescriptor;
/// 将一个原生原始采样规范化为一个 HalObservation。
///
/// 不可失败(infallible):畸形/越界输入产生 UNKNOWN 标记的、
/// degraded 观测,而不是 panic 或返回 Err。
fn normalize(&self, raw: Self::Raw, ctx: &NormalizeCtx) -> HalObservation;
}
两个方法名值得注意:
describe()产出的是规范设备描述SensorDescriptor(见下文),把设备“是什么、能感知什么、原生采样长什么样”沉淀成可序列化、可往返的元数据;normalize()是硬件/FFI 边界校验点:它必须不可失败——畸形或越界的原始输入一律产出带UNKNOWN标记的 degraded 观测,绝不 panic、绝不返回Err(这是 ADR-300 四条“不可谈判规则”中的第 1 条,源码以//!文档写死在 lib.rs)。
normalize 需要调用方注入一个 NormalizeCtx,它携带 observation_id、观测所在的 Container(空间/区域,来自 ADR-306 本体图)以及 at_unix_ms 捕获时间戳。HAL 从不自己铸造 id、也从不读墙钟——时间由调用方注入,从而保证适配器确定性与可复现测试(adapter.rs)。
SensorDescriptor(descriptor.rs)把设备绑定到其本体 SensorId,并给出 modality(现象类别)、capabilities(能力标签)与 sampling(原生采样/单位元数据)。它以 serde 无损往返,供 ADR-316 的集群控制器统一枚举异构硬件:
pub struct SensorDescriptor {
/// 该设备被认证为本体中的传感器身份(ADR-305)。
pub sensor_id: SensorId,
/// 设备感知的现象类别。
pub modality: Modality,
/// 能力标签——设备宣称能观测的现象。
pub capabilities: Vec<CapabilityTag>,
/// 原生帧的采样 / 单位元数据。
pub sampling: SamplingSpec,
}
SamplingSpec(descriptor.rs)记录 sample_rate_hz(None 是一等的 UNKNOWN——事件驱动或未指定采样率的源不是错误)、原生单位标签(如 "csi-complex"、"m/s^2")与维度。注意它是描述性而非规定性:只记录设备原生如何产出采样,管线无需理解原始帧本身。
Modality:现象类别,管线区分“如何被感知”的唯一位置
Modality 枚举(modality.rs)是“世界是如何被感知的”在管线中的唯一区分点——所有模态流过同一个 SensorHal trait、进同一个规范 Observation,因此世界模型从不在模态专有的帧形状上分支:
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum Modality {
Csi, // WiFi CSI(ESP32/Nexmon/FeitCSI,经 ADR-279)
Ieee80211bf, // IEEE 802.11bf 原生感知(ADR-310,phase 2)
Ble, // BLE 测距 / RSSI
Uwb, // 超宽带测距
Mmwave, // 毫米波雷达(ADR-063)
Acoustic, // 声学 / 超声感知
Camera, // 光学相机
Lidar, // 激光雷达点云
Imu, // 惯性测量单元(加速度计 + 陀螺仪)
Custom(String), // 开放扩展点:携带校验后自由标签的模态
}
- 封闭变体覆盖 ADR-320 点名的模态;
Custom(String)是未枚举模态的开放扩展点,须经Modality::custom()/validate()在边界做标签校验(非空、长度受限、不含控制字符,上限 MAX_LABEL_LEN = 128)。 is_rf()(modality.rs)标出 RF 模态(Csi/Ieee80211bf/Ble/Uwb/Mmwave),它们整体复用 ADR-279 的逐设备隐空间适配器;HAL 只在其上补充非 RF 与测距模态。
HalObservation 与 Observation:承载 provenance 与证据等级的统一事实
HAL 边界上流动的统一事实类型是 HalObservation(observation.rs):
pub struct HalObservation {
/// 本次测量经由的模态。
pub modality: Modality,
/// 逐观测的不确定性(可能为 UNKNOWN)。
pub uncertainty: Uncertainty,
/// 该 HAL 采样映射到的规范本体观测。
pub observation: Observation,
}
关键设计是:内层的 observation 直接复用 ADR-306 本体的 Observation,而不是再造一个每 crate 一形的结构(ADR-300 规则 3)。本体 Observation(entity.rs)已经强制携带:id、产生它的 sensor: SensorId(ADR-305 认证身份)、located_in: Container、注入的 at_unix_ms、恰好一个 EvidenceLevel 以及强制的 SemanticProvenance。HAL 的 HalObservation 只是在其上叠加了 modality 与逐观测 uncertainty 两块边界上下文,并通过委托访问器暴露 evidence_level()、provenance()、sensor(),绝不复制或允许它们分叉(observation.rs)。
一张由相机导出的观测与一张由 CSI 导出的观测是同一类型、不同 provenance——而相机观测永远不会把 WiFi 输出抬升到相机等级;每张观测各自携带自己的诚实证据等级(CLAUDE.md 纪律:绝不把 WiFi 感知包装成相机级)。
EvidenceLevel:ADR-282 证据阶梯 L0–L5
每个事实都携带恰好一个证据等级。EvidenceLevel(provenance.rs)在 ADR-306 中即定义,并沿用 ADR-282 的 L0–L5 阶梯,排序有意义(L0 < L1 < … < L5):
| 等级 | 含义 |
|---|---|
L0 |
声明/假定,无信号证据 |
L1 |
启发式/合成 |
L2 |
单一表面的信号证据 |
L3 |
跨表面相互印证 |
L4 |
已校准且经留出集验证 |
L5 |
见证/认证(ADR-319) |
每个叶实体还携带强制的 SemanticProvenance(provenance.rs):evidence(内容寻址句柄)、model_version(ADR-136)、calibration_version(ADR-135/ADR-301)、privacy_decision(ADR-141),且每项都是受限字符串句柄而非内嵌数据。观测跨任何表面边界都不能丢失其谱系——没有投影能静默升级或丢弃证据等级。
Uncertainty 与 Confidence:UNKNOWN 是值,不是错误
HAL 边界贡献的逐观测不确定性采用一等 UNKNOWN 建模(observation.rs):
Confidence:Unknown或闭区间[0.0, 1.0]内的Known(f64);Known构造时自动 clamp,NaN/inf 折叠为Unknown,绝不传播“中毒”数值;Uncertainty:携带confidence与degraded布尔。degraded区分两类 UNKNOWN:degraded = false的合法不可量化源(如不量化置信度的事件驱动传感器)vsdegraded = true的畸形/越界原始输入产生的尽力而为占位。两者都不是错误,但下游融合可以据此加权或丢弃。
这正是 ADR-300 规则 1 的落地:UNKNOWN 是一等值、绝不是错误或 panic。
实现如何落地:合成参考适配器(CSI + IMU)
ADR-320 的验证要求至少两个模态的 fixture SensorHal。crate 里随附两个确定性合成参考适配器(adapter.rs):
SyntheticCsiAdapter(RF 代表,Raw = CsiSample:逐子载波幅值+相位)——形如上,它“演示”一个 CSI 生产者如何被提升为规范观测,但不声称任何真实设备;SyntheticImuAdapter(非 RF 第二模态,Raw = ImuSample:3 轴加速度 m/s² + 3 轴角速度 rad/s)——证明一个完全不同的现象类别也能以同一规范观测进入系统。
两者一律标注 SYNTHETIC / L0:证明的是抽象本身,而不是一台已部署设备(CLAUDE.md 纪律)。关键实现点:
- 边界校验(adapter.rs):空帧、幅相长度不匹配、超 MAX_CSI_TAPS = 4096 的越界输入、NaN 等非有限值 → 一律降级为 UNKNOWN,不 panic、不捏造置信值;
- 确定性置信度:CSI 取幅值均值经饱和映射
mean/(mean+1)生成Known置信;IMU 以加速度幅值与 1g 的接近度生成置信。无随机数、无时钟; - 诚实证据锁定(adapter.rs):合成观测的 provenance
calibration_version被钉为常量SYNTHETIC_CALIBRATION = "synthetic",is_synthetic()恒真,证据等级恒为 L0,永远无法别名成实测/已校准观测(ADR-279 不变量 6)——它到不了>= L2的印证/校准等级。
下面给出 SyntheticCsiAdapter::normalize 的完整实现,读者可据此理解“原生帧 → 规范观测”的典型样板:
fn normalize(&self, raw: Self::Raw, ctx: &NormalizeCtx) -> HalObservation {
let observation =
synthetic_observation(self.sensor_id.clone(), ctx, "synthetic-csi-adapter@0");
// 边界校验:空、不匹配、越界或非有限输入降级为 UNKNOWN,
// 而不是 panic 或捏造一个高置信值。
let malformed = raw.amplitudes.is_empty()
|| raw.amplitudes.len() != raw.phases.len()
|| raw.amplitudes.len() > MAX_CSI_TAPS
|| raw.amplitudes.iter().any(|v| !v.is_finite())
|| raw.phases.iter().any(|v| !v.is_finite());
let uncertainty = if malformed {
Uncertainty::degraded()
} else {
let sum: f64 = raw.amplitudes.iter().map(|&v| f64::from(v).abs()).sum();
let mean = sum / raw.amplitudes.len() as f64;
Uncertainty::known(mean / (mean + 1.0))
};
HalObservation { modality: Modality::Csi, uncertainty, observation }
}
两个合成适配器的 describe()(adapter.rs)则各自产出能力标签(CSI:"amplitude"/"phase";IMU:"accel"/"gyro")与采样规格(CSI 100 Hz csi-complex 52 维;IMU 200 Hz m/s^2|rad/s 6 维)。
边界安全:把“不受信任的硬件输入”关在 trait 边界外
ADR-320 把 trait 明确为“不受信任的硬件输入被校验”的边界(CLAUDE.md:在每个硬件/FFI 边界校验;默认最小权限)。从源码可以看到这条纪律如何落到类型上:
CapabilityTag与Modality::Custom载荷都来自可能不受信任的硬件描述符,它们在构造时用同一套标签规则校验(label.rs):非空、字节长度 ≤MAX_LABEL_LEN(128)、不含 ASCII 控制字符;错误被建模为LabelError::{Empty, TooLong, ControlChar};- 设备被认证为 ADR-305 的
Sensor(描述符中的sensor_id)之后,它的观测才被信任;未经认证设备的观测在 trait 边界被拒。
这一点同样写进了 crate 文档的“四条不可谈判规则”(lib.rs):UNKNOWN 一等值永不 panic;证书做密码学绑定(虽在 HAL 范围之外,但设备须先认证);下游只有一种规范语义;诚实证据不互相抬级。
从 HAL 到世界模型:喂给 ADR-311 融合
SensorHal 产出的 HalObservation 是 ADR-306 本体 Observation 的包装,因而直接落入本体图(挂在对应 Sensor 节点下)——本体是唯一表示,HAL 是它的摄入漏斗。任意模态集合的观测流入 ADR-311 融合,后者把它们解析成一个概率世界状态。
ruview-fusion crate(lib.rs)把 ADR-311 的不变量说得很清楚:“许多观测解析为一个世界状态,而不是多条 feed 进一个可视化”——一个并排展示 WiFi 层、mmWave 层、BLE 层的仪表盘不是融合。它摄入一组 PresenceObservation(规范 HalObservation + 逐源占用 Claim),可能跨模态、可能冲突,最终产出单一的 WorldState。
而 HAL 保证了融合永远看不到模态专有的帧——只有 provenance 与证据统一、且证据等级取所有贡献观测的最小值(绝不抬到最弱必要输入之上)的 Observation。这使 ADR-311 “many observations → one world state” 不变量在异构硬件上可落地:见 fusion 的测试,Csi + Mmwave、Csi + Ble、Csi + Mmwave(L3) 等跨模态集合都以统一 HalObservation 参与融合并保留逐源 provenance。
类别与诚实纪律:是统一摄入边界,不是“检测器”
- 本 ADR 把 RuView 的类别从 WiFi-DensePose 管线切换为开放的空间智能操作系统层,但不做任何精度声明:HAL 交付的是统一摄入边界,不是检测器。新接入传感器的任何能力仍须由 ADR-302 门控、经 ADR-318 针对其具体环境认证——接上一台相机本身并不能授予一条已验证的能力。
- 某一模态的硬件支持在真实芯片上以捕获证据被演示之前,只能记为 CLAIMED;一次通过的 trait 测试证明的是抽象,而非一台已部署的设备。成功构建或模拟器运行不算硬件证据(CLAUDE.md)。这正是 crate 内合成参考适配器坚持 SYNTHETIC/L0 的原因。
将既有生产者映射到 trait
crate 文档(lib.rs)用一张表记录“现有生产者收编到 trait 时实现哪个 SensorHal”(本 crate 只定义 trait 与合成参考实现,不重写既有生产者;RF 模态整体复用 ADR-279 逐设备隐空间适配器):
| 既有生产者 | 来源 ADR | Modality |
原生帧 | 说明 |
|---|---|---|---|---|
| ESP32-S3/C6 CSI 节点 | ADR-279 / firmware | Csi |
RfFrameV2(子载波复数) |
复用 ADR-279 原生帧→共享隐空间适配器;HAL 只把隐空间提升为观测 |
| Nexmon CSI | ADR-279 | Csi |
RfFrameV2 |
逐设备适配器进共享隐空间;同 trait,不同原生布局 |
| FeitCSI / Intel / Atheros / Realtek | ADR-279 | Csi |
RfFrameV2 |
同一共享隐空间路径;带宽/天线结构保持原生,不规范化 |
| 802.11bf 感知 | ADR-310(phase 2) | Ieee80211bf |
原生 11bf 测量帧 | 随其落地纳入同一 trait |
| mmWave 雷达 | ADR-063 | Mmwave |
距离-多普勒 / 点云帧 | ADR-063 融合生产者成为 SensorHal 实现 |
| 多站 WiFi | ADR-029 | Csi |
多链路 RfFrameV2 集合 |
多链路、一个认证 Sensor、一个观测 |
非 RF 模态(相机、激光雷达、声学)以与 RF 相同的 provenance 与隐私纪律进入受治理平面:相机不是无隐私捷径——它继承 ADR-277 治理并携带自己的诚实证据等级。
影响(Consequences)
- 新传感硬件落地 = 一个
SensorHal实现,而非一条定制管线;翻译矩阵保持 O(模态数),呼应 ADR-306 如何收缩表面矩阵; - ADR-300 验收条款“识别硬件(identify the hardware)”变得可实现:新传感器类型以 HAL 描述 → 认证为 ADR-305
Sensor→ ADR-301 校准 → ADR-302 门控 → ADR-318 认证,走完同一条 phase-1 主干,闭合最后一个未决条款; - 新增的是一条 trait 边界与一个
Observation类型;既有 RF 适配器被重新表达为实现而非重写,保留 ADR-279 原生帧/共享隐空间设计; - 非 RF 模态(相机、激光雷达、声学)以与 RF 相同的 provenance 与隐私纪律进入受治理平面,相机继承 ADR-277 治理并携带自身证据等级;
- 作为 phase-2 Proposed ADR,trait 形态在 ADR-311 融合与 ADR-310 802.11bf 落地时可能修订——这是分阶段程序的预期。
验证方式(Validation)
设计期验证落在 ruview-hal crate 的 cargo test 上,源码测试与 ADR-320 验证条款一一对应(lib.rs):
- 描述符无损往返:
descriptor_round_trip—— CSI/IMU 及自定义模态(thermal-array)的SensorDescriptorserde 往返相等且validate()通过; - 合成采样归一化:
reference_adapter_normalizes_synthetic_sample—— CSI 与 IMU 各自产出统一HalObservation,携带 sensor id、container、时间戳、恰好一个证据等级(L0)、is_synthetic()为真、degraded为假;IMU 观测证据等级严格小于 L2; - 畸形输入 → UNKNOWN 而非 panic:
degraded_input_yields_unknown_not_panic—— 空帧、长度不匹配、NaN、超过MAX_CSI_TAPS的越界帧、含f32::INFINITY的 IMU 样本,全部产出is_unknown() && degraded的良构观测且不可别名到实测; - 标签边界校验:
label_validation_at_boundary—— 空标签、含换行控制字符、超长标签各自得到精确的LabelError变体;Modality::Csi.is_rf()为真、Modality::Imu.is_rf()为假; - serde 往返与确定性:
hal_observation_serde_round_trip(含 UNKNOWN 观测,序列化稳定输出"csi"/"unknown"/"L0")、normalization_is_deterministic(同输入+ctx 恒等输出)、synthetic_cannot_alias_to_measured(合成校准句柄 + L0 把它钉在印证之下)、confidence_is_bounded(clamp 与 NaN 折叠)。
跨 ADR 验证(设计期描述):Observation 与 ADR-306 本体 Observation 节点往返无 provenance 丢失;来自不同模态的一组观测可被 ADR-311 融合 fixture 接受。任何模态的硬件支持在真实芯片上捕获启动/运行日志并发出 Observation 之前只能停留在 CLAIMED——成功构建或模拟器运行不是硬件证据。
如何继续深入与验证
- 阅读 ADR 原文:ADR-320-sensor-hal.md(含 Context/Decision/Consequences/Validation 全文);
- 通读实现 crate:ruview-hal 的
lib.rs、adapter.rs、modality.rs、observation.rs、descriptor.rs、label.rs,并对照 crate 清单 v2/crates/ruview-hal/Cargo.toml(仅依赖ruview-ontology,dev 依赖serde_json); - 对照本体契约 ruview-ontology(
Observation/Sensor/EvidenceLevel/SemanticProvenance)与消费方 ruview-fusion(ADR-311 融合引擎及其跨模态测试); - 关联 ADR 上下文:ADR-300(感知基座程序)、ADR-279(RF 帧契约)、ADR-305、ADR-306、ADR-310、ADR-311、ADR-318。
实际运行验证按仓库根目录 README.md、v2 workspace Cargo.toml 的说明构建并执行 cargo test(HAL 为纯抽象 crate:无 I/O、无 async、无推理,其测试均为确定性、同步、可离线运行)。需要留意的前提与限制:ADR-320 定位为 phase-2 Proposed 设计,代码中的实现是其“initial implementation(ADR-300 phase 2)”形态,trait 形状在 ADR-311 融合与 ADR-310 802.11bf 实际落地后可能继续修订;实现中观测事实以认证后的 SensorId(ADR-305 本体身份)承载,而非直接携带裸 DeviceId 字符串——这是原 ADR 文本与当前实现演化之间的一个差异点,阅读时宜以 crate 源码为准。
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