ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

光储充一体化微网优化调度:CNN-LSTM预测与AFSA-MOPSO多目标优化实践

光储充一体化微网优化调度:CNN-LSTM预测与AFSA-MOPSO多目标优化实践

简介:这份资源面向电力系统与新能源技术领域的研究人员及技术开发者,聚焦光储充一体化微网优化运行这一课题,针对光伏发电间歇性与电动汽车大规模接入引发的负荷波动问题,给出从预测到调度的完整研究方案。压缩包内共1个docx文件,约55KB,以论文正文与MATLAB代码复现说明为主,涵盖光伏功率预测模型(BA-CNN-LSTM)、电动汽车负荷预测模型(迁移学习LSTM)以及多目标优化运行策略(AFSA-MOPSO)三大模块,代码采用模块化设计,便于扩展与集成。读者可据此掌握蝙蝠算法优化CNN-LSTM超参数的实现思路、迁移学习在负荷预测中的应用方式,以及以运行成本最小和互动功率差值最小为目标的多目标求解流程,并了解硬件在环验证、不确定性量化等后续研究方向。目前已有54人学习,适合希望深入光储充微网建模与智能算法调度的中高级读者参考。

1. 光储充一体化微网:当光伏、储能和充电桩被塞进同一个母线

去年夏天有个做园区配电的朋友找我,说他们那套光储充示范站白天光伏发得多、充电桩却没人用,晚上车来了光伏又歇了,储能电池一天两充两放,循环寿命掉得比预期快。问题不在设备,在调度策略——光伏出力、电动汽车充电负荷、分时电价、电池衰减成本,这四个变量互相打架,靠人工经验根本排不出最优解。这就是光储充一体化微网优化运行策略要解决的事:把光伏阵列、储能电池、充电桩挂在同一段直流或交流母线上,用智能算法预测光伏出力和电动汽车负荷,再通过多目标优化调度决定每一刻谁充谁放、充多少放多少。它适合新能源电站运维、园区微网设计、充电运营商和做能量管理系统(EMS)的工程师。下面我从预测模型选型一路讲到多目标调度落地,中间会拆开 CNN-LSTM、迁移学习和 AFSA-MOPSO 这几个热搜词背后的实操细节,也会把我在现场踩过的坑摊开说。

2. 光伏与电动汽车负荷预测:CNN-LSTM 怎么搭、迁移学习怎么省数据

预测是调度的输入。光伏出力受云层、温度、辐照度影响,电动汽车负荷受车主行为、工作日/周末、充电桩功率等级影响,两者时间尺度不同但都需要分钟级到小时级的精度。常见做法是用 CNN 提局部特征、LSTM 抓时序依赖,再配合迁移学习解决新站点数据不足的问题。

2.1 为什么选 CNN-LSTM 而不是单独 LSTM 或 Transformer

单独 LSTM 对光伏功率的爬坡和骤降响应慢,因为它把每个时间步同等对待,而云遮导致的功率突变往往只体现在连续几个采样点上。CNN 的卷积核能在时间维度上滑动,把相邻几个点的局部模式(比如 15 分钟内功率从 800W 掉到 200W)提取成特征向量,再喂给 LSTM 做长程建模。Transformer 在超短期预测上精度不差,但参数量大,边缘端 EMS 工控机跑不动,而且对样本量要求高,新投运的充电站往往只有两三个月数据,训不起来。

我一般用一维卷积,卷积核大小取 3 或 5,对应 15 分钟或 25 分钟的局部窗口。LSTM 层数不超过两层,隐藏单元 64 到 128 之间,再大就过拟合。输出层根据预测步长决定:如果只预测下一时刻功率,一个全连接层加线性激活;如果预测未来 4 小时 16 个点,就用全连接层输出 16 维向量。

2.2 用 PyTorch 搭一个可跑通的 CNN-LSTM 预测网络

下面这段代码是我在多个园区微网项目里复用过的骨架,输入是过去 24 小时的光伏功率、辐照度、温度、湿度,输出是未来 1 小时的光伏功率。

