Files
massive-cloud/docs/algorithm-integration-spec.md

12 KiB
Raw Blame History

算法集成规范(Route B · 通用接口接入)

本文是宏量点云处理平台对多类算法批量接入的对外规范,面向算法团队 + 验收方。一句话总结:

算法侧只交付 一个 CLI + 一份 result.json,平台用统一插件机制 接入、并发执行、按 manifest 渲染表单、按 result.json 渲染结果。

满足工信部验收"算法在软件中集成运行"且 20 分钟内出结果的要求。


1. 总体架构

前端 (React)
  └── AlgorithmRunPanel        ← 由 manifest 自动生成插件列表 + 多选并发
  └── GenericResultView         ← 按 result.json 统一渲染(误差/测量/图片/3D 叠加)
  └── PointCloudViewer          ← pluginOverlay prop 接收插件生成的热力 / PLY

后端 (Node + tRPC)
  └── algorithm.list            ← 列出已注册插件
  └── algorithm.run             ← 单插件执行(入并发池)
  └── algorithm.runBatch        ← 多插件并发执行(入并发池)
  └── /api/files/read/analysis/:taskId/:resultId/:filename   ← 通用产物路由

算法层(按算法各自交付)
  └── manifest/<id>.json        ← 元数据 + UI 表单 + runtime
  └── adapters/<id>_run.py      ← 平台薄适配层
  └── fixtures/<id>.json        ← 未就绪时的 mock 预设

并发池:默认 4 路(环境变量 ALGORITHM_JOB_CONCURRENCY 可覆盖), 单算法超时默认 20 分钟(manifest 内 runtime.timeoutMs 可覆盖)。


2. Manifest JSON 协议

文件位置:server/plugins/manifests/<plugin-id>.json。完整示例见 coupler-panel.json 等。

{
  "id": "coupler-panel",                // 全局唯一 kebab-case ID
  "name": "车钩面板检测",                // UI 显示中文名
  "description": "...",
  "version": "1.0.0",                   // 算法版本
  "schemaVersion": 1,                   // result.json schema 版本
  "category": "几何测量",               // UI 分组(可选)
  "requiresPointCloud": true,           // 默认 true
  "requiresCad": false,                 // 默认 false;true 时需车型下有 CAD
  "inputs": [
    {
      "name": "profile",
      "label": "调参档位",
      "type": "select",                 // number | integer | string | boolean | select
      "default": "auto",
      "options": [
        { "value": "auto", "label": "自动" },
        { "value": "high_density", "label": "高密度" }
      ],
      "description": "辅助说明,渲染在字段下方"
    }
  ],
  "runtime": {
    "kind": "process",                  // 真实算法
    "cmd": ["{pythonBin}", "{adapterDir}/coupler_panel_run.py", "{inputJson}"],
    "envExtra": {
      "PYTHONPATH": "{algRoot}/挠度-偏转角/delivery/coupler_panel_release"
    },
    "timeoutMs": 1200000                // 默认 20 分钟
  }
}

或 mock 模式(用于占位未就绪算法):

"runtime": {
  "kind": "mock",
  "fixture": "flatness-suite.json",     // 相对 fixtures/ 的文件名
  "simulateMs": 1500                    // 模拟耗时,UI 演示进度条
}

2.1 cmd 占位符

占位符 含义
{pythonBin} 容器内 Python 解释器($PYTHON3_PATH 或 python3)
{inputJson} runner 写到 workDir 的 input.json 绝对路径
{workDir} 任务工作目录(也是 outDir)
{adapterDir} server/plugins/adapters/ 绝对路径
{algRoot} 算法源码根目录($ALG_ROOT 或 /home/lipan/massive-cloud/alg)

3. 输入契约 — input.json

runner 在 workDir 写出 input.json 后调用算法 CLI,固定路径: /data/tasks/<taskId>/analysis/<resultId>/input.json。

{
  "schemaVersion": 1,
  "pluginId": "coupler-panel",
  "pluginVersion": "1.0.0",
  "resultId": 123,
  "taskId": 88,
  "workDir": "/data/tasks/88/analysis/123",
  "outDir":  "/data/tasks/88/analysis/123",   // 与 workDir 相同
  "inputs": {
    "pointCloudPaths": ["/data/tasks/88/pointcloud/scan.ply"],
    "cadPath": null,                          // 或 "/data/models/55/cad/panel.stl"
    "config": {                               // 来自 UI 表单
      "profile": "auto",
      "rightAxisSign": "1"
    }
  }
}

