# 振动六层链 —— 接口规格与缺口(v0.1,2026-09-17) > 用途:用户令 2026-09-17 #2「无生成端的那批,能否根据旧包产物通过逆向工程推导实现?」的**实测答复**。 > 结论一句话:**接口已经写在包里的"壳"上,实现缺失;而"能否对拍验证"这件事,缺的正是标准答案** —— > 六层链最关键的两件产物(`model_run_l6.parquet`、`fusion_38.csv`)**从未随过包**,无从逐值对拍。 > 因此本轮**没有**凭猜测写实现(本项目的硬规矩:允许响亮降级,**不许造数**)。 --- ## 1. 壳在包内:四步怎么调、传什么,代码里已经写死了 `src/windcms/pipeline.py::analyze()` 就是那条链的编排(`scripts/windcms.py analyze` 是它的命令行入口): | 步骤名 | 实际执行的脚本 | 参数 | 环境变量 | |---|---|---|---| | `ingest` | `scripts/rudong_tcm_index.py` + `scripts/rudong_tcm_spectra.py` | `--root <导出> --out <窗>/index.parquet` / `--out <窗>/spectra` | — | | `oem_scan` | **`scripts/rudong_tcm_oem_scan.py`**(缺) | 无 | `M5_WINDOWS`(逗号分隔窗清单)、`SCAN_BANDS` | | `energy_share` | **`scripts/rudong_line_energy_share.py`**(缺) | 无 | 同上 | | `model_run` | **`scripts/rudong_model_run.py`**(缺) | 无 | 同上 | | `fusion` | **`scripts/rudong_fusion_run.py`**(缺) | 无 | 同上 | | `report` | `scripts/windcms.py report --farm <场>` | — | — | | `kb` | `scripts/windcms.py kb --farm <场>` | — | — | **实测证据**(2026-09-17): ``` $ python scripts/windcms.py analyze --window w0316 --steps model_run [model_run] rc=2 0.1s python.exe: can't open file '…\scripts\rudong_model_run.py': [Errno 2] No such file or directory ``` ⇒ 缺的是**实现**,不是接口。每步该调谁、该吃哪个环境变量,壳里一条不落。 ## 2. 产物契约(`src/windcms/config.py` 里写明的路径) ``` 'fusion' : outputs/<场>/m5_cms_tcm/fusion_38.csv ← 缺(从未随包) 'model_l6' : outputs/<场>/m5_cms_tcm/model_run_l6.parquet ← 缺(从未随包) 'model_summary' : outputs/<场>/m5_cms_tcm/model_run_summary.json ← 在(随包件,已按人工件归类) 'fleet_z' : outputs/<场>/m5_cms_tcm/fleet_scalar_z.parquet ← 在(随包件) 'baseline' : outputs/<场>/m5_cms_tcm/baseline_38.json ← 在(随包件,含人工坐实的两层基线) ``` ## 3. 为什么"逆向工程 + 逐值对拍"这条路在这四步上走不通 逐值对拍的前提是**有标准答案**。四步里: | 步骤 | 产物 | 盘上有没有样件 | 能否对拍 | |---|---|---|---| | `oem_scan` | `oem_frequency_scan.parquet`、`bearing_freq_scan.parquet`、`gear_freq_scan.parquet`、`cage_slip_scan.parquet`、`blade_1p_*.{parquet,csv}` 等 | **在**(约 23 件测量形态件) | 有样件 ⇒ **可以对拍**,但口径(扫哪些频带、窗如何合并、阈值)得先反推 | | `energy_share` | 未见同名产物 | 未识别 | 无样件 ⇒ 不可对拍 | | `model_run` | `model_run_l6.parquet`(+摘要 json) | **缺** | **无标准答案** ⇒ 只能凭口径实现,无法证明"复现" | | `fusion` | `fusion_38.csv`(+ `handoff_vibration_v2.json`) | `fusion_38.csv` **缺**;`handoff_vibration_v2.json` 在,但**它是人工交证件**(见 §4) | 同上 ⇒ 不可对拍 | ## 4. `handoff_vibration_v2.json` 不是"算出来的产物",而是**人工交证件** 实测其结构:`meta`(version/date/from/to/supersedes/principle/**v2.1_changes**/**v2.2_changes**/ ★消费端接线约定/★派生件同步纪律)、`detectability_prior`、`cross_source_requests`、`baseline_三层`、 `per_turbine`(v1_generator / v1_gearbox 的**人工裁决** + claim_window)、`field_actions_v1`、 `calibration_updates_v1`、`open_items`、`tcm16_schedule`。 ⇒ 它由振动线**人写**交给 windscada 线,正文里就是判断、承诺与待办。**逆向工程对它无意义** ("推"出来的只是同义改写)。它已按用户令 2026-09-17 #3 登记进人工件台账 `outputs/<场>/_human_artifacts.json`(`kind=human`),**不参与呼应校验**。 ## 5. 结论与待办(给研发/振动线的最小清单) 1. **四个脚本的源码**(`rudong_tcm_oem_scan.py` / `rudong_line_energy_share.py` / `rudong_model_run.py` / `rudong_fusion_run.py`)。接口已知(§1),放进 `scripts/` 即可被壳调用。 2. **两件标准答案**:`model_run_l6.parquet` 与 `fusion_38.csv` 的**任一份历史产出**(哪一窗都行)。 有了它,`oem_scan` 侧那 23 件测量件就能照 `temp_monthly` 的老办法(反推口径 → 逐值对拍 → 一致才敢用) 转成"✓ 已在重算链上";没有它,实现出来也**无法证明**复现,只能当成"另一个算法"。 3. 在那之前,系统按现口径如实呈现:这 36 件(`vib_handoff_and_scans` 里非人工的那部分)标 **✗ 无生成端**, 页面缺件时给结构化说明(不静默、不造数、不从交付包补)。 > 台账现状(2026-09-17):✓ 呼应成立 1742 件 · ✗ 无生成端 285 件 · ◆ 人工件 209 件 · 无法验证 0 · 未归类 0。 --- ## 6. 逆向工程进展:`fleet_scalar_z.parquet` 的口径已反推(可对拍) `fusion` 步的产物里,**只有 `fleet_scalar_z.parquet` 有标准答案**(随包件在盘上),因此先攻它。 新增 `scripts/rudong_fusion_run.py`(`--verify` 与样件逐值对拍,**不改任何件**)。 ### 6.1 反推出来的口径 ``` val = 窗内 median(scalar_value),按 (turbine, sensor_name, meas_name, condition_key) 分组 n = 组内记录数(样件口径:n ≥ 3) fleet_med = 跨机组 median(val)(同一 sensor/meas/bin 下) z = (val - fleet_med) / (1.4826 × MAD(val)) ← 稳健 z 分数 ``` **尺度是怎么定下来的**(四条候选逐个数对):std(ddof=0)=1.44859 → z=−0.1725 · MAD=0.80209 → z=−0.3116 · IQR/1.349=1.46899 → z=−0.1701 · **1.4826×MAD=1.18918 → z=−0.2102 ← 与样件 −0.210156 命中**。 ### 6.2 逐值对拍结果(样件 8,887 行 · 窗 w0127) | 列 | 一致 | 比率 | |---|---:|---:| | `val` | **8887 / 8887** | **100%** | | `fleet_med` | 8359 / 8887 | 94.1% | | `z` | 8180 / 8887 | 92.0% | | `n` | 8606 / 8887 | 96.8% | 行数:本器 9,505 行 vs 样件 8,887 行(多 618 行,全部键都在样件里 ⇒ **只多不缺**)。 ### 6.3 剩下的差异定位(还差最后几步) 1. **meas 口径**:样件的 28 个 meas 全是**标量指标类**(`CrestFactor` `Kurtosis` `Peak` `Rms_*` `iso_rms*` `Indicator_*` `Tooth_damage_indicator_*` `rms_200` `rms_Env_6000_Tr`), 而索引里另有 80 余个 `FFT_*` / `Time_*` / `Env_*` / `Cep_FFT_*` 名(波形/谱名)与 `Disk Usage` / `Memory Usage` / `Health` / `RpmProfile`(运维项)被排除。 ⇒ 规则大概是"只留 meas_type ∈ {Kurtosis, CrestFactor, Peak, RmsOverAll} 的标量", 但 `Indicator_*` / `iso_rms` / `rms_200` 这几类在索引里 `meas_type` 为空 ⇒ **筛选字段还没找准**。 2. **每键机组子集**:样件每个 (sensor,meas,bin) 只含 14–37 台(中位 28),本器是 34–37 台 ⇒ 样件多了一层"机组级有效性"过滤(猜测与 `parse_error` / `overload` / `alarm_type` 或组内一致性有关)。 3. `fleet_med`/`z` 那 5–8% 的差异,很可能就是**被上面两条筛选改变后的基线**导致的 —— 一旦筛选口径对齐, 基线应当自动落回(`val` 100% 一致已说明取值链是对的)。 ### 6.4 结论 - **能推**:这条链上"内容是窗内标量函数"的那部分**确实可以逆向工程**,且已经推到 `val` 逐值 100%、`z` 92% 的程度;`scripts/rudong_fusion_run.py --verify` 把这件事做成了可重复的机器判据。 - **判据纪律**:对拍未通过时本器**拒绝**声明复现(退出码 5、打印"不要拿本器产物替换样件"), 产物也**不替换** —— 宁可继续留 ✗,也不拿 92% 的东西冒充标准答案。 - **下一步**(按代价排序):① 找准"哪 4 类 meas 该留"的筛选字段(读一次索引就能定); ② 找准机组级有效性过滤;③ 对齐后 `fleet_scalar_z` 转 raw-derived,`fusion` 步再补 `fusion_38.csv` (那件无标准答案,需要振动线给一份历史产出)。