import torch import torch.nn as nn class CNNLSTMForecaster(nn.Module): def __init__(self, input_dim=4, cnn_channels=32, kernel_size=3, lstm_hidden=64, output_steps=4): super().__init__() # 一维卷积提取局部时序特征,padding保证长度不变 self.conv1 = nn.Conv1d(in_channels=input_dim, out_channels=cnn_channels, kernel_size=kernel_size, padding=kernel_size // 2) self.relu = nn.ReLU() self.pool = nn.MaxPool1d(kernel_size=2, stride=2) # 降采样,减少LSTM计算量 # LSTM输入维度是卷积通道数,batch_first=True让输入形状为(batch, seq, feat) self.lstm = nn.LSTM(input_size=cnn_channels, hidden_size=lstm_hidden, num_layers=1, batch_first=True) self.fc = nn.Linear(lstm_hidden, output_steps) def forward(self, x): # x形状: (batch, seq_len, input_dim) -> 转成(batch, input_dim, seq_len)给Conv1d x = x.permute(0, 2, 1) x = self.relu(self.conv1(x)) x = self.pool(x) x = x.permute(0, 2, 1) # 转回(batch, seq_len', channels)给LSTM lstm_out, _ = self.lstm(x) last_step = lstm_out[:, -1, :] # 取最后一个时间步的隐藏状态 return self.fc(last_step)

逻辑说明:卷积层先把输入序列的局部波动模式抽出来,池化层把序列长度减半,LSTM 在压缩后的序列上跑,最后取最后一个时间步的隐藏状态做全连接输出。参数方面,cnn_channels控制特征图数量,光伏预测一般 16 到 32 够用;kernel_size取 3 对应 15 分钟采样下的 45 分钟窗口;lstm_hidden超过 128 后验证集损失不再下降;output_steps按调度周期设,如果 EMS 每 15 分钟滚动优化一次,就设 4。

训练时损失函数用 MSE 或 HuberLoss,Huber 对光伏骤降的异常点更鲁棒。优化器 Adam,学习率 1e-3,batch size 64,早停耐心值设 10 个 epoch。归一化用 MinMaxScaler 按天做,不要用全局归一化,否则阴天和晴天的功率尺度差异会被抹掉。

2.3 迁移学习:新站点只有一个月数据时怎么把模型救活

新投运的充电站或光伏站,历史数据往往只有几周,直接训 CNN-LSTM 会过拟合。直推式迁移学习的思路是:在数据充足的老站点上预训练一个 CNN-LSTM,然后把卷积层和 LSTM 层的参数冻结,只微调最后的全连接层。如果新旧站点辐照度分布差异大,也可以解冻 LSTM 最后一层一起微调。

具体操作分三步。第一步,用老站点两年数据训好基模型,保存 state_dict。第二步,加载基模型,把conv1和lstm的requires_grad设为 False,只留fc可训练。第三步,用新站点一个月数据跑 50 个 epoch,学习率降到 1e-4,因为只调全连接层,学习率太大会把预训练特征打乱。

# 加载预训练模型并冻结底层 model = CNNLSTMForecaster() model.load_state_dict(torch.load('pretrained_cnn_lstm.pth')) for name, param in model.named_parameters(): if 'fc' not in name: param.requires_grad = False # 冻结卷积和LSTM层 # 只把全连接层参数传给优化器 optimizer = torch.optim.Adam(filter(lambda p: p.requires_grad, model.parameters()), lr=1e-4)

有个坑要注意:如果新站点的采样间隔和老站点不一样,比如老站 15 分钟、新站 5 分钟,不能直接迁移,得先把新站数据重采样到 15 分钟,或者改卷积核大小重新预训练。另外,迁移学习不是万能后悔药,如果新站点在完全不同的气候区(比如从西北干热迁到东南沿海高湿),辐照度波动模式差异太大,冻结底层反而会拖累精度,这时候只迁移卷积层、LSTM 重新训更稳。

3. 多目标优化调度:AFSA-MOPSO 怎么在光储充之间找平衡

预测给出未来一段时间的功率曲线后,调度层要决定储能充放电功率和充电桩分配策略。目标通常有三个:运行成本最低、光伏消纳率最高、电池循环寿命损耗最小。这三个目标互相冲突——多充多放能提高消纳但伤电池,少充少放保寿命但可能弃光。多目标粒子群(MOPSO)能同时优化多个目标并输出一组帕累托解,但标准 MOPSO 容易早熟收敛,所以引入人工鱼群算法(AFSA)的觅食和聚群行为来增强全局搜索。

