上一篇(五)我们把数据闭环整条跑通了:四种转速各录一段,软件在 21837 种库组合里挑出冠军模型 Library 116(LSTM),RAM 3.7 KB、Flash 4.1 KB、单次推理 4.4 ms,平衡准确率 100%,烧回板子后串口一路 CLASS DETECTED,看起来一切尽在掌握。
但真把板子放在桌上连跑几天,问题就浮出来了:分类模型只认识我教过它的那几种状态。一旦马达出了我压根没录过的故障,它不会说「这不对劲」,而是硬把数据塞进四个已知类别里的某一个,然后依旧自信地报出 100%。
这个坑我自己先踩了一次。前面训练步进马达的过程中,偶尔出现马达停转并且异常发热的情况,一路查下来是杜邦线接触不良,造成步进马达缺相(断相)。

图 1 ULN2003 驱动板:排针与白色插座是接触不良最集中的位置,任何一相虚接,四相节拍就变成三相或其它
这类故障在真实设备上不会提前打招呼——端子松动、线缆被反复拉扯疲劳、驱动芯片过热失效,轻则失步异响,重则绕组温度过高甚至烧坏,更严重的还会起火。所以这一期我换了个思路:不再教模型「认识故障」,而是只教它「什么叫正常」,凡是偏离正常的,一律报警。这就是 NanoEdge AI Studio 的 AD(Anomaly Detection,异常检测)模式——它这次不仅想抓缺相,堵转、卡滞、负载突变这些我没见过的异常,也希望能一并拦下来。
整体流程仍是那五步:Project Settings → Signals → Benchmark → Validation → Deployment,只是每一步的心态完全变了。过程如下。
一、新建项目:在 What are we doing today 里选 AD
打开 NanoEdge AI Studio 5.2.0,新建项目时软件会先问一句「What are we doing today?」,四个入口对应四种学习范式:
• Detect Anomalies(AD):用端侧学习识别信号里的异常模式,可以在学习阶段持续积累知识,预测潜在的异常行为;
• Classify variations(C):给数据打标签做分类,上一篇走的就是这条路;
• Detect outliers(O):判断样本是否属于那一个唯一类别,适合数据高度不平衡、只关心一类的情形;
• Extrapolate(E):按已知模式外推未知数据,做趋势预测。
这次点第一行 AD 的 CREATE,左侧工具栏随即出现绿色高亮的 AD 徽标,整个工程从根上就和上一篇不同了。
两种范式最本质的差别,在于要不要预先知道故障长什么样。分类必须先录到故障数据、打上标签,可缺相、堵转这些故障恰恰最难在产线上按需复现;异常检测只要把「正常」喂饱,剩下的交给「不像」两个字。

图 2 What are we doing today?:AD / C / O / E 四种范式,本期选第一行 Detect Anomalies 的 CREATE
二、Project Settings:板子和传感器沿用上一期
项目设置页与上一篇完全同构,只是模式徽标从 C 换成了 AD。项目名沿用自动生成的 Project-2026-10-29-16-38,描述我直接写成「步进马达断相检测」,方便日后回看:
• Target:STMICROELECTRONICS - NUCLEO-H533RE,Cortex-M33,Internal RAM 272 KB / Internal Flash 512 KB,左侧 Memory Footprint 自动带出 Library Max RAM 272 KB、Max Flash 512 KB;
• Sensor:Time Series → Accelerometer → 3 Axes,对应胶贴在马达壳体上的那只 LIS3DH;
• 注意页面底部那行灰字警告:Sensor 设置在项目保存之后不能再改,想换轴数或换传感器,只能重建项目。
硬件一行没动:LIS3DH 仍贴在马达上,按上一篇配好的 100 Hz / ±2g 连读;ULN2003 仍由四个 GPIO 驱动。上一篇攒下的采集固件、接线、波特率全部照用——这也是 NanoEdge AI Studio 让人舒服的地方,换算法范式不需要换硬件。
三、一行命令,把固件从 AI 模式切回 Data 采集模式
上一篇交付完代码后,板子上跑的是「推理版」固件:数据口静默,控制台一路刷推理结果。要采新数据就得回去重烧采集程序?我在固件里做了双通道,直接敲命令就行。
控制台口敲 help 能列出全部命令:ver | info | model | sink | start | stop | ai | data | both | rst,再用 listcmd 可以看到每条的自解释说明,其中关键的几个是:
• ai:只推理,数据口不发字节(上电默认,由 app_cfg.h 的 APP_CFG_DATA_SINK 决定);
• data:只把原始 CSV 从数据口上行,专供再次采集;
• both:又推理又发 CSV,联调对照时特别好用;
• model:打印当前实现的类 ID、标签、帧去向与输入契约;
• rst:清零计数但不重启采集。
在命令行输入 data,回显 #mode=DATASET 与 #sink=UPLINK csv-only: raw CSV on data port, no #AI,921600 的数据口立刻开始吐三轴原始 CSV;想切回推理,输入 ai,回显 #mode=AI model=neai-classifier-6ae2a08ef5f380e32de051b2 与 #sink=MODEL ai-only,数据口安静下来,#AI 结果重新回到控制台。
截图左侧那段 #AI,3,Motor-STOP,p=0,0,0,100,rms=8 正是上一篇那个分类模型在报「停止」,右侧串口助手的 COM15-921600 则是留给 NanoEdge AI Studio 的数据通道。两个口不混流,采集时不会被推理输出污染,这是上一期固件设计里最值回票价的一处。

