4、数据采集与模拟:真实数据源介绍(SCADA、PMU)、模拟数据生成器设计、时间序列数据格式规范
好,咱们进入第四讲。这一章聊的是数据源头——说白了,就是你的可视化系统到底吃什么样的数据。
我见过不少新手,一上来就急着画界面、调颜色,结果数据一接进来,格式不对、时间戳乱跳、采样率对不上……整个系统直接崩掉。嗯,数据采集这块,其实是整个可视化系统的地基。地基不稳,上面画得再漂亮也没用。
4.1 真实数据源:SCADA 与 PMU
电力系统里,数据源主要分两大类。我习惯把它们比作「体检报告」和「心电图」——一个看整体趋势,一个看实时细节。
4.1.1 SCADA 系统
SCADA,全称是监控与数据采集系统。它在电力系统里已经用了三四十年了,非常成熟。
- 采样频率:通常是 1 秒到 5 秒一次。别嫌慢,够用了。
- 数据类型:电压、电流、有功功率、无功功率、开关状态、变压器档位等。
- 传输方式:通过 RTU(远程终端单元)采集,经通信网络上传到主站。
- 特点:数据量大但更新慢,适合做历史趋势分析和状态监控。
4.1.2 PMU 系统
PMU,相量测量单元。这玩意儿是近十几年才大规模部署的。它厉害在哪?
- 采样频率:每秒 30 到 120 个数据点。注意,是每个周波采多个点。
- 数据类型:电压相量、电流相量、频率、频率变化率。带角度信息。
- 同步方式:通过 GPS 授时,所有 PMU 的时间戳是严格同步的。
- 特点:精度高、速度快,能捕捉到暂态过程。比如电网振荡、低频振荡,SCADA 根本看不到,PMU 看得一清二楚。
4.2 模拟数据生成器设计
做开发时,你不可能天天连真实 SCADA 系统。一是没权限,二是真实数据波动太小,测不出边界情况。所以,我们需要一个模拟数据生成器。
我个人习惯用 Python 写一个轻量级的生成器。核心思路就三个字:像真的。
4.2.1 基础波形生成
电力数据不是纯随机数。它有规律:
- 电压、功率在一天内有「峰谷」变化
- 频率基本稳定在 50Hz 附近,偶尔有小波动
- 开关状态是离散的 0/1
我一般用正弦波叠加随机噪声来模拟负荷曲线。举个例子:
import numpy as np
import pandas as pd
from datetime import datetime, timedelta
def generate_load_curve(start_time, hours=24, interval_sec=1):
"""
生成模拟负荷数据
"""
n_points = int(hours * 3600 / interval_sec)
t = np.linspace(0, hours, n_points)
# 基础负荷:100MW 基线 + 50MW 日间波动
base_load = 100 + 50 * np.sin(2 * np.pi * t / 24 - np.pi/2)
# 叠加随机噪声(±5%)
noise = np.random.normal(0, 5, n_points)
# 加入尖峰脉冲(模拟大负荷启动)
spike = np.zeros(n_points)
spike_indices = np.random.choice(n_points, size=5, replace=False)
spike[spike_indices] = np.random.uniform(20, 40, 5)
load = base_load + noise + spike
# 生成时间戳
timestamps = [start_time + timedelta(seconds=i*interval_sec)
for i in range(n_points)]
return pd.DataFrame({
'timestamp': timestamps,
'load_mw': load
})
4.2.2 多数据源模拟
真实系统里,SCADA 和 PMU 是同时存在的。我建议模拟器也同时输出两种数据:
| 数据源 | 模拟频率 | 字段示例 | 备注 |
|---|---|---|---|
| SCADA | 每 2 秒 1 条 | timestamp, substation_id, voltage_kv, current_a, power_mw | 带随机延迟 0-500ms |
| PMU | 每秒 50 条 | timestamp, pmu_id, voltage_mag, voltage_angle, freq_hz | GPS 同步,时间戳精确到微秒 |
| 状态量 | 事件触发 | timestamp, breaker_id, status (0/1) | 模拟开关变位 |
嗯,这里要注意:SCADA 和 PMU 的时间戳精度不一样。SCADA 精确到秒就够了,PMU 必须精确到毫秒甚至微秒。你如果混在一起处理,一定要做时间对齐。
4.3 时间序列数据格式规范
数据格式这事,看着简单,但坑最多。我见过有人用 CSV 存 PMU 数据,结果时间戳精度丢了;也有人用 JSON 存 SCADA 数据,结果解析速度慢到崩溃。
我个人推荐两种格式:
4.3.1 面向存储的格式:Parquet
如果你要存大量历史数据,别用 CSV。用 Parquet。
- 压缩率高:同样数据,Parquet 比 CSV 小 5-10 倍
- 列式存储:查询某列时极快
- 支持复杂类型:时间戳、嵌套结构都能原生支持
示例结构:
# Parquet 表结构(SCADA 数据)
- timestamp: datetime64[ns] # 主键
- substation_id: string
- voltage_kv: float32
- current_a: float32
- power_mw: float32
- quality_flag: int8 # 0=正常, 1=可疑, 2=无效
4.3.2 面向传输的格式:JSON Lines
实时传输时,我习惯用 JSON Lines(每行一个 JSON 对象)。
- 可读性好:调试时一眼能看出问题
- 流式处理:可以逐行解析,不占内存
- 兼容性好:几乎所有语言和工具都支持
示例:
{"timestamp": "2024-01-15T08:30:00.123456Z", "pmu_id": "PMU_001", "voltage_mag": 220.5, "voltage_angle": -12.3, "freq_hz": 50.01}
{"timestamp": "2024-01-15T08:30:00.143456Z", "pmu_id": "PMU_001", "voltage_mag": 220.4, "voltage_angle": -12.4, "freq_hz": 50.00}
{"timestamp": "2024-01-15T08:30:00.163456Z", "pmu_id": "PMU_001", "voltage_mag": 220.6, "voltage_angle": -12.2, "freq_hz": 50.02}
4.3.3 质量标签与数据治理
真实数据不总是干净的。我建议每条数据都带一个质量标签:
| 质量码 | 含义 | 处理建议 |
|---|---|---|
| 0 | 正常 | 直接使用 |
| 1 | 可疑(超出正常范围但未越限) | 标记后使用,或插值替换 |
| 2 | 无效(通信中断、设备故障) | 丢弃,不参与计算 |
| 3 | 人工置数(运维人员手动输入) | 特殊标记,谨慎使用 |
嗯,这块我多说一句。很多可视化系统只画「好看」的数据,把质量码忽略了。但真实场景里,数据质量直接影响决策。你想想看,调度员看到一条电压越限告警,结果是因为数据无效导致的误报——这责任谁担得起?
小结
这一章我们聊了三件事:
- 真实数据源:SCADA 看趋势,PMU 看细节,两者互补
- 模拟生成器:别只生成理想数据,要包含异常场景
- 数据格式:存储用 Parquet,传输用 JSON Lines,时间戳必须带时区
下一章,我们会把这些数据真正「喂」给可视化系统。到时候你会看到,数据从采集到展示,中间要经过多少道工序。嗯,做好准备,那才是真正考验架构能力的地方。