开头
这几年做新能源微电网和电动汽车充电站的优化调度项目,我最大的感受就是:很多人拿到“电动汽车+光伏+充电站”这类课题时,第一反应是直接堆模型、上算法,结果模型建得很大,求解跑不通,或者算出来的结果根本没法落地。这个“基于多时间尺度的电动汽车光伏充电站联合分层优化调度”项目,核心要解决的就是一个很现实的问题——光伏出力是波动且难预测的,电动汽车充电负荷是随机且不确定的,如果在单一时间尺度下做调度,要么计划太乐观执行不了,要么优化太保守经济性差。
我当时做这个课题选的工具就是 Matlab,搭配 YALMIP 工具箱调用求解器(我用的是 CPLEX,后来也试过 Gurobi),代码结构分为“日前调度 + 日内滚动优化 + 实时反馈修正”三层,对应不同时间尺度。这个方案实测下来,既保证了充电站运行的可靠性,又把购电成本和弃光率压到了比较理想的水平。这篇文章我就把这个项目的完整思路、模型设计、Matlab 实现过程、以及我在调试中踩过的一些坑,一五一十写出来,希望能给正在做相关方向的同行一些参考。
这个内容适合谁看?如果你是电气工程、能源系统方向的研究生,或者在做充电站、微电网、储能调度相关项目的工程师,这篇文章应该对你有直接帮助。就算你还没有系统学过优化理论,只要懂一点 Matlab 基础,也能按这套思路搭出能跑的代码框架。
1. 项目整体设计与思路拆解
1.1 为什么单时间尺度调度在光伏充电站场景下不好使
先说说最根本的问题:为什么不能只做一次调度,然后照着执行就好?
光伏充电站面临的最大矛盾在于,光伏出力和充电负荷都是“变”的,但变化的时间尺度不一样。光伏出力受云层遮挡影响,分钟级可以发生大幅波动;而电动汽车充电负荷的随机性更强——用户什么时候来、来了要充多少、用什么功率充,这些都是动态变化的。
如果我们只做一个“日前调度”,即根据明天光伏和负荷的预测曲线,提前安排储能充放电计划和充电桩功率,那么在实际运行中一旦光伏突降或者负荷突增,原来的计划就全部作废了。反过来,如果只做实时的“反馈控制”,没有任何前瞻性,那储能和充电桩的功率分配就会很被动,无法在经济性上做到最优。
打个比方,这就像你从北京开车去上海。出发前查了路况、规划好路线,这是“日前计划”;开到一半发现前方堵车,改走其他高速,这是“日内调整”;到了路口发现施工,临时绕行,这是“实时修正”。三层缺一不可,少了任何一环,要么走冤枉路要么等太久。
从这个角度出发,我最终确定了“日前调度 + 日内滚动优化 + 实时反馈修正”的三层多时间尺度框架。时间尺度分别取:日前层是1小时一个时段,共24个时段;日内层是15分钟一个时段,滚动窗口取4小时(16个时段);实时层每5分钟执行一次校正,把日内层留给各设备的功率指令做微调。
1.2 分层优化的核心逻辑:上层定计划、下层控偏差
说完了时间尺度,再说说“分层”这个设计。为什么要分层,而不是把所有约束和目标塞进一个大模型里?
原因是多目标、多设备的优化问题如果全放在一个模型里,变量数量和约束规模会非常大。尤其是充电桩数量一多,比如一个站里几十个充电桩,每个桩的功率、启停状态、SOC约束都要考虑进去,单层模型的求解时间会成倍增加。而且在工程上,充电站的实际控制结构本身就是分层的——站控层下发总功率指令,各充电桩控制器负责执行。所以从模型结构上分层,和物理系统天然兼容。
我的做法是把问题分成两层:
上层:站控层(系统层)。这一层是“大脑”,负责在日前和日内尺度上,决策充电站与电网的交互功率、储能的充放电计划、光伏的消纳方式(全量上网还是部分弃光)、以及充电站内部的总功率分配比例。目标函数是经济性最优(购电成本最小、储能损耗最小、弃光惩罚最小),并满足变压器容量、储能SOC、联络线功率等约束。
下层:设备层(执行层)。这一层是“手脚”,负责把上层给的充电站总功率指令,具体分配到每一个充电桩上。它要处理每辆电动汽车的充电需求(SOC目标、离站时间),还要考虑充电桩的容量约束。由于上层给出的功率目标和下层各桩的实际负载能力之间的偏差,会出现一个上下层指令不一致的问题,我在代码里用迭代修正的方式让上下层收敛,也就是每次上层算完总功率后,下层按优先级分配,如果某个桩达不到目标功率,再把误差反馈给上层重新调整。
这样设计的好处很明显:上层模型规模小,求解快,可以做较长时间窗口的优化;下层模型即使充电桩数量很多,也只是在做功率分配,不需要求解大型优化问题,用简单的规则计算就能完成,运行速度快。
1.3 方案选型:Matlab + YALMIP + CPLEX
决定用 Matlab 实现,主要是三个考虑。第一,Matlab 的矩阵运算和数据处理能力非常适合做这类电力系统的数学建模;第二,YALMIP 这个工具包可以让你用接近数学表达式的语法来写优化问题,从模型到代码的转换成本低;第三,代码跑出来的结果可以直接用 Matlab 强大的绘图工具做可视化,分析曲线、对比方案都很方便。
至于求解器,我优先推荐 CPLEX。它在处理线性规划、混合整数线性规划问题上非常成熟,尤其是含充放电状态变量这类0-1变量的时候,速度和收敛稳定性是其他求解器比不了的。如果你没有 CPLEX 的许可证,也可以用 Gurobi 或者开源的 SCIP、CBC。我用过 Gurobi 跑同样的模型,性能差别不大。需要特别提醒的是,YALMIP 本身不是求解器,它只是一个建模层,真正解算的是你配置的后端求解器,这一点很多新手会搞混。
2. 核心细节解析与实操要点
2.1 多时间尺度模型的衔接机制
这一小节是整个项目的灵魂,我建议你把这个衔接机制理解透再动手写代码。
三个调度层之间的数据流向是这样的:日前调度层基于光伏预测曲线和EV负荷预测曲线,求解出未来24小时各时段的联络线功率、储能充放电功率、充电站总功率计划,并将这些计划下发作为日内层的“参考轨迹”。日内滚动层每15分钟启动一次,根据更新的光伏实测和EV接入情况,对未来4小时重新做优化,但它的目标函数里加了一个“对日前计划的跟踪惩罚项”——也就是说,日内层不是完全推翻日前计划,而是在尽可能贴近日前计划的前提下做局部修正。实时反馈层每5分钟工作一次,比较当前实测功率和日内计划值,如果偏差超过一定阈值,就对储能出力或桩级功率做小幅调整。
这个机制的设计意图是:既保证长时段的整体经济性(日前层负责全局),又保证短时段的运行可靠性(日内层负责局部),同时兼顾执行的可行性(实时层负责修正)。如果你只做日前不做日内,那晴天突然转多云的情况下,储能SOC很可能在下午就提前用光;如果只做日内不做日前,那谷电时段就没法提前安排储能充满,经济效益会明显打折扣。
有个细节值得留意:日内层的时间窗不能太短也不能太长。太短,比如只滚动1小时,那对未来的预见性不足,容易出现储能过充过放,一个滚动周期下来整个SOC轨迹是“近视眼”的;太长,比如滚动8小时,计算时间上去了,而且远时段的预测精度本来就不高,优化意义有限。我试过4小时窗口,效果和计算时间的平衡是最好的。
2.2 上下层目标函数与约束条件的设计
写优化模型之前,关键是想清楚“目标”是什么,“约束”有哪些。我用表格把主要模型要素列出来,方便你对照。
| 模型要素 | 上层(站控层) | 下层(设备层) |
|---|---|---|
| 目标函数 | 购电成本 + 储能放电损耗成本 + 弃光惩罚 | 跟踪上层总功率指令偏差最小 + 充电需求满足率最高 |
| 决策变量 | 联络线功率、储能充放电功率、充电站总功率 | 各充电桩输出功率 |
| 时间尺度 | 日前1h / 日内15min | 实时5min / 日内15min |
| 主要约束 | 功率平衡、变压器容量、储能SOC及功率上下限、联络线功率限值 | 充电桩功率上下限、EV充电需求约束、实时功率平衡 |
目标函数具体展开写一下。上层日前层的目标函数我定义成:
[ \min \sum_{t=1}^{24} \left[ C_{\text{buy}}(t) \cdot P_{\text{grid}}(t) + C_{\text{bat,loss}} \cdot \left( P_{\text{dis}}(t) + P_{\text{ch}}(t) \right) + C_{\text{curtail}} \cdot P_{\text{curtail}}(t) \right] ]
其中 (C_{\text{buy}}(t)) 是分时电价(峰谷电价会直接影响调度结果),(P_{\text{grid}}(t)) 是站与电网的交互功率,(P_{\text{dis}}(t)) 和 (P_{\text{ch}}(t)) 分别是储能放电和充电功率,(P_{\text{curtail}}(t)) 是弃光功率。
功率平衡约束是:
[ P_{\text{pv}}(t) + P_{\text{dis}}(t) + P_{\text{grid}}(t) = P_{\text{load,station}}(t) + P_{\text{ch}}(t) + P_{\text{curtail}}(t) ]
这个式子表达的意思很直观:光伏发电 + 储能放电 + 电网购电,等于充电负荷 + 储能充电 + 弃光。任何时刻都必须满足这个等式,物理上就是能量守恒。
储能SOC的约束是:
[ SOC(t+1) = SOC(t) + \eta_{\text{ch}} P_{\text{ch}}(t) \Delta t - \frac{P_{\text{dis}}(t) \Delta t}{\eta_{\text{dis}}} ]
同时加上 (SOC_{\min} \leq SOC(t) \leq SOC_{\max}),以及充放电功率上限约束。这里要注意充放电不能同时进行,这个非凸约束需要引入0-1变量处理,这也是为什么推荐用CPLEX这类混合整数规划求解器。
下层设备层的逻辑相对简单,核心是:
[ \min \sum_{i} \left( P_i(t) - P_i^{\text{ref}}(t) \right)^2 ]
约束是 (0 \leq P_i(t) \leq P_i^{\max}),并且每辆车在离站时SOC要达到用户设定值。层与层之间的衔接是通过“目标级联分析”的思路实现的:上层下发的总功率作为下层优化的目标值,下层分配完各桩功率后,将实际可达的最大功率反馈给上层,上层在下一轮优化中修正这一约束。
2.3 储能和充电桩建模的坑
储能建模是整个项目中最容易踩坑的地方。我一开始用的是“理想储能模型”——就是只考虑能量和功率约束,忽略充放电效率差异。结果跑出来的方案在峰谷电价切换时,会出现充电1小时再放电1小时的“振铃”现象,实际中根本不可能这么操作,因为效率损失会吃掉大部分套利空间。
后来我改成了更实际的模型:充电和放电效率分开考虑(充电效率0.95,放电效率0.95),并且给储能设置了最小连续充/放电时间约束,也就是如果开始充电,至少要持续充2个时段。这个改进显著减少了无意义的频繁切换,CSV跑出来的结果也合理很多。
充电桩建模也有讲究。如果你把每个充电桩都建模成连续可调的功率,那其实是在美化现实——大部分交流慢充桩都是定功率输出,直流快充桩虽然可以调功率,也是分档位调,不是连续可调。我在项目中把充电桩分成两类:一类是定功率的慢充桩(7kW,只能选充或不充),另一类是60kW快充桩(可在 20kW/40kW/60kW 三档之间切换)。这样做模型更贴近实际,但多了一些整数变量,求解时间会明显增加,我在代码中做了归一化处理来优化性能。
3. 实操过程与核心环节实现
3.1 Matlab 环境配置与代码框架
先说环境准备。我用的是 Matlab R2021b,YALMIP 是直接从 GitHub 下载的 master 分支,求解器是 CPLEX 12.10。如果你的 Matlab 版本比较新或者比较老,YALMIP 的兼容性一般都不是问题,它支持大部分主流的求解器接口。
代码的整体目录结构我这样组织:
project/ ├── main.m % 主程序入口 ├── config/ │ ├── params.m % 全局参数初始化 │ ├── load_data.m % 光伏和EV数据处理 │ └── price_data.m % 分时电价数据 ├── model/ │ ├── day_ahead_layer.m % 日前调度层 │ ├── intraday_layer.m % 日内滚动层 │ └── realtime_layer.m % 实时修正层 ├── solver/ │ ├── build_DA_model.m % 建立日前层优化模型 │ ├── build_ID_model.m % 建立日内层优化模型 │ └── build_RT_model.m % 建立实时层优化模型 ├── plot/ │ └── plot_results.m % 结果可视化 └── data/ ├── pv_power.csv % 光伏出力数据 └── ev_load.csv % EV充电负荷数据主程序 main.m 的流程是:加载参数和数据后,先调用日前调度层得到24小时的基础计划;然后进入一个模拟时钟循环,在循环中每隔15分钟调用一次日内滚动层,每隔5分钟调用一次实时修正层;最后把三层的结果汇总做分析并绘图。
3.2 光伏出力与EV负荷场景生成
光伏出力数据和EV负荷数据是模型的重要输入,我这里的做法是较好参考意义——用历史典型日曲线作为基础,叠加随机扰动生成多个场景,保证优化结果对预测误差有鲁棒性。
光伏出力:我采用了Beta分布随机变量来模拟云层遮挡带来的波动。具体来说,在晴天基准曲线的基础上,每个时段的光照强度乘以一个由Beta分布产生的随机因子。Beta分布的形状参数可以控制波动的大小,形状参数越大,出力越平稳;越小,波动越剧烈。代码实现很简单:
% 生成光伏出力场景 pv_base = pv_curve(t); % 基准晴空光伏出力曲线 beta_params_a = 8; beta_params_b = 8; s = betarnd(beta_params_a, beta_params_b, 24, 1); % 每个时段一个随机因子 pv_scenario = pv_base .* s'; % 叠加随机波动EV充电负荷的生成,我用的是蒙特卡洛方法。先假设每辆EV到达充电站的时间服从一个正态分布(峰值在下午5点到7点),车辆的初始SOC也服从正态分布(均值40%,标准差15%),然后随机生成每个充电桩一天内的充电需求。这个过程即使用相同参数跑多遍,每次生成的负荷曲线都有差异,这使得后续的鲁棒性验证更有说服力。
3.3 日前调度层的实现与求解
日前调度层的核心是用 YALMIP 建立混合整数规划模型并求解。我把代码精简后,结构大致是:
function da_result = day_ahead_layer(params, pv_forecast, ev_forecast) % 定义决策变量 P_grid = sdpvar(1, 24, 'full'); % 电网交互功率,购电为正 P_ch = sdpvar(1, 24, 'full'); % 储能充电功率 P_dis = sdpvar(1, 24, 'full'); % 储能放电功率 SOC = sdpvar(1, 25, 'full'); % 储能SOC,多取一个点为末端约束 u_ch = binvar(1, 24, 'full'); % 充电状态标志 u_dis = binvar(1, 24, 'full'); % 放电状态标志 P_curtail = sdpvar(1, 24, 'full'); % 弃光功率 % 目标函数 Objective = sum(price .* P_grid) + ... sum(bat_loss_coef * (P_ch + P_dis)) + ... sum(curtail_penalty * P_curtail); % 约束条件 Constraints = []; % 功率平衡约束 Constraints = [Constraints, P_grid + P_pv + P_dis == P_load + P_ch + P_curtail]; % 储能SOC递推约束 for t = 1:24 Constraints = [Constraints, SOC(t+1) == SOC(t) + eta_ch * P_ch(t) - P_dis(t)/eta_dis]; end Constraints = [Constraints, SOC(1) == params.SOC_init]; Constraints = [Constraints, SOC(25) == params.SOC_end]; % 日末SOC还原 % 充放电状态互斥约束 Constraints = [Constraints, u_ch + u_dis <= 1]; % 功率上下限约束 Constraints = [Constraints, 0 <= P_ch <= params.P_ch_max * u_ch]; Constraints = [Constraints, 0 <= P_dis <= params.P_dis_max * u_dis]; Constraints = [Constraints, -params.P_grid_max <= P_grid <= params.P_grid_max]; Constraints = [Constraints, 0 <= P_curtail <= P_pv]; % 弃光不能超过当前光伏出力 % 求解 ops = sdpsettings('solver', 'cplex', 'verbose', 0, 'showprogress', 0); optimize(Constraints, Objective, ops); % 结果保存 da_result.P_grid = value(P_grid); da_result.P_ch = value(P_ch); da_result.P_dis = value(P_dis); da_result.SOC = value(SOC); da_result.P_curtail = value(P_curtail); end我特别解释一下几个关注点。第一,SOC的初始值和末端值都要约束,末端值设为和初始值相等,是为了保证储能跨日的可循环性——不然模型会把储能电量在日末全部放光,第二天就无法工作了。第二,充放电互斥约束一定不能省,这在物理上是必须的,否则模型会同时充电和放电来“套利”效率损失,跑出来的结果完全不可用。第三,弃光功率的约束是0到光伏出力之间,因为从物理意义上,你只能放弃已经发出来的电,不能放弃不存在的电。
求解完成后,我会把日前计划保存下来,作为日内滚动层的参考轨迹。一般CPLEX求解这个24时段的MIP问题,规模大概在几百个变量,求解时间通常在几秒到十几秒,完全在可接受范围内。
3.4 日内滚动层和实时修正层的实现
日内滚动层和日前层在代码结构上很相似,但有两个关键差异。
第一个差异是时间窗口。日内层每次只优化未来4小时(16个时段,15分钟一个),但只执行第一个时段的决策,然后滚动到下一个执行时刻。这就是“滚动优化”的核心。用代码表达就是:
for t = 1:T_total_interval if mod(t, steps_per_intraday) == 0 % 每15分钟触发一次 % 构建本地窗口的索引 window_indices = t:min(t + 16 - 1, T_total_interval); % 求解窗口内的优化问题 id_result = build_and_solve_intraday(params, pv_realtime(window_indices), ev_realtime(window_indices)); % 应用第一个时段的决策 apply_control(id_result.P_grid(1), id_result.P_ch(1), id_result.P_dis(1)); end end第二个差异是目标函数多了一项跟踪惩罚项。我的表达式是:
[ \min \left( \text{购电成本} + \text{储能损耗} + \lambda \cdot \sum_{t \in \text{window}} \left( P_{\text{grid}}(t) - P_{\text{grid}}^{\text{DA}}(t) \right)^2 \right) ]
就是说,日内优化不能完全偏离日前给出的购电计划,不然会给电网造成较大的功率冲击。(\lambda) 是跟踪权重系数,需要根据实际情况调。我试过从0.01到10的取值,发现取0.5左右时,既能保证经济性调整的灵活性,又不会让联络线功率波动太剧烈。这个参数网上没有统一标准,我建议你按自己的场景做几次敏感性分析。
实时修正层就更简单了,它不做优化,只做比例校正。逻辑是:如果当前PCC点实测功率比日内计划值超了2%以上,就按比例削减快充桩功率;如果低于计划值,就把削减的功率恢复回来。如果储能还有剩余的调节裕度,也优先用储能来吸收偏差。这个层级的代码量不大,但非常重要,有了它系统才真正具备抗扰动能力。
3.5 求解性能优化与结果可视化
在实际调试中,我发现模型规模一大,求解时间就会变得不可控。做了几个优化后,性能有明显提升。
第一个优化是变量归一化。在建模之前,把所有功率变量都除以基准功率(比如100kW),SOC保留0到1的标幺值。这样数值都在0.1到10的范围里,求解器的数值稳定性会好很多,尤其是CPLEX的预处理阶段能更高效地识别约束结构。
第二个优化是求解器参数设置。我给CPLEX设置了'mip.tolerances.mipgap', 0.01,意思是允许1%的最优性间隙就停机。对于工程场景,1%的间隙带来的成本差异微乎其微,但求解时间可以从几十秒降到几秒,性价比非常高。如果你对最优性要求很高,可以设成0.001,但要有等待更久的心理准备。
第三个优化是给储能SOC变量做适当的“热启动”。因为在滚动优化的过程中,相邻两个窗口的优化结果本身不会差太多,所以我把上一个窗口的SOC最优解作为当前窗口的初始值传给求解器:
assign(SOC, prev_SOC_value); ops = sdpsettings('solver', 'cplex', 'usex0', 1);实测下来这个技巧能把日内层的求解时间减少一半左右。
结果可视化我习惯画四张图:第一张是光伏出力与消纳情况,直观看出弃光有多少;第二张是储能SOC曲线和充放电功率,看储能有没有按预期在谷充峰放;第三张是PCC点联络线功率对比,比较日前计划、日内计划、实际执行值三条曲线的偏差;第四张是充电站各类成本柱状图,汇总购电成本、损耗成本、惩罚成本。Matlab画这种多子图非常方便,我直接用subplot布局,效果足够清晰。
4. 常见问题与排查技巧实录
4.1 求解器报错的典型场景
做优化的过程中,三句话离不开求解器。我把经常遇到的问题整理成了表格,方便你直接对照排查。
| 报错信息 | 可能原因 | 解决思路 |
|---|---|---|
Solver not found! | YALMIP没识别到CPLEX/Gurobi | 确认求解器路径已加入Matlab路径,yalmiptest验证配置 |
NaN in solution | 模型无解或约束冲突 | 检查功率平衡约束,尤其是标幺化后单位是否一致 |
Infeasible problem | 约束过紧或变量范围不合理 | 放松一下充电桩功率上限,或取消末端SOC约束测试 |
Numerical issues | 数量级差的太远,比如1e6和1e-6混在一起 | 做变量归一化,避免使用绝对值极大的惩罚因子 |
Out of memory | 场景数太多或变量规模爆炸 | 减少场景数量,关闭YALMIP的debug模式 |
遇到infeasible问题时,我用的一个高效排查套路是:把目标函数直接设为常数,先只求一个可行解。如果连可行解都没有,说明约束本身有矛盾,再用check(Constraints)命令查看哪条约束残差最大,基本能快速定位问题。
4.2 模型算出来但结果不合理怎么办
结果不合理的情形,我在调试中也遇到过很多次,挑两个典型的说说。
第一种是储能充放电过于频繁,峰谷切换时出现振铃。前面提到过,这通常是因为模型太“理想”,没有考虑充放电切换的惰性。解决办法有两种:一是加0-1状态变量并限制最小开关时间,二是给储能充电和放电过程加上一个小幅度的线性惩罚,让模型在收益不明显时选择“什么都不做”。我最终用的是第二种方案,原因是它不增加整数变量,求解速度更快。
第二种是充电站整体运营成本看起来很低,但充电桩利用率也很低。这说明模型在“躺平”——因为目标函数只考虑降低成本,没有考虑满足充电需求的重要性。如果充电站不以营利为目的,可能问题不大;但如果要考虑用户满意度,就必须在下层目标函数里加入“充电需求满足率”或“等待时间惩罚”。我后续改进时在目标中加了客服满意度函数,本质上是给未完成的充电需求一个电压惩罚项,效果立竿见影。
4.3 分层迭代不收敛的调参经验
分层优化的收敛问题,是这种架构下最容易出的坑。我负责任的告诉你:很多论文里写“迭代3次收敛、误差小于1e-6”,实际项目中往往需要20到40次迭代才能稳定,甚至不收敛。
我遇到的不收敛场景分为两种。一种是上下层之间振荡,上层给出的总功率指令偏高,下层完不成,反馈回去后上层又把指令调低,低了下层又觉得太保守,结果来回震荡。缩小步长可以让振荡衰减,但收敛速度变慢。后来我改用了“阻尼迭代”的思路——每次修正只采信反馈偏差的一半,相当于给反馈环路加了一个低通滤波器:
P_total_ref(t+1) = P_total_ref(t) + alpha * (P_total_feedback(t) - P_total_ref(t)); % alpha取0.3~0.5,实测收敛稳定另一种不收敛是约束太“硬”导致的。比如某时段上层要求充电站总功率是300kW,但下面所有充电桩加储能、光伏怎么凑也凑不到300kW,那反馈永远达不到目标。这种情况的根源是上层模型还没学习到下层的可行域,需要在下层加入一些变量的松弛,把死约束变成软约束,并在目标函数里对松弛部分加重惩罚。
调参方面我建议你用“单一变量原则”:每次只改一个参数,记录迭代曲线,对比不同参数下的收敛速度和稳态度。千万别同时改两个参数,否则你根本不知道是哪个参数起到了作用。
4.4 数据敏感性分析的几点心得
项目接近完成时,你做做数据的敏感性分析是很有价值的。我可以分享一下自己的做法:对光伏出力,我把预测误差从5%增加到30%,看充电站运营成本的变化幅度;对EV负荷,我把到达时间的均值从下午5点移到晚上8点,看储能充放电计划要怎么适应。
我跑了五组对比实验,总结出几个规律:
- 光伏预测误差对日前调度的影响远大于对实时修正的影响。这是因为实时修正层执行周期短,预测误差还没积累起来就开始纠正了。
- EV到达时间的随机性对储能调度的影响最明显。如果EV集中在傍晚到达,那储能应该在下午提前充满电,否则到了晚上一起充电时,变压器容量可能会顶不住。
- 分时电价和光伏出力的时间错配是决定储能经济性的关键。光伏大发在中午,而峰电在晚上,这中间3到5个小时的跨度正是储能的盈利空间。如果光伏出力和峰电时段错开太远,储能容量就需要相应加大,不然电量存不住。
这些结论对实际运营者来说很有参考价值,因为它们直接指向了建设充电站时的容量配置和设备选型问题。
5. 从单站到集群的扩展思考
5.1 多站协调调度的潜力与难点
做完单站模型后,我一直在思考一个问题:如果手上有好几座充电站,彼此之间有光资源差异和负荷差异,能不能联合起来统一调度?
答案是肯定的,原理很简单:某站光伏出力大雨负荷小,可以先给储能充电,如果储能满了还能以内部结算价把电卖给同网内的另一座站,减少总的购电成本。这在数学模型上可以抽象成一个多区域的优化问题,需要把各站的联络线功率、共享储能容量和统一的分时电价等因素全部纳入考虑。
但难点也很明显——多站联合调度的信息交互是个问题。不同充电站可能分属不同运营商,谁也不愿意把自己的实时负荷数据完全共享出来。实际工程中往往要用分布式优化的方法,比如交替方向乘子法,把全局问题分解成各站独立求解再加协调项,这样既保护了数据隐私,又达到了全局优化的效果。我在Matlab里曾经实现过一版两站联合调度的原型,ADMM分解后的求解时间比单站整包求解多了大约3倍,但随着站数增加,这种分解算法的优势才会越来越明显。
5.2 与电网互动的商业模式延伸
电动汽车充电站本质上是一个“产消者”:既能发电(光伏)、又能用电(充电)、还能储能(电池车+站内电池)。这种角色在电力市场背景下是有额外价值的。
比如现在不少地方推行的需求响应机制,电网公司在用电紧张时发出削峰指令,充电站如果能快速削减一部分充电功率,就能拿到一笔可观的响应补贴。在我的模型里,这个需求响应信号可以很自然地作为一个“外部约束”加入日内层的优化问题——在响应时段内,充电站总功率上限被强制压低,然后模型自动调整储能的放电计划来满足剩余充电需求。
还有虚拟电厂的概念,把多个分散的充电站和储能聚合起来,作为一个整体参与电力辅助服务市场。这个方向我在论文里看到很多,真正落地的案例还不算多,但它确实是把充电站从成本中心变成利润中心的一个尝试。
5.3 强化学习与模型驱动结合的思路
最后说说这个项目做完后,我个人倒腾的一个小思考。传统优化方法(就是我上面写的这套)强依赖模型精度,光伏预测和EV负荷预测的准确性在客观上决定了调度结果的优劣。而强化学习这类数据驱动的方法不需要精确建模,但在约束处理和数据样本量上又有明显短板。
我尝试过一种混合路线:用强化学习决策储能充电的“策率”(比如充电时机的选择),再用优化模型算出具体功率分配。这种方案不解决所有问题,但至少提供了一个方向——把模型驱动和数据驱动的优点结合起来。若你时间充裕,可以在这个思路上做做实验。
结尾
做这个项目前前后后花了不少周末,过程中踩过的坑比想象中多,但收获也是真材实料的。我个人最大的感受是,优化调度这个方向,难的不是算法公式,而是如何把物理系统的约束和实际运行中的不确定性“翻译”成数学模型。多时间尺度加分层优化的框架,本质上就是在用不同精度的信息做不同时间层次上的决策,这是一种非常实用且符合工程直觉的思路。
最后再分享一个小经验:如果你拿到的课题和我这个类似,不要一上来就追求大而全的模型。先跑通单时间尺度的基础版本,看清结果中的不合理之处,再逐步加入日内滚动、实时修正这些机制。每一步都对比改动前后的调度曲线和成本指标,你才能真正理解每个模块到底是干什么用的。希望这篇分享能帮你省掉一些我当初填坑的时间,也期待你跑出一份让自己满意的调度结果。