S7-1500流量累积的博途实现:从原理到SCL编程
2026/9/11 18:06:01 网站建设 项目流程

简介:西门子S7-1500 PLC流量累积数据处理案例基于博途STEP7环境编写,面向自动化、电气工程及计算机等相关专业学生、教师与企业工程师,适用于课程设计、毕业设计及项目初期验证等场景。压缩包共9个文件,主要包含ap13项目文件、xml配置文件、plf与idx组态索引文件以及一个项目备份压缩包,整体仅4.61MB,便于直接导入TIA Portal查看程序结构和在线仿真调试。案例代码已经过完整运行测试,功能确认无误,重点展示了流量信号的采集转换、累积计算、数据块存储与处理流程,有助于理解西门子PLC中模拟量处理、定时中断和DB块应用等关键知识点。读者既可对照案例学习工程化编程思路,也能在此基础上修改参数、添加功能,满足不同控制需求。该资源已有213人学习浏览,对PLC入门者与进阶开发者均具参考价值。

1. 为什么说 S7-1500 流量累积不只是“流量乘以时间”

做水处理或能源管理的同行,应该都遇到过这个场景:现场有台西门子S7-1500,博途TIA Portal里已经做好了模拟量采集,上位机要的是每天的流量累计值,而不是瞬时流量。你一边翻STEP7帮助,一边想着是不是用TON累加,结果发现精度和掉电保持都处理不干净。流量累积数据处理的核心就三件事:用循环中断固定采样周期、把瞬时流量按时间积分、把累积结果放到带掉电保持的DB里。下面直接讲博途STEP7里一套可复现的S7-1500流量累积方案,适合刚入门西门子PLC编程的工程师,也给正在调流量计积算的老手一些参数参考。

2. 流量累积的原理与博途STEP7选型:从瞬时流量到累积量

2.1 流量累积的基本公式:为什么离散采样必须考虑周期抖动

流量累积在数学上就是对瞬时流量做时间积分。设瞬时流量为 Q(t),单位是 m3/h,从 T0 到 T1 的累积体积 V = ∫ Q(t) dt。PLC 只能做离散采样,所以实际实现变成 V = Σ Q_i * Δt_i,其中 Q_i 是第 i 次采样的瞬时流量,Δt_i 是这次采样距上次采样的真实时间间隔。如果每次都按固定的 100ms 中断周期计算,公式可以简化为 V = T * Σ Q_i,但这里有个隐患:中断周期不是绝对准确,相邻两次启动之间的间隔可能有零点几毫秒到几毫秒的抖动,更糟的是某个周期被高优先级中断挤掉。按固定 T 计算累加,时间越长误差越明显。

所以我在写这一步时会刻意把 Δt_i 也给算进去。SCL 里可以直接读取系统时间戳,或者更简单的方法是:在循环中断 OB 里用一个静态变量记录上次扫描的系统时钟,然后算真实间隔。这样即使中断有抖动,累计结果也不会跟着漂。下面这段是 S7-1500 中常见的积分核心逻辑:

// 循环中断 OB30 中调用,每 100ms 一次 // 输入: gFlowInstant 瞬时流量 REAL 单位 m3/h // 静态变量: prevTime DTL 上次时间戳, total LREAL 累积量 IF firstRun THEN prevTime := GET_SYSTEM_TIME(); firstRun := FALSE; RETURN; END_IF; nowTime := GET_SYSTEM_TIME(); // TIME_BETWEEN 返回 TIME 类型,单位 ms,转小时处理 deltaT := TIME_TO_REAL(TIME_BETWEEN(prevTime, nowTime)) / 3600000.0; total := total + gFlowInstant * deltaT; prevTime := nowTime;

这里把时间戳和间隔计算放到了中断里,而不是在 OB1 里用 TON 凑周期,原因后面细说。参数上注意gFlowInstant必须已经做过量程转换,单位保持一致,否则累积结果会直接放大或缩小 60 倍甚至 3600 倍。

