简介:西气东输某支干线站控系统配套的BBRTU阀室控制系统技术设计方案,面向油气长输管道自动化工程师与SCADA系统集成人员。方案采用基于ControlWave Micro控制器的RTU作为远方控制单元,覆盖监控阀室、监视阀室及高位检测点的数据采集与处理、线路紧急截断阀监控、供电系统监控、阴极保护变量采集、可燃气体报警和ESD紧急停车等核心功能,并重点阐述了光通信冗余机制如何在光通道任一处故障时仍保证RTU与上下游站控系统的正常通信。硬件部分详列ControlWave Micro 150CPU的ARM处理器、超低功耗、IEC 61131-3编程标准、双以太网口与多串口、扩展机架、隔离I/O、宽温工作及危险区域认证等特性;软件部分则介绍ControlWave Designer提供的ST、FBD、LD、SFC、IL五种编程语言及向导工具,利于快速配置与调试。文档共1个doc文件,115KB,结构涵盖概述、系统描述、系统功能、硬件与软件设计等章节,适合作为阀室RTU方案设计、选型评估和运维排障的参考。目前已有86人学习浏览。
1. BBRTU 阀室控制系统:SCADA 末端的“最后一公里”只靠一台 RTU 撑住
BBRTU 阀室控制系统这个技术方案,解决的是长输管道里最容易被忽略的问题:监控阀室、监视阀室和高位检测点分散在几十公里甚至上百公里的管线上,调度中心要靠什么在最短时间内知道哪座阀室发生了泄漏、截断阀有没有关、阴保站是否失电。这套方案在每个阀室放一台 ControlWave Micro 作为 RTU,把工艺变量采集、供电监控、可燃气体报警和紧急停车(ESD)逻辑全部下沉到控制器本地,再通过光通信向上下游站控同时收发数据,任一处光纤断掉都不丢通信。对管道自控从业者来说,真正值得细看的是它的工作温度范围覆盖 -40℃ 到 70℃、整机功耗最低不到 1 瓦,以及一组可以核算到 99.99% 以上的 MTBF/MTTR 指标。下面从硬件选型、组态语言和通信调试三个层面展开。
2. ControlWave Micro 硬件选型与 I/O 冗余核算
2.1 为什么阀室现场更适合混合控制器
阀室机柜通常就是一个户外小站,没有空调,供电靠太阳能浮充或蓄电池,冬天零下几十度、夏天机柜里直逼六十度。普通 PLC 在 0℃ 到 60℃ 还算稳定,但放到 -40℃ 到 70℃ 且断电后还要保留历史数据,就比较吃力。ControlWave Micro 把 PLC、RTU、PAC 的特点揉在一起:ARM 处理器主打低功耗,CPU 内部有 4MB SDRAM 程序区、1MB 电池保护的 SRAM,以及 8MB 或 16MB Flash 用于程序和归档。这意味着调度中心和它断链时,它还能在本地存带时间标签的报警和历史数据,恢复通信后再回填,而不是停机等主站来问。
接口组合也是阀室场景很需要的配置。CPU 标配两个 RS-232 和一个 RS-485,标准 9 针 D 型口,波特率最大 115.2 kbps;以太网口有一到两个,10/100M 自适应。每个机架最多插两个通讯扩展模块,扩展后整机最多 11 个串行口。常见的用法是:一路 RS-485 接 Modbus RTU 仪表或第三方 PLC,一路 RS-232 接本地触摸屏,扩展口接数传电台或拨号 Modem,以太网口走光通信和本地调试。普通 PLC 要么网口多串口少,要么串口多网口少,像这样组合齐全的并不多。
除此之外,ControlWave Micro 支持 Class I Division 2 Groups C&D 危险区域认证,意味着阀室这种可能出现燃气泄漏的二区场所可以直接安装;CE 认证和 3V/m 抗干扰指标也写在了方案里。对于无人值守的线路截断阀室来说,硬件密码锁和看门狗、故障指示灯这套东西,比单纯拼 CPU 主频更有实际意义。选型时不要只看处理器是 ARM9 150MHz,要看到“睡眠模式”和 1.2W 带网口功耗:这意味着太阳能供电系统可以再缩小一个规格。
2.2 光通信双链路:不是简单地把数据发两遍
方案里有一句话很关键:RTU 通过光通信将数据同时向上下游站控发送,光通信通道任一处发生故障,均可保证 RTU 正常通信。现场落地时,我一般会让 RTU 的两个以太网口分别接到两套光纤收发器或两台工业交换机,上游站控和下游站控各占据一条独立链路。ControlWave Designer 里把两个网口都激活,设置为主备链路模式,正常时主链路承担全量数据,备用链路同样建立 TCP 连接但不切换;当主链路连续心跳失败后,备用链路立刻接管。
链路健康检测建议用控制器内置的通信状态字,而不是等 SCADA 侧自己判断超时。心跳周期我习惯设 5 秒,连续 3 次失败再切换,避免光纤抖动导致频繁倒换。同时把两个网口的 link status 和通信失败计数器映射到两个 DI 点,送到站控制系统的报警画面里。还有人会问,既然两条链路都能通,是不是可以做双链路同时发送?ControlWave 的环境下可以,但要注意两条链路上送到站控的数据如果时间戳不一致,站控侧要做去重和择优处理,否则历史库会产生重复记录。方案里的“同时发送”,本质是冗余保证,不是让两套站控同时写数据库。
2.3 阀室 I/O 清单与模块配置
原方案把阀室分成非阴保站和阴保站两类,阴保站多出的测点主要用于阴极保护电位的采集和阴保电源控制。按“用户点数 + 30% 备用”向上取整,再落到标准卡件通道上,配置关系很清楚。
| 阀室类型 | AI 实配 | DI 实配 | DO 实配 | 说明 |
|---|---|---|---|---|
| 非阴保站 | 16 点(7 用户) | 32 点(15 用户) | 16 点(5 用户) | 全部按 30% 备用预留 |
| 阴保站 | 16 点(11 用户) | 32 点(15 用户) | 16 点(7 用户) | AI/DO 为阴保增加 |
这里有一点容易被新人忽略:30% 备用算出来之后,不是直接买 30% 的卡件,而是按整卡的通道数向上取整。比如非阴保站 AI 用户 7 点,30% 预留 2.1 点,系统需要提供 10 点,于是选 2 块 8 通道 AI 卡,实际提供 16 点,剩余 6 点作为工程余量。DI 同理,1 块 16 通道卡不够,配 2 块 16 通道卡,实配 32 点,用户只用 15 点,余量非常充足。这个余量在投产初期非常有用,业主往往在联合调试阶段才提出增加压力变送器或阀位反馈,没有预留通道就得停机加卡。
机架方面,3 槽基本机架适合高位检测点,4 槽和 8 槽基本机架适合监控阀室;如果后续要增加阴保站测点,可以再挂 2、4、8 槽扩展机架。需要注意的是扩展机架受控制器背板总线和供电能力限制,扩展距离和模块数量不是无限增加,设计 I/O 清单时最好按单机架不超过 8 槽来约束。
2.4 用 Python 验算 MTBF 可用性
方案里给了一组 MTBF 数据和最终可用性结论,但它的计算过程不够透明。现场做技术方案评审时,业主经常要求复核 MTBF 可用性,我习惯用一段 Python 把串联模型算出来,放到计算书里。
# 串联可维修系统的可用性近似计算 # 按原方案 1# 监控阀室的硬件配置:1 CPU + 1 机架 + 1 电源 + 2 AI + 1 DI + 1 DO mtbf = { "chassis": 154763, "cpu": 193000, "power": 196800, "ai": 197716, "di": 197700, "do": 171471, } qty = {"chassis": 1, "cpu": 1, "power": 1, "ai": 2, "di": 1, "do": 1} mttr = 2.0 # 现场运维通常按 2 小时估 lambda_total = sum(qty[k] / mtbf[k] for k in qty) mtbf_sys = 1 / lambda_total availability = mtbf_sys / (mtbf_sys + mttr) print(f"系统 MTBF = {mtbf_sys:.0f} h") print(f"可用性 = {availability:.6%}")这段代码的逻辑是串联模型:每一块板卡都是一个故障源,系统总故障率为各卡故障率之和,系统 MTBF 取倒数,最终可用性用 MTBF / (MTBF + MTTR) 计算。AI 卡数量是 2,所以故障率乘以 2;MTTR 取 2 小时,对应现场维修人员从站控中心到阀室的大致车程加处理时间。这个模型没有考虑冗余备份,属于保守估计,所以计算结果应该比方案给出的 99.9981% 略低或接近。如果差得远,优先检查 MTTR 假设和模块数量,而不是怀疑公式。
3. ControlWave Designer 组态:IEC 61131-3 语言怎么选才不返工
3.1 五种语言与阀室场景的对应关系
ControlWave Designer 提供五种 IEC 61131-3 编程语言:ST、FBD、LD、SFC、IL。很多人以为选一种语言写到头,实际上 ControlWave 的程序可以在同一个任务里混排,工程文件里各功能块、各程序单元用不同语言编写,编译后统一链接。阀室这种既有逻辑联锁又有流量计算和数据通信的场合,语言选型直接决定后期维护难度。
| 语言 | 全称 | 阀室里的典型用途 |
|---|---|---|
| ST | 结构化文本 | 复杂计算、数据打包、流量累积 |
| FBD | 功能块图 | 模拟量处理、PID、AGA 计算 |
| LD | 梯形图 | 截断阀联锁、电机启停逻辑 |
| SFC | 顺序功能图 | 开阀/关阀顺序、ESD 流程 |
| IL | 指令表 | 简单布尔逻辑、底层驱动兼容 |
我的习惯是:逻辑联锁用 LD,因为现场电工出身的人看得懂梯形图;计算和通信用 ST,因为浮点运算、CRC 拼包、数组处理比梯形图清爽得多;SFC 用在远程开阀和 ESD 复位顺序上,能直观看到流程卡在哪一步。IL 现在用处不大,除非要做一些很底层、很省内存的小逻辑。ControlWave Designer 本身支持复制粘贴、自定义工具栏、I/O 仿真和离线测试,换语言不需要换工程,所以前期多花一小时规划任务划分,比后期在梯形图里写密密麻麻的移位寄存器强。
3.2 用 ST 写一段截断阀联锁
截断阀联锁是阀室控制里最核心的程序段,逻辑不复杂,但安全优先级必须清晰。最常见的错误是把“关阀”和“开阀”做成互斥输出,现场一旦电磁阀卡涩或信号抖动,两个输出同时滑落,阀门状态不可预期。下面这段 ST 逻辑适合做第一层保护:
(* 紧急截断阀关阀条件:ESD 或可燃气体报警或远程指令 *) valve_close_cmd := (esd_active OR fire_gas_alarm OR remote_esd_received) AND NOT valve_closed AND NOT valve_fault; IF valve_close_cmd THEN close_solenoid := TRUE; open_solenoid := FALSE; ELSE close_solenoid := FALSE; open_solenoid := TRUE; END_IF;这段 ST 的核心思想是“关阀优先”:任何一个危险条件成立,就置位关阀电磁阀,同时断开开阀输出。valve_fault作为禁止项,防止执行机构故障状态下继续输出命令。现场接线时还要保证关阀电磁阀是失电关阀,也就是正常状态带电、ESD 或断电时失磁关闭,这样即使控制器宕机,阀门也能靠硬件回路回到安全侧。ACCOL III 功能块库里有现成的 ESD 和顺序计划块,如果要做首出记忆,可以在这个基础上再包一层,记录第一个触发源,方便调度中心判断事故原因。
3.3 ACCOL III 功能块库:流量计算和历史归档
ControlWave Designer 自带 200 多个 IEC 61131-3 标准功能块和函数,包括定时器、计数器、数学运算、类型转换、比较选择,这些基础块足够覆盖阀室的常规控制逻辑。真正让它和普通 PLC 拉开距离的是 ACCOL III 功能块库,这套库是基于 Bristol Babcock 二十年 SCADA 和过程控制经验积累的,里面有 60 多个针对石油天然气、水和污水、过程计量的功能块。
对阀室来说,最常用的是平均值计算、累积值计算、PID、Lead/Lag,以及 AGA 天然气流量计算。如果是计量阀室,直接用 ACCOL III 里的 AGA 流量块,压力和温度补偿已经在块内部实现,不需要自己从理想气体状态方程开始推。自己写流量计算最大的坑是单位换算和压缩因子处理,投产后再发现计量偏差,改程序要重新做计量标定,代价很大。历史数据方面,ACCOL III 支持控制器内存储带时间标签的报警和历史归档,断网期间不丢失,恢复后回填,这是普通 PLC 需要额外加历史模块才有的能力。
3.4 历史数据回填机制的组态要点
历史回填听起来简单,实际调试时最容易出问题的是采样周期和归档区容量不匹配。我一般把模拟量采样周期设 1 秒,开关量按变化记录,也就是变位才写一条,这样 8MB Flash 可以撑很长时间。组态时要在 ControlWave Designer 里明确归档区大小,并设置报警优先级,避免大量重复报警把存储空间占满。
另外,回填依赖 RTU 和站控的时间同步。RTU 自带实时时钟,但断网期间时钟会漂移,如果站控侧没有定期校时,回填数据的时间戳可能偏移几秒到几十秒。所以站控 SCADA 里要加一个时间偏差检查画面,偏差超过 5 秒就报警,并触发校时。现场验收时不要只看历史曲线连续,还要随机抽一段断网时间,核对数据时间戳与现场实际工况时间是否吻合。
4. 阀室联锁逻辑落地与 Modbus RTU 通信调试
4.1 截断阀本地/远程控制的优先级
截断阀控制的优先级必须非常明确:硬接线 ESD 优先级最高,其次是控制器内的 ESD 逻辑,再往下才是调度中心的远程指令。远程指令里还要区分允许项和禁止项。我经手的管道项目通常规定:远程只允许关阀,不允许远程开阀;开阀必须人工到现场确认上下游压力平衡后,在本地操作箱操作。这个限制要在 SCADA 数据库和 RTU 程序里同时做,SCADA 侧把远程开阀按钮做成禁用的软点,RTU 侧把远程开阀指令直接过滤掉,双保险。
这样做还有一个附带好处:避免调度中心误操作或操作员培训不足导致的误开阀。RTU 程序里开阀条件一般包括:
- 阀室本地/远程切换开关处于远程位。
- 阀前后压差在允许范围。
- 无 ESD 锁定信号。
- 站控下发开阀命令且命令持续保持。
每一步都满足后,控制器才允许开出开阀电磁阀;任何一步不满足,指令就被丢弃并触发报警。这套逻辑和 ESD 联锁放在一起,构成了阀室控制的完整安全链。
4.2 光通信双链路的组态与故障切换
光通信双链路的组态不能只靠硬件接线,软件侧的参数决定了故障切换是否可靠。参考配置如下:
| 参数 | 推荐值 | 作用 |
|---|---|---|
| 心跳周期 | 5 秒 | 链路健康检测 |
| 切换阈值 | 连续 3 次失败 | 防止瞬时抖动频繁切换 |
| 主链路 | 上游站控侧 | 正常时优先走主链路 |
| 备用链路 | 下游站控侧 | 中断后接管,恢复后自动回切 |
心跳周期太短容易误判,尤其是光通信路径上经过光端机和交换机时,转发延迟会导致偶发超时;心跳周期太长则故障切换慢。5 秒心跳加 3 次失败,大约 15 秒内完成判断和切换,对长输管线来说可以接受。RTU 侧还要注意的是,两条链路使用不同的 IP 地址,站控数据库里的点源地址要区分清楚,否则切换后数据来源识别错误。
4.3 用 C++ 调试 Modbus RTU 从站:CRC 和字节序
阀室 RTU 本身是 SCADA 的远方终端,但对下还要接很多 Modbus RTU 设备,比如流量计算机、阴极保护数据采集器、第三方 IO 扩展模块。现场调试时我经常用 Visual C++ 写一个小工具直接读从站寄存器,验证接线和通讯参数。Modbus RTU 调试最烦的不是组包,而是 CRC 和字节序。先给一个标准的 CRC16 计算函数:
// Modbus RTU CRC16 计算,返回给主站,低字节在前 #include <cstdint> #include <cstddef> uint16_t crc16_modbus(const uint8_t* data, size_t len) { uint16_t crc = 0xFFFF; for (size_t i = 0; i < len; ++i) { crc ^= data[i]; for (int bit = 0; bit < 8; ++bit) { if (crc & 0x0001) { crc = (crc >> 1) ^ 0xA001; } else { crc >>= 1; } } } return crc; }CRC 多项式的反射形式是 0xA001,初始值是 0xFFFF,网上很多代码写成 0x8005 正序计算,结果在 Modbus 上是错的。计算出 16 位 CRC 后,低字节先发、高字节后发。构造 0x03 读保持寄存器报文时,寄存器地址和数量都是大端序:
uint8_t req[8] = {0x01, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00}; uint16_t crc = crc16_modbus(req, 6); req[6] = crc & 0xFF; req[7] = crc >> 8; // 前 6 字节:从站地址 1,功能码 3,寄存器地址 0,数量 1 // 最后 2 字节:CRC 低字节在前,高字节在后读 32 位浮点时要格外注意字序。ControlWave 和多数仪表默认是大端模式,也就是高字在前;但有些国产仪表用低字在前,调试时如果读到数值特别大或特别小,先交换前后两个 16 位寄存器再解析。我的经验是先读一个已知的整数寄存器,确认 CRC 和字节序没问题,再去解析浮点,否则会把 CRC 和字节序问题混在一起,浪费半天时间。
5. 现场调试三板斧:I/O 仿真、离线测试与数据回填验证
5.1 用 I/O 仿真把联锁跑顺
ControlWave Designer 支持 I/O 仿真和程序离线测试,这是投产前最值得花时间的环节。现场信号没接全时,可以直接在组态环境里强加 AI 值和 DI 状态,观察程序和 DO 输出变化。我会先做静态测试:把 ESD 信号置 1,确认截断阀关阀输出立即动作,再逐一测试可燃气体报警、供电故障、远程 ESD 指令,确认每个触发源都能独立关闭阀门。然后做动态测试,模拟通信中断恢复,观察历史回填是否触发。
仿真测试时要注意强制值的释放。ControlWave 里强制 I/O 后,如果程序下载或热重启,强制状态可能保持,导致现场信号被覆盖。测试完成后要把所有强制点清空,并对照 I/O 清单逐点确认状态为“正常”,避免带着强制点投产。
5.2 数据回填的验收方法
回填验证需要站控侧和历史库配合。断网一段时间重新恢复后,从站控历史库导出回填时间段的数据,检查时间戳连续性。我一般用 CSV 导出后用 Python 快速检查:
import pandas as pd df = pd.read_csv("rtu_history_20250710.csv", parse_dates=["timestamp"]) df = df.sort_values("timestamp") gap = df["timestamp"].diff() > pd.Timedelta("60s") print(df.loc[gap, ["timestamp", "tag_name", "value"]].head())这段代码把历史数据按时间排序,找出相邻记录间隔超过 60 秒的位置。正常情况下,只有设备检修断电才会出现长间隔;如果断网窗口内仍出现缺口,说明回填没覆盖那段区间,需要检查 RTU 归档区容量是否被打满,以及站控侧的回填请求时间范围是否正确。把这条逻辑跑完,看到回填时间戳连续覆盖了断网窗口,阀室控制系统这趟调试才算真正收口。
本文还有配套的精品资源,点击获取