RuView 学习型多人计数器 Cog:基于 Candle 的 WiFi CSI 人数估计设计与落地(ADR-103 深度解读)
本篇文章以 RuView 仓库中的架构决策记录 ADR-103(Learned Multi-Person Counter) 为骨架,结合其在 v2/crates/cog-person-count 中的真实 Rust/Candle 实现、sensing-server 中现存的启发式计数函数以及 docs/benchmarks/person-count-cog.md 基准日志,系统讲解:为什么用 WiFi CSI 做人数估计要从"启发式规则"走向"学习型网络",ADR-103 设计了怎样的编码器-计数头-置信度头架构、如何做多节点融合,以及它在仓库中目前真实落地到什么程度(含诚实的精度数据与局限)。读完你可以在仓库中直接定位到 Cog 的完整源码、配置 Schema、测试用例与训练脚本,并理解把它接入现有传感服务时的回退路径。
1. 问题起点:#499 幽灵骨架与 PR #491 的启发式止血
ADR-103 的动机来自两个历史问题:
- Issue #499:3 节点 ESP32-S3 部署中出现"双重骨架"(double skeletons)——真实只有 1 人时 UI 却渲染出 2 个以上的人影。
- PR #491:用
RollingP95流式自校准估计器替换掉硬编码的分母(variance/300、motion_band_power/250、spectral_power/500),并把多节点dedup_factor暴露成运行时旋钮,从而缓解了#499。
在源码中能看到这段历史在传感服务器中的残留:score_to_person_count(smoothed_score, prev_count) 目前仍位于 v2/crates/wifi-densepose-sensing-server/src/csi.rs#L983,它根据 prev_count 分成三档(0|1、2、其它),再对 smoothed_score 做阈值切分,输出 1~3 之间的整数:
pub fn score_to_person_count(smoothed_score: f64, prev_count: usize) -> usize {
match prev_count {
0 | 1 => {
if smoothed_score > 0.85 { 3 }
else if smoothed_score > 0.70 { 2 }
else { 1 }
}
2 => {
if smoothed_score > 0.92 { 3 }
else if smoothed_score < 0.55 { 1 }
else { 2 }
}
_ => {
if smoothed_score < 0.55 { 1 }
else if smoothed_score < 0.78 { 2 }
else { 3 }
}
}
}
从 field_bridge.rs 还能看到另一个佐证:单条 ESP32 链路能可靠解析的人数上限被定为 MAX_SINGLE_LINK_OCCUPANCY = 3,超出即视为噪声。也就是说,ADR-103 之前的"槽位启发式"本质是手工标定阈值 + 运动状态平滑 + 单链路硬上限的组合。
ADR-103 的判断是:这能给我们一个"能自适应房间的稳定启发式",但到不了已发表的 WiFi CSI 计数论文的水平。下表对比了学术界与 RuView 当时的差距(表格内容直接来自 ADR-103,注意硬件条件差异是核心变量):
| 系统 | 设置 | 报告精度 | 方法 |
|---|---|---|---|
| WiCount(CMU, 2017) | Intel 5300 3×3 MIMO | 89%(±1 内) | CSI 幅度上的 LSTM |
| DeepCount(2018) | Atheros 3×3 | 92%(±1 内),5 房间 | CNN + 跨环境迁移 |
| CrossCount(2019) | Atheros,6 房间 | 84% 跨房间(±1 内) | 域对抗 CNN |
| HeadCount(2021) | Intel 5300 | MAE < 1 人,5 环境 | 多流 CSI + 注意力 |
| RuView today(PR #491) | ESP32-S3 1×1 SISO | 已校准启发式;未对 ground truth 测量 | RollingP95 + dedup_factor |
文献用的是 3×3 MIMO 科研网卡,RuView 用的是 1×1 SISO ESP32-S3 节点,因此论文数字不能直接照搬;但 ADR-103 认为两者之间的架构性差距足够大——在本仓库硬件上做学习型计数,应能明显优于槽位启发式,而且训练所需的基础设施已经具备(Candle + RTX 5080 曾在 2.1 秒内训出 pose_v1.safetensors,参见 docs/benchmarks/pose-estimation-cog.md)。
2. 五大已有但尚未组合成计数器的原语
ADR-103 的核心论证之一是:做学习型计数器不需要从零造轮子,仓库里已经有五块积木,缺的只是把它们"组合成一个计数器"的编排层:
- 成对的 CSI + 相机标签数据集 —— 采集脚本 scripts/collect-ground-truth.py 与对齐脚本
scripts/align-ground-truth.js。从采集源码看,collect-ground-truth.py在 L308-L332 用n_persons = len(result.pose_landmarks)从 MediaPipe 结果取人数并写入每帧 JSON——也就是说计数标签在既有姿态数据管线里是免费附带的,无需重新开展数据采集。 - Stoer-Wagner min-cut,用于划分"人可分离的子载波分组" ——
ruvector-mincut已是 workspace 依赖,Candle 训练器曾报告Min-cut value: 0.1538 — partition: [55, 1] subcarriers。 - 对比预训练的 CSI 编码器 ——
ruvnet/wifi-densepose-pretrained(HF),12.2M 训练步、60K 帧、128 维 embedding、在 M4 Pro 上约 165k emb/s。 - Candle 训练管线 —— 一天前刚验证:RTX 5080 上 400 epochs 只用 2.1 秒,ONNX 导出逐位一致,签名 Cog 二进制已上 GCS。
- 多节点融合级 ——
multistatic_bridge.rs已用可调dedup_factor聚合各节点特征向量,新模型的输出可以做成现有 dedup 除数的即插即用替代品。
3. 决策:把人数估计做成一个 Cog(cog-person-count)
ADR-103 的决策是:训练并交付一个小型学习型多人计数器,作为新的 Cognitum Cog(cog-person-count),打包路径复刻 cog-pose-estimation(ADR-101),并接入 sensing-server 现存的计数调用点 csi.rs::score_to_person_count,作为槽位启发式的即插即用替代。模型未安装时按 ADR-100 的 stub-fallback 模式回退到启发式。
在仓库中该 crate 已经真实落地,路径为 v2/crates/cog-person-count,模块划分与 ADR-103 的仓库布局一节基本一致:
src/main.rs—— Cog 入口,实现 ADR-100 的四动词运行时契约:version | manifest | health | run;src/inference.rs—— Candle 前向推理(编码器 + 计数头 + 置信度头);src/fusion.rs—— 置信度加权 log-sum 融合 +(v0.2.0 预留的)Stoer-Wagner min-cut 上限裁剪;src/publisher.rs—— 输出{count, confidence, count_p95_low, count_p95_high}结构化 JSON 事件;src/manifest.rs、src/runtime.rs—— 内嵌签名清单与长驻轮询循环;cog/manifest.template.json、cog/config.schema.json、cog/README.md;tests/smoke.rs+fusion.rs内嵌单元测试、benches/infer_bench.rs。
Cargo.toml(见 v2/crates/cog-person-count/Cargo.toml)确认其依赖栈与 pose cog 同源:candle-core/candle-nn 0.9(默认 CPU,cuda feature 可选开启)、safetensors 0.4、ureq(轮询用)、clap、serde_json;dev 依赖含 criterion(ADR-163 稳态推理时延基准)。发布描述词也写明了诚实边界:"8-class count head 随包发布,但超出训练范围的人数会被标记 low_confidence"。
3.1 架构(v0.1.0,ASCII 原图)
┌──────────────────────────────┐
per-node CSI window │ Encoder (frozen first 50 ep) │
[56 sub × 20 frames] ─► init from ruvnet/wifi- │
│ densepose-pretrained │
│ → 128-dim embedding │
└──────────────┬───────────────┘
│
┌────────────────┴────────────────┐
▼ ▼
┌────────────────────┐ ┌────────────────────────┐
│ Count head │ │ Confidence head │
│ Linear(128→64) │ │ Linear(128→32) │
│ ReLU │ │ ReLU │
│ Linear(64→8) │ │ Linear(32→1) + sigmoid│
│ → softmax over │ │ → calibrated p(correct)│
│ {0..7} persons │ └────────────────────────┘
└────────┬───────────┘
│ (per-node prediction)
│
N nodes' per-node │
counts + confidences ▼
┌─────────────────────────────────────┐
│ Multi-node fusion (Stoer-Wagner) │
│ • build graph: nodes × subcarrier │
│ feature similarity │
│ • min-cut → distinct-person bound │
│ • combine with per-node count head │
│ via confidence-weighted vote │
└──────────────────┬──────────────────┘
▼
{ count: int,
confidence: float [0,1],
count_p95_low: int,
count_p95_high: int,
per_node_breakdown: [...] }
3.2 源码中的真实网络结构
inference.rs 的模块注释给出了与 ADR-103 架构一一对应的、已实现的网络形状:
Conv1d(56 -> 64, k=3, dilation=1, padding=1)
Conv1d(64 -> 128, k=3, dilation=2, padding=2)
Conv1d(128 -> 128, k=3, dilation=4, padding=4)
mean over time -> [128] ← 共享编码器
├── Linear(128->64) -> ReLU -> Linear(64->8) → softmax over {0..7}(计数头)
└── Linear(128->32) -> ReLU -> Linear(32->1) → sigmoid → confidence(置信度头)
常量定义同样可定位:INPUT_SUBCARRIERS = 56、INPUT_TIMESTEPS = 20、COUNT_CLASSES = 8,即输入是 [56, 20] 的 CSI 窗口——与 pose cog 完全一致的张量形状。CountNet::new 用 vb.pp("enc") / vb.pp("count_head") / vb.pp("conf_head") 三个命名空间构建参数,保证与 PyTorch 训练端(scripts/train-count.py)导出的 safetensors 布局逐位兼容;infer() 先校验输入长度必须等于 56×20,再 reshape 为 (1, 56, 20) 做一次前向,得到 probs(softmax 后、8 类分布)与 conf(sigmoid 后的置信度标量)。
ADR-103 特别强调的五个设计点,在源码中都能验证:
- 前 50 epochs 冻结编码器:沿用 HF 存在模型已有的 128 维 embedding,只训上层头部,属于标准迁移学习套路,避免重学对比几何。
- 对
{0..7}做分类而非回归:人数是整数,分类输出天然给出每个计数的校准概率,置信度头因此才有意义。 - Stoer-Wagner 只在融合期使用:用 min-cut 对单节点计数做上界约束(一个节点能看到的不同人数不会超过子载波图的 min-cut 数),再做置信度加权投票。
- 输出四元组而非单个整数:
{count, confidence, count_p95_low, count_p95_high},下游 Cog/仪表盘/告警可按自己的置信度阈值决策——这正是闭环 #499 体验的关键:模型不确定时,仪表盘渲染一个带 "?" 徽标的火柴人,而不是两个"幽灵"。 - 不换硬件:仍是现有 ESP32-S3 1×1 SISO,增益来自学习特征 + 多节点融合,而非更大的天线阵列。
4. 训练路径:与 pose cog 同一管线,标签白拿
ADR-103 给出的训练对照表直接复用"昨天刚验证"的 pose 管线,差异只在输入初始化、输出头与损失函数:
| Pose cog(当时) | Count cog(本文档) | |
|---|---|---|
| 输入 | [56, 20] CSI 窗口 |
[56, 20] CSI 窗口(完全一致) |
| 编码器初始化 | 随机(HF 架构不匹配) | 从 HF 存在模型初始化(架构兼容——同一编码器 Φ) |
| 输出头 | Linear(128→256→34) 关键点 |
Linear(128→64→8) 计数类 + Linear(128→32→1) 置信度 |
| 损失 | 置信度加权 SmoothL1 | 分类交叉熵 + Brier 分数不确定性校准 |
| 标签 | MediaPipe 关键点 | 相机人数(MediaPipe pose_landmarks 长度) |
| 数据 | 1,077 对(P7) | 同一来源、同一脚本——collect-ground-truth.py 每帧已记录 n_persons |
关键推论是:计数标签是免费的——collect-ground-truth.py 已经在每帧写入 "n_persons"(源码 L332),align-ground-truth.js 在滑窗时保留该字段,因此无需新的数据采集战役即可用产出 pose_v1 的同一批 1,077 个样本开训。ADR-103 指出大规模数据路径由 Issue #645 追踪(~30K 样本),训练的重现命令(来自 docs/benchmarks/person-count-cog.md):
python3 scripts/train-count.py --paired data/paired/wiflow-p7-1779210883.paired.jsonl \
--k-fold 5 --epochs 100 --out-results kfold_results.json
python3 scripts/train-count.py --paired data/paired/wiflow-p7-1779210883.paired.jsonl \
--v2 --epochs 400 \
--out-safetensors count_v1.safetensors --out-onnx count_v1.onnx \
--out-results count_train_results.json
其中 --paired 指向 P7 会话的成对数据(1,077 样本,标签分布 {0: 533, 1: 544});训练端按每子载波 Z-score 归一化。v0.0.1 的损失公式为 cross_entropy(count) + 0.3·BCE(conf) + 0.1·Brier(conf),优化器 AdamW(lr 1e-3,余弦热重启 T_0=50),RTX 5080 上 400 epochs 仅 5.6 秒。
5. 多节点融合:置信度加权 log-sum 与 min-cut 上界
单节点计数头+置信度头给出 {0..7} 上的分类分布;有 N 个节点就有 N 个分布 + N 个置信度标量。ADR-103 给出两条融合路径:
- 置信度加权 log-sum(贝叶斯乘积):
log p_fused(k) = Σ_n c_n · log p_n(k)。无额外参数,来自最优专家组合文献,v0.1.0 交付这条。 - Stoer-Wagner 上界:以节点间两两子载波特征相似度为边建图,min-cut 大小 = 节点 mesh 能分辨的不同人数硬上界;把融合分布裁剪到
{0..min-cut}支撑集后重归一化。这正是ruvector-mincut加入 workspace 的原因,v0.2.0 增加该裁剪。
fusion.rs 提供了两条路径的完整实现:
fuse_confidence_weighted(preds):空输入返回"1 人 + 0 置信度"的 stub 默认(与全局 stub 一致);单输入原样透传;置信度统一取max(EPS_CONF=1e-3)下界防止 log(0),概率统一取max(1e-9)下界;log-sum 后做"减最大值再指数化重归一化"的数值稳定处理;融合后的confidence取各节点最大值而非平均值——"至少有一个高置信度观测"比"一堆低置信度平均"更有价值。fuse_with_mincut_clip(preds, max_distinct):v0.2.0 钩子已就位(ruvector_mincut实装留待后续 PR,函数签名先固定以免运行时 API 断裂)。裁剪{max+1..7}的质量并重归一化;若全部质量都在上界之上(退化情形),则把质量放到上界类上而非输出零分布。
融合的数值/退化边界都有单元测试守护(fusion.rs 内嵌 tests),包括:空输入默认、单输入透传、双节点一致投票锐化峰、高置信度节点压过低置信度分歧、归一化保持、min-cut 裁剪不破归一化、P95 区间覆盖 ≥95% 质量。
5.1 v0.0.1 运行期融合的现状
需要诚实指出:当前 runtime.rs 的注释明确说明——v0.0.1 只交付单节点模式:设备上的 /api/v1/sensing/latest 端点已在返回前跨节点聚合,因此每个 Cog 内部的多节点融合推迟到各节点单独上送原始帧的 v0.2.0。运行循环每 poll_ms(默认 40ms,见 config.schema.json)用 ureq 轮询 sensing 端点,滑窗维护最近 56×20 个幅度值,engine.infer 成功后调用 publisher::person_count(tick, &pred, 1)(N=1 时融合为 no-op)。
6. 为什么学习型计数器优于槽位启发式
ADR-103 对"为何该换"给出的失败模式对照表,是理解整篇 ADR 论证价值的核心,全文继承如下:
| 槽位启发式的失败模式 | 学习型计数器的规避方式 |
|---|---|
| #499 —— 固定分母钳位导致 1 人渲染成 2+ 组 | 编码器产出固定维度 embedding;计数头对特征形状而非幅值不变 |
dedup_factor 按房间手工调优是可见的运维负担 |
计数头的 softmax 天生就是"学出来的房间归一化器" |
| 节点变多时槽位启发式的计数反而更噪 | 多节点融合在置信度上可加:每个节点要么降低不确定性、要么保持中立,绝不放大它 |
| 每帧没有不确定性信号 | 每次 emit 都带 confidence 与 count_p95_low/high |
| 新环境灾难性失败 | 按房间 LoRA 适配器(ADR-079 P9 计划)可热切换、无需重训 |
7. 验收门(Acceptance Gates)
| 门 | v0.1.0(初版) | v0.2.0(数据扩展后) |
|---|---|---|
| Day-0 部署(免校准) | 同房间测试集 ±1 内 ≥ 80% | ±1 内 ≥ 90% |
| 跨房间(held-out 环境) | ±1 内 ≥ 60% | ±1 内 ≥ 75% |
| 平均绝对误差(MAE) | ≤ 0.6 人 | ≤ 0.4 人 |
| 每帧置信度反映精度 | confidence 与 (predicted==true) 的 Spearman r ≥ 0.5 |
r ≥ 0.7 |
| Pi 5(Cog)推理时延 | 冷启动 < 5 ms/帧 | < 5 ms/帧 |
| GCS 二进制体积 | ≤ 4 MB(对齐 cog-pose-estimation) |
≤ 4 MB |
v0.1.0 刻意保持谦逊——它受限于数据采集规模(#645)。"框架本身就是交付物,精度随数据增长而到来"。
8. 仓库布局与传感服务器接线
ADR-103 规划的落地形态:
v2/crates/cog-person-count/ # NEW(本 ADR)
├── Cargo.toml
├── src/
│ ├── main.rs # cog runtime: version | manifest | health | run
│ ├── lib.rs
│ ├── inference.rs # Candle 对每节点 CSI 的前向
│ ├── fusion.rs # Stoer-Wagner 上界 + 置信度加权 log-sum
│ └── publisher.rs # 发射 {count, confidence, count_p95_low, count_p95_high}
├── cog/
│ ├── manifest.template.json
│ ├── config.schema.json
│ ├── README.md
│ └── artifacts/ # 由发布管线填充
│ ├── count_v1.safetensors
│ ├── count_v1.onnx
│ └── train_results.json
└── tests/
├── smoke.rs # 5+ 测试
└── fusion_test.rs # 多节点融合数学
外加一个小的服务器端接线改动:
v2/crates/wifi-densepose-sensing-server/src/csi.rs::score_to_person_count—— 通过/api/v1/edge/registry发现运行时后调用 Cog(与cog-pose-estimation相同),Cog 未安装时按 ADR-100 的 stub-fallback 模式回退到 PR #491 的启发式。
需要注意:仓库中的实际实现对此做了一次务实调整——cog/README.md 描述为 out-of-process 并行运行:sensing-server 继续发射自己的槽位启发式计数(回退路径,未安装本 Cog 的用户仍能得到一个数字,只是校准较差),而 cog-person-count 二进制轮询同一 /api/v1/sensing/latest 端点、对每窗口跑 count_v1、向 stdout 发射 person.count 事件,由设备的 cognitum-cog-gateway 经标准 cog 事件通道路由到仪表盘。操作者通过"装或不装"这个 Cog 来选择路径,无需重新编译 sensing-server;下游消费者订阅任一路事件流即可。
9. 事件输出格式与运行契约
9.1 每帧输出
config.schema.json 定义了运行配置的三字段:sensing_url(默认 http://127.0.0.1:3000/api/v1/sensing/latest)、model_path(必填,指向 count_v1.safetensors,相对路径基于 /var/lib/cognitum/apps/person-count/ 解析)、poll_ms(整数,10~1000,默认 40)。main.rs 中 Cmd 枚举实现了 ADR-100 的四动词契约:version、manifest、health、run --config <path>。
publisher.rs 定义 person.count 事件(每行一个 JSON,走 stdout):
{
"ts": 1779210883.444,
"level": "info",
"event": "person.count",
"fields": {
"tick": 12345,
"count": 2,
"confidence": 0.81,
"count_p95_low": 1,
"count_p95_high": 3,
"n_nodes": 3,
"probs": [0.01, 0.03, 0.81, 0.13, 0.01, 0.005, 0.003, 0.002]
}
}
health.ok 事件则上报 backend(candle-cpu / candle-cuda / stub)、synthetic_count、synthetic_confidence 与 synthetic_p95_range。
9.2 诚实裁剪(honest-clip)机制
源码 inference.rs 定义了 MAX_TRAINED_CLASS = 1:计数头有 8 个 logit,但已发布的 count_v1 权重只在类 0/1(空/有人)上受过监督——argmax 落到 2..7 属于分布外。为此:
CountPrediction::is_low_confidence()返回argmax() > MAX_TRAINED_CLASS;clamped_count()把上报计数钳到最高受训类(宁可少报也不编造多人头数),原始分布仍留在probs供诊断;publisher.rs中count用钳制值、raw_count保留原始 argmax、分布外时事件level升为warn。
下游消费端可以做三种渲染策略:高置信度时直接渲染最可能计数;不确定时渲染 [count_p95_low, count_p95_high] 区间加 "?" 徽标;OOD 时只看 clamped_count。这就是 ADR-103 所说"关闭 #499 概念循环"的落地形态:人数变成学出来的任务,而不是带运行时旋钮的启发式。
10. 安全设计:极小的攻击面
cog/README.md 的安全性一节强调:本 Cog 是纯 CSI 数据消费者而非服务器,零网络监听端口、零额外文件写入(仅 pid/output.log/error.log 由 cog-gateway 外部管理)。威胁与缓解措施包括:
| 威胁 | 缓解 |
|---|---|
| 不可信模型文件 mmap | count_v1.safetensors 经 VarBuilder::from_mmaped_safetensors 加载(文档化 unsafe 块);发布管线按 ADR-100 用 COGNITUM_OWNER_SIGNING_KEY 签名,cog-gateway 校验 Ed25519 签名与 weights_sha256 后才放置文件 |
| 损坏模型产生非有限输出 | CountPrediction::is_finite() 在 cmd_health 与 run 循环发射 person.count 前检查,非有限输出 fail-closed |
| sensing-server 拉取失败 | 发射 WARN 事件并跳过该帧——与 pose cog 相同的"fail-open-as-log"模式,不崩溃、不泄漏 fd、不卡死 pid 文件 |
| 融合除零 / log(0) | fuse_confidence_weighted 将置信度下限设为 1e-3、概率下限设为 1e-9;空输入返回 stub 默认而非传播 NaN |
| min-cut 裁剪后质量全在 cap 之上 | fuse_with_mincut_clip 重归一化存活前缀;退化情形把质量放在 cap 类而非输出零分布 |
| 经 stdout 伪造输出 | 事件严格按 ADR-100 契约一行一个 JSON;无交互提示、无 shell 转义、无 ANSI 控制序列 |
11. 性能与实测数据
cog/README.md 与 docs/benchmarks/person-count-cog.md 记录了真实测量:
- 二进制体积:Release 构建(workspace 已开启
opt-level=3、lto="fat"、codegen-units=1、strip=true)在 x86_64 上为 2.36 MB,小于 pose cog 的 4.5 MB(不传递依赖wifi-densepose-train)。 - 冷启动(Windows x86_64、candle-cpu,30 次连续
health):pose cog 76.2 ms,person-count cog 53.3 ms。 - Pi 5 真机冷启动:9.2 ms/次调用(30 次共 0.276 s),略慢于 pose cog 的 8.4 ms,因为共享编码器后双头推理(计数 softmax + 置信度 sigmoid)约 2× 工作量;仍在 ADR-103 < 5 ms 热路径预算内(一旦长驻
run循环落地、safetensors 保持 mmap 常驻)。 - 热路径:stub 后端亚毫秒/帧(8 类单次 softmax 几乎零成本);带真实权重时受三个 Conv1d 层约束,Pi 5 上预计 ≤ 2 ms。
- v0.0.1 实测精度(单一 30 分钟单人会话数据):整体 65.1%,类 1(有人)精度 0%——模型在 epoch 100 左右过拟合,"最佳"检查点实际是预测了 eval 窗口的类分布(大多数类 0)。v0.0.2(5-fold 随机 CV + 标签平滑 0.1 + 类别平衡采样 + patience 20 早停 + 温度缩放 T=0.9262):整体 62.3%、类 1 精度提升到 34.3%、MAE 0.377;5-fold CV 参考值显示架构在公平划分下类 1 能力约 57.1%。
- 诚实结论:confidence 校准是当前真实短板——v0.0.1 Spearman 0.023、v0.0.2 仅 0.013,温度缩放无法修复"对着噪声
argmax==truth指示符训练的置信度头";根本瓶颈与pose_v1相同,都是数据稀缺(#645),修复路径同样是多房间成对录制,而非更多训练技巧。
发布制品(v0.0.2)已在 cognitum-v0 上实装:count_v1.safetensors 392,088 B(sha256 32996433516891a37c63c600db8b95e42192a53bd538c088c82cd6a85e55513c),arm/x86_64 二进制均以 COGNITUM_OWNER_SIGNING_KEY(Ed25519)签名,清单在 cog/artifacts/manifests/{arm,x86_64}/manifest.json。
12. 后果与风险(ADR 原文结论)
积极面
- 关闭 #499 开启的概念闭环——多人计数成为学习任务而非带运行时旋钮的启发式。
- 复用了当周已交付的全部原语:Candle GPU 训练(ADR-101)、HF 编码器、Cog 打包(ADR-100)、edge 模块注册表(ADR-102)、Stoer-Wagner mincut、成对数据管线(PR #641)。
- Day-2 跨房间校准沿用 ADR-079 P9 为 pose 规划的同一 LoRA 路径,两个 cog 共享同一套微调机制。
- 显式的
confidence+count_p95_low/high输出让 UI 渲染不确定性而非凭空制造幽灵。
消极面
- 精度受限于与
pose_v1相同的成对数据稀缺(#645);无更多多房间数据时,v0.1.0 只能带有限的绝对精度发布。 - GCS 目录中多一个需要维护的 Cog 二进制(每架构 4 MB)。
- 融合段 min-cut 在 Pi 5 上约增加 0.3 ms/N 节点帧(
ruvector-mincut微基准),在 ≤5 ms 预算内可接受但值得追踪。
风险与缓解
- 标签噪声:P7 会话 MediaPipe 检出率仅 47%——一半帧即使房间里明显有人,
n_persons也是 0。缓解:训练前按MediaPipe confidence ≥ 0.7过滤,并用置信度加权损失(pose_v1用过同一技巧)。 - 冻结过度:若 50 epochs 冻结编码器训练不收敛,提前解冻;
train_results.json的遥测可支撑经验决策。 - min-cut 在单人场景过度约束:N=1 时子载波图 trivially 有 min-cut=1,融合降级为"信任单节点计数头"——可接受,但需要回归测试(
tests/fusion_test.rs::single_node_degrades_gracefully)。
13. 迁移路线
- 落地本 ADR + 新 crate 脚手架(一个 PR,暂不含模型——与 ADR-101 首个 PR 发布 stub cog 相同)。
- 用既有 1,077 个成对样本 +
n_persons标签训练count_v1.safetensors(与产出pose_v1相同的 Candle 管线)。 - 按 ADR-100 交叉编译 + 签名 + GCS 上传;按 ADR-101 的模式实装到
cognitum-v0。 - 接线
csi.rs::score_to_person_count:安装 Cog 时调用它,保留 PR #491 启发式作为回退。 - v0.2.0:用 #645 推动的多房间数据重训,按 ADR-079 P9 增加 LoRA 按房间适配器。
14. 相关文档导航(仓库内相对路径)
- 本 ADR 全文:docs/adr/ADR-103-learned-multi-person-counter.md
- 相机监督训练管线(同一数据路径):docs/adr/ADR-079-camera-ground-truth-training.md
- Cog 打包规范:docs/adr/ADR-100-cog-packaging-specification.md
- 姿态估计 Cog(本 Cog 首发模板):docs/adr/ADR-101-pose-estimation-cog.md
- 边缘模块注册表:docs/adr/ADR-102-edge-module-registry.md
- 本 Cog 基准日志(含 v0.0.1/v0.0.2 全部实测):docs/benchmarks/person-count-cog.md
- Cog 使用者视角 README:v2/crates/cog-person-count/cog/README.md
- 将被替代的启发式:v2/crates/wifi-densepose-sensing-server/src/csi.rs
- 标签自带来源(每帧写
n_persons):scripts/collect-ground-truth.py
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 StartedRust0627
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00