2.2 为什么用循环中断 OB 而不是定时器 TON

很多刚开始做西门子PLC编程的朋友习惯用 TON 做定时触发。TON 本身没问题,问题在于它放在 OB1 里时,启动和停止的时机依赖扫描周期。当 OB1 里面有 PID、通信或大量位运算时,扫描周期可能从 5ms 跳到 20ms,用 TON 叠加出来的“周期”根本不稳定。而且 TON 只负责逻辑延时,不会自动给你一个均匀的采样时钟,你需要自己判断 started 到 done 的时刻,代码写起来绕。

S7-1500 的循环中断 OB(如 OB30)是由系统时钟触发的硬件级定时中断,不受 OB1 扫描时间影响,默认时间单位是 ms,可以在属性里设置 10ms、100ms、1s 等。我用得最多的是 100ms 和 1s:瞬时流量变化快的场合用 100ms,平稳的管道流量用 1s 也够,CPU 负载还会低一点。下面这张表是我在项目中常用的对比,供选型时参考。

触发方式稳定性最小周期精度风险典型用途
OB1 + TON受扫描周期波动影响受扫描周期限制简单逻辑延时,不适合流量累积
OB30 循环中断硬件定时,可靠可设 10ms流量累积、PID 采样
OB10 时间中断按日历触发1 秒日报表、整点记录

需要提醒的是,循环中断的周期设得太短会占用大量 CPU 时间片。如果 S7-1500 同时在跑运动控制或 PROFINET 实时通信,把 OB30 周期从 100ms 改到 10ms 之前,最好先看一下 CPU 的诊断缓冲区里是否出现“循环中断超时”的提示。

2.3 数据类型与 DB 设计:REAL 还是 LREAL?累积值存哪里

流量瞬时值从模拟量模块过来后,一般是 INT 或 REAL。S7-1500 原生支持 REAL,我建议统一用 REAL 存瞬时流量,累积量则要看总量级。举个例子:管道流量 100 m3/h,连续运行一年,累积量是 876000 m3。REAL 的精度大约是 7 位有效数字,到 876000 时有效分辨率只剩 0.1 m3 左右,看起来还行;但如果累积好几年,总量到 500 万 m3 之后,REAL 的分辨率可能已经超过 0.5 m3,这对贸易结算来说是不可接受的。

这时候两个选择:一个是累积量用 LREAL(64 位浮点),S7-1500 支持,但注意 LREAL 的运算在低端 CPU 上会稍微慢一点;另一个是把累积量拆成两个 REAL,一个存整数部分,一个存小数部分,每次累加后做进位。我一般优先用 LREAL,因为代码简单,而且 S7-1500 的算力足够。DB 里还有一个点不能忘:累积量对应的 DB 变量要勾选 Remanence(保持性),否则 PLC 掉电后总量归零,现场要骂人的。博途里右键 DB 变量,在属性里可以找到 Remanence 设置,选择 “Retain” 即可。除了累积量本身,上次时间戳和初始化标志也要设为保持,不然重启后时间基准会断掉。

3. 在博途TIA Portal中编写S7-1500流量累积程序的落地步骤

3.1 创建项目与硬件组态,建立 PLC 变量表

在博途 V16 或更高版本(V17/V18 也行)里新建项目后,添加 S7-1500 CPU,再把 AI 模块插入机架。模拟量模块的通道地址不是手动填的,TIA Portal 会自动分配,比如 IW64。随后在 PLC 变量表里把会用到的点建好,命名尽量见名知意。最基础的一套变量在下面表里,后面写的 SCL 程序就基于这张表。

符号名地址/数据块数据类型含义
AI_Flow_RawIW64INT流量变送器经模拟量模块输入的原始值
HMI_ResetTotalDB1.HMI_ResetTotalBOOL上位机或触摸屏触发清零
Flow_InstantDB1.Flow_InstantREAL量程转换后的瞬时流量 m3/h
Total_AccumDB1.Total_AccumLREAL累积流量 m3

