闭环四:设备与预测维护

第 5 课 · 共 7 课 约 9 分钟

用时间序列读数和阈值触发维保工单,引入 MaintenanceTask 对象与 request-maintenance。

本课目标

读完这一课,你将能够

  • 读懂时间序列属性的三个读取接口和 1,000 个点的内联上限,并据此设计存什么
  • 写出 MaintenanceTask 对象类型和 request-maintenance Action,让重复申请被拒绝
  • 设计两级阈值的触发条件,并说出传感器掉线这种静默故障怎么兜底

读数是时间序列,阈值是数据

前三个闭环的数据都是一件一件到来的事件,这一课的数据是连续不断的读数。这个闭环的决定是:这台设备要不要现在安排维保?对象是 machine,它通过 lineMachines 属于一条 productionLine,身上有两个时间序列属性:vibration 和 temperature。设备对象就是设备的孪生:读数、位置、维保历史都挂在这一个对象上。

车间里的三维位置,可以用建模 SDK 的锚点绑到这个对象上。生成的空间只是看起来合理,不是测绘,锚点在有人核对前要标 verified: false。

时间序列属性的值是 { seriesId, points },points 是一列 { time, value }。AIDC 内联最多 1,000 个点,读取有三个接口:firstPoint、lastPoint 和 streamPoints。这个上限决定了设计:本体(Ontology)里只放最近一段有意义的读数,比如每小时的最大值;每秒几百个点的原始波形留在物联网系统里。

# 最新的一个点
curl -H "Authorization: Bearer $AIDC_API_KEY" \
  "https://www.ai-dc.ai/api/v1/ontologies/cell-example/objects/machine/M-07/timeseries/vibration/lastPoint"

# 一段时间内的点
curl -X POST -H "Authorization: Bearer $AIDC_API_KEY" -H "Content-Type: application/json" \
  -d '{"range":{"type":"absolute","startTime":"2026-09-29T00:00:00Z","endTime":"2026-09-29T12:00:00Z"}}' \
  "https://www.ai-dc.ai/api/v1/ontologies/cell-example/objects/machine/M-07/timeseries/vibration/streamPoints"

阈值不要写死在触发条件里。给 machine 加两个小属性:vibrationLimit 和 temperatureLimit。阈值是数据,随设备走,改它不需要重新发布工作流。下面只写振动,温度照抄;任一读数超线都算,在 when 里用 || 连起来。

新对象与新动词:维保工单

本课引入一个只存在于语义层的对象类型 MaintenanceTask,用 request-maintenance 来建。它和 machine 之间是一对多的 machineMaintenanceTasks,外键在维保工单这一端:

{
  "kind": "objectType",
  "apiName": "MaintenanceTask",
  "title": "维保工单",
  "description": "一次设备维保:由自动化或人申请,只在语义层,用 Action 记。",
  "schema": {
    "titleColumn": "reason",
    "columns": [
      { "name": "taskId", "type": "string", "primaryKey": true, "title": "编号" },
      { "name": "machineId", "type": "string", "title": "设备", "references": { "entity": "machine", "column": "machineId" } },
      { "name": "status", "type": "string", "title": "状态" },
      { "name": "reason", "type": "string", "title": "原因", "notNull": true },
      { "name": "triggerReading", "type": "double", "title": "触发时的读数" },
      { "name": "createdAt", "type": "timestamp", "sharedProperty": "createdAt" }
    ]
  }
}

request-maintenance 一次做两件事:建一张维保工单,并把设备的 maintenanceState 标为 requested。这个属性只在语义层维护(writeback: true),提交条件用它挡住重复申请。两条规则在同一个事务里:

{
  "kind": "actionType",
  "apiName": "request-maintenance",
  "title": "申请维保",
  "schema": {
    "parameters": [
      { "name": "machine", "type": "object", "objectType": "machine", "required": true },
      { "name": "reason", "type": "string", "required": true, "maxLength": 300 },
      { "name": "reading", "type": "double" }
    ],
    "rules": [
      { "type": "createObject", "objectType": "MaintenanceTask", "values": { "taskId": "$uuid", "machineId": "$machine", "status": "open", "reason": "$reason", "triggerReading": "$reading", "createdAt": "$now" } },
      { "type": "modifyObject", "objectType": "machine", "object": "$machine", "values": { "maintenanceState": "requested" } }
    ],
    "submissionCriteria": [
      { "condition": { "type": "comparison", "left": { "param": "machine", "property": "maintenanceState" }, "operator": "isNot", "right": { "literal": "requested" } },
        "failureMessage": "这台设备已经有未完成的维保申请" }
    ],
    "summary": "{machine} 申请维保:{reason}",
    "actionLog": true
  }
}