图 3 Project Settings:描述「步进马达断相检测」,Target 仍是 NUCLEO-H533RE + Accelerometer 3 Axes
四、采集:Regular 用来学,Abnormal 只用来看
Signals 页的布局和上一篇不同:左栏不再是 Class 1/2/3/4,而是分成 Regular signals 与 Abnormal signals 两组。这个划分本身就说明了 AD 的逻辑——Regular 用来学「什么是正常」,Abnormal 只用来验证「异常能不能被抓出来」,它不参与训练。
按老办法录:马达正常转动,一档一档来,每条信号 768 列 = 3 轴 × 256 采样点,100 Hz 下正好是 2.56 秒一帧。这次刻意多采了几组,希望把「正常」的边界撑得更实:Regular 共 4 条(107 / 256 / 106 / 110 行,合计 579 行)。
然后是「造故障」,这一步是整个流程里最不体面的体力活:
• 断相(缺相):马达运转中把 ULN2003 某一相的杜邦线拔掉或虚接,马达立刻失步、抖动、声音变大、发热;
采完在 Signals 页就能看出差别:Regular 的三轴时域波形包络收得很紧,Axis 3 基线稳在 1000 附近(那正是 1g 重力落在该轴上的分量),FFT 主峰干脆;Abnormal 的曲线明显更「毛」,包络发散、低频段能量抬起来。频谱横轴是归一化频率,0.46 大约对应 46 Hz(100 Hz 采样下奈奎斯特为 50 Hz)。这些「不像」,就是模型要学的东西。

