MATLAB滑动RMS如何接报警:窗口未满和缺样不能当成正常

MATLAB滑动RMS如何接报警:窗口未满和缺样不能当成正常

平方、平均、开方并不是实时RMS最难的部分。真正容易出错的是:现在平均了多少个有效样本,它们是否覆盖同一段连续时间,当前"不报警"究竟是数值正常还是数据不足。本文把value、valid和alarm作为一个整体处理。

环境、运行与真实结果

实测环境:MATLAB R2025b(25.2.0.2998904),Windows PCWIN64,2026-10-19。主实验与独立复核仅依赖基础MATLAB;没有用Python/Octave结果代替MATLAB运行。

关键结果:W=32时第31号样本才首次有效;第125号NaN和140号Inf使125至171号窗口无效,172号恢复。

把下方完整代码保存为 causal_rms_demo.m,在该目录运行:

matlab 复制代码
r = causal_rms_demo;

也可以从该目录执行 matlab -batch "r=causal_rms_demo;"。本次实际批处理由总入口调用同一份demo函数;这里给出单篇直接调用方式,未另行声称启动过第二种独立进程。默认在当前源码旁的 results 目录保存PNG、CSV、MAT。

原理、接口与边界

接口 causal_rms_contract(x,W,threshold) 对每个计划采样槽给出 value、valid、alarm、count。W=32、Fs=200 Hz、阈值为 3;完整 W 个计划槽都有限才有效。NaN、+Inf、-Inf 都是无效测量。未满窗或任一槽无效时 RMS=NaN、valid=false、alarm=false;阈值比较为严格大于,等于阈值不报警。

适用范围限定为实数 double:有效值可以是 0,非零绝对值须在 1e-12,1e6;范围外抛 RMS:Range。本例严格 RMS 对每个有效窗独立取 scale=max(abs(w)),计算 scale*sqrt(mean((w/scale).^2));全零窗单独返回 0。没有全历史 cumsum 能量,也没有大历史累计值相减。此范围内非零比值最小 1e-18,平方不会下溢。count 使用 movsum 处理 0/1 有效标志;工程用途仍应依据采样率和量纲设置范围与阈值,不把教学接口当成无限动态范围数值库。

实测事件(样本从 0 计数) 结果
首个有效 RMS n=31,0.155 s
输入 n=80 从 2 跳到 4 首个阈值报警 n=93;延迟 13 点/65 ms
n=125 为 NaN、n=140 为 Inf n=125...171 无效,n=172 恢复
n=260...299 连续缺失 n=260...330 无效,n=331 恢复
首样本的三种启动策略 严格 NaN;缩窗 2;补零 0.353553390593
忽略坏点后直接比较阈值 有 47 个原本质量不合格的窗口给出报警

47 个窗口称为"质量不合格的报警",不能笼统称假阳性,因为其中输入真实幅度确实高。W/Fs=0.16 s 是窗口名义时长,首末采样间跨度 (W-1)/Fs=0.155 s;报警延迟取决于输入、窗口和阈值,不等于固定 W/2。

对照策略分别使用补零、缩短启动窗口、忽略无效值。比较前把 Inf 显式转为 NaN;仅写 omitmissing 并不自动把 Inf 当缺失。图 causal_rms.png 展示启动偏差、有限样本计数、坏点离窗恢复与报警门控。独立复核用未缩放的逐窗平方和核对本例输入,并新增"大值离窗后恢复 1e-12 小值"、全零、W=1、阈值相等、全缺失和范围错误检查。

接入传感器时按以下顺序处理:

  1. 校准 ADC 到物理量单位,把 CRC、超量程、饱和或设备状态异常映射为无效槽。
  2. 保持固定时间栅格;丢帧插入 NaN,不删除行或压缩时间。
  3. 分包处理时保存上一包末尾 W-1 个计划槽(包括坏点);每包重新调用无状态函数会重启满窗等待。
  4. 同时消费 valid 与 alarm。无效窗口不代表传感器恢复正常,也不应自动解除上层安全锁存;数据质量告警单独处理。
  5. 根据业务另行确定滞回、连续触发次数和锁存策略,并用注入故障检验恢复边界。

看图定位错误

启动段要同时看数值与count,不能因为曲线已经画出来就认定有效。缺样区间的NaN不是零,恢复点172和331体现的是最后一个坏点离开整个32点窗口。忽略坏点产生的47次报警是质量不合格,不能直接称假阳性。

