DolphinDB 无监督聚类实战:工况自动识别与设备群体分群

摘要

工业时序平台上跑的分析,绝大多数是有监督的:告警有阈值、评分有规则、寿命预测有历史故障当标签——问题定义清楚,答案对错可查。

但现场还有一类问题,压根没人打过标签:

"这台设备现在是正常运转、待机,还是在空转?"——没人时刻记录工况,但这个状态决定了能耗是否在白白流走。

"厂里这两百台设备,哪几台'性格'相同?"——想给同类设备建立对比基线,但"同类"没人定义过。

这类问题没法写规则(写规则的前提是你知道答案长什么样),只能靠无监督****聚类:让数据自己说话,把相似的聚到一起,结构浮出来之后人再来命名。说得更直白些:有监督回答"我问什么你答什么",无监督回答"我还不知道该问什么,先帮我看看数据里有什么结构"。

本文用 DolphinDB 的窗口特征 + kmeans 把无监督分析落地到两层场景:单台设备的工况自动分段(运行/待机/空转的时间线)和全厂设备的群体分群(同型组与离群设备发现)。核心观点是:聚类算法本身十分钟讲完,功夫全在特征指纹的设计和聚类结果的工程化使用上。

特别说明:本文基于个人在 IoT 场景下的设计与使用经验,涉及的方法与判断来自有限测试,不代表官方或专业结论。


一、没有标签的问题

先把有监督和无监督的分界说清楚,再谈怎么用。

1.1 两种问题的分界

有监督 无监督
典型问题 振动超没超标?还能用几天? 现在是什么工况?哪些设备相似?
前提 有明确标准(阈值/历史标签) 没人给过答案
输出 判定值 / 预测值 簇结构(人再命名)
验证方式 对已知答案 对业务合理性(解读)

分界很实用:能写出"什么算对"的问题用规则或监督模型;写不出的,才考虑聚类。这也是聚类容易被滥用的原因——它不需要标签,什么都不给也能跑出结果,但结果有没有意义,全看特征设计。

1.2 本文的两层场景

  • 时间维度:工况自动分段。单台设备的时间线切成一段一段的"状态"(运行 / 待机 / 空转),没有人盯着的工况记录,从数据里自动长出来

  • 设备维度:群体分群。全厂设备按运行"指纹"自动分成同型组,组内互为基线,组外即离群

两层用的是同一套工具箱(窗口特征 + 标准化 + kmeans),只是聚类的对象不同——一个是时间窗,一个是设备。


二、特征准备:给"状态"造指纹

聚类是按特征空间的距离聚的,特征长什么样,簇就是什么样。这是无监督分析里花心思最多的环节。

2.1 工况指纹:短窗特征

判断"这 15 分钟是什么工况",不看单个点,看一段窗口内的统计形态:

// 每 15 分钟一个窗口,算工况指纹
winFeat = select
    deviceId,
    bar(ts, 15m)                                   as window,
    avg(power)                                     as p_mean,    // 平均功率
    std(power)                                     as p_std,     // 功率波动
    sum(iif(power < 5.0, 1, 0)) \ count(*)         as low_ratio, // 低功率占比(待机嫌疑)
    sum(iif(power > 72.0, 1, 0)) \ count(*)        as high_ratio // 满载占比(额定 80kW 的 90%)
from loadTable("dfs://iot", "power")
where ts between datetime(2026.03.01) : datetime(2026.03.31)
group by deviceId, bar(ts, 15m)

四个特征的设计意图:正常运转的特征是"中等功率、波动平稳";待机是"低功率、低占比变化";空转/间歇是"中低功率、波动大"。每个特征都要有物理含义——聚类完解读簇的时候,全靠这些物理含义说话。

2.2 标准化:聚类前的必做动作

四个特征的量纲完全不同(功率几十,占比零点几),直接聚类的话距离被大量纲的特征主导,小量纲特征形同虚设。标准化成 z-score,各特征等权参与:

// 特征标准化:减均值除标准差
feats = select
    (p_mean    - avg(p_mean))    \ std(p_mean)     as z_pmean,
    (p_std     - avg(p_std))     \ std(p_std)      as z_pstd,
    (low_ratio - avg(low_ratio)) \ std(low_ratio)  as z_low,
    (high_ratio- avg(high_ratio))\ std(high_ratio) as z_high
from winFeat

