ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

TIA博途V15中SCL实现滑动平均值滤波的工程实践

TIA博途V15中SCL实现滑动平均值滤波的工程实践 简介基于TIA博途V15的滑动平均值滤波算法SCL语言程序文档面向需要处理模拟量信号噪声与波动的PLC工程师。内容系统讲解TIA博途V15环境下使用SCL语言实现滑动平均滤波的完整过程先在程序块中创建FB并定义IN、Average、WindowSize等接口参数再以环型队列维护固定窗口的实时数据每次新值到达时按先进先出更新队列并计算窗口平均值从而平滑输入信号、提升系统稳定性。文档还给出OB30周期性中断任务中调用FB、连接MD500模拟量地址进行仿真测试的操作方法并演示如何将滤波FB封装为全局库便于在后续项目中直接拖用、免去重复编程。全文围绕项目创建、算法流程、SCL代码、测试验证四部分展开配有接口图、流程图与程序参考适合具备一定PLC基础、希望快速落地滤波算法的工程技术人员。资料为单个docx文档压缩包约694KB轻量实用目前已有2349人学习下载对同类工控算法应用有较高参考价值。1. 一份V15的SCL滤波文档为什么值得你放下手里的FBD重读一遍现场遇到模拟量信号毛刺温度曲线抖动得像心电图PID被高频噪声带得输出飘忽——这种时候大多数人第一反应是加个时间常数或者用比较器跳变再不行就上FBD堆一个平均值模块。直到我看到《基于TIA博途的滑动平均值滤波算法SCL语言程序V15》这份文档才发现自己过去几年在LAD/FBD里绕了不少弯路。它给我的不是一段能直接粘贴的魔法代码而是一整套在TIA博途V15里用SCL语言实现滑动平均值滤波的工程范式环形缓冲区怎么建、累加和怎么维护、窗口长度放哪儿、上电初值怎么处理全讲清楚了。这份材料适合谁调试S7-1200/1500模拟量通道、写PID前级滤波、处理称重或流量信号噪声的自动化工程师。如果你正被传感器噪声折磨又不想动不动就上高成本的硬件滤波这篇文章就把文档里的思路拆开揉碎从原理讲到参数标定再到复制代码时最容易翻车的那些细节最后给一段可以直接抄进TIA博途V15的SCL程序骨架。2. 滑动平均值滤波的工程原理为什么它比一阶低通更适合PLC场景2.1 滑动平均的数学本质窗口、延迟与噪声衰减倍数滑动平均值滤波做的事情非常朴素维护最近N个采样值输出这N个数的算术平均值。用式子写就是y(k) (x(k) x(k-1) ... x(k-N1)) / N。每来一个新采样就把最老的那个值踢出去把新值加进来再算一次平均。这个「踢旧加新」的动作决定了它和一阶低通滤波有本质区别。一阶低通比如y(k) a*y(k-1) (1-a)*x(k)的惯性系数α需要根据采样周期和截止频率反复凑凑不好现场就是振荡。滑动平均呢唯一的参数就是窗口长度N没有系数可调物理意义一目了然N越大曲线越平但是延迟越大。对于白噪声输出噪声幅度的衰减倍数大约是1/√N——窗口取16噪声残留降到原来的四分之一窗口取64降到八分之一。这个特性让我在很多项目里宁可牺牲一点响应速度也不去做一阶低通的参数扫描。代价同样要算清楚信号的纯滞后是(N-1)/2个采样周期。采样周期100ms、窗口16意味着你看到的平均值大约比真实值滞后750ms。做温度、液位这种大惯性对象完全无感但如果是流量快速波动或者需要快速响应的压力控制窗口就得往小压或者改用下一章说到的加权做法。2.2 为什么用SCL语言而不是FBD/LAD来写这个滤波算法标题里点明了「SCL语言程序」不是随便选的。同样是滑动平均用FBD画出来是什么状态窗口长度8就要画8个移位寄存器加8个加法器再除8改窗口要重新拉线窗口32以上画面能占好几屏检查连线查到眼瞎。LAD的移位指令做固定长度还行但你要在运行中看缓冲区状态、做边界判断很不顺手。SCL语言在TIA博途里写这类算法有四个碾压优势。第一数组和循环是天生的语法ARRAY[0..N-1]加FOR循环几行就描述完整个窗口移位第二静态变量的接口表里能直接看到缓冲区、累加和、索引的当前值调试时监控表一拉全出来第三算法逻辑用文本呈现代码评审、复制到文档、跨工程师移交都比看图省事第四SCL在S7-1200/1500上是完整指令集支持如果你用的CPU是S7-1500STL反而受到限制SCL才是主力。我通常的做法是凡是超过3个FB或者含有数组、循环、状态机的算法一律直接用SCL写不在FBD里死磕。标题里这份文档的SCL程序虽然不是TIA工程文件只是一个.docx但里面代码量并不大核心逻辑半小时就能复现进工程远比在FBD里重画一遍省时间。2.3 TIA博途V15版本兼容性与这份docx能复用多少先说结论这份文档给的SCL代码在V15环境下写就但代码本身是文本V15之后的所有版本——V16、V17、V18——都能直接复用。TIA博途的SCL语法从V14到V18的演进非常平稳像:赋值、IF/ELSE、FOR循环这些基础语法没有变文档里的代码抄到V17里编译不会报警告。需要留意的是工程文件层面的兼容方向V16可以打开V15创建的项目V17可以打开V16但反过来不行。如果现场还在用V14甚至V13 SP2没法直接打开这个V15的工程或库文件只能新建一个FB然后把SCL代码逐行敲进去。这份docx的好处恰恰在这里——它不依赖工程格式代码以文本形式存在跨版本迁移成本几乎为零。我在V17里恢复旧项目时踩过一个坑直接从V15的Word文档复制代码进SCL编辑器会带进来一堆全角括号和弯引号编译直接报错这个细节后面避坑章节专门讲。所以拿到这份docx别急着粘贴先把代码过一遍替换成半角符号再往编辑器里放。3. 落地到TIA博途V15SCL滑动平均滤波FB的实现步骤3.1 在TIA博途V15里创建FB_AVG_FILTER接口定义与静态变量规划打开TIA博途V15在程序块文件夹里点「添加新块」选择函数块FB语言下拉框选SCL名字我习惯叫FB_AVG_FILTER编号可以自动分配。V15的块属性弹窗里可以勾选「在多重实例中使用」这里不需要直接单实例就行。块接口的规划是整个滤波器的地基我们在Input/Output/Static/Constant四个区域里分头设计。输入有三个VALUE是原始采样值REAL类型RUN为TRUE时执行滤波FALSE时保持输出不更新RESET为TRUE时清空整个缓冲区。输出有两个AVG是滤波结果VALID表示缓冲区是否已经填满——这个信号后面会讲为什么重要。静态变量放核心工作区BUFFER是长度50的REAL数组SUM是累加和INDEX指向当前要覆盖的最旧位置FILL_COUNT记录已填充点数。注意V15里数组下标从哪里开始是声明时定的声明ARRAY[0..49]就是从0开始。常量区定义一个WINDOW_SIZE : 50编译时会把整个块的窗口长度锁死成50。为什么不把窗口长度做成Input变量因为TIA博途的SCL在V15不支持动态数组数组长度必须是常量这一点后面避坑章节再展开。3.2 SCL核心代码环形缓冲区、累加和与索引回绕下面这段就是标题里那份文档的算法主体我用SCL语言重写并加了注释可以直接抄进刚才建的FB// 环形缓冲区滑动平均滤波器 // 输入: VALUE原始采样 RUN采样使能 RESET清空 // 输出: AVG滤波结果 VALID缓冲区已满 // 常量: WINDOW_SIZE窗口长度(这里固定为50) IF #RESET THEN // 复位动作: 清空缓冲区、累加和与索引 #INDEX : 0; #SUM : 0.0; #FILL_COUNT : 0; #VALID : FALSE; FOR #i : 0 TO #WINDOW_SIZE - 1 DO #BUFFER[#i] : 0.0; END_FOR; END_IF; IF #RUN AND NOT #RESET THEN // 冷启动处理: 缓冲区全零时直接用首值填满窗口 // 避免投运瞬间输出从0逐步爬升, 造成假信号 IF #FILL_COUNT 0 THEN FOR #i : 0 TO #WINDOW_SIZE - 1 DO #BUFFER[#i] : #VALUE; END_FOR; #SUM : #VALUE * INT_TO_REAL(#WINDOW_SIZE); #FILL_COUNT : #WINDOW_SIZE; #INDEX : 0; END_IF; // 环形覆盖: 先减掉最旧值, 再写入新值, 累加和保持实时 #SUM : #SUM - #BUFFER[#INDEX]; #BUFFER[#INDEX] : #VALUE; #SUM : #SUM #VALUE; #INDEX : #INDEX 1; // 索引回绕: 到达窗口末尾时重新指向0 IF #INDEX #WINDOW_SIZE THEN #INDEX : 0; END_IF; // 输出平均值, 标记缓冲区有效 #AVG : #SUM / INT_TO_REAL(#WINDOW_SIZE); #VALID : TRUE; END_IF;这段程序做的事一句话版本维护一个定长环形队列每次采样用新值覆盖最旧值同时用累加和减去旧值加上新值最后除以窗口长度得到平均值。为什么要用累加和而不在每个周期把50个数全加一遍因为在TIA博途里每个扫描周期把50个REAL加在一起不仅扫描时间长而且会产生大量不必要的浮点运算。累加和法把每周期计算量从O(N)降到了O(1)窗口越大优势越明显。参数需要说明的集中在两个点。第一冷启动分支里的FILL_COUNT它解决的是PLC刚上电、BUFFER全为0时的跳水问题。如果不做首值填充上电后输出会从0开始爬50个周期才接近真实值现场阀门就会猛地开关一次。第二INT_TO_REAL(#WINDOW_SIZE)每次都要写因为SCL里INT不能直接和REAL做除法必须显式转换。如果你想换成「采满N个点才输出」的严格模式把冷启动分支删掉、保留FILL_COUNT自增并在FILL_COUNT WINDOW_SIZE时不刷新AVG即可但我不推荐工业现场多数场景首值填充更稳妥。3.3 调用与参数初始化把FB放进循环中断OB里并监控程序块写完了调用位置是滤波效果的第二条命。我一般会建一个循环中断OB比如OB35把中断时间设为100ms然后把FB_AVG_FILTER的实例DB在OB35里调用。调用语句非常简单// OB35: 循环中断组织块, 中断时间100ms AVG_DB.RUN : TRUE; AVG_DB.RESET : FALSE; AVG_DB.VALUE : HMI_TEMP_RAW; // 模拟量原始值, 来自AI模块或HMI FB_AVG_FILTER(); PIC_TEMP_AVG : AVG_DB.AVG; // 把滤波结果送到PID或HMI // 如果缓冲区未满, 输出保持上一周期值并拉低VALID IF NOT AVG_DB.VALID THEN PIC_TEMP_AVG : 0.0; END_IF;这里解释了调用时序的核心矛盾滤波算法要求等间隔采样但OB1的扫描周期随程序长短浮动如果直接在OB1里调用这个FB每次调用的间隔可能是20ms也可能是45ms窗口对应的物理时间就不是固定的N个采样周期滤波曲线会扭曲。OB35是硬件定时中断V15里可以在CPU属性里设定间隔我用的是100ms这个值要和传感器、PLC扫描周期匹配。监控和调试时我在监控表里同时拉出AVG_DB.BUFFER数组的前几个元素、SUM、INDEX和AVG。确认BUFFER在滚动、SUM始终等于数组元素之和这个滤波器就活了。注意提示在监控表里直接给VALUE强制写入一个阶跃信号观察AVG要用(N-1)/2个周期才能跟到目标值这是滑动平均的固有延迟不是程序写错了。4. 窗口长度与采样周期的配合决定滤波性能的必调参数4.1 窗口长度N怎么定噪声衰减、阶跃延迟与现场对象匹配滑动平均滤波器没有第二个可调旋钮全部性能都压在窗口长度N上。N的选值要同时看两个指标输出残存噪声幅度和阶跃响应延迟。按照白噪声统计特性残存噪声幅度是输入的1/√N按照滑动平均的定义信号阶跃后输出要经过N个周期才能完全跟上半程到达时间大约是(N-1)/2个周期。把常用窗口列出来看一眼量级窗口长度N阶跃半程延迟采样周期输出噪声幅度相对输入推荐场景41.5约50%压力、快速流量控制83.5约35%一般压力/流量显示167.525%温度、液位、称重3215.5约18%液位、缓慢温度场6431.5约12%环境温湿度、储罐液位实际调试时我从16起步采样周期100ms、窗口16延迟750ms左右绝大多数加热炉、储罐、反应釜的温度和液位对象都吃得起这个延迟。如果PID还在振荡先把窗口加一倍看趋势如果投运时操作工说阀门响应太肉就减半。这个「先16后加倍/减半」的办法比理论计算直接因为现场噪音特性千差万别白噪声假设只是近似。有一个边界条件要提醒如果信号本身就是缓慢变化的真实值叠加的是周期性的干扰——比如泵的脉动——滑动平均对周期干扰的抑制取决于窗口是否覆盖整数个干扰周期。窗口取泵周期的整数倍附近效果最好比如泵速1500rpm对应周期40ms采样周期100ms、窗口5覆盖5个采样点约500ms正好12.5个泵周期纹波能压掉一大截。这个计算比单纯加大窗口有效。4.2 采样周期与调用位置为什么必须在OB35而不是OB1里跑前面调用的代码已经演示了OB35的用法这里把原理和参数讲透。滑动平均的数学推导默认采样间隔相等这个「等间隔」是滤波精度的隐形前提。TIA博途V15里OB35的中断时间可以在PLC属性的「循环中断」里设置单位是微秒我常用的是100000微秒也就是100ms。如果CPU扫描时间本身超过中断时间OB35会被挤占这时候需要在属性里把「扫描负荷」调大我一般留出20%裕量。在OB1里调用为什么不行OB1一个周期里可能执行了100条程序耗时20ms下个周期又执行了95条耗时18ms采样间隔一直在抖。滤波器的窗口N是一个固定的「采样个数」但每个采样对应的物理时间不相同结果就是输出的平均值在时间轴上被压缩或者拉伸曲线出现毛刺般的畸变。更隐蔽的问题是如果在OB1里用上升沿或计数器来采样恰好碰到某一段时间OB1因为通信任务拉长到40ms整个窗口的滞后时间就不稳定了。所以我的习惯是模拟量滤波一律放进OB35并且OB35的中断时间固定下来就不再改。如果现场有多个不同采样频率的滤波通道就建两个循环中断OB比如OB35跑100ms的液位滤波、OB32跑20ms的压力滤波每个OB里只调对应的FB实例互不干扰。提示把滤波FB放进OB35之后不要再在OB1里调用同一个实例否则一个DB被两个OB读写数据一致性直接崩。4.3 数据类型、溢出与扫描时间REAL累加和的三个隐藏限制SCL里最顺手的数据类型是REAL32位浮点绝大多数模拟量通道的工程量也在这个量级。但累加和SUM是把N个REAL加在一起长期运行有几个隐藏限制必须提前封住。第一个是精度REAL有效精度约7位十进制当SUM积累到非常大的数值、同时传感器变化只有零点几时新值的增量可能被大数吃掉滤波输出的最后一位开始跳动。温度显示你可能看不出区别但做高精度称重时就会出现「尾数乱飘」。第二个是溢出。窗口64、信号量级10000SUM的峰值是640000REAL表示这个量级毫无压力。但如果你做的是力传感器毫伏级信号REAL的小数精度在分子上损失更快。我的处理方式分两步一是静态变量里把SUM声明为LREAL——64位浮点代价是扫描时间略微变长但精度范围扩大几个数量级二是在SUM超过一个阈值比如1000000.0时做一次全量重算把BUFFER里的数全部累加一遍并重置SUM避免误差长期累积。第三是扫描时间。环形缓冲区加累加和的写法每个采样周期只做三次数组操作扫描时间可以忽略。但如果你图省事每周期用FOR循环把BUFFER全部相加——注意窗口64时每周期多出64次浮点加法加64次循环跳转在OB35的100ms中断里这点时间不算什么可如果窗口开到512CPU扫描时间的压力就上来了。标题里这份文档采用的就是累加和方案这也是我推荐直接照抄的原因——它在扫描时间上已经做到了最优。5. 滑动平均滤波器常见问题与避坑从初值到周期不稳的数字脚印5.1 上电瞬间输出跳变缓冲区全零导致的假信号现象PLC上电、程序进入RUN的瞬间AVG从0开始一路爬升可能持续几秒到几十秒现场的调节阀跟着输出一起猛开液位还没上来阀门已经开了一半。原因也很直白静态变量在DB里初值默认是0BUFFER里50个元素全为0SUM也为0第一个扫描周期的平均值就是(50*0 VALUE)/50数值被拉到很低之后每个周期真实值占比逐渐升高曲线看起来就像在爬坡。解决代码里的冷启动分支就是一个标准的补丁。FILL_COUNT 0时直接用当前VALUE填充整个BUFFERSUM直接用VALUE * WINDOW_SIZE一次性算好输出从一开始就落在真实值附近。我见过更笨的解决办法是在OB100启动组织块里把RUN置FALSE、等缓冲填满再开放输出但这会让投运过程多一个手动步骤。首值填充的副作用是输出在启动瞬间对第一个采样值很敏感如果第一个值正好是毛刺滤波初值会偏高但最多一个窗口周期后就被拉回来了比爬坡假信号安全得多。5.2 调用周期不稳定滤波曲线出现梯田状畸变现象滤波输出没有噪声了但曲线不是平滑的弧线而是一级一级的「梯田」尤其在信号快速变化时输出台阶特别明显。原因FB被放在了OB1里条件调用或者虽然放在OB35里但OB35的间隔被CPU负载挤得忽长忽短。滑动平均对等间隔采样的要求被破坏同一个窗口在不同时间跨度上对应不同的物理时间平均值的解读就错位了。解决把调用位置彻底固定到循环中断OB这是TIA博途V15里从架构上解决问题的方法。如果OB35已经承担了很多任务去看CPU属性里的循环中断占用率高于40%就把滤波拆到OB32或OB34等优先级更低的中断里。还有一种情况是MRP、PN通信的同步负载在某个时刻抢占中断这需要在PLC属性里把通信处理时间调低给循环中断让路。5.3 窗口长度用变量可调编译报错与缓冲区溢出的双重翻车现象想把窗口长度做成HMI可调参数于是在静态变量里声明N : INT然后在数组声明里写ARRAY[0..N-1]编译直接报错改成BUFFER长度固定100、运行中用N控制使用个数N调大时程序在某些时刻访问到未初始化的数组区域输出突然跳变。原因TIA博途V15的SCL语言规定数组上下界必须是编译期常量运行时变量不能作为数组长度而第二种做法的隐患在于N增大时没对新纳入的缓冲区元素赋初值SUM与BUFFER内容不再一致。解决窗口长度固定为常量HMI上只提供「滤波强度」的三档或五档选择每个档位对应不同的常量方案。具体做法是建三个FB实例分别编译成窗口16/32/64HMI选择时切换调用哪个实例的输出需要切换的变量做个无扰动切换——切换瞬间输出保持上一档的AVG不变稳定一个窗口周期后再用新档位。这个「多实例分档」做法在V15里稳定可靠也绕开了动态数组的限制。5.4 从docx复制SCL代码进TIA博途全角符号和格式字符编译报错现象从Word文档里把SCL代码全选复制粘进TIA博途V15的SCL编辑器点击编译出现一堆语法错误报错位置多半在括号、引号、行尾。查看代码发现括号是中文全角、引号变成了弯引号、行尾还带着文档里的不间断空格。原因Word的「自动替换」功能把半角标点美化成全角或弯形复制到代码编辑器里这些字符不是合法SCL符号编译器自然不认。解决我在从docx迁移代码时有一套固定的清洗流程。第一步把代码先粘进记事本或VS Code用正则把全角字符替换成半角换成()、“”换成、 换成空格。第二步检查行尾有没有不可见字符在VS Code里开启显示空格和制表符一眼能看出问题。第三步也是最重要的粘进TIA博途之后立刻用CtrlShiftF格式化整个块让SCL编辑器重排缩进这样能暴露出所有隐藏格式问题。提示SCL的字符串常量里如果出现中文引号是合法的但那是在字符串内部不是语法符号位置。5.5 累加和漂移LREAL也救不了长期运行的小数偏差现象滤波程序运行几天后AVG与实测仪表值出现几十毫伏甚至更大的偏差重启PLC后偏差消失再过几天又出现。原因SUM在每次采样时做减法、加法两组浮点运算的舍入误差不断累积。REAL的精度约2的-24次方每天几万次采样误差累计起来就变成了肉眼可见的偏移。解决除了把SUM改LREAL还必须在FB里加一个周期性全量校正。我通常在SUM的绝对值超过设定阈值——比如100000.0——或者INDEX正好回绕到0时执行一次FOR循环把BUFFER全部重新累加一遍赋给SUM把累积误差清零。代码的开销是每WINDOW_SIZE个周期多一次全量累加折合到单周期成本几乎为0但这一个操作能杜绝长期漂移的隐患。这个「索引回绕就重算」的习惯是从一次称重现场的教训里学来的当时每天零点漂移0.3公斤客户忍了一周才报修重启即好、不重启就飘最后定位就在这。6. 验证滤波效果与进阶从阶跃测试到加权滑动平均6.1 用PLCSIM和监控表验证滤波器两条曲线证明它真的有效写完代码别急着上真机用TIA博途V15自带的PLCSIM跑一遍仿真验证能省掉大量现场排障时间。我的验证套路分两步。第一步是阶跃响应测试在监控表里给VALUE强制写入一个从100跳到800的阶跃信号观察AVG的变化曲线确认输出按台阶爬升、每周期向目标靠近、大约(N-1)/2个周期后到达中间值N个周期后完全到达。如果曲线没有台阶而是直接跳到几百说明冷启动分支没生效或者RESET逻辑有问题。第二步是噪声抑制测试给VALUE写入一个正弦波叠加随机噪声的序列比如500 100*SIN(时间) 20*RANDOM观察AVG是否在平滑的同时保留了正弦的趋势。这里不需要真的写程序用监控表强制写几十个周期的手动数据也能看出来。判断标准就一条滤波后曲线的标准差明显下降但转折处的相位延迟没有大到让控制回路无法接受。现场投运前我还会让PLC连续跑过夜第二天早上看SUM和AVG有没有漂移这个习惯帮我堵住了不止一次累加和误差的漏洞。6.2 进阶变形加权滑动平均、抗尖峰混合与窗口倍增思路如果你已经跑通了基础版三个变形能解决更刁钻的现场问题。第一个是加权滑动平均给窗口内不同位置的值赋予不同权重新值权重大、旧值权重小响应速度比等权平均快但代码改动很小——在累加和法里给每个BUFFER元素配一个权重常量数组输出变为加权总和除以权重总和延迟能从(N-1)/2缩短到大约N/3。第二个是抗尖峰混合滤波先用中值滤波剔除窗口内明显异常的孤立尖峰把剔完的数再做滑动平均。做法是在冷启动和输出之间插入一个选择——当新值与AVG的偏差超过设定阈值时本次新值不进入BUFFER而是用上一周期AVG占位。这个「超限拒收」逻辑对付称重传感器的脉冲干扰特别有效代价是对快速真实变化信号有一定钝化阈值要按传感器量程的1%到5%去标定。第三个是窗口倍增的思路不是改N而是改OB35中断时间来切换带宽。比如平时用100ms、窗口16调试模式切到50ms、窗口32两种配置的滞后时间几乎相同但噪声衰减特性不同。V15的循环中断时间在RUN模式下不能在线修改需要停机下载所以这个切换只能做在工程调试阶段。这三个变形都不是标题文档里的内容是我在实际项目里沿相同思路延伸的用法基础版本稳定之后在这些方向上做文章收益最高。最后说一个我自己的教训滑动平均不是万能药它天生不适合突变信号保护——急停、联锁、断线检测这些路径上绝不能加平均滤波否则真实故障会被抹平延迟。所以我在工程里坚持的准则是滤波只放在模拟量测量链和PID前级所有安全逻辑都从原始信号直接引线。这个习惯保住了我几次差点被滤波吞掉报警的现场。希望这份SCL程序骨架和参数标定方法能帮你少走弯路先从窗口16跑起来再按现场曲线去调你会发现之前那些心电图一样的曲线其实一百行SCL就能驯服。希望帮到你。本文还有配套的精品资源点击获取
返回列表