RMS包含直流能量,去均值标准差回答另一个问题。本例以每个计划槽为单位;如果删除丢样行后直接输入,就会把时间压缩,函数无法仅凭数组恢复真实时间。

完整可复制代码

以下代码包含全部局部函数、实验输入、断言与出图,不依赖另外下载的辅助脚本。不要把局部函数单独从外部调用;修改数据时在同一demo入口中调用它。

matlab 复制代码
function r = causal_rms_demo(outDir)
% Fixed complete-window RMS and data-quality-aware alarm; base MATLAB.
if nargin<1,outDir=fullfile(fileparts(mfilename('fullpath')),'results');end
if ~exist(outDir,'dir'),mkdir(outDir);end
Fs=200;W=32;threshold=3;n=(0:399)';t=n/Fs;
truth=2*ones(size(n));truth(n>=80 & n<200)=4;
x=truth;x(n==125)=NaN;x(n==140)=Inf;x(n>=260 & n<=299)=NaN;
[strict,valid,alarm,count]=causal_rms_contract(x,W,threshold);
finite=~isnan(x) & ~isinf(x);clean=x;clean(~finite)=0;
sumSq=movsum(clean.^2,[W-1 0],'Endpoints','shrink');
zeroFilled=sqrt(sumSq/W);
omitInvalid=sqrt(sumSq./max(count,1));omitInvalid(count==0)=NaN;
normalizedMissing=x;normalizedMissing(~finite)=NaN;
shrinking=sqrt(movmean(normalizedMissing.^2,[W-1 0],'includemissing','Endpoints','shrink'));
assert(find(valid,1,'first')-1==W-1,'RMS:Startup','Full window contract differs.');
assert(all(~valid(n>=125 & n<=171)) && valid(n==172),'RMS:Recovery','Invalid-sample recovery differs.');
assert(all(~valid(n>=260 & n<=330)) && valid(n==331),'RMS:LongGap','Long-gap recovery differs.');
assert(all(~alarm(~valid)),'RMS:Gate','Invalid window must never alarm.');
assert(find(alarm,1,'first')-1==93,'RMS:AlarmTime','Alarm latency differs.');
assert(abs(shrinking(1)-2)<1e-12 && abs(zeroFilled(1)-2/sqrt(W))<1e-12, ...
    'RMS:StartupControls','Startup policies not exposed.');
unqualifiedOmit=nnz((omitInvalid>threshold) & ~valid);
assert(unqualifiedOmit>0,'RMS:Omission','Omission failed to hide missing quality.');
tab=table(n,t,x,truth,count,valid,strict,alarm,shrinking,zeroFilled,omitInvalid, ...
    'VariableNames',{'Sample','Time','Input','InjectedTruth','FiniteCount','Valid','StrictRMS', ...
    'Alarm','ShrinkingRMS','ZeroFilledRMS','OmitInvalidRMS'});
writetable(tab,fullfile(outDir,'causal_rms_samples.csv'));
fig=figure('Visible','off','Color','w','Position',[80 80 1150 850]);
subplot(2,2,1);plot(n,truth,'k-',n,strict,'b-',n,zeroFilled,'r--',n,omitInvalid,'m:');grid on;
xlabel('Sample index');ylabel('Input / RMS');ylim([0 4.6]);
legend('Injected truth','Strict','Zero filled','Omit invalid','Location','best');
title('Missing values are not known zeros');
subplot(2,2,2);plot(n,shrinking,'m-',n,zeroFilled,'r--',n,strict,'b-');grid on;
xlim([0 45]);ylim([0 2.3]);xlabel('Sample index');ylabel('RMS');
legend('Shrinking','Zero filled','Strict','Location','best');title('No qualified estimate before sample 31');
subplot(2,2,3);stairs(n,count,'k-');hold on;stairs(n,W*double(valid),'b--');grid on;
xlim([110 190]);ylim([0 35]);xlabel('Sample index');ylabel('Finite samples in last 32');
legend('Count','32 when valid','Location','best');title('NaN at 125, Inf at 140: recovery at 172');
subplot(2,2,4);stairs(n,double(alarm),'b-');hold on;
stairs(n,double(omitInvalid>threshold),'m--');grid on;
xlim([70 230]);ylim([-0.1 1.2]);xlabel('Sample index');ylabel('Alarm state');
legend('Qualified alarm','Omit-invalid threshold only','Location','best');
title('Quality gating is separate from the RMS threshold');
set(findall(fig,'Type','axes'),'FontSize',10);
print(fig,fullfile(outDir,'causal_rms.png'),'-dpng','-r150');close(fig);
r=struct('Fs',Fs,'windowSamples',W,'sampleSpanSeconds',(W-1)/Fs, ...
    'nominalWindowSeconds',W/Fs,'threshold',threshold, ...
    'firstValidIndex',find(valid,1,'first')-1,'firstAlarmIndex',find(alarm,1,'first')-1, ...
    'recoveryAfterInfIndex',172,'recoveryAfterLongGapIndex',331, ...
    'unqualifiedOmitAlarmSamples',unqualifiedOmit,'startupShrinkingRMS',shrinking(1), ...
    'startupZeroFilledRMS',zeroFilled(1),'validSamples',nnz(valid),'alarmSamples',nnz(alarm), ...
    'contract','Every one of the latest W scheduled samples must be present and finite; otherwise RMS=NaN and alarm=false.');