图 5 Signals 页:左栏 Regular signals 4 条 / Abnormal signals 2 条,每条 768 列(3 轴 × 256 点);中为三轴时域与 FFT,下为 Browser 叠加对比
五、Benchmark:50888 种组合,让它自己慢慢练
点 START,软件开始自动搜索整条机器学习流水线。AD 模式的搜索空间比上一篇更大:本例测了 50888 种库组合,耗时 02 h 44 min 03 s。因修炼时间比较长,可以让它自己在那儿慢慢练,人去干别的事。
练完的结果页信息量很足:
• Quality Index 93.9 pts、Balanced Accuracy 95.9%;
• RAM 10.2 KB(其中含 Buffer 3.1 KB)、Flash 2.3 KB;
• ML Pipeline 高亮路径:Dimension Reduction No → Detrend No → STFT No → Features Extraction Yes → Pooling No → Absolute Value No → Interleaved Operation No → Model ICM;
• Best Library Results 散点图:绿色(正常)几乎全贴在 100% 一线,红色(异常)散落在 10%~65%,中间那条虚线就是判决阈值;
• Best Library Confusion Matrix:579 个正常样本判对 577、误报 2;207 个异常样本报出 191、漏掉 16。
平衡准确率就是这么来的:正常侧 577/579 = 99.66%,异常侧 191/207 = 92.27%,两者平均 95.96%。看异常检测一定要把这个方向拆开看——只报总准确率会被「正常样本多」骗过去,一个什么都不报的模型也能白拿 73.6%。
还有个值得记一笔的对比:上一篇分类模型 RAM 3.7 KB / Flash 4.1 KB,这一篇异常检测正好反过来,RAM 涨到 10.2 KB、Flash 降到 2.3 KB。ICM 要在端侧持续维护一套「正常」的记忆结构,运行时内存自然大些,代码本身反倒更短。
图 6 Benchmark 完成:50888 种组合、2 h 44 min,质量指数 93.9 / 平衡准确率 95.9%,散点与混淆矩阵把 579 正常、207 异常分得很清楚
六、Validation:成绩单与「凭手感」选型
Validation 把候选模型排成一张成绩单,列出 Library ID、Quality Index、Balanced Accuracy、RAM、Flash、Minimum Learning Iteration、Execution Time 与 Model。前几名咬得非常紧:
• ID 89(带皇冠):93.9 pts / 95.93% / RAM 10.2 KB / Flash 2.3 KB / 最小学习 74 / ICM;
• ID 88:93.8 / 95.90% / 11.9 KB / 3.8 KB / 74 / ICM;
• ID 87:93.7 / 95.89% / 10.3 KB / 4.1 KB / 93 / ICM;
• ID 78:93.7 / 95.93% / 10.0 KB / 3.9 KB / 117 / ICM;
• ID 86:93.3 / 95.62% / 9.0 KB / 1.3 KB / 47 / ZSM——体积最小、上手最快,但准确率掉了一档。
Minimum Learning Iteration 这一列是 AD 模式特有的指标,代表模型需要多少个信号才能把「正常」学会。一帧 256 点、100 Hz 下是 2.56 秒,ID 89 只要 74 帧,约 3 分钟就能进入可靠检测;ID 61 要 147 帧,得先旁听 6 分多钟。对需要开机即报警的场合,这一列的权重不比准确率低。
说实话,选模型时我总不知道选哪个为好——前五个的质量指数只差 0.2 分,准确率更是咬死。我的做法是多试几次:左侧 NEW EXPERIMENT 再跑一轮 Benchmark,把两三轮的成绩单横向摆开,再凭经验挑一个感觉最好的。本期最终选了带皇冠的 ID 89,准确率、体积、学习速度三项都不吃亏。

图 7 Validation 成绩单:候选模型密集挤在 93 分附近,ID 89(ICM)综合最优;Minimum Learning Iteration 是 AD 模式独有的选型维度
七、Emulator:先在上位机把整场戏演一遍
每个模型右侧的播放键都能打开 Emulator,从串口喂数据进去,不烧板子就能看效果。AD 模式的界面比上一篇「素」很多:没有四类概率柱状图,也没有环形图,只剩一个大数字 SIMILARITY 和一条 Threshold 虚线(默认 90)。
先要经历学习阶段。图 8 是 ID 88 的学习过程:横轴是喂进去的信号,纵轴 Iteration Number 一路爬到 74 就停住,那条虚线正是 Minimum Learning Iteration;77 个信号完成「认门」之后,右下角的 GO TO DETECTION 才会亮起。
图 8 Emulator 学习阶段(Library 88):迭代到 74 次即达 Minimum Learning Iteration,学习期走完才能进入检测
进入检测就是图 9 的样子:正常段相似度稳稳压在 100%,中间喂入异常数据的那一段(第 45~72 次)骤降到 25%~60%,全部落在阈值 90 以下被标红;换回正常数据后曲线立刻弹回 100%。右侧饼图统计这一轮 555 次检测里 Regular 506 / Abnormal 49,学习阶段用了 242 个信号。

图 9 Emulator 检测(Library 89):正常段相似度 100%,中段异常跌到 25%~60% 触发报警,555 次检测分出 506 正常 / 49 异常
图 10 是我故意反着喂的一轮:开头 33 次全是异常数据,相似度在 25%~48% 之间乱跳、整段标红(322 次判为异常),换成正常数据后立刻回到 100%(110 次)。这一轮 432 个信号、学习用了 128 个。它说明两件事:模型不会因为先见到异常就被带跑;同时也提醒一句,学习期内的相似度抖动不作数,别在模型还没学够的时候下结论。

