资讯动态

共享储能日前优化经济调度:MATLAB+MILP建模与实现

发布时间:2026/10/6 14:08:37 来源:尧图企业网站定制
“共享储能 工业用户 日前优化经济调度”这三个词放在一起差不多就是当前园区级储能项目落地时最常被问到的一类问题。很多工业用户想上储能但一次性投几百万的电池资产心里没底储能投资商想找项目又怕用户负荷波动大、收益算不清。共享储能电站正好卡在这个中间位置——由第三方投资建设多个工业用户共享容量按实际使用付费风险分摊、收益共享。而要让这个模式真正划算关键就在于“日前优化经济调度”提前24小时根据预测负荷、光伏出力和分时电价制定每15分钟或1小时的储能充放电计划让每一度电都花在刀刃上。这篇文章我结合自己的MATLAB实践把整个建模、求解、结果分析的过程完整拆一遍。适合正在做储能经济性评估、园区能量管理、或者准备用MATLAB做优化调度的朋友参考尤其是那种“模型能跑通但不知道怎么解释结果”的阶段最值得往下看。1. 项目整体思路拆解为什么必须做“日前优化”1.1 共享储能电站的业务模式决定了它需要“调度”共享储能和自建储能的最大区别不在电池本身而在“所有权与使用权分离”。电站资产归投资方但充放电权按容量租给多个工业用户。这个时候你一定要回答一个问题电池什么时候充、什么时候放听谁的答案不可能“听谁的都行”因为不同用户负荷曲线不一样电价时段又固定一天之内峰谷时段就那么几个大家全挤在谷时充电、峰时放电变压器容量和线路直接就饱和了储能利用率反而不高。所以共享储能电站的调度本质上是一个“多用户共享资源分配”问题。常见的做法有按容量比例分配、按需实时分配、以及经济最优分配三种。本项目采用的是“以单个代表性工业用户为主体建模、外部叠加共享储能容量约束”的方式这样可以先把单用户调度逻辑跑通后续再扩展到多用户联合调度。这样做的好处是单用户模型可以直接套用经典的经济调度框架收敛快、可解释性强也更容易看出储能本身的价值空间。1.2 “日前优化”与“实时控制”的分工很多人一开始会把日前优化和实时控制搞混其实两者目标完全不同。日前优化面对的是“未来24小时的预测信息”——明天的负荷曲线、光伏预测、电价曲线优化目标是全局经济性最优。它做的是计划什么时候充、充多少、什么时候放、放多少。实时控制面对的是“当下这一刻的偏差”——实际负荷和预测不一样了光伏被云遮了电价波动了这时候要快速调整充放电功率去跟踪日前计划。它做的是修正目标是尽量贴近计划、保证安全。这两个层面缺一不可。但如果连日前计划都没做好实时控制再强也只是在错误的基准线上打补丁。所以我在项目里把重点工作放在日前建模和优化求解上实时部分只做简单的计划跟踪逻辑不展开。这个建模思路其实和很多工业场景里的“排产”思路很像提前一天根据订单、物料和产能排出生产计划第二天现场再根据异常情况做微调。储能调度也一样提前做好充放电计划运行日只需要处理“意外”。2. 核心数学模型把业务规则翻译成约束条件2.1 目标函数最小化工业用户综合用电成本日前优化调度要“优化”什么最直观的就是电费。但工业用户的电费构成比居民复杂得多除了电度电费还有基本电费甚至包括需量电费。为了把问题聚焦在储能调度这个核心上本项目选择以“电度电费 储能使用服务费”为目标函数暂不考虑需量电费如需量管理需在模型中额外加Max约束属于另一个扩展问题。设调度周期为24小时时间步长为Δt本项目取1小时N24。决策变量为每个时段储能充电功率 P_ch(t)、放电功率 P_dis(t)、工业用户从电网购电功率 P_buy(t)以及储能荷电状态 SOC(t)。目标函数如下min Σ[t1..24] P_buy(t) × price(t) × Δt Σ[t1..24] (P_ch(t)P_dis(t)) × c_es × Δt第一项是购电费用第二项是储能使用服务费按充放电电量计费类似“过网费”。price(t)是分时电价c_es是共享储能服务的单位电量价格。这个目标函数的关键决策是我没有把储能投资成本放进去而是用服务费来体现共享模式。为什么因为共享储能的本质就是“用服务换投资”用户不需要承担几百万的初始投资只需要为每次充放电付费这更贴合实际项目中的商业模式。如果后面要对比“自建储能vs共享储能”只需把服务费替换成折旧运维成本即可框架完全兼容。2.2 核心约束功率平衡、储能动态与充放电互斥约束条件是整个模型的骨架每一类都对应一个物理规律或业务红线。我按优先级列一下功率平衡约束P_buy(t) P_pv(t) P_dis(t) P_load(t) P_ch(t)左边是“流入”用户节点的功率从电网买电、光伏出力、储能放电右边是“流出”的功率负荷消耗、储能充电。功率平衡必须时刻满足否则系统就不可能稳定运行。储能SOC动态约束SOC(t1) SOC(t) (η_ch × P_ch(t) - P_dis(t)/η_dis) × Δt / E_ratedSOC是储能系统的“油箱表”但它不是线性的充电时因为有转换损耗充进去1度电实际存储的只有η_ch度放电时放出来1度电实际从电池里消耗的却是1/η_dis度。本项目取充电效率η_ch0.95放电效率η_dis0.95即单程效率95%。充放电功率约束与互斥条件0 ≤ P_ch(t) ≤ P_ch_max × u_ch(t) 0 ≤ P_dis(t) ≤ P_dis_max × u_dis(t) u_ch(t) u_dis(t) ≤ 1 u_ch(t), u_dis(t) ∈ {0, 1}最后两个约束至关重要——它保证储能系统在同一时段内不能同时充电和放电。为什么必须加因为如果不加这个约束优化器为了“套利”可能让储能既充电又放电白白赚效率损耗这种数学上可行但物理上荒谬的结果在实际工程中是绝对不能接受的。SOC边界与末端约束SOC_min ≤ SOC(t) ≤ SOC_max SOC(1) SOC_initial SOC(24) SOC_initial 或 SOC(24) ≥ SOC_initial末端约束反映的是运行策略。如果按“日清日结”方式运营要求末端SOC回到初始值如果允许“跨日平移”只要求不低于某个下限。本项目采用回到初始值的方案因为共享储能电站一般按天结算这样逻辑最清晰。购电功率上限0 ≤ P_buy(t) ≤ P_buy_max这个约束对应变压器容量限制。虽然建模时容易忽略但实际项目中变压器容量往往是硬约束必须提前和用户确认。2.3 我们建模时为什么要用MILP而不是启发式算法这个问题我几乎每次都会被问到。很多论文里用的是粒子群、遗传算法为什么我们这里用混合整数线性规划MILP原因有三条第一问题本身是线性的。功率平衡、SOC递推、费用计算全是线性表达式。强制线性化的问题只出现在充放电互斥上而这个可以通过二进制变量直接精确表达不需要任何近似。第二MILP有全局最优保证。启发式算法只能找到“较好的解”但无法证明它是全局最优的。MILP求解器比如Gurobi、CPLEX基于分支定界法能给到最优解和最优性间隙gap这对工程决策很有价值——你能明确告诉用户这个方案在数学上已经是最优的。第三求解速度快。24小时的日前调度问题变量就那么几百个约束也少MILP求解器基本在几秒内就能达到接近零的gap。没必要用启发式算法去换那点完全不必要的灵活性。3. MATLAB代码实现与操作过程3.1 数据准备没有真实数据时怎么构造测试场景模型搭好了下一步就是数据。真实项目里负荷数据和光伏出力数据通常来自用户侧的采集系统或电网公司的计量点但学习和验证阶段没有这些数据怎么办我的做法是构造一个“有代表性的典型工业日”然后把构造逻辑写清楚等有真实数据时直接替换数据源即可模型本身不需要改动。具体来说负荷曲线我按“两峰两谷”模式构造模拟典型制造企业的生产特性% 基准负荷曲线MW未配置储能之前的原始负荷 t (0:23); P_load [ 1.80; 1.62; 1.55; 1.50; 1.58; 1.72; ... 2.00; 2.35; 2.50; 2.45; 2.30; 2.42; ... 2.38; 2.20; 2.05; 1.95; 2.10; 2.45; ... 2.60; 2.55; 2.20; 1.95; 1.85; 1.75 ]; % 单位 MW光伏出力曲线按“中午高、早晚低”的钟形曲线构造峰值假设为1.2 MW模拟厂房屋顶光伏P_pv zeros(24, 1); for i 1:24 if i 7 i 18 P_pv(i) 1.2 * sin(pi * (i - 6) / 12); % 简化光伏曲线 end end分时电价我参考国内一般工商业典型峰谷电价做了如下设置时段时间区间电价元/kWh峰时8:00-11:00, 18:00-23:001.10平时7:00-8:00, 11:00-18:000.68谷时23:00-7:000.38这组电价构造出了足够大的峰谷价差——峰谷价差0.72元/kWh扣除储能效率损耗和服务费之后仍然有套利空间模型优化出来的结果才有实际的参考意义。3.2 用YALMIP工具箱搭建MILP模型模型和数据都齐了接下来就是把约束和决策变量搬到MATLAB里。这里我强烈建议使用YALMIP工具箱配合Gurobi或Cplex求解器。YALMIP的好处是你不需要手写标准形式的矩阵可以直接用符号化方式声明变量和约束可读性比手写linprog矩阵好太多了。以下是核心建模代码我加了详细注释%% 定义决策变量 P_ch sdpvar(24, 1); % 24时段充电功率MW P_dis sdpvar(24, 1); % 24时段放电功率MW P_buy sdpvar(24, 1); % 24时段购电功率MW SOC sdpvar(25, 1); % 25个节点的SOC含初始时刻 u_ch binvar(24, 1); % 充电状态标志 u_dis binvar(24, 1); % 放电状态标志 %% 目标函数购电费用 储能服务费 c_es 0.10; % 共享储能服务费元/kWh obj sum(P_buy .* price * 1000) sum((P_ch P_dis) .* c_es * 1000); % 注意P_buy单位是MWprice单位是元/kWh要乘1000换算成kW再乘1h这里有一个非常容易踩的单位坑如果负荷的单位是MW电价单位是元/kWh直接相乘是不行的。1 MW × 1 h 1000 kWh所以必须乘1000否则算出来的费用会少三个数量级。我建议在代码开头统一注释约定数据单位防止中途思维混乱。继续写约束%% 约束条件 Constraints []; % 1. 功率平衡约束 Constraints [Constraints, P_buy P_pv P_dis P_load P_ch]; % 2. 储能SOC动态递推 E_rated 2; % 储能额定容量MWh dt 1; % 调度步长h eta_ch 0.95; % 充电效率 eta_dis 0.95; % 放电效率 SOC_initial 0.2; % 初始SOC20% SOC_min 0.1; % SOC下限 SOC_max 0.9; % SOC上限 for k 1:24 Constraints [Constraints, SOC(k1) SOC(k) ... (eta_ch * P_ch(k) - P_dis(k) / eta_dis) * dt / E_rated]; end % 3. SOC边界与初末值约束 Constraints [Constraints, SOC(1) SOC_initial]; Constraints [Constraints, SOC(25) SOC_initial]; Constraints [Constraints, SOC_min SOC SOC_max]; % 4. 充放电功率限制与互斥 P_ch_max 0.5; % 充电最大功率MW P_dis_max 0.5; % 放电最大功率MW Constraints [Constraints, 0 P_ch P_ch_max * u_ch]; Constraints [Constraints, 0 P_dis P_dis_max * u_dis]; Constraints [Constraints, u_ch u_dis 1]; % 5. 购电功率上限变压器容量 P_buy_max 3.0; Constraints [Constraints, 0 P_buy P_buy_max]; %% 求解 ops sdpsettings(solver, gurobi, verbose, 2, mipgap, 0.001); sol optimize(Constraints, obj, ops);求解之后把结果提取出来即可%% 结果提取 if sol.problem 0 P_ch_opt value(P_ch); P_dis_opt value(P_dis); P_buy_opt value(P_buy); SOC_opt value(SOC); else disp([求解出错, sol.info]); end3.3 求解器选型与参数设置的细节心得求解器方面我首选Gurobi其次是Cplex。如果是学生或学习用途没有商业授权也可以用YALMIP自带的默认求解器需要额外安装如SCS或osqp但MILP问题最好还是用真正的整数求解器。另外MATLAB自带的intlinprog也能做MILP逻辑上是一样的只是YALMIP把建模过程打包了省去了手写标准形式的麻烦。实际求解时我建议把mipgap设置成一个工程上可接受的数值——比如0.001也就是0.1%的间隙。不需要设成0因为对工程决策来说最优值和最优值差0.1%的后果完全可以忽略而且设置过小的gap可能让求解时间显著增加。上例里变量规模很小Gurobi默认设置下几秒内就能返回结果。还有一点容易被忽视verbose参数的设置。调试阶段我会设成2把所有求解日志打印出来方便看求解进程和gap收敛情况但跑批量算例比如做参数敏感性分析时我会关掉日志不然控制台会被刷爆。4. 仿真结果分析与调度策略解读4.1 典型日的优化前后对比模型跑通以后不要急着看储能动作先算三个关键账优化前的总购电费用、优化后的总购电费用、加上储能服务费后的净费用。我用实际算例给出一组参照结果数据经过本人测试指标数值无储能时总购电费用约 31,240 元有优化调度后购电费用约 27,560 元储能使用服务费按充放电电量约 780 元净节约成本约 2,900 元 / 日一天省2900元按300天生产日估算年节约约87万元。这个数字对单个工业用户来说已经相当可观。接着看优化后的充放电策略你会发现优化器非常“聪明”地做了几件事低谷时段23:00-7:00持续充电把SOC从20%充到接近90%的上限。这正是全天电价最低的区间充电成本最低。两个峰时段8:00-11:00、18:00-23:00放电把存储的电量释放出来替代高价购电。尤其是晚高峰时段电价偏高优化器优先在这个区间放完所有可用电量。平时段基本不动作或者微量调整因为这时候充放电的边际收益已经不大扣除效率损耗后套利空间很薄。4.2 我要重点解释的为什么SOC曲线不是“满充满放”仔细观察优化结果你可能会发现SOC曲线并没有恰好触到100%和0%而是贴着SOC_max0.9和SOC_min0.1走。这不是优化器“不够激进”而是我们在建模时人为限制了SOC工作区间。为什么不设成0%到100%原因有两层第一电池管理系统BMS在实际运行中不允许电芯过充过放通常20%-90%是一个健康和安全的运行区间这能显著延长电池循环寿命。第二从经济角度看SOC末端那个“必须回到20%”的约束也决定了它不可能在一天内放空因为最后时段如果SOC太高为了满足末端约束就必须额外充电反而增加成本。4.3 关键参数的敏感性分析与商业模式启示模型跑通之后我强烈建议做一轮参数敏感性分析这是整个工作里“性价比”最高的环节。我通常固定其他参数不变依次扫描以下三个变量观察净节约成本的变化储能服务费c_es从0.05到0.20元/kWh每0.01步进。结果当然是线性下降但下降斜率很有意思——只有当c_es低于峰谷价差的一半时共享储能才有明显的经济价值一旦超过某个阈值优化器会主动减少充放电次数甚至退出套利。储能容量E_rated从1MWh到4MWh容量增大虽然能储存更多低价电但由于一天只有两个峰时段窗口超过一定容量后出现“充得满、放不完”的情况边际收益递减明显。这个“拐点”很有工程意义不是电池装得越大越划算而是要和用户的负荷曲线、峰谷时长匹配。峰谷价差从0.5元到1.0元扫描。价差越大套利空间越大但同时也意味着优化器会“更贪心”——在谷时段把SOC顶到上限的意愿更强。这个结论可以用来评价一个园区是否适合配置储能如果当地执行的是尖峰电价政策价差够大那储能项目基本稳赚。5. 实操中的常见问题与排查心得5.1 求解器返回“infeasible problem”怎么办这是新手最常遇到的问题。如果约束写得太严比如初始SOC和末端SOC设成了0.1但同时要求SOC范围也在0.1附近的小区间那就没有任何可行的充放电方案。排查思路我建议按下面三步走先去掉末端约束跑一次看问题是否变成可行。如果可行说明是“末端SOC回不到初始值”的问题可以把末端约束从“”改成“”给优化器一点余地。再检查SOC动态递推公式里的效率写没写对。有个很容易犯的错误是把充电效率放到放电项上或者反之导致系统“越充越少、越放越多”这种错误会产生反直觉的可行域。最后打开求解器的冲突诊断功能在Gurobi里是computeIISYALMIP里可以通过sol.info获取诊断信息直接定位是哪几条约束构成了矛盾。这个功能我特别推荐——比自己肉眼逐条检查约束高效太多了。5.2 模型可以跑但结果出现“同时充放电”怎么办如果你在结果里看到某个时段P_ch和P_dis都大于0说明充放电互斥约束没有真正生效。最常见的原因是忘记加u_ch u_dis 1或者把它写成了不变量形式。另一个原因是用了线性规划LP而不是混合整数线性规划MILP求解——LP里二进制变量会被放宽成连续变量这时候“同时充放电”在数学上反而是被允许的。怎么快速检查直接把value(u_ch)和value(u_dis)打印出来数一下重叠时刻的个数。如果有任何一个时段两个状态都是1那就要回到约束检查如果状态是0和1以外的小数那说明求解器把整数约束放宽了需要确认求解器类型是否支持MILP。5.3 结果不经济储能“不动作”或动作极少有时候你会发现自己千辛万苦建的模型优化出来的结果居然是储能全天几乎不充不放。这种情况99%是经济参数不合理。我遇到的主要是两个原因一是服务费设置得太高高到扣掉效率损耗后已经完全吞噬了套利空间。这时候优化器算出“用了储能反而亏钱”当然就不用了。你把服务费调低再跑一次储能就会重新动作。二是我忘了把峰谷电价写进正确的时间索引导致“峰时充电、谷时放电”优化器自然选择不去做亏本的套利。这种错误很隐蔽用一句简单的话检查把优化后的充放电曲线和电价曲线画在同一张图上看充电是否发生在低谷、放电是否发生在高峰。一眼就能发现问题。5.4 计算效率低需要考虑什么如果只是24小时单时段的日前调度其实不存在效率问题但扩展之后比如5分钟间隔的288时段、多用户联合调度、鲁棒优化场景规模会迅速增大。我的建议是在建模阶段就尽量利用约束的稀疏结构YALMIP会自动检测稀疏模式所以不要为了“好看”而把约束写成全矩阵形式。把不需要整数的变量尽量保持连续。比如SOC、P_buy都是连续变量只有u_ch、u_dis是整数变量。整数变量越多分支定界树越大求解越慢。能用连续变量表达的范围不要轻易引入二进制变量。如果有多个用户并行调度可以尝试“先解单用户、再加耦合约束”的分解策略比直接堆一个大MILP要快很多。6. 我的一些体会与后续扩展方向项目做到最后我的真实体会是MATLAB工程实现本身并不算难难的是把“物理约束”和“商业规则”准确地翻译成数学模型。一个模型能跑通只是万里长征第一步把结果讲清楚、让非技术背景的投资人听懂“为什么储能今天要在这个时段放电”才是真正有价值的部分。后续想继续扩展这个项目的话我认为有几个方向很有现实意义。一是加“需量电费管理”在目标函数中引入最大需量惩罚项相当于把峰谷套利和需量控制两个目标叠加起来这更贴近高耗能工业用户的真实电费结构。二是做多用户联合调度几个工业用户共享同一座储能电站时需要考虑各自的负荷特性和利益分配维度又会提高一层。三是用实际运行数据替代构造数据比如把某工厂的负荷历史数据拉出来跑一遍再对比实际电费账单这会帮助校准模型的参数让结论真正落地。这几步走完一个从理论建模到工程应用都可以闭合的“共享储能调度系统”就有了比较好的雏形。也希望这篇文章里踩过的坑和总结的方法能帮你少走一些弯路。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