不建议直接拿 IW64 参与累积,如果以后仪表量程或偏置变了,你得改好几处程序。先把原始值转换成一个独立的 Flow_Instant,再交给累积函数,这是模块化编程的基本习惯。

3.2 用 SCL 写一个可复用的流量累积 FB

在博途中插入一个 FB,语言选择 SCL,名字可以叫FlowTotalizer。这个 FB 要能复用,所以输入输出都用形参,不直接访问全局变量。完整的核心代码如下:

FUNCTION_BLOCK "FlowTotalizer" { S7_Optimized_Access := 'TRUE' } VERSION : 0.1 NON_RETAIN VAR_INPUT InstantFlow : REAL; // 瞬时流量,单位 m3/h Reset : BOOL; // 复位累积量 END_VAR VAR_OUTPUT Total : LREAL; // 累积量,单位 m3 END_VAR VAR init : BOOL := FALSE; // 第一次扫描标志 prevTime : DTL; // 上次采样的系统时间 nowTime : DTL; // 本次采样的系统时间 deltaT : REAL; // 实际时间间隔,单位小时 END_VAR // 复位:清空累计值并重新建立时间基准 IF Reset THEN Total := 0.0; init := FALSE; END_IF; // 第一次扫描只记录时间,不累加,避免从电源接通瞬间开始误算 IF NOT init THEN prevTime := GET_SYSTEM_TIME(); init := TRUE; RETURN; END_IF; nowTime := GET_SYSTEM_TIME(); // TIME_BETWEEN(prevTime, nowTime) 返回 TIME 类型,单位 ms deltaT := TIME_TO_REAL(TIME_BETWEEN(prevTime, nowTime)) / 3600000.0; Total := Total + LREAL_TO_REAL(InstantFlow) * deltaT; prevTime := nowTime;

这段代码有几个地方要在参数层面确认:Reset是电平型,不是边沿型。如果 HMI 上一个复位按钮按下再松开,必须用R_TRIG生成一个脉冲,或者干脆让上位机保持 100ms 以上的高电平。GET_SYSTEM_TIME返回的是 CPU 系统时间,函数名在博途不同版本中基本一致,我用 V16 和 V20 都没有问题。TIME_TO_REAL把毫秒数转成 REAL,再除以 3600000 就是小时。

提示:如果库版本里找不到TIME_BETWEEN,可以改用T_DIFF,效果一样;或者把 DTL 先转成 LDT 再手动做减法。

如果不想用GET_SYSTEM_TIME,也可以直接利用 OB30 的启动信息,它在 OB 内部可用,但复用性差,所以我更推荐上面的写法。

3.3 在主程序里调用 FB,并配置循环中断 OB30

FB 写好后,在主 OB1 里或者直接在 OB30 里调用。因为流量累积只关心时间间隔,不需要在 OB1 里扫描,所以最常见的做法是把 FB 放在 OB30 中,让累积动作被周期中断驱动。OB30 默认就是 100ms 周期,如果你要改,双击 OB30,在属性里找到“循环时间”设置。调用代码写在一段 SCL 里即可:

"FlowTotalizer_Instance"( InstantFlow := "Flow_Instant", Reset := "HMI_ResetTotal", Total := "Total_Accum" );

如果你把Total定义成 FB 的输出,那么它不会自动保持。要保证掉电不丢,建议把Total改成一个VAR_IN_OUT,并传入 DB 里的保持性变量。比如先在 DB1 里建一个Total_Accum,属性改成Retain,再把这个变量挂到Total接口上。这样掉电后 CPU 重新启动,整个累积过程从掉电前的数值继续。

3.4 连接模拟量输入与量程转换,确认瞬时流量单位

流量累积的入口是Flow_Instant,它必须是从模拟量原始值转换出来的标准工程量。如果变送器输出 4-20mA,对应 0-100 m3/h,原始值AI_Flow_Raw的范围是 0~27648(单极性)。量程转换可以直接在 OB1 里用NORM_XSCALE_X,也可以手动算,我一般写一行 SCL:

"Flow_Instant" := ("AI_Flow_Raw" / 27648.0) * 100.0;

注意这里的前提是变送器已经做了零点和满度校准,4mA 对应 0,20mA 对应 100 m3/h。如果你的仪表用的是 0-10V 但把 0V 对应 0,也是一样的公式。唯一要小心的是有的变送器把 4mA 对应某个底值而不是 0,比如自带显示的流量计下限是 1 m3/h,那就得先做线性偏移:

"Flow_Instant" := 1.0 + ("AI_Flow_Raw" / 27648.0) * 99.0;

这类偏置如果搞错,累积量在一开始就会有固定误差,并且误差会随时间线性增长,现场核对总量时很难定位。建议在博途的监控表里同时看AI_Flow_RawFlow_Instant,用标准信号源或者手操器给一个已知流量值,先把比例调对再管累积。

4. S7-1500流量累积的常见坑与参数调整

4.1 采样周期 vs 中断周期:丢了周期怎么办

循环中断不是不会丢周期的。当 CPU 被高优先级 OB 占用,或者 OB30 自身的执行时间超过设定周期,博途会在诊断缓冲区里记录事件“循环中断 OBs 的启动时间已超出”。一旦出现这个提示,说明你的累积周期不是名义上的 100ms 了,而程序还按 100ms 计算,累积量就会偏小。这时候除了优化 OB30 里的指令,最有效的办法是把累积量的计算改成时间戳差值法,也就是第 2.1 节里的做法。这样即使某次中断晚了几毫秒,deltaT会变长,该补的时间照样补回来。

现象可能原因处理方式
累积值偏小中断周期被抢占,程序仍按名义周期算使用时间戳差值法
累积值偏大deltaT 单位把毫秒当日历秒检查除法从 3600000 写成 3600
累积值突然跳变掉电恢复后时间戳保持,与当前时间差很大上电后重新初始化 prevTime

如果你用的是 OB1 里的定时器,那么 OB1 扫描周期一波动,定时器周期也会波动,而且你还感知不到,这是最危险的。我的原则是:流量累积必须放在固定周期中断 OB 里,并且要保留真实时间间隔用于修正。

4.2 累积量溢出、清零与掉电保持的取舍

LREAL 的数值范围很大,正常工况下几乎不可能溢出。但清零逻辑要小心,如果Reset是 HMI 上的一个带保持的按钮,可能导致每个扫描周期都触发清零。正确做法是在 OB1 里生成一个上升沿脉冲,然后置位一个单独的Reset_Command,累积 FB 再对这个命令信号执行清零。实际上我遇到过因为 HMI 上开关量没做脉冲,导致流量累积值一直归零的情况,查了两小时才定位到。

关于掉电保持,博途里有两个办法:一是把 FB 的实例 DB 改成保持型,这样所有静态变量都保持;二是把Total接口放到一个单独的保持型 DB 里,FB 内只操作这个接口。方案一省事,但会把中间变量也保持,占用保持存储区;方案二更精准,适合希望每次上电都重置时间戳的场合。我比较推荐方案二,因为prevTimeinit这些中间变量在上电后必须重新初始化,如果也跟着保持,程序会在旧时间戳基础上硬算,容易产生一个非常大的 deltaT,累积量会猛跳一大截。

4.3 博途SCL中TON怎么用?为什么不能用TON做流量累积

“博途SCL中TON怎么用”是很多入门者会搜的问题。TON 在 SCL 里可以直接以函数块方式调用,例如:

"TON_Instance"( IN := #startCondition, PT := T#1S, Q => #done, ET => #elapsed);

这段代码的逻辑是:IN为 TRUE 后计时,计时到 PT 后Q置 TRUE,IN为 FALSE 时ET清零。单独看它没有问题,但你要是想做流量累积,TON 只能提供一个“到点脉冲”,累积还是在 OB1 里做。OB1 扫描周期一变,你得到的累积间隔就不是 1s,可能是逐次扫描的随机值。而且 TON 在 OB1 里运行时,如果 OB1 被中断,TON 的计时依然按系统时钟走,但你的采样动作没有同步发生,瞬时流量值还是上一次扫描的旧值,这就会造成“时间到了但数据是旧的”的系统误差。

所以我的做法是:TON 留在 OB1 里做设备逻辑顺控,流量累积全部走 OB30 和 GET_SYSTEM_TIME。如果搜索博途 TON 的时候你搜到了很多教程,先别急着把流量累积也套进去,看你手里有没有一个固定的中断源。

4.4 仿真与在线调试:用监控表验证累积误差

流量累积程序调试不能光看 HMI 上的数字跳不跳,要能验证单位时间内增量和瞬时流量是否对得上。博途的仿真功能支持 S7-1500,虽然没有模拟量硬件,但可以给AI_Flow_Raw强制一个数值。步骤如下:先把 OB30 周期设为 1000ms,然后在监控表里把Flow_Instant强制成 60.0(m3/h),跑 10 分钟,理论累积量应该是 60.0 * 10 / 60 = 10.0 m3。如果监控值不是 10.0,优先检查deltaT的单位。很多新手把 MS 直接当成秒,导致累积结果放大 3600 倍。

另一个坑是博途仿真里的系统时间会跟随电脑时间,但如果你中途改过 OB30 周期或下载过程序,时间戳不会断裂,因为GET_SYSTEM_TIME读的是绝对时间,不依赖 OB 调用次数。这比用计数器加假设的周期靠谱得多。验证时建议做一个简单的离线累加表:记录每分钟结束时的Total差值,和该分钟内Flow_Instant的平均值做对比,偏差超过 0.5% 就要回头查量程转换和中断周期配置。

5. 进阶技巧:按日期自动切换日累积量,并缓存昨日结存量

5.1 读取系统日期,判断是否跨天

现场报表通常需要“今日累积量”和“昨日累积量”。与其等上位机去读总累积量做差分,不如在 PLC 里直接维护两日累计。在 S7-1500 里,GET_SYSTEM_TIME返回的 DTL 结构带有年、月、日字段,SCL 可以直接读取并比较。

在 OB30 中调用流量累积 FB 后,增加一段跨天判断:

nowDate := GET_SYSTEM_TIME(); IF (nowDate.YEAR <> curDate.YEAR) OR (nowDate.MONTH <> curDate.MONTH) OR (nowDate.DAY <> curDate.DAY) THEN yestTotal := todayTotal; todayTotal := 0.0; curDate := nowDate; END_IF; todayTotal := todayTotal + InstantFlow * deltaT;

这段代码里curDate需要是 DTL 类型且掉电保持,yestTotaltodayTotal建议用 LREAL。注意跨天判断放在累积计算之前,这样不会把新一天的第一笔流量算到昨日。如果你希望昨日值在跨天后保持一整天,可以把yestTotal设置成保持型,并在上位机确认后再手动复位。

5.2 掉电恢复时的日期处理

如果 PLC 停机一天以上才上电,curDate因为保持属性仍然是旧日期,上电第一周期会立即触发跨天,todayTotal清零,yestTotal变成停机前一天的累积值。这个行为一般符合“昨日结存量”的定义。真正要小心的是todayTotal在停机期间的累计丢失,如果工艺要求不能丢量,就得靠仪表侧的机械计数器或者断电续存模块兜底。PLC 的程序再怎么优化,也只能保证上电后的计算连续性。

5.3 用仿真验证跨天逻辑

把 Windows 系统时间改到 23:59:59,或者直接用博途仿真里的系统时间功能,运行 1 秒后即可看到yestTotal被赋值。仿真中还要验证日期边界在月底或年底是否正常,因为 DTL 的月份进位是系统完成的,不需要你写闰年判断。最后用一张表记录验证结果:开始时今日值、昨日值、跨天后的今日值,三者前后关系必须对得上。这一步做完,流量累积程序就可以放心交付了。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询