feat: standardize algorithm measurement results

This commit is contained in:
2026-08-14 00:12:53 +08:00
parent ac1387debc
commit 185dd36bf3
15 changed files with 1273 additions and 19 deletions

View File

@@ -0,0 +1,259 @@
# 算法交付与测量结果输出规范
版本:1.0.0
状态:自 2026-08-13 起,新交付算法必须遵循;存量算法逐步迁移。
适用范围:`alg/` 下所有向宏量点云处理平台输出测量结果的算法。
## 1. 目的
本规范统一算法 `result.json` 中正式测量结果的表达,解决以下问题:
- 任务号被写在名称字符串里,例如 `任务46_车体横断面对角线差值`;
- 同一任务的多个测量位置无法分组,例如任务 5 的左梁和右梁;
- 头车、中车使用不同任务号,但结果中没有检测对象上下文;
- `dx_y`、`dx_z` 等计算中间量混入正式测量结果;
- 前端只能解析中文名称,无法稳定排序、筛选、统计和生成报告。
平台进程、进度事件、产物文件等接入方式仍参考
[`docs/algorithm-integration-spec.md`](../../docs/algorithm-integration-spec.md)。涉及测量结果字段时,**以本规范为准**。
## 2. 基本原则
1. **任务号结构化**:正式测量项必须提供 `taskNo`,不得只写进 `name`。
2. **名称不编码**:`name` 只写项目名称,不包含 `任务XX_`、左右位置或车型前缀。
3. **指标编码稳定**:每项结果必须有稳定的 `metricCode`,程序关联以它为准,不能依赖中文名称。
4. **一个测量值一条记录**:同一任务存在左/右、前/后等多个结果时,保留多条记录并使用 `position` 区分,`taskNo` 相同。
5. **正式结果与中间量隔离**:只有可交付、可判定、可进入报告的值才能进入 `measurements`;中间量放入 `extra.intermediateMetrics`。
6. **不猜任务号**:任务号必须来自经确认的业务任务清单。无法确定时应阻止正式交付并请求确认,不能按数组序号或名称相似度推断。
7. **值和单位分离**:`value` 必须是 JSON 数值或 `null`;禁止输出 `"0.073/700mm"` 这样的混合字符串。
8. **JSON 必须标准合法**:禁止 `NaN`、`Infinity`;失败且无有效值时使用 `null`。
## 3. 职责边界
### 3.1 业务/产品负责人
- 提供并确认正式任务清单:`taskNo`、项目名称、适用检测对象、理论值、公差、单位;
- 审批任务号变更,算法工程师不得自行重新编号。
### 3.2 平台/集成人员
- 在算法输入或适配层提供 `inspectionObject`,例如 `head`(头车)、`middle`(中车);
- 将经确认的业务任务清单配置为映射表;
- 校验最终 `result.json`,不让缺少任务号的正式结果进入业务表格。
### 3.3 算法工程师
- 为算法指标定义稳定的 `metricCode` 和准确的 `name`;
- 输出 `position` 等算法能够确定的测量位置;
- 在已获得检测对象和任务映射时输出正确的 `taskNo`;
- 只把最终可交付结果放进 `measurements`,中间量放进 `extra.intermediateMetrics`;
- 交付前运行本目录的校验脚本。
> 最终写入平台的 `result.json` 必须满足本规范。若算法核心包不负责业务任务号,平台薄适配层必须在写出 `result.json` 前补齐,不能交给前端猜测。
## 4. 顶层结构
```json
{
"schemaVersion": 1,
"measurementContractVersion": "1.0",
"status": "completed",
"message": "测量完成",
"context": {
"inspectionObject": "head",
"inspectionObjectName": "头车"
},
"measurements": [],
"extra": {
"intermediateMetrics": []
}
}
```
字段说明:
| 字段 | 必填 | 说明 |
|---|---:|---|
| `schemaVersion` | 是 | 平台插件结果版本;当前平台存量插件仍使用 `1` |
| `measurementContractVersion` | 是 | 本测量结果规范版本,当前固定为 `"1.0"` |
| `status` | 是 | `completed` 或 `error` |
| `message` | 否 | 面向用户的简短说明 |
| `context` | 有对象差异时必填 | 本次测量的业务上下文 |
| `measurements` | 完成时必填 | 正式测量结果;允许空数组,不允许混入调试指标 |
| `extra` | 否 | 算法报告、质量信息、中间量等非正式结果 |
`schemaVersion` 与 `measurementContractVersion` 含义不同,不得互相替代。
## 5. 正式测量项 `measurements[]`
推荐完整结构:
```json
{
"taskNo": "5",
"metricCode": "underframe.longitudinal_beam.vertical_deflection",
"name": "纵向梁到直线的垂直偏差(挠度)",
"position": "左梁",
"value": 0.265,
"unit": "mm",
"tolerance": 7.0,
"toleranceLabel": "±7 mm",
"status": "pass"
}
```
| 字段 | 必填 | 类型/取值 | 说明 |
|---|---:|---|---|
| `taskNo` | 是 | 字符串 | 正式业务任务号;即使全是数字也必须用字符串 |
| `metricCode` | 是 | 字符串 | 稳定指标编码,建议 `<域>.<部件>.<指标>`,仅用小写字母、数字、点、下划线和短横线 |
| `name` | 是 | 字符串 | 纯项目名称,不带 `任务XX_` 和位置前缀 |
| `position` | 多位置时必填 | 字符串 | 如 `左梁`、`右梁`、`一位端`;不适用时省略 |
| `value` | 是 | 数值或 `null` | 测量值;无有效结果时为 `null` |
| `unit` | 有量纲时必填 | 字符串 | 如 `mm`、`°`、`%`;无量纲使用 `"1"` |
| `theoreticalValue` | 否 | 数值 | 理论值,与 `unit` 相同 |
| `baseline` | 斜率形式时必填 | 正数 | 基准长度,单位为 mm |
| `tolerance` | 简单对称公差时使用 | 非负数 | `±tolerance`,与 `unit` 相同;斜率形式表示分子公差 |
| `toleranceLower` | 非对称公差时使用 | 数值 | 相对理论值的下偏差 |
| `toleranceUpper` | 非对称公差时使用 | 数值 | 相对理论值的上偏差 |
| `toleranceLabel` | 复杂公差时使用 | 字符串 | 报告展示原文,例如 `+8.5/-3 mm` |
| `status` | 是 | `pass` / `warning` / `fail` | 正式结果不得使用 `info` |
| `qualityFlags` | 否 | 字符串数组 | 质量告警代码,不替代 `status` |
约束:
- 同一 `taskNo + metricCode + position` 在一次结果中必须唯一;
- `name` 不得匹配 `^任务\s*\d+`;
- `status=pass|warning|fail` 必须基于已确认的公差规则;
- `value=null` 时不得判为 `pass`;
- 若尚未获得正式任务号,该项不得进入最终 `measurements`。
## 6. 检测对象与任务号映射
当同一指标在不同检测对象上对应不同任务号时,必须先根据 `context.inspectionObject` 选择任务号。
本次已确认的映射如下:
| 检测对象 | 指标 | `taskNo` |
|---|---|---:|
| 任意 | 纵向梁到直线的垂直偏差(左、右挠度) | 5 |
| 头车 `head` | 车钩面板到参考面的右偏转角 | 8 |
| 中车 `middle` | 车钩面板到参考面的右偏转角 | 9 |
| 头车 `head` | 车钩面板到纵向中心的右偏转角 | 10 |
| 中车 `middle` | 车钩面板到纵向中心的右偏转角 | 12 |
任务 5 的左右梁应输出两条记录:两条的 `taskNo` 和 `metricCode` 相同,`position` 分别为 `左梁`、`右梁`。
如果输入中没有头车/中车信息,车钩算法不得自行默认成头车;应报契约错误,或由平台适配层在具备检测对象信息后完成映射。
## 7. 斜率形式与角度值
车钩“右偏转角”目前存在两种可能的交付口径,必须由业务任务清单明确:
### 7.1 以角度交付
```json
{
"value": 0.0063,
"unit": "°",
"tolerance": 0.08
}
```
### 7.2 以偏差值/基准长度交付
```json
{
"value": 0.073,
"unit": "mm",
"baseline": 700,
"tolerance": 1.0,
"toleranceLabel": "1/700 mm"
}
```
禁止把 `value` 写成字符串 `"0.073/700mm"`。角度值和换算后的偏差值不能共用同一个 `metricCode`;如两者都需保留,一个作为正式结果,另一个放入中间量并使用不同编码。
## 8. 中间数据与诊断数据
以下数据通常不属于正式测量项:拟合系数、法向量、点数、残差、`dx_y`、`dx_z`、`D_y`、`D_z`、ICP fitness、RMSE 等。
统一放入:
```json
{
"extra": {
"intermediateMetrics": [
{
"metricCode": "coupler.panel.dx_y_max",
"name": "Y 方向计算用最大 X 偏差",
"value": 0.0101,
"unit": "mm"
}
]
}
}
```
中间量:
- 不需要 `taskNo`;
- 不设置业务 `status` 和公差;
- 不进入“算法结果”项数、合格率和正式报告;
- 可用于日志、排障和算法复核。
## 9. 禁止的旧写法
```json
{
"measurements": [
{ "name": "任务46_车体横断面对角线差值", "value": 0.592 },
{ "name": "左梁 · 纵向梁到直线的垂直偏差(挠度)", "value": 0.265 },
{ "name": "最大 x 偏差 dx_y", "value": 0.010, "status": "pass" }
]
}
```
问题分别是:任务号嵌入名称、位置嵌入名称、中间量冒充正式结果。
正确写法见 [`examples/head-car-coupler-and-deflection.result.json`](examples/head-car-coupler-and-deflection.result.json)。
## 10. 兼容与迁移
- 本规范是**目标输出契约**。当前平台前端尚未使用所有新增字段时,也必须保留这些字段,不能因此退回名称拼接方案;
- 存量 `任务46_xxx` 等结果允许历史回放,但算法下一版本应拆为 `taskNo="46"` 和纯 `name`;
- 平台适配层可以暂时从经确认的旧配置中迁移任务号,但不得长期正则解析中文名称;
- 修改任务号、单位、换算口径、公差或 `metricCode` 均属于接口变更,必须在算法版本说明中记录;
- 核心计算方法未变、只调整输出结构时,也应至少提升算法补丁版本并注明“输出契约迁移”。
## 11. 交付检查清单
算法工程师提交前逐项确认:
- [ ] `result.json` 是 UTF-8 标准 JSON,无 `NaN`/`Infinity`
- [ ] 顶层包含 `measurementContractVersion: "1.0"`
- [ ] 每个正式测量项都有 `taskNo`、`metricCode`、纯 `name`、`value`、`status`
- [ ] 多位置结果使用相同任务号并通过 `position` 区分
- [ ] 头车/中车任务号来自 `context.inspectionObject`,没有默认猜测
- [ ] `measurements` 中没有 `info` 状态和算法中间量
- [ ] 单位、公差、理论值和换算口径与正式任务清单一致
- [ ] `extra.intermediateMetrics` 中的指标不参与合格判定
- [ ] 已执行:`python3 validate_result.py result.json`
- [ ] 版本说明记录了输出字段变化
## 12. 校验方式
本目录提供两种校验:
```bash
# 零第三方依赖,执行本规范的关键业务规则校验
python3 validate_result.py /path/to/result.json
# 如本机安装了 jsonschema,可进行完整 JSON Schema 校验
python3 -m jsonschema -i /path/to/result.json result.schema.json
```
示例自检:
```bash
python3 validate_result.py examples/head-car-coupler-and-deflection.result.json
```