约束:

  • 算法不读全局环境,所有产物落 outDir;
  • 不修改 algorithm package 自身目录;
  • CLI 必须非交互:禁用 GUI / 弹窗。

4. 输出契约 — result.json

算法在 outDir/result.json 写最终结果:

{
  "schemaVersion": 1,
  "status": "completed",            // 或 "error"
  "message": "可选简短说明",
  "measurements": [
    { "name": "右偏转角", "value": 0.83, "unit": "°", "tolerance": 1.0, "status": "pass" },
    { "name": "最大 X 偏差", "value": 0.42, "unit": "mm", "tolerance": 1.0, "status": "pass" },
    { "name": "拟合内点率", "value": 0.92, "status": "info" }
  ],
  "errorStats": {                   // 可选;用于 GenericResultView 误差卡片
    "min": -1.83, "max": 1.28, "mean": 0.04, "stdDev": 0.32,
    "totalPoints": 1500000,
    "withinTolerance": 1450000, "outOfTolerance": 50000
  },
  "overlay3d": {                    // 可选;前端通过 PointCloudViewer.pluginOverlay 消费
    "kind": "vertexColors",         // 或 "ply" / "none"
    "file": "heatmap.bin",          // 相对 outDir
    "vertexCount": 123456,
    "dispMin": -0.5, "dispMax": 0.5
  },
  "images": ["plot.png", "images/diagnostic.png"],   // 相对 outDir
  "extra": { "...": "算法专属字段,前端折叠展示" }
}

measurements[*].status ∈ {pass | warning | fail | info},前端按 badge 颜色渲染。


5. 进度上报协议

子进程通过 stdout 单行 JSON 上报进度,runner 解析后写入数据库:

{"type":"progress","percent":0.12,"message":"去噪"}
{"type":"progress","percent":0.45,"message":"平面拟合"}
{"type":"progress","percent":0.80,"message":"轮廓度计算"}

字段说明:

  • percent:允许 01 或 0100,runner 自动归一化为 0~1;
  • message:当前阶段名,会写入 resultData.stage。

stderr 仅作日志,runner 会原样转发到平台日志。


6. 错误约定

情况 算法行为 平台行为
数据不合规 / 计算失败 写 result.json 且 status="error",message 中文+英文摘要 DB.status="error",前端红字提示
进程异常退出(无 result.json) exit code 非 0 runner 写一条带 exitCode 的 error 记录
超时 runner 发 SIGKILL DB.status="error",错误信息为「超时 N ms」

7. tRPC 接口

// 列出所有插件
trpc.algorithm.list.useQuery()
//   → { plugins: PluginSummary[], scheduler: { running, queued, limit } }

// 单个算法
trpc.algorithm.run.useMutation()
//   .mutate({ taskId, pluginId, params })
//   → { id, pluginId, schemaVersion }

// 并发批量
trpc.algorithm.runBatch.useMutation()
//   .mutate({ taskId, jobs: [{ pluginId, params }] })
//   → { jobs: [{ id, pluginId }], scheduler }

每次 run 都会在 task_analysis_results 表插入一条 type='plugin' 的记录。 前端通过原有 trpc.analysisResult.list 轮询,按 pluginId 区分。


8. 数据库变更

迁移文件:drizzle/0007_task_analysis_plugin_columns.sql。

ALTER TABLE task_analysis_results
  MODIFY COLUMN type ENUM(..., 'plugin') NOT NULL;
ALTER TABLE task_analysis_results
  ADD COLUMN pluginId VARCHAR(64) NULL AFTER type,
  ADD COLUMN schemaVersion INT NULL AFTER pluginId;
CREATE INDEX idx_task_analysis_plugin_id ON task_analysis_results (pluginId);

升级流程:

  1. 应用 SQL 迁移(pnpm db:push 或手动执行)。
  2. 重启 Node 服务。
  3. 新增算法只需新增 manifest + adapter,无需再变更数据库。

9. 文件路由(产物)

GET /api/files/read/analysis/:taskId/:resultId/:filename

返回 outDir 下任意相对路径的文件(含 images/foo.png 等)。前端通过 result.json.images / overlay3d.file 直接拼接 URL。

与现存 /read/analysis/heatmap/...、/read/analysis/assembly-vertex/... 共存;具体路由优先匹配,最后由通用路由兜底。