2.3 设备指纹:长周期特征

设备分群用更长周期的"性格"统计——日均功率、波动、待机占比、负载率,一台设备一行:

// 设备指纹:3 月整月的运行性格
profile = select deviceId,
    avg(power)                                as avg_power,
    std(power)                                as power_std,
    sum(iif(power < 5.0, 1, 0)) \ count(*)    as idle_ratio,
    avg(power) \ 80.0                         as load_factor   // 负载率(额定 80kW)
from loadTable("dfs://iot", "power")
where ts between datetime(2026.03.01) : datetime(2026.03.31)
group by deviceId


三、第一层:工况自动分段(时间维度)

3.1 聚类:kmeans 三步走

把标准化特征喂给 kmeans,聚成三类:

// 特征矩阵 + kmeans(k=3,固定随机种子保证可复现)
X = matrix(feats`z_pmean, feats`z_pstd, feats`z_low, feats`z_high)
kmodel = kmeans(X, 3, 10, 0.01, 42)

// 结果包含:每个样本的簇号 + 各簇质心
// 把簇号拼回窗口表
taggedWin = select deviceId, window, p_mean, p_std, low_ratio,
                   kmodel 中的簇号列 as cluster
from winFeat

到这里算法部分就结束了——十分钟的事。真正的功夫在下一步。

3.2 解读:簇号变工况名

kmeans 只会告诉你"簇 0/1/2",不会告诉你哪个是运行。簇的语义靠人解读:算每个簇在原始特征上的均值,对着物理含义命名。

// 各簇的原始特征均值——解读的依据
select cluster,
    count(*)        as win_count,
    avg(p_mean)     as avg_power,
    avg(p_std)      as avg_fluct,
    avg(low_ratio)  as avg_low
from taggedWin
group by cluster

假想解读结果:

簇 平均功率 波动 低功率占比 人工命名
0 68 kW 3.1 0.02 RUNNING(稳定运行)
1 2.4 kW 0.8 0.97 IDLE(待机)
2 35 kW 19.5 0.41 CYCLING(间歇/空转嫌疑)

命名的纪律:命名写进一张映射表,后续所有分析引用映射表,而不是把"簇 2 = 空转"硬编码进查询——下次重训聚类,簇号会变,映射表一改全链路生效。

3.3 从分段到经营指标

工况时间线出来后,最有价值的一条统计是空转/间歇时长——设备开着电耗着,但没在干活:

// 空转时长与估算浪费电量(间歇段功率均值 × 时长)
select deviceId,
    count(*) * 15                    as cycling_minutes,
    sum(p_mean) * 0.25               as wasted_kwh   // 每窗 0.25 小时
from taggedWin
where cluster = 2
group by deviceId
order by wasted_kwh desc

按浪费电量排序的设备清单,就是现场治理的第一张工单。注意这里的"空转"是嫌疑清单不是定罪——间歇工况也可能是正常的工艺节拍(批次生产本来就是间歇的),清单交给工艺的人裁决。聚类给坐标,人给结论。

3.4 k 怎么选与工况漂移

两个绕不开的工程问题:

  • k 的选择:理论上可以算轮廓系数、画肘部图,但工程上更务实的做法是在 2~5 之间试,看哪个 k 的簇解读起来物理上最通顺。k 不是算出来的,是"解读得动"的那个

  • 工况漂移:设备老化、季节变化都会让工况分布移动。指纹窗口特征要定期重算重训(比如每月),并把新旧工况命名做一次对照——簇号变了不要紧,映射表保证语义连续


四、第二层:设备群体分群(设备维度)

4.1 同型群的发现

设备指纹表(一台设备一行)同样标准化后聚类:

// 设备分群:按运行性格聚成同型组
Xp = matrix(stdFeats`z_avgpower, stdFeats`z_powerstd,
            stdFeats`z_idleratio, stdFeats`z_loadfactor)
pModel = kmeans(Xp, 4, 10, 0.01, 42)

// 拼回指纹表:每台设备一个同型组号
groupedDev = select deviceId, avg_power, idle_ratio, load_factor,
                   簇号 as peerGroup
from profile

解读同型组比解读工况更容易——比如可能得到:"第 2 组 = 高负载连续运转的重型泵群"、"第 3 组 = 低负载高待机的备机群"。同型组是数据定义的,不是型号表定义的:同型号的设备可能因为安装位置、工艺位置不同而"性格"迥异,分群比型号表更贴近实际运行。