3.1 目标函数与约束条件怎么写

运行成本包括从电网购电费用减去余电上网收益,再减去充电服务费收入。光伏消纳率是实际消纳量除以可发电量。电池寿命损耗用吞吐量法折算,每充放 1 kWh 对应一定的循环次数衰减。约束条件包括:储能 SOC 在 10% 到 90% 之间、充放电功率不超过额定值、充电桩总功率不超过变压器容量、功率平衡约束。

写成数学形式,决策变量是每个调度时段的储能充放电功率和充电桩功率分配系数。目标函数加权求和时,权重不是拍脑袋定的,我一般用熵权法从历史运行数据里算,或者让运行人员根据季节调整——夏天光伏多,消纳率权重调高;冬天电池活性差,寿命权重调高。

3.2 AFSA-MOPSO 的混合策略与参数设置

标准 MOPSO 的速度更新公式里,个体最优和全局最优引导粒子飞行,但全局最优只从外部存档里随机选一个,容易让整个种群往一个方向挤。AFSA 的聚群行为让粒子向邻近伙伴的中心移动,觅食行为让粒子向食物浓度高的方向移动,追尾行为让粒子跟随最优邻居。把这三条规则嵌入 MOPSO 的速度更新,相当于给粒子加了局部社交引导,能维持种群多样性。

具体实现时,粒子位置编码成[P_ess_1, P_ess_2, ..., P_ess_T, k_1, k_2, ..., k_T],前 T 维是储能功率,后 T 维是充电桩分配系数。速度更新分两部分:MOPSO 部分用惯性权重 w、个体学习因子 c1、全局学习因子 c2;AFSA 部分用聚群因子和觅食因子。参数设置上,种群规模 100 到 200,外部存档容量 100,惯性权重从 0.9 线性降到 0.4,c1 和 c2 都取 1.5 左右。AFSA 的感知距离 visual 取搜索空间的 1/5,步长 step 取 visual 的 0.3 倍,拥挤度因子 delta 取 0.6。

import numpy as np def afsa_mopso_update(particles, velocities, pbest, archive, w, c1, c2, visual, step): """一次迭代的混合更新,particles形状(num_particles, dim)""" num, dim = particles.shape for i in range(num): # MOPSO部分:向个体最优和存档中随机选的全局最优飞行 gbest = archive[np.random.randint(len(archive))] r1, r2 = np.random.rand(dim), np.random.rand(dim) v_mopso = w * velocities[i] + c1 * r1 * (pbest[i] - particles[i]) \ + c2 * r2 * (gbest - particles[i]) # AFSA聚群行为:找visual距离内的邻居,向邻居中心移动 dist = np.linalg.norm(particles - particles[i], axis=1) neighbors = particles[(dist > 0) & (dist < visual)] if len(neighbors) > 0: center = neighbors.mean(axis=0) v_afsa = step * (center - particles[i]) / (np.linalg.norm(center - particles[i]) + 1e-8) else: v_afsa = np.zeros(dim) # 混合速度,0.7权重给MOPSO,0.3给AFSA velocities[i] = 0.7 * v_mopso + 0.3 * v_afsa particles[i] = particles[i] + velocities[i] return particles, velocities

逻辑说明:每个粒子先按 MOPSO 规则算一个速度,再按 AFSA 聚群规则算一个速度,加权合成。visual控制邻居范围,太大则聚群行为退化成全局搜索,太小则粒子各飞各的。step控制向邻居中心移动的幅度,取 visual 的 0.2 到 0.4 倍比较稳。混合权重 0.7/0.3 是我在几个算例里试出来的,AFSA 权重再高会拖慢收敛速度。

3.3 帕累托前沿的筛选与最终调度方案确定

AFSA-MOPSO 跑完后外部存档里是一组帕累托最优解,每个解对应不同的成本、消纳率和寿命损耗组合。实际下发调度指令前要选一个折中解。常用方法有模糊隶属度法:对每个目标算隶属度,取综合隶属度最大的解。也可以让运行人员根据当天电价和天气在帕累托前沿上手动选点——晴天光伏多,选消纳率高的解;阴天光伏少,选成本低的解。