有了这条提交条件,触发条件被反复满足也不会堆出十张任务。维保完成时把 maintenanceState 改回去的 Action 写法与它对称,这里从略。

自动化:两级阈值

触发条件取最后一个点。points 已按时间排好,last 就是最新读数,用 80% 做预警线:

"trigger": {
  "change": {
    "type": "machine",
    "when": "=last(pluck(object.vibration.points, 'value')) > object.vibrationLimit * 0.8",
    "cooldown": "12h",
    "input": { "machine_id": "=object.machineId" }
  }
}
  1. 01读数最新点和最近一段的最大值
  2. 02定级别超过 80% 预警,超过 100% 超限
  3. 03AI 分析结合趋势解释原因
  4. 04申请维保request-maintenance,仅超限时
  5. 05通知维保主管

两个级别用步骤自己的 when 区分:预警只通知,超限才写维保工单并通知。计算步骤先把一段读数缩成几个数,最大值、平均值,再交给模型解释,不要把上千个点塞进提示词。

自主程度是「自动」:建维保工单风险低、随时能取消,所以自动做。停机和排产是人的决定,也是这个闭环的人工关口。智能体读读数、判断级别、写说明,不停任何设备。

出错与验证:最危险的是没有变化

  • 传感器掉线:没有新读数,就没有变化,也就没有触发。这是静默故障,change 抓不到,要用一个时间是条件的看门狗,按 hoursSince 判断「读数多久没来」。
  • 读数抖动:一次尖峰就申请维保。冷却 12 小时,加上「每台设备一张未完成申请」,已经把噪声挡在外面;还不够,就让计算步骤要求最近几个点都超线。
  • 阈值设错:太低就天天告警。第一个版本只通知,按一周的告警数调阈值,再加写入。维保工单关闭时记下有没有发现故障,日后调阈值才有证据。预警线放在 80%,是给维保留一个班次的提前量;这个比例是示例,按设备劣化的速度定。
  • 窗口太短:窗口决定能看多远。每小时一个点,1,000 个点约 41 天;每分钟一个点,只有不到 17 小时。先定窗口,再定规则。
  1. 对照现场

    用 lastPoint 取一台设备的最新读数,和现场仪表对一对,再谈阈值。

  2. 先申请,再校验

    在一台测试用的设备上真的申请一次,再对同一台设备 --validate-only:这一次应当失败,给出那句失败信息。

  3. 预演

    对一台超限和一台正常的设备各预演一次,只有超限那台的计划里有 request-maintenance。

aidc semantic apply request-maintenance --param machine=M-07 --param reason="振动超限" --param reading=7.4 --validate-only
aidc semantic automate run machine-watch --param machine_id=M-07 --channel test --preview
aidc semantic edits-history machine --pk M-07

要点

  • 时间序列内联最多 1,000 个点,读取用 firstPoint、lastPoint、streamPoints;本体里只放有意义的摘要。
  • 阈值是数据,写成设备的属性,而不是写死在工作流里。
  • request-maintenance 用 maintenanceState 加提交条件,保证一台设备同时只有一张未完成申请。
  • 没有变化就没有触发:传感器掉线要靠以时间为条件的看门狗兜底。

练一练

为你的设备读数设计阈值

选一种有读数的东西:设备、温湿度、库存水位或系统延迟都可以。

写出每个读数的采样间隔,算一算 1,000 个点能覆盖多长时间,再决定存原始值还是每小时摘要。

小测

选一个答案,马上看解析。

Q1传感器掉线了,没有新读数进来。change 触发的维保工作流会怎样?

Q2为什么把 vibrationLimit 做成设备的属性,而不是写在触发条件里?

Q3同一台设备的振动连续几小时超线,会建多少张未完成的维保工单?

延伸阅读