4.2 同型群的两个用法

用法一:组内基线。 能耗、待机率、负载率,都应该和同型组比而不是和全厂比——重型泵群和备机群比待机率没有意义。同型组的均值就是天然基线:

// 组内对比:偏离同型组均值的设备
select d.deviceId, d.peerGroup,
    d.idle_ratio,
    g.group_avg_idle,
    d.idle_ratio \ g.group_avg_idle   as ratio_to_group
from groupedDev as d
inner join (
    select peerGroup, avg(idle_ratio) as group_avg_idle
    from groupedDev group by peerGroup
) as g on d.peerGroup = g.peerGroup
where d.idle_ratio > g.group_avg_idle * 1.5

用法二:离群发现。 一台设备到它所属簇质心的距离就是离群得分——距离越远,"性格"越不合群。离群设备值得看两眼:可能是工况特殊(合理),也可能是故障早期(设备的"性格变化"先于参数越限):

// 离群得分 = 标准化特征到所属簇质心的欧氏距离
// score 大的设备 = 行为偏离同型群

4.3 新设备归群

新设备接入后跑一段时间,算出它的指纹,套用现有质心就近归群——不用重跑聚类,模型复用。归群后的新设备立刻享受组内基线的待遇,无需冷启动。


五、工程细节与坑

5.1 标准化不做,聚类白做

再说一遍因为它最重要:量纲差异会让距离计算完全被大量纲特征绑架。z-score 标准化是聚类的入场券,不是可选步骤。标准化的均值和标准差要用全量数据算(不是逐行各自算),并保存下来——新样本归群时要用同一套标准化参数。

5.2 聚类结果不稳定,纪律来兜底

kmeans 对初始质心敏感,重跑可能得到不同的簇号排列。两个纪律:固定随机种子保证可复现;簇语义走映射表而不是硬编码簇号,簇号怎么变都不影响下游。

5.3 特征比算法重要十倍

kmeans 换成别的聚类算法,结果差异通常远小于特征设计的差异。一个自检方法:解读簇的时候如果说不清"这一簇为什么聚在一起",问题几乎总在特征——要么物理含义不清,要么混进了无关特征(比如把设备编号当特征喂进去)。

5.4 聚类不取代规则

工况分段出来后,有人会问"那还写什么运行状态判断规则?"。答案是互补:规则适合明确的、稳定的判定(成本低、可解释、实时);聚类适合规则写不出来的模糊地带(发现结构、建基线组)。工程上常见组合:聚类离线发现结构 → 结构转成规则参数 → 规则上线跑实时。聚类是规则的上游,不是对手。

5.5 簇是快照,会过期

指纹来自一段时间的数据,设备会换工况、会老化、会大修。分群结果要有"生成日期",定期重训,重要决策(比如基线对比)别拿半年前的分群当依据。


六、写在最后

平台上的有监督分析——告警、评分、预测——都在回答已经定义好的问题。无监督分析是另一只眼睛:回答还没被定义的问题。

本文两层场景,一句话各自收拢:

  • 工况自动分段:短窗指纹 + kmeans + 人工命名映射表,把没人记录的工况从数据里挖出来,空转嫌疑清单直接换成电费语言

  • 设备群体分群:运行性格指纹聚成同型组,组内基线替代"全厂一刀切"的对比,离群设备是故障的早期线索

四个值得带走的工程判断:

  • 特征设计决定一切:每个特征都要有物理含义,解读簇的时候全靠它

  • 标准化是入场券:不做的聚类是大量纲特征的独角戏

  • 簇语义走映射表:算法给簇号,人给名字,名字进配置不进代码

  • 聚类给坐标,人给结论:空转是嫌疑不是定罪,离群是线索不是故障

最后回到那句分界:有监督回答"我问什么你答什么",无监督回答"我还不知道该问什么"。两者交替使用——聚类发现的结构,转成规则和标签,喂给下一轮有监督分析。数据平台的分析能力,就是这样一圈一圈长出来的。


参考链接:

DolphinDB 官网:DolphinDB 智能数据平台

DolphinDB 官方文档:DolphinDB 技术文档

posted @ 2026-10-31 17:45  Xxtaoaooo  阅读(3)  评论(0)    收藏  举报