save(fullfile(outDir,'causal_rms.mat'),'r','x','truth','t','n','strict','valid','alarm','count','tab');
fprintf('RMS PASS first_valid=%d first_alarm=%d recovery=172 long_gap_recovery=331 omit_unqualified=%d\n', ...
    r.firstValidIndex,r.firstAlarmIndex,unqualifiedOmit);
fprintf('RMS startup shrinking=%.12g zero_filled=%.12g strict=NaN\n',shrinking(1),zeroFilled(1));
end

function [value,valid,alarm,count] = causal_rms_contract(x,W,threshold)
% One value per scheduled sample. NaN and either infinity mean missing.
validateattributes(x,{'double'},{'real','vector','nonempty'});
validateattributes(W,{'double'},{'scalar','integer','positive','finite'});
validateattributes(threshold,{'double'},{'real','scalar','positive','finite'});
x=x(:);finite=~isnan(x) & ~isinf(x);
% Educational sensor range: zero, or finite magnitudes in [1e-12, 1e6].
assert(all(x(finite)==0 | (abs(x(finite))>=1e-12 & abs(x(finite))<=1e6)), ...
    'RMS:Range','Supported finite inputs are zero or magnitudes from 1e-12 to 1e6.');
count=movsum(double(finite),[W-1 0],'Endpoints','shrink');
valid=((1:numel(x))'>=W) & (count==W);
value=nan(size(x));
% Scale each independent window; no cumulative energy or historical subtraction.
for k=W:numel(x)
    if valid(k)
        w=x(k-W+1:k);scale=max(abs(w));
        if scale==0,value(k)=0;else,value(k)=scale*sqrt(mean((w/scale).^2));end
    end
end
alarm=valid & value>threshold;
end

本次如何交叉核对

独立检查用逐窗口直接平方和核对本例正常幅值,并测试全零、W=1、阈值恰好相等、全缺失和范围错误。另加入大值离窗后恢复到1e-12小值的例子,确认结果不会被全历史累计和污染。

这些是实际运行后的数值交叉检查;公开代码中的断言保证核心实验可直接复现。输出里的NaN表示该指标不能由当前记录确认,不应替换成0。

相关入口与复现问题

CSV采样日志清洗。

把一个坏点放在阈值附近,补零、忽略坏点与严格完整窗会在哪些采样点给出不同结论?

官方资料

MathWorks movmean、movsum用于核对后向窗、端点缩窗及缺失值处理。Inf必须显式归类,不能假定omitmissing自动代表所有无效测量。

资料核查于2026-10-19;当前官方页面可能显示较新版本,本文运行环境以实际R2025b记录为准。

相关推荐
开开心心就好1 小时前
白天晚上自动调蓝光,护眼工具亮度可调
服务器·开发语言·算法·pdf·ocr·intellij-idea·idea
张张123y1 小时前
后端第十一课:RAG 落地实战(从 0 到 1 接上私有知识库,并设计一个好用的 RAG)
开发语言·人工智能·python·机器学习·fastapi·rag
梅梅绵绵冰1 小时前
JVM虚拟机
java·开发语言·jvm
Shaoshing1 小时前
消息顺序
java·开发语言
云水初2 小时前
【项目篇】基于LanGraph的自动处理客服邮件的 Agent汇总
开发语言·笔记·python·学习·fastapi·langgraph
茉莉玫瑰花茶2 小时前
GO [ 内存与变量逃逸分析 ]
开发语言·后端·golang
lhh_qrsly2 小时前
OTA 加密完整方案(嵌入式 IoT 常用)
开发语言·物联网·php
Nontee2 小时前
Quartz 新手入门:定时任务的两个承诺
开发语言·windows·python
\光辉岁月/2 小时前
2.异常类
java·开发语言·异常类