PLC跨小时统计方案对比:R_TRIG与T#5S定时稳定性分析 我先给结论跨小时统计这个具体场景里用 T#5S 这类固定周期定时方案整体上比直接靠 R_TRIG 抓上升沿要稳得多。但如果你只是把 T#5S 当成一个“每5秒必须累加固定数值”的脉冲来用那你也一样会踩坑。这个问题我前前后后答过不少同行理由每次都得从头讲一遍因为它不是简简单单选 A 还是选 B 的事它牵扯到 PLC 的扫描周期、系统时钟、信号抖动、任务分配甚至现场干扰。我先跟你交个底我调试过的跨小时统计场景产量累积、能耗小时报表、流量班报基本上都有。多见于中大型产线项目需求听着简单——“每个整点把当前累计值存下来然后归零继续下一小时统计”。但现场落地时你经常会发现统计值在小时间切换的瞬间少了几秒或者在某个整点突然跳变。事后找原因十有八九不是程序逻辑不通而是你选错了“统计基准”。下面我把这个事拆开讲从原理到实操再到我踩过的坑一次说清楚。1. 先把问题说透跨小时统计到底在统计什么1.1 这两种做法的本质区别跨小时统计常见目标是“某个值在每小时内的累计量”。比如一条产线一个小时能产出多少件一台设备的运行时间是多少分钟一条管道一小时的流量累计是多少立方。最终呈现出来的通常是一张按整点切分的数据表。实现方式看似有两条路用 R_TRIG 去抓某个信号的上升沿每来一个沿计数加一。例如外部流量计输出脉冲一个脉冲代表 0.1 立方米数脉冲就是数流量累计。用 T#5S 定时触发每隔 5 秒把瞬时测量值做一次累加当作积分的采样点。例如每 5 秒读一次瞬时流速乘以 5 秒累加到当小时总量里。关键词落在这里R_TRIG 干的是“事件计数”T#5S 方案干的是“时间积分”。别小看这两个词的区别它们决定了后续所有稳定性问题。R_TRIG 类方案关心“来没来一个沿”T#5S 方案关心“过了多少时间、这段时间里值是多少”。跨小时统计的本质就是切时间窗口因此你的一切判断基准都应该围绕时间展开而不是围绕脉冲展开。1.2 “稳定”到底要满足哪几个条件问“哪种稳”先得把“稳”拆开。我在现场判断一个统计方案是否稳定通常会对照下面四件事不丢数该记的脉冲、该累加的时间段不能被漏掉。不重复同一个脉冲、同一个时间段不能被记两次。抗抖动信号在临界值附近抖动不能导致计数反复跳变。抗时间异常整点切换、手动校时、系统时间自动同步时统计值不能大幅跳变。这四条同时满足才叫稳。如果用 R_TRIG 去检测外部脉冲你的稳定性上限取决于信号质量、扫描周期和硬件去抖能力。如果用 T#5S 定时累加你的稳定性上限则取决于系统时钟精度和任务周期准确度。这两种方案的“风险面”完全不一样不能笼统说谁绝对好。我见过很多新手上来就写一个 R_TRIG觉得代码短、逻辑直观。后来现场一跑发现高速脉冲漏计、低速信号抖振统计值天天对不上。原因是把“硬件计数问题”错误地抛给了“软件边沿检测”你以为自己写的是计数器其实你写的是一个宽度敏感度极低的信号采样器。2. R_TRIG 在跨小时统计中的稳定性分析2.1 R_TRIG 的工作原理与常见用法R_TRIG 在 PLC 里是上升沿检测功能块输入从 False 变 True 的那一个扫描周期输出 True然后自动复位。代码上通常是一行指令加一个实例变量它本身不会丢周期也不会占用多少资源属于基础指令。在跨小时统计里常见的 R_TRIG 用法有这么几种检测外部传感器或仪表的脉冲输出一个沿代表一个计数单位。检测某个累计值的变化例如累计流量显示寄存器从 100 变到 101 时触发累加。检测时间字段的变化比如秒的数值从 59 变 0认为到达整分钟或整小时的边界。前两种属于对物理事件计数第三种属于对时间边界判定。问题往往出在第二和第三种用法上因为它们都把“数值变化”或“时间边界”当成了物理脉冲来处理忽略了程序扫描周期带来的采样盲区。R_TRIG 最核心的隐患在于它只能在一个扫描周期内看见一次沿。这个“看见”至少要满足两个条件信号低电平和高电平的持续时间都必须大于程序扫描周期否则等于在高速摄像机面前放一个闪一下就灭的灯泡你就是看不见。2.2 最容易翻车的三种现场工况第一种脉冲宽度小于扫描周期直接漏计。这是个经典问题。假设你程序扫描周期是 50ms外部流量计在流量大时一个脉冲只有 20ms 高电平。PLC 执行到上升沿检测指令时输入可能已经变回 False等于这个脉冲在软件层面不存在。结果就是高速段统计值持续偏小流量越大差得越多对不上班报。第二种信号抖动导致同一事件被重复计数。有些传感器输出在临界点附近会来回跳比如光电检测头检测到工件边缘时信号电平在几百毫秒内反复翻转。R_TRIG 没有硬件去抖能力每翻一次就记一次最后统计值比实际多的不是一星半点。你以为加了“脉冲计数”实际等于加了个抖振放大器。第三种整点切换与边沿信号刚好撞车。这是跨小时场景最常见的隐蔽雷。统计窗口在 00:00:00.000 切换外部脉冲如果在 59.999 秒时过来本应归属上一个整点最后一项但程序可能在整点后第一个扫描周期内才扫描到边沿于是把它记进了新的一小时。数据不多不少就是一个脉冲的归属问题但对账时就能查出来这一小时多了一件上一小时少了一件。我做过一个模拟项目现场有台设备每 40ms 输出一个方波从高速计数器读取是稳定的但从普通数字量点用 R_TRIG 读取高速运行时漏得厉害。后来把脉冲先接到硬件计数口再由 PLC 周期性地读取计数值的差值问题才消失。本质上就是“用硬件计数代替软件边沿捕捉”。2.3 什么时候可以放心用 R_TRIG说了这么多问题不是说 R_TRIG 在跨小时统计里完全不能用。如果满足以下条件用 R_TRIG 还是可以的外部信号源已经过硬件去抖或者信号占空比远大于 PLC 扫描周期。用数字量输入通道且确认通道带硬件滤波后的延迟可控。你统计的是低速事件例如每分钟几次的按键、开关切换。或者你已经用高速计数器 / 硬件计数器模块R_TRIG 只用来做“计数结果变化”的检测而不是直接数源脉冲。重点提醒一句R_TRIG 更适合当“结果变化通知器”不适合当“原始事件计数器”。如果你非要用它直接数脉冲请先搞清楚现场信号的频率上限和最小脉宽再去对扫描周期否则就是给自己埋雷。3. T#5S 定时统计的稳定性分析3.1 T#5S 方案到底是怎么跑的T#5S 字面的意思是 5 秒定时常见做法是写一个固定周期中断任务或者用定时器产生一个 5 秒脉冲在每一个脉冲到来时执行一次统计逻辑。跨小时统计里典型的 T#5S 方案长这样每个 5 秒周期算一次瞬时值乘以 5 秒累加到当小时累计量。到整点时把当前累计值写入小时记录表再清零。连续运行 24 小时最后形成 24 条小时记录。这个方案的好处是不依赖外部脉冲波形不需要管信号上升沿有多宽、下降沿有多抖。只要定时任务本身周期准统计逻辑就准。它把“外部事件”转换成了“时间切片”而时间切片在同一套 PLC 系统里是一致的不会像外部脉冲那样受现场干扰。稳定性来源在于它关注的是“时间”而跨小时统计本来就是在切时间窗口。你用一把尺子去量长度而不是数砖块思路更贴合需求。3.2 固定时长累加的三个隐患但是纯 T#5S 方案也有自己的坑最明显的是下面三个。隐患一任务周期并非绝对恒定。PLC 扫描周期受程序大小、通信任务、中断任务影响可能会波动。即使你设置的是 5 秒定时中断实际触发间隔也可能是 4.96 秒、5.03 秒、5.01 秒。如果程序里写死“本周期代表 5 秒”长时间跑下来就有累积误差。隐患二固定值累加会放大时钟漂移。有些代码是这样写的每 5 秒执行一次每次给总量加一个固定增量比如设备运行状态为 True 就加 1。一小时理论上加 720 次代表 3600 秒。但实际上一小时里可能只跑了 719 次或者跑了 721 次取决于任务分发。这个误差不是一次性修正的而是每周期累计。按 5ms 的周期抖动算每小时偏差可以达到 3.6 秒一个白班 8 小时就是近 30 秒的误差。用于小时报表还能忍用于交接班精确统计就麻烦。隐患三整点归零动作本身不准确。跨小时切换要求“整点那一刻归零”。但 T#5S 是每 5 秒触发一次某个触发点可能落在 10:00:00.100也可能落在 09:59:59.950。取决于任务调度归零动作不是精准钉在整点。于是上一小时统计值可能多算了 0.1 秒的量下一小时少了 0.1 秒虽然绝对量不大但在交接班对账时非常显眼。这三个隐患共同指向一件事你不是在“测量时间”你是在“假设时间”。假设每周期都是精确 5 秒假设归零一定发生在整点这两个假设在若干小时后必然与实际时钟出现偏差。3.3 定时方案的改进路径所以只比 T#5S 和 R_TRIG永远是二选一但真正稳的做法其实是第三种定时方案 时间戳差分。思路很简单不要假设本周期是 5 秒而是每个周期开始时读一次系统时间周期结束时再读一次两次之间的真实时间差才是本周期要用的积分时长。累加量照常算但乘以的不再是那个想象的“5”而是实际经过的秒数。我在项目里把这个方法叫“真实时间积分”。它保留了 T#5S 定时触发不依赖脉冲波形的优点又消除了“固定 5 秒”这个假设带来的误差。你会发现程序复杂了一点点但稳定性提升非常明显。4. 我推荐的跨小时统计实现方案时间戳差分4.1 核心理念时间别当成脉冲当成连续量接触这个问题的同行越多我越觉得关键不是具体用哪条指令而是对时间的理解。R_TRIG 的用法是把时间边界当成电平跳变比如秒数从 59 变 0。可一个 PLC 扫描周期可能在 59.8 秒时看到值还是 59下一秒在 60.3 秒时看到值已经是 0中间那个上升沿你没有完整观察过。R_TRIG 所谓“检测到上升沿”其实是在一个离散采样序列里发现“上一拍和这一拍不一样”。这个不一样不保证你捕捉到真实变化的精确时刻。跨小时统计需要的是在一个连续时间轴上做积分。正确姿势是每个扫描周期都记录系统时间戳用相邻两次时间戳求出真实间隔再用间隔乘以瞬时速率。这样即使某个周期实际间隔是 4.93 秒或 5.08 秒统计结果依然是真实时间轴上的积分不依赖任务调度是否完美。打个比方R_TRIG 像数公交车一班一班地数如果车开快了或者堵车了你可能漏数或多算。时间戳差分像记车牌时间每一辆过去都记录真实到站时刻最后一算就是全天的真实发车频率。4.2 可直接套用的 ST 代码骨架下面给一个 ST 风格的功能块骨架适用大多数支持系统毫秒时间戳的主流 PLC 平台。你直接改变量名就能用。FUNCTION_BLOCK FB_HourlyTotal VAR // 统计状态 lastTime : LDT; // 上次采样时刻 hourlyTotal : LREAL; // 当前小时累计量 totalAll : LREAL; // 所有小时累计量可选 lastHourVal : INT; // 上次所在小时 // 配置 rateInput : LREAL; // 瞬时速率例如瞬时流量 END_VAR VAR_OUTPUT completedHour : INT; // 已完成的小时序号 END_VAR // 每到固定周期例如 5s调用一次 now : GET_SYSTEM_TIMESTAMP(); // 读取系统时间戳 // 首周期初始化 IF lastTime 0 THEN lastTime : now; lastHourVal : HOUR_OF(now); RETURN; END_IF; // 计算真实时间间隔单位换算为小时或秒 deltaSec : TIME_TO_REAL(now - lastTime); IF deltaSec 0.0 THEN deltaSec : 0.0; // 时间回拨保护 END_IF; // 按真实时间间隔积分 hourlyTotal : hourlyTotal rateInput * deltaSec; totalAll : totalAll rateInput * deltaSec; // 检测跨小时 IF HOUR_OF(now) lastHourVal THEN completedHour : lastHourVal; // 在这里把 hourlyTotal 存入历史数组或发送到上位机 // 然后清零本小时累计 hourlyTotal : 0.0; lastHourVal : HOUR_OF(now); END_IF; lastTime : now;代码里的关键只有三个读时间戳、计算真实间隔、检测小时变化。真正的统计核心不是“每 5 秒执行一次”而是“每次拿到的真实间隔是多少”。如果你需要更细的分辨率可以改用毫秒级或微秒级时间戳函数逻辑一样只是单位换算不同。扫描周期越快积分精度越高。4.3 整点归零与边界切分的实现细节整点归零我建议直接用小时值比较不要用 R_TRIG 去检测“秒 0”。为什么因为秒 0 只会出现在一个扫描周期里如果 PLC 扫描周期碰巧跨越了整点边界你的检测指令可能根本没看到秒等于 0 的那个时刻。而小时值比较则不同上一周期小时值是 9下一周期小时值是 10你不需要看到 09:59:59.999 这种瞬间你只需要连续比对两次即可。它本质上还是“变化检测”但对象是系统时间戳派生的整数不是外部信号因此不受脉冲宽度和物理干扰影响。边界切分还有一个细节历史记录数组的索引。我习惯用小时值本身做索引方便上位机直接按小时取数。记录数组长度为 24 或 48索引范围要显式限制防止月初或跨天时越界。数据入库后建议把“完成时间戳”一起记上用于排查对账问题。4.4 提高可靠性的配套措施统计积分任务放在固定周期中断里不要放在主扫描里等一个不确定的调用点。系统时间最好与上位机或时间服务器同步。没有校时条件的至少要让 PLC 自身 RTC 稳定运行。读瞬时速率时注意传感器信号是否先经过滤波。滤波太猛会使速率响应滞后积分结果略微滞后于实际但不会累计漂移。时间戳差分时必须处理时间回拨。现场手动校时把时间往前拨差分会变成负值处理不好一小时统计值会瞬间减少一大块。我的处理是负间隔直接按 0 处理并把 lastTime 重置为当前时间。如果你所在平台时间戳函数价格昂贵也可以在循环中断里用一个 1ms 自由运行计数器代替效果类似。唯一要注意计数器回绕处理办法是计算差值前判断是否发生回绕。这一套做完你会发现你在跨小时统计上几乎不会再出“整点跳变”和“一小时差几秒”的问题。因为它把系统时钟当成了唯一基准而系统时钟在工业现场通常比外部脉冲可靠得多。5. 常见故障排查实录5.1 故障1每小时多记5秒或少记5秒现象小时报表显示运行时长 3605 秒或 3594 秒而不是 3600 秒。一查代码很多人是这么写的每 5 秒执行一次每次给时间累计加 5。这就有问题了。执行周期不等于 5 秒整数倍任务晚到一点、被通信任务抢占一下实际间隔就是 5.02 秒。每次都按 5 秒算一小时 720 次执行误差被反复放大。处置方法改时间戳差分。实在不想改程序的可以统计执行次数在整点时用真实系统时间差修正一次但只能消除本期误差不能消除中间过程的不平滑。最好还是直接把固定值改成真实间隔。5.2 故障2整点数据突变统计值跳变现象上一小时最后一条记录偏大下一小时第一条记录偏小或者出现负值。原因多半是整点归零逻辑依赖了外部信号或精确秒值。比如用 R_TRIG 检测秒等于 0结果扫描周期错过了那个瞬间旧值没有及时写入历史表到下个周期才写造成记录时间整体往后挪了一个周期。处置方法改用小时值比较。用 HOUR_OF(now) 去判断“小时是否变化”而不是用 R_TRIG 去抓一个瞬间状态。这样只要任务还在执行哪怕是晚执行几毫秒也能正确判断“现在已经是下一小时了”记录归属不会错。5.3 故障3校时后统计值错乱现象现场手动校准系统时间把 10:30 改成 10:00结果 10:00 到 10:30 这段时间的累计值要么多一块、要么消失。原因很简单时间戳差分依赖系统时钟连续单调。系统时间往回拨相邻两次时间戳差值变成负数如果不处理积分结果会瞬间反向。处置方法在差分计算后加保护。负间隔按 0 处理同时重置基准时间。另外校时尽量选择在整点时刻或停机时段进行避免影响关键统计时段。如果上位机有 NTP 自动校时也要评估校时策略不能让时钟在统计窗口中心来回跳动。5.4 故障速查表故障现象可能原因优先排查方向统计值整体偏小R_TRIG 漏计高速脉冲检查信号脉宽与扫描周期统计值偏大且无规律信号抖动被 R_TRIG 重复计数观察输入波形 / 硬件去抖整点数据归属错乱使用秒0 作为边界改为小时值比较每小时差几秒固定 5 秒累加改用时间戳差分校时后统计值跳变时间回拨未保护差分小于 0 时清零历史数组越界索引没做边界限制按 24 或 48 长度取模这张表是我现场调试时最常翻的。它的价值不在于每条都写到而在于告诉你去现场之后第一步不要改程序先对照现象锁定原因再动手。统计类问题大部分不是逻辑复杂而是基准选错。6. 选型建议什么场景该用哪种方案6.1 决策清单我帮很多人做过选型最后落到一句话R_TRIG 适合“数事件”T#5S 适合“切时间”。跨小时统计是切时间所以 T#5S 方向天然比 R_TRIG 稳。但不是所有带“小时统计”字样的功能都适合 T#5S要看你统计的对象。如果你要数的是外部开关量脉冲例如电表脉冲、流量计脉冲、工件计数这种情况下用 T#5S 反而不合适因为物理事件是离散的你用定时采样去猜脉宽误差更大。这种场景应该优先硬件计数或高速计数模块然后周期性地读取计数值差值再用时间戳差分做统计。如果你要统计的是连续量积分例如液体流量、电能累计、设备运行时间这类值本身就是时间轴上的积分用 T#5S 加时间戳差分是最稳的。R_TRIG 在这里没有用武之地因为连续量没有“上升沿”。决策清单大致这样统计对象是脉冲数量优先硬件计数R_TRIG 只做辅助。统计对象是连续量优先定时积分且积分时长用真实时间间隔。统计对象是运行状态时长定时扫描状态累加真实时间间隔不要用固定值。统计边界是整点比较小时值不要用秒值做边沿检测。6.2 扩展从跨小时到跨天、跨月统计这套方法同样可以扩展到跨天、跨月甚至跨年统计。核心逻辑不变时间戳差分算时长日期字段变化时保存上一周期数据并清零。唯一要注意的是日期字段比较索引按周统计要单独算星期几按班次统计则要维护班次时间表。跨天统计里常见一个坑系统时间用 24 小时制跨零点时小时值从 23 变为 0此时既要保存当天最后一条小时记录还要把日累计清零。如果你只在“小时值变化”分支里处理跨零点的那一次会被当成普通跨小时处理日累计清零就会漏。我建议单独写一个跨日处理分支判断条件是“日期字段 ! lastDate”。跨月同理月累计归零要判断“年-月”组合是否变化而不是只看月份。否则年初跨月时月份从 12 变 1看起来变了实际上年也变了但你的逻辑里没有“年”这个维度统计记录就会张冠李戴。我自己在做一个多班次能耗统计系统时就吃过这个亏。开始只判断小时后来发现零点那一小时的数据总是被记到前一天。后来把跨日判断单独拎出来逻辑才算真正闭环。最后说点心得我在实际项目里踩过几次坑之后总结下来就是跨小时统计的稳定性不取决于你用 R_TRIG 还是 T#5S而取决于你有没有把系统时间当成基准。R_TRIG 拿来看“沿”T#5S 拿来做“周期”真正可靠的是三者的结合——T#5S 做触发节奏系统时间戳做真实计时小时值比较做边界切分。如果你现在正在调这类问题我建议你先把现有代码里所有依赖“固定时长累加”的地方列出来逐一改成时间戳差分。对于暂时不能大改的系统至少把整点边界判断从 R_TRIG 改为小时值比较能立刻解决大部分“整点跳变”问题。等系统稳定了再逐步把积分逻辑统一到时间基准上来。还有一个细节想提醒你统计程序写完别忘了加“校时保护”。这是最容易遗漏的环节因为平时系统时间不会乱跳一旦现场有人手动校过时统计值就可能变成负数或异常大值。加了保护哪怕遇到校时也只是损失一小段数据不会整表报废。这个内容后续还可以继续扩展比如把统计结果编码上传到上位机数据库或者在前端页面生成按小时、按班次、按日的趋势曲线。但不管是哪种扩展底层的时间基准只要扎扎实实用对上层展示就永远不会出大乱子。