我一般会在 EMS 里留一个接口,把帕累托前沿的前 10 个解和对应的三个目标值显示出来,运行人员点一下就能切换。这样既保留了多目标优化的灵活性,又不会让算法替人做所有决定。

4. 避坑与排查:光储充微网调度里最容易翻车的五个地方

4.1 预测模型离线精度高、上线就崩

现象:验证集 MAPE 只有 5%,接到 EMS 上跑,第一天预测误差就超过 20%。原因通常是训练数据做了全局归一化,而线上数据分布漂移,或者训练时用了未来信息(比如用 t+1 时刻的温度预测 t+1 时刻的功率)。解决:改成滚动归一化,每次预测前用过去 7 天数据重新算 scaler;检查特征工程里有没有引入未来时刻的变量,所有输入特征必须是当前时刻及之前可获取的。

4.2 储能 SOC 在调度周期末没有回归目标值

现象:一天调度下来 SOC 从 50% 掉到 10%,第二天早上没电可放。原因:目标函数里只写了运行成本和消纳率,没有加 SOC 终值约束。解决:在约束条件里加一条SOC_end >= SOC_start,或者在目标函数里加惩罚项,SOC 偏离初始值越多惩罚越大。惩罚系数取 0.5 到 1.0 元/kWh 等效。

4.3 AFSA-MOPSO 收敛曲线震荡不收敛

现象:迭代 200 次后帕累托前沿还在大幅跳动。原因:惯性权重没有随迭代下降,或者 AFSA 的 visual 设得太大导致聚群行为主导了搜索。解决:惯性权重从 0.9 线性降到 0.4;visual 取搜索空间对角线的 1/10 到 1/5;检查速度有没有越界,给速度加一个上限,取位置范围的 20%。

4.4 充电桩负荷预测把同时率当 1 处理

现象:预测的充电负荷远高于实际,导致储能过度放电。原因:充电桩不是每台都同时满功率运行,同时率通常 0.3 到 0.6。解决:在负荷预测后乘一个同时率系数,系数从历史数据里统计,工作日取 0.4、周末取 0.6,或者用泊松分布模拟到达率再算期望功率。

4.5 迁移学习微调时把预训练特征打乱

现象:新站点微调后精度比不迁移还差。原因:微调时学习率设成了 1e-3,全连接层的大梯度回传把卷积层参数也带偏了。解决:冻结层一定要设requires_grad=False,优化器只传可训练参数;微调学习率降到 1e-4 到 1e-5;如果还是不行,试试只迁移卷积层,LSTM 随机初始化重新训。

5. 从仿真到现场:把调度策略塞进 EMS 的最后一公里

仿真跑通只是第一步,真正下发到 EMS 还有几个细节要处理。第一,AFSA-MOPSO 的计算时间要控制在调度周期内。如果 EMS 每 15 分钟滚动一次,算法必须在 2 分钟内出结果。种群规模 100、迭代 100 次在工控机上大概 40 秒,够用;如果不够,把种群降到 50、迭代降到 80,精度损失在 2% 以内。

第二,帕累托前沿的解要转成 EMS 能识别的指令格式。我一般把储能功率转成充放电标志位加功率值,充电桩分配系数转成每台桩的限流值,通过 Modbus TCP 或 IEC 61850 下发。下发前加一个变化率限制,储能功率每分钟变化不超过额定功率的 10%,避免电池被指令阶跃冲击。

第三,现场一定要留手动覆盖开关。算法再稳也有翻车的时候,比如通信中断导致 SOC 读数是上一周期的,或者充电桩实际功率和预测偏差太大。运行人员能一键切回固定充放电策略,比什么后悔药都管用。

验证方法上,我习惯在并网前跑一周的开环测试:算法算出的指令只记录不下发,和实际运行数据对比,看预测误差和调度偏差。开环测试通过后再闭环,闭环第一周每天导出 SOC 曲线和成本曲线,确认没有异常波动。

最后说个我自己的习惯:每次现场调试,我都会把当天的光伏出力、充电负荷、储能 SOC、电价四条曲线画在一张图上,肉眼过一遍。算法给的帕累托前沿再漂亮,如果四条曲线的峰谷对不上,那一定是某个约束写错了。这个笨办法帮我抓过至少三次参数配置错误。希望帮到你。

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

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进