图 10 反序验证(Library 89):先喂 322 次异常再喂 110 次正常,曲线在切换点干脆地从 30% 档跳回 100%
阈值是可以动的。默认 90 偏保守,误报会多一些;往下压则漏报减少、但正常波动更容易被误伤。我的做法是拿 Regular 数据先跑一遍,看相似度的下沿落在哪里,再把阈值定在那条线之上留一点余量。
八、Deployment:生成库与代码,回 CubeIDE 上板
点 DEPLOY LIBRARY 进入 Deployment。工具链仍选 GNU Arm Embedded (GCC),编译标志保持默认:勾选 mfloat-abi(使用硬浮点 ABI,注意硬/软浮点不能混链)、不勾 fshort-wchar、勾选 fshort-enums(省内存)。右侧同步生成可直接抄进 main 的示例代码,头部是 ST 的 BSD-3 版权声明,注释写着 Demo: NanoEdge AI process to include in main program body。
图 11 Deployment:GNU Arm Embedded (GCC) 与编译标志(mfloat-abi、fshort-enums 勾选),右侧为可复制的示例代码
弹出的 Compile 面板把交付内容说得很清楚:Target NUCLEO-H533RE、Cortex-M33、Max RAM 272 KB / Max Flash 512 KB,Selected library ID 89、Model ICM、Buffer size 3 * 256、Sensor Type Accelerometer,指标 Quality Index 93.9 / Balanced Accuracy 95.9% / RAM 10.2 KB / Flash 2.3 KB,右下角 GET LIBRARY .a 一键下载静态库。
5.2.0 里有个我很喜欢的新提示:Explore your library's algorithm and preprocessing details with any LLM——下载的包里附带了 JSON 描述和一个 prompt 示例,把模型内部结构与预处理细节交给大模型去解读,省掉自己啃库的功夫。

图 12 Compile 面板:库 ID 89 / ICM / Buffer 3×256 / RAM 10.2 KB / Flash 2.3 KB,并附带可交给 LLM 解读的 JSON 与 prompt
回到 IDE,改动依旧只有两处:把新的 libneai.a 换进工程、头文件路径指向新生成的 NanoEdgeAI.h;把上一篇调用「返回类别 ID」的地方,改成异常检测的学习 / 检测两个入口,取回的不再是 0~3 的类别号,而是一个 0~100 的相似度。工程里再加一条判断:相似度低于 90 就置位报警,驱动蜂鸣器、点亮红色 LED,同时把当前 rms 与相似度一并打到串口,方便事后追溯。
烧录上电,马达正常转动时串口稳定输出相似度 100%;伸手拔掉 ULN2003 上任意一相的杜邦线,马达声音立刻变尖、抖动变大,相似度在下一帧就掉进 30~50% 区间,蜂鸣器跟着叫了起来。而这一刻,模型从没见过断相——它只是知道,这不像它学到的「正常」。
九、踩到的几个坑
• 接触不良既是故障源,也是数据污染源:采 Regular 前一定先把排针、杜邦线压到位,否则把虚接时的抖动当成「正常」学进去,阈值就废了。本期 4 条 Regular 是在确认马达声音、电流都稳定之后才录的。
• Abnormal 不参与训练:AD 模式下放进 Abnormal 组的数据只用于验证,误放进 Regular 会让模型把异常也认作正常——这是整条流程里最隐蔽的一个坑。
• 学习期别急着下结论:没到 Minimum Learning Iteration 之前相似度抖动很大,图 8 里那 74 次是「上课时间」,不是误报。
• 换传感器或轴数要新建项目:Project Settings 底部那行警告不是摆设,保存后 Sensor 配置不可修改。
• RAM 确实涨了:从上一篇的 3.7 KB 涨到 10.2 KB(含 3.1 KB 缓冲)。H533 有 272 KB 无所谓,换到更小容量的 Cortex-M0+ 上就得提前算这本账。
• 异常样本要够多样:断相我实际只拔了某一相,对其他相虚接同样有效,但换一台机械结构、换一个负载,最好补采数据重新 Benchmark。
十、测试结果
下面是实际测试运行输出:
十一、小结
这一期最大的收获其实不是那 93.9 分,而是思维方式的一次切换:分类是「我教你认什么」,异常检测是「我只告诉你什么叫好,坏的地方你自己找」。对马达这种故障模式远多于正常模式的设备,后者显然更划算——不必提前复现断相、堵转、卡滞,只要把正常录扎实,剩下的交给相似度。
数据也很能说明问题:50888 种组合、2 h 44 min 自动搜索,最后落到一个 RAM 10.2 KB / Flash 2.3 KB、74 次学习就能上岗的 ICM 模型上;579 个正常样本只误报 2 次,207 个异常样本报出 191 次。全程没手写一行模型代码,也没动一次硬件接线。
2个多小时的AI训练等待也是太漫长了,其实也不必完全训练100%,组合太多,可能前30%就已经落到最好的模型区。如果急着测试,可以直接暂停继续后面的测试。
感谢 DigiKey 与 EEPW 的支持!
我要赚赏金
