资讯详情

基于LSTM的短期光伏预测与储能调度:Python源码与数据集工程实践

📅 2026/10/10 13:43:20 | 华诺云谱 👁 阅读
基于LSTM的短期光伏预测与储能调度:Python源码与数据集工程实践
简介这是一份基于LSTM的短期光伏预测算法Python实现源码适合计算机相关专业学生用于期末大作业、课程设计或毕业设计参考。项目以园区真实运行数据为输入覆盖数据预处理、单变量预测、模型训练与评估等完整流程并同时提供负荷预测、规则集对照与外部气象数据获取脚本能够帮助读者理解时序预测在新能源场景中的落地方式。压缩包共11个文件以Jupyter Notebook为核心演示载体辅以Python脚本、Excel数据集、结果图表和Markdown说明文档整体约3.89MB结构紧凑便于按步骤复现。作者注明项目经过严格调试评审分95分以上代码可运行性有保障。目前已有224人学习/下载适合需要快速搭建光伏预测基线、产出高分课程报告或拓展毕业设计方案的开发者。1. 先把这事的边界说清楚LSTM光伏预测到底解决了什么园区光伏并网之后最头疼的不是装机容量而是“明天到底能发多少”。发电量估不准储能就得凭感觉充放调度员心里没底弃光率下不来。基于LSTM的短期光伏预测算法python源码数据集这套项目正是把这件事做成了一条可跑通的链路用园区历史气象和发电数据训练LSTM模型预测未来数小时的光伏出力再配一套规则集储能调度逻辑把预测结果直接转化成SOC充放电指令。它不只是一份LSTM模型代码而是光伏预测、负荷预测、储能调度三件事合在一起的完整工程。适合正在做毕业设计、课程大作业或者刚接手光储项目想快速搭一个预测基线的人。这份资源里既有能直接跑的多变量和单变量预测版本也有七天SOC数据和ClearOutside气象抓取脚本足够支撑一个完整的研究闭环。2. 数据先行园区光伏数据集与ClearOutside气象抓取2.1 项目里的数据资产到底有什么打开压缩包先别急着找模型文件。我一般会把数据类文件全部过一遍确认有哪些字段、什么时间粒度、覆盖多长周期再决定特征怎么拼。这份资源里最显眼的是SOC_1101-1107.xlsx从文件名就能看出是11月1日到7日连续一周的电池荷电状态记录单位是百分比直接反映储能系统每天在什么时段充电、什么时段放电。有了这段SOC曲线后面做规则集调度评估时才有对照基准不然预测做完后连“充放电策略是否合理”都没法验证。园区数据部分没有直接给一个统一命名的CSV而是分散在几个ipynb里通过相对路径读取。常见做法是每个笔记本开头用pd.read_csv()加载同一份园区数据文件字段一般包含时间戳、光伏实际功率、负荷功率、以及温度和辐照度等气象变量。如果你准备在自己的机器上复现第一步就是把所有ipynb里的数据路径改成你本地解压后的绝对路径否则一上来就抛FileNotFoundError这几乎是下载项目后最先遇到的坑。clearoutside_url.py是这份资源里容易被忽略但价值很高的脚本。ClearOutside是一个面向户外活动的天气预报网站它提供逐小时的全球辐照度GHI、温度、云量占比等字段正好可以当光伏预测的外部气象源。脚本的核心逻辑是构造带经纬度参数的URL请求后从HTML里正则出辐照度数据。值得留意的是这个脚本依赖requests和re两个库属于Python标准库加第三方请求库的组合不需要额外装Selenium之类的重型工具。2.2 数据清洗与序列构建的基本操作拿到原始数据后不能直接塞进LSTM。园区数据的采样间隔可能不统一凌晨时段光伏功率恒为零偶尔还有负值厂区倒送电的瞬时记录这些都要先处理。我惯用的清洗套路如下import pandas as pd import numpy as np # 读取园区光伏数据假设时间列名为time功率列名为pv_power df pd.read_csv(park_data.csv, parse_dates[time]) df.set_index(time, inplaceTrue) # 统一重采样到15分钟粒度缺失值做前向填充 df df.resample(15min).mean().ffill() # 清除物理上不可能的负功率和超过装机容量的异常点 df.loc[df[pv_power] 0, pv_power] 0 df.loc[df[pv_power] df[installed_capacity], pv_power] df[installed_capacity] # 构造时间特征小时、是否白天辅助模型理解周期性 df[hour] df.index.hour df[is_daylight] ((df[hour] 6) (df[hour] 18)).astype(int)重采样到15分钟是光储项目里比较通用的折中方案5分钟粒度噪声大1小时粒度又太粗预测结果没法支撑储能调度。前向填充能处理短时间缺失但如果出现连续好几个小时的空洞我建议直接删掉那一段而不是硬填否则模型会学到错误的平滑规律。负功率置零这里有个隐性问题如果负值是由于逆变器夜间自耗电产生的那这些值本来就该是接近零的小量直接置零不会影响大局。2.3 从ClearOutside抓气象数据的常见做法如果你所在地区没有现成的气象站数据clearoutside_url.py可以作为特征补充的来源。使用方法很简单把经纬度改成目标园区坐标脚本会返回当天的逐时辐照度序列。实际使用中要注意时区问题# 抓取到的气象数据是当地时区要和园区数据的时区对齐 # 如果园区数据是UTC抓取时就要指定时区转换 import pytz from datetime import datetime local_tz pytz.timezone(Asia/Shanghai) utc_tz pytz.utc local_time datetime.now(local_tz) utc_time local_time.astimezone(utc_tz)时区不匹配是气象数据与实测功率拼接时最隐蔽的坑。ClearOutside返回的是目标地点的当地时间如果你的园区数据用了UTC拼完之后辐照度峰值会整体偏移几个小时模型训出来之后预测曲线永远“慢半拍”。我的习惯是拿到数据后先打印两边的索引头尾和峰值时刻肉眼核对一遍再建模。3. LSTM多变量光伏预测序列建模与超参数调优3.1 为什么光伏预测场景里LSTM能立住光伏功率本质上是一个强时间依赖序列今天的出力曲线和昨天的曲线在形状上有高度相似性但辐照度受云层影响又带有随机波动。LSTM的优势在于它的门控机制能记住“今天上午十点开始多云”这类中期模式同时忽略“某分钟突然跳变”这类噪声这在15分钟粒度的短期预测里特别关键。相比XGBoost这类树模型LSTM不需要手工构造滞后特征滑窗本身就是特征工程相比传统ARIMALSTM对非线性映射的拟合能力更强尤其适合阴晴转换频繁的场景。项目里提供了“多变量”和“单变量”两个预测版本多变量版本用到的特征通常是历史光伏功率、温度、辐照度、湿度四者结合。单变量版本则只靠历史功率自身滚动预测适合没有气象数据的情况但精度上限要低一截。下面这段代码是多变量预测的主流程。3.2 多变量LSTM模型构建与训练import numpy as np from tensorflow.keras.models import Sequential from tensorflow.keras.layers import LSTM, Dense, Dropout from tensorflow.keras.optimizers import Adam from sklearn.preprocessing import MinMaxScaler # 假设df已经清洗完成特征列为[pv_power, temp, irradiance, humidity] features [pv_power, temp, irradiance, humidity] data df[features].values.astype(float32) # 归一化注意要用训练集的scaler去做transform避免未来数据泄漏 scaler MinMaxScaler(feature_range(0, 1)) scaled_data scaler.fit_transform(data) # 构建序列样本用过去48个15分钟点12小时预测未来4个点1小时 def create_sequences(data, lookback48, horizon4): X, y [], [] for i in range(len(data) - lookback - horizon 1): X.append(data[i:ilookback, :]) y.append(data[ilookback:ilookbackhorizon, 0]) # 只预测pv_power return np.array(X), np.array(y) lookback, horizon 48, 4 X, y create_sequences(scaled_data, lookback, horizon) # 按时间顺序切分前80%训练后20%验证不做随机shuffle train_size int(len(X) * 0.8) X_train, X_val X[:train_size], X[train_size:] y_train, y_val y[:train_size], y[train_size:] # 构建模型两层LSTM加Dropout防过拟合 model Sequential([ LSTM(64, return_sequencesTrue, input_shape(lookback, X.shape[2])), Dropout(0.2), LSTM(32, return_sequencesFalse), Dropout(0.2), Dense(horizon) ]) model.compile(optimizerAdam(learning_rate0.001), lossmse, metrics[mae]) model.summary()这个结构的核心是两层LSTM的堆叠第一层64个单元输出完整序列让第二层能看到所有时间步的隐含状态第二层32个单元只输出最后一个时间步的结果再接Dense层映射到4个预测值。Dropout层放在LSTM之间比放在最后更有效因为它能抑制时序特征在层间传递时的过拟合。learning_rate0.001是Adam优化器对中规模时间序列预测比较稳的起点如果你发现训练Loss震荡不降可以把学习率降到0.0005而不是调大batch size。3.3 训练过程与Loss趋势判断# 训练100个epoch用EarlyStopping防止过拟合 from tensorflow.keras.callbacks import EarlyStopping early_stop EarlyStopping(monitorval_loss, patience10, restore_best_weightsTrue) history model.fit( X_train, y_train, validation_data(X_val, y_val), epochs100, batch_size32, callbacks[early_stop], verbose1 ) # 训练完保存模型同时把scaler一并存下来预测时要用 model.save(lstm_pv_multivariate.keras) import joblib joblib.dump(scaler, scaler.save)判断训练是否正常主要看val_loss的下降曲线。前10个epoch如果val_loss快速下降然后进入平台期这是正常表现。如果val_loss在20个epoch附近开始反弹回升说明过拟合开始EarlyStopping会主动拉回最优权重。restore_best_weightsTrue保证最后保存的是验证集上表现最好的那组参数而不是最后一个epoch的权重这个小设置能实打实提升预测精度。一个经常被忽略的问题预测时输入的特征顺序必须和训练时完全一致。比如训练时特征列是[pv_power, temp, irradiance, humidity]预测时如果只拿了[temp, irradiance, humidity, pv_power]模型输出的结果就是废的。因为LSTM是按照输入张量的最后一维顺序解析特征的顺序一旦错位所有门控计算都建立在错误语义上。4. 单变量降级方案与负荷预测LSTM的复用与边界4.1 单变量光伏预测没有气象数据时怎么保底不是所有园区都有气象站更多情况是光伏功率采集器里只有功率一个字段。项目里的“用园区的数据测试光伏预测-单变量.ipynb”给出了明确的降级方案只用历史功率滑窗预测未来功率。这种做法把LSTM当成了一个时序自回归模型训练代码和多变量版本几乎一样区别只在特征维度降为1。# 单变量版本只用pv_power一列构造序列 single_data df[[pv_power]].values.astype(float32) scaler_single MinMaxScaler(feature_range(0, 1)) scaled_single scaler_single.fit_transform(single_data) # 滑窗参数不变但特征维度从4变为1 X_single, y_single create_sequences(scaled_single, lookback48, horizon4) # 模型结构不变只需要把input_shape的第三维改成1单变量模型的优势是部署简单不需要维护气象抓取和清洗链路。但代价很直接它无法感知明天是否阴天只能假设“未来的出力模式延续最近12小时”。在多云天气高频切换的夏季单变量模型的预测曲线会明显滞后于实际波动。我一般把单变量版本视作模型基线用来衡量增加气象特征后到底提升了多少精度而不是真的作为正式预测方案上线。4.2 负荷预测用同一套LSTM框架复用的细节差异项目里的“用园区的数据测试负荷预测LSTM.ipynb”把同样的网络结构迁移到了负荷预测场景。负荷序列和光伏序列有一个本质区别负荷有极强的日周期性和周周期性早上八点和晚上七点各有一个尖峰周末负荷明显低于工作日。这意味着做负荷预测时时间特征的构造比光伏场景更讲究。# 负荷预测时把星期几也编码成特征 df[weekday] df.index.weekday df[is_weekend] (df[weekday] 5).astype(int) # 序列构建时把is_weekend拼进特征矩阵 features_load [load_power, hour_sin, hour_cos, is_weekend]这里我把hour拆成了hour_sin和hour_cos两个值而不是直接用整数0到23。原因是24这个数字在数值上被人为分成高低两个区间模型要花额外的参数去学习“23之后是0”这种跨越关系而正弦余弦编码能天然表达时间的循环连续性。这个技巧在光伏和负荷预测里都能用尤其在负荷模型里效果更明显因为负荷波峰波谷和时间点的绑定更紧密。4.3 模型复用时的评分指标选择做完负荷预测再回去看光伏预测的评估我建议不要只看RMSE或MAE。光伏夜间功率为零白天的预测误差会被夜间大量零值稀释RMSE算出来的数字可能看起来不错但白天峰值时段偏差还是很大。更合理的做法是分时段评估# 按小时分段计算误差突出白天时段的表现 df_result[hour] df_result.index.hour daytime_error df_result[(df_result[hour] 8) (df_result[hour] 16)] mae_daytime np.mean(np.abs(daytime_error[pred] - daytime_error[true]))这种分时段统计能帮你判断模型到底是在“蒙对夜间零值”还是“真的预测准了白天出力”。我见过不止一个项目汇报的RMSE低得漂亮实际白天误差已经超过30%问题就出在评估指标被夜间零值稀释了。5. 避坑笔记光伏预测项目里的五个典型翻车现场5.1 时间序列泄漏训练集和验证集混在一起Shuffle现象模型在训练集上Loss很低验证集上表现也不错但一上真实数据预测就完全对不上。原因对时间序列数据做了随机train_test_split导致训练集里混入了验证集之后的时间段。LSTM记忆力很强直接记住了验证集的“上下文”评估分数失去参考价值真实部署时面对的是未来数据效果自然雪崩。 解决按照时间顺序切分训练集永远取时间上靠前的部分验证集取靠后的部分。代码里train_size int(len(X) * 0.8); X_train X[:train_size]这个写法就是为了保证连续性。5.2 归一化泄露整个数据集一起Fit Scaler现象验证集上预测曲线整体偏低且越往后期偏差越大。原因MinMaxScaler在全部数据上做了fitscaler记录的最大最小值包含了验证集和未来数据的信息。这在学术实验里不明显但模拟在线预测时测试集的最大值被提前“看到”了。 解决先切分再归一化只在训练集上执行scaler.fit_transform验证集和预测时用同一个已拟合的scaler做transform。5.3 夜间零功率主导Loss模型学成了“全天零值预测器”现象训练过程中Loss一直在降画预测曲线发现全天的预测值都趋近于一条接近零的线。原因夜间零功率样本占全天50%以上MSE计算时这些样本贡献了大量零误差模型发现只要预测成零就能拿到很低的Loss。白天峰值那部分误差在总Loss里占比被稀释。 解决训练时给白天样本更高的权重或者干脆用白天时间段单独训练一个模型夜间直接置零。项目里的“规则集-第一版.png”设计思路里就考虑了分时段处理的逻辑。5.4 预测结果逆归一化乱套scaler维度对不上现象预测出来的功率值忽大忽小有时候甚至变成负数。原因多变量模型输出的是4个未来时间步的pv_power预测但输出经过了全特征维度的scaler逆变换导致结果被乘上了其他特征的尺度。 解决预测时只对pv_power这一列单独做逆变换不要对全特征矩阵做整体inverse_transform。建议在序列构建阶段就把预测目标单独取出来像代码里y.append(data[ilookback:ilookbackhorizon, 0])那样只取第0列。5.5 滑窗长度拍脑袋定没有做敏感性测试现象改lookback从24改成48之后预测曲线平滑了不少但滞后加重改成96又反应迟钝。原因lookback决定了模型记忆回顾的时间窗口。48个15分钟点覆盖12小时能覆盖“上午的天气趋势影响下午出力”这种跨半天关联96个点覆盖全天但也会引入过多过时信息。 解决分别测24、48、72三组lookback取验证集MAE最低的那组。这个测试跑起来很快在光伏预测场景里48通常是比较均衡的选择。6. 规则集储能调度从预测结果到SOC联动控制的闭环验证6.1 把预测功率翻译成充放电指令项目根目录里的“规则集-第一版.png”和“基于规则集的.ipynb”把光伏预测结果接到了储能控制逻辑上。规则集的核心思路不复杂预测未来一小时光伏出力明显大于负荷时储能充电把多余电量存起来预测出力不足时储能放电补缺口。SOC数据的联动是这里的关键一环。# 规则集调度核心逻辑输入为预测光伏功率pv_pred和负荷功率load_pred # 输出为储能指令1表示充电-1表示放电0表示待机 def dispatch_rule(pv_pred, load_pred, soc, soc_min0.2, soc_max0.9): surplus pv_pred - load_pred if surplus 0 and soc soc_max: # 光伏富余且电池没充满充电 charge_power min(surplus, (soc_max - soc) * capacity) return 1, charge_power elif surplus 0 and soc soc_min: # 光伏不足且电池还有余量放电 discharge_power min(-surplus, (soc - soc_min) * capacity) return -1, discharge_power else: return 0, 0这个规则集比直接比较功率大小多了SOC上限下限的双重保护。soc_max设为0.9而不是1.0是为了给电池留出安全余量避免过充soc_min设为0.2是防止深度放电损伤电芯。这些边界参数在项目里的SOC_1101-1107.xlsx中可以找到实际运行轨迹来反向验证。第一天SOC从早上七点的0.55爬到中午12点的0.88下午稳定在0.85附近说明调度策略确实在正午光伏高发时段驱动了充电动作。6.2 验证规则集效果的参照方式拿了七天SOC数据之后最简单的验证方法就是把SOC_1101-1107.xlsx的实测SOC变化曲线和规则集模拟出来的SOC曲线放在同一张图里对比。如果两者趋势大体一致说明规则集的充电阈值和SOC边界设得合理。如果不一致优先检查SOC初始值是否对齐以及调度的时间粒度是不是统一到了15分钟。我自己的习惯是先用“模拟程序.ipynb”跑一遍离线仿真把连续一周的预测结果作为输入驱动规则集输出一段SOC变化序列再和真实SOC数据做相关性分析。相关性系数能达到0.8以上这套调度逻辑就具备实际参考价值了。从那次做完这个项目之后我每次拿到预测模型第一件事不是调精度而是先把预测结果接进下游调度逻辑里跑一遍闭环看整体系统的行为是否符合物理直觉。这一步能暴露很多模型层面看不出来的问题希望帮到你。本文还有配套的精品资源点击获取
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