10. 已接入的算法

页面右侧算法步骤与插件、平台适配器、算法源码包的对应关系如下。全部 5 个步骤都是真实 process 运行时,没有任何 mock。

页面步骤 插件 ID 版本 manifest 平台适配器 算法源码包 / 工程 算法入口 算法负责人
整车配准 vehicle-registration 1.3.0 server/plugins/manifests/vehicle-registration.json server/plugins/adapters/vehicle_registration_run.py alg/整车配准v1.3 translate_align_pointcloud_to_cad.translate_align_by_front_tail 待算法侧补充
车钩面板检测 coupler-panel 1.2.1 server/plugins/manifests/coupler-panel.json server/plugins/adapters/coupler_panel_run.py alg/delivery_v1.2.1/coupler_panel_release coupler_panel.pipeline.run_coupler_measurement_pipeline 待算法侧补充
平整度 / 平面度 / 轮廓测量 flatness-suite 1.5.0 server/plugins/manifests/flatness-suite.json server/plugins/adapters/flatness_suite_run.py alg/平整度、平面度、轮廓测量v1.5 pipeline.MeasurementPipeline(contour / plane / curvature) 待算法侧补充
列车点云测距 train-measurement 3.2.0 server/plugins/manifests/train-measurement.json server/plugins/adapters/train_measurement_run.py alg/train_measurement_system_v3.2_0629 task_manager.TaskManager 待算法侧补充
构架挠度测量 underframe-deflection 1.2.1 server/plugins/manifests/underframe-deflection.json server/plugins/adapters/underframe_deflection_run.py alg/delivery_v1.2.1/underframe_deflection underframe_deflection.run_single_beam_pipeline 待算法侧补充

说明:

  • 负责人列用于区分各算法工程师归属;当前源码和 manifest 未记录个人姓名,需算法侧补充。
  • train-measurement 即用户常说的 train_measurement_system,当前接入版本为 train_measurement_system_v3.2_0629。
  • 车钩面板检测和构架挠度测量同属 delivery_v1.2.1 交付包,但分别是两个独立算法工程目录。
  • flatness-suite 的 contour 子模块依赖较重(~2GB torch)。adapter 在 torch 缺失时会自动跳过 contour,plane / curvature 仍能正常出结果。

10.1 容器内依赖安装

镜像构建阶段:

# 通用部分 — 已经在 Dockerfile 里自动执行
python3 -m pip install --break-system-packages \
  --default-timeout=300 --retries 10 \
  -i https://pypi.tuna.tsinghua.edu.cn/simple \
  -r requirements-geometry.txt

# 可选:启用 flatness-suite 的 contour 子模块(CPU 版,~2GB)
python3 -m pip install --break-system-packages \
  -r requirements-flatness-contour.txt

10.2 算法源码挂载

docker-compose.yml 已配置:

app:
  environment:
    - ALG_ROOT=/alg
  volumes:
    - ${LOCAL_ALG_DIR:-./alg}:/alg:ro     # 只读挂载,安全

宿主机上的 alg/ 目录(与本仓库 alg/ 结构一致)只读挂载到容器内 /alg。 manifest 里所有 {algRoot} 占位符在运行时都解析为该路径。算法源码通过卷挂载 而非打入镜像,避免 13.5GB 的构建上下文。


11. 验收检查清单

  • 系统中存在「算法运行(多选并发)」面板,能列出 5 类算法
  • 多选执行后所有算法并行调度(默认 4 路并发池)
  • 单次运行有进度条,超时时间按各算法 manifest 配置
  • 结果统一渲染:误差统计 / 测量值表 / 图片 / 3D 叠加按钮
  • 数据库记录算法 ID + schemaVersion,可回放、可追溯
  • 算法层与 UI 层完全解耦:新增算法不改前端 / 不改 tRPC / 不改数据库

12. 后续接入新算法的步骤

  1. 把算法封装成一个 Python 入口:python -m my_algo.run <input.json>, 保证:读 inputs.pointCloudPaths[0] → 计算 → 写 outDir/result.json。
  2. 复制一份 manifest(如 coupler-panel.json)改 id、cmd、envExtra、inputs。
  3. (可选)写一个 adapter server/plugins/adapters/my_algo_run.py 做参数翻译, 或者算法 CLI 直接吃 input.json。
  4. 重启服务,前端自动刷出新条目,不需要任何前端 / SQL 改动。