
绿电比例一提微网项目的储能配置问题就摆上了台面。做过几个微网方案之后我有个很深的感受电池储能容量优化配置看着是简单的“选多大电池”实际是把功率、能量、运行策略、电价机制和新能源出力耦合在一起的多维决策问题。用混合整数规划方法去解这个题是目前工程落地验证最多、也最容易被审图专家接受的路线之一。这篇文章把我在微网电池储能容量优化配置项目里的建模思路、求解流程和踩坑记录完整拆一遍适合正在做微网规划、储能容量设计的工程师也适合刚入门的研究生找一份可以直接参考的路线图。1. 为什么微网储能容量不能拍脑袋定——从三个真实矛盾说起1.1 功率和能量是两个不同维度的决策微网项目里最常听到的问题就是“配多少度电”。但真正落到设备选型你首先得回答两个数额定功率是多少千瓦额定容量是多少千瓦时。这两个数对应完全不同的投资逻辑。额定功率决定PCS变流器和配套并网设备的尺寸也决定储能系统在某个瞬间能吞吐多少电。额定容量决定电池本体能存多少能量也就是能扛多久。前者按kW计价后者按kWh计价两者没有简单的换算关系。同样是1MWh储能做成500kW/2h系统和做成200kW/5h系统初投资差不少能参与的服务场景也完全不同。我在实际项目中遇到过一个比较典型的案例某微网负荷峰值约500kW光伏装机400kWp业主一开始给的储能需求是“1000kWh电池”。结果一测算1000kWh虽然容量不小但如果没有把充电功率提上去中午光伏大发时段根本来不及把多余电量存下来下午晚高峰又放不出来。后来把方案改成500kW/900kWh投资没有增加多少运行效果反而更好。这说明容量优化配置如果只看kWh而忽略功率维度很容易配出一个“好看但不好用”的储能系统。所以建模时决策变量天然就要分成功率和能量两类目标函数里也必须分别给出成本系数。这不是为了学术上的完备性而是因为工程采购合同本来就是按这两个维度签的。1.2 运行策略和容量配置互为因果储能容量优化不是静态的“挑电池”因为储能的价值必须通过充放电运行才能体现。问题在于运行策略和容量配置是相互绑定的。假设你定了一个“两充两放”的固定运行策略再去反推需要多大容量大概率会低估。因为实际调度中光伏出力是波动的负荷曲线也不规则不可能总卡在标准时间段里完成两次完整循环。反过来如果你假设储能可以做完美预测的最优调度完全不考虑运行约束又会高估容量需求。我在做微网项目时通常会把容量配置和日运行调度放进同一个模型里求解。也就是说模型不仅要回答“建多大储能”还要回答“在每一天的每个时段储能怎么充、怎么放、什么时候买电卖电”。这种规划-运行一体化的做法比先定策略、再算容量的传统流程可靠得多。混合整数规划方法正好能承担这个任务投资变量是长期的、一次性决策运行变量是短期的、逐时段决策两者可以在同一个目标函数下被联合优化。1.3 确定性设计遇上不确定的源荷微网里的新能源出力不确定性是绕不开的。光伏出力看天气风电出力看风速负荷也有明显的随机波动。如果只拿一条“典型日曲线”去优化结果很可能在极端天气下失配。我的处理思路是不追求一开始就上复杂的随机规划而是先做多场景的确定性优化。把历史数据聚成若干个典型场景晴天、多云、阴天、冬季小负荷、夏季大负荷等每个场景赋予权重然后统一放进MILP框架。本质上这是场景法的简化版工程沟通成本低模型规模也可控。等这个版本稳定之后再根据需要引入鲁棒优化或者机会约束。2. 为什么选混合整数规划方法与问题的匹配逻辑2.1 启发式算法能算但“全局最优”担保是关键很多同行拿到这个优化问题第一反应是用遗传算法、粒子群算法。这些方法的优势是几乎不需要对目标函数和约束做线性化处理实现起来快而且能处理各种不规则函数。但如果追求工程可落地启发式算法有几个硬伤。第一结果不稳定。同一套参数、同一个算例分几次运行可能得到不同结果。向业主汇报时很难回答“为什么这次比上次省了20万”。第二缺少最优性证明。启发式算法告诉你“我找到了一个不错的解”但没办法告诉你这个解离理论最优差多少也就是没有gap的概念。第三约束处理容易出问题。微网模型的约束很多功率平衡、SOC边界、充放电互斥惩罚函数调不好就得到不可行解。我并不是完全否定启发式算法。在变量规模极大、模型非线性极强且不追求严格最优的时候它仍然是有效工具。但对于微网电池储能容量配置这种中等规模、结构清晰的优化问题使用混合整数规划可以获得全局最优解并且求解器会输出最优质间隙这在工程论证中非常有用。2.2 MILP为什么适合这个场景混合整数规划也就是MILP要求目标函数是线性函数约束是线性等式或不等式同时允许部分变量是整数或0-1变量。你看微网储能配置问题几乎每一块都能落进这个框架目标函数投资成本、运维成本、购电成本、售电收入都是线性可加的。功率平衡光伏出力、负荷、储能充放电功率、电网交换功率四者相加等于零这是线性等式。SOC递推下一时刻电量等于上一时刻电量加上充电量、减去放电量乘以效率系数这也是线性关系。充放电互斥引入两个0-1变量分别表示是否充电和是否放电然后加一条二者之和不大于1的约束线性表达非常自然。容量离散化如果电池只能按簇采购那就把电池簇数定义为整数变量每一簇对应的能量和功率作为常数。MILP求解器靠的是分支定界、割平面这类系统化搜索方法理论上在有限时间内收敛到全局最优并且能用gap量化当前解与最优解的偏离程度。我见过的微网配置模型决策变量一般几百个约束大几千行Gurobi或Cplex求解只需几秒到几分钟完全满足设计阶段的时效要求。2.3 什么时候该放弃MILPMILP不是万能的。如果储能的效率曲线是强非线性曲面或者电池寿命模型里耦合了充放电深度、循环次数、温度等因素直接写成线性约束会非常费劲。工程上常用的折中方案是分段线性化。把效率曲线切成几段每一段用一个线性函数近似整体仍然保持MILP结构。实际算下来分四五段的精度已经足够支撑容量决策完全没必要为了追求“精确非线性”去上MINLP求解器。后者不但求解慢而且经常陷入局部最优可解释性差。我的建议是能写成线性就尽量写成线性能用0-1变量表达的逻辑判断就不要绕弯子。MILP的价值不只是算出结果更是让业主和审查方理解模型的每一项假设。3. 建模硬核部分变量定义、目标函数与约束方程3.1 决策变量三层结构一个完整的微网储能容量优化模型变量可以分成三层。投资层决定“建不建、建多大”运行层决定“每个时刻怎么运行”辅助层处理逻辑互斥和状态选择。x_ess ∈ {0, 1} 电池储能系统是否建设 N_bat ∈ Z 电池簇数可选模型 P_ess_rated ∈ R 储能额定功率kW E_ess_rated ∈ R 储能额定容量kWh P_ch_t ∈ [0, P_ess_rated] 储能充电功率t 1..T P_dis_t ∈ [0, P_ess_rated] 储能放电功率t 1..T SOC_t ∈ [0, 1] 储能荷电状态标幺值 P_buy_t ∈ R 从电网购电功率 P_sell_t ∈ R 向电网售电功率 P_cur_t ∈ R 弃新能源功率 u_ch_t ∈ {0, 1} 是否充电 u_dis_t ∈ {0, 1} 是否放电 u_buy_t ∈ {0, 1} 是否购电 u_sell_t ∈ {0, 1} 是否售电这里有一点要特别说明功率和容量既可以都用连续变量也可以把容量拆成整数变量。如果供应商只提供标准容量档位建议把容量建模成“电池簇数”的整数乘子如果只是做规划层面论证先用连续变量拿到连续最优值再向标准型号回靠工程上是更常见的做法。3.2 目标函数把投资摊到每年把收益算到运行我在项目里习惯用等年值法做目标函数也就是把一次性初投资换算成等额年成本再和年运行费用相加。这样做的好处是口径统一投资是按“年”跟运行成本比较的不会出现“花两千万省一年电费”这类算不清账的问题。等年值投资成本的计算公式C_inv_annual (c_bat * E_ess_rated c_pcs * P_ess_rated c_bop * P_ess_rated) * CRF(r, Y) CRF r * (1 r)^Y / ((1 r)^Y - 1)其中c_bat是电池单位容量成本元/kWhc_pcs是变流器单位功率成本元/kWc_bop是辅助设备成本元/kWr是折现率Y是项目寿命。年度运行费用包括购电费、运维费、弃电惩罚再减去售电收入Minimize: C_inv_annual C_om sum_t (price_buy_t * P_buy_t * dt) - sum_t (price_sell_t * P_sell_t * dt) alpha_cur * sum_t P_cur_t * dt alpha_loss * sum_t P_loss_load_t * dtalpha_cur是弃电惩罚系数一般取新能源上网电价或者绿电环境价值alpha_loss是失负荷惩罚系数通常取远高于电价的数值比如5~10元/kWh用来保证模型不会为了省钱而故意甩负荷。3.3 约束条件怎么一条条写不漏约束条件是整个模型里最需要细心的地方。漏掉一条关键约束后面的全部“最优结果”都可能没有任何实际意义。下面是我在一个实际微网项目里用到的核心约束集合1) 功率平衡 P_pv_t P_wind_t P_dis_t P_buy_t P_load_t P_ch_t P_sell_t P_cur_t 2) 储能SOC递推 SOC_t SOC_{t-1} (eta_ch * P_ch_t - P_dis_t / eta_dis) * dt / E_ess_rated 3) SOC边界 SOC_min SOC_t SOC_max 4) 充放电互斥 P_ch_t P_ess_rated * u_ch_t P_dis_t P_ess_rated * u_dis_t u_ch_t u_dis_t 1 5) 功率上限 0 P_ch_t P_ess_rated 0 P_dis_t P_ess_rated 6) 并网点交换功率限制 P_buy_t P_grid_max * u_buy_t P_sell_t P_grid_max * u_sell_t u_buy_t u_sell_t 1 7) 新能源出力上限 0 P_pv_t P_pv_forecast_t 0 P_wind_t P_wind_forecast_t 8) 弃电功率非负 P_cur_t 0 9) 典型日初始SOC耦合 SOC_start_of_day SOC_end_of_previous_day逐条拆解一下为什么要这么写。功率平衡是每一时刻主电路必须满足的物理约束没有什么好讨价还价的。SOC递推的dt要注意单位换算如果t粒度是小时dt1如果是15分钟dt0.25。SOC边界一般取0.1到0.9避免电池深度过充过放寿命和安全性都会好很多。充放电互斥这条特别容易踩坑。因为目标函数里有购电成本和售电收入如果不对充放电状态加以约束模型可能会在同一个时段里“一边充电一边放电”通过两个方向上的能量交易赚取虚假收益。加两个0-1变量就能把这种漏洞堵死具体做法就是把充电功率和放电功率分别与对应状态变量相乘。并网点交换约束是另一个容易忽略的点。微网和配网之间的交换功率受变压器容量和线路载流量限制必须写清楚。购售电互斥通常用于避免同买同卖制造不必要的过网费除非当地政策允许并鼓励微网在同一个结算周期内既购又售。还有一种情况是按天优化时不连续。如果只做单典型日优化第一天初始SOC是固定的最后一天的结束SOC不设约束优化器会把最后时刻的SOC放空到最低点来省钱。工程处理上通常要求典型日首末SOC相等或者至少给一个结束SOC下限。4. 求解流程从YALMIP到Gurobi的完整链路4.1 建模工具与求解器怎么选建模语言和求解器的搭配直接影响开发效率。我早期用MATLABYALMIP比较多后来逐渐转向Python因为数据清洗、画图、后处理都在同一个生态里写起来顺手得多。组合方案适用场景备注MATLAB YALMIP Gurobi/Cplex学术验证、快速原型建模语法简洁但MATLAB授权成本高Python Pyomo Gurobi/Cplex工程落地、二次开发数据管线完整模型可读性好Python 求解器原生API追求极致性能建模代码量大适合固定业务模型Python-MIP CBC开源免费场景性能弱于商业求解器适合小规模教学GAMS Cplex大规模调度问题语法偏古典团队学习成本较高从性价比来看PyomoGurobi是目前最稳妥的组合。Pyomo负责把数学模型转成标准形式Gurobi负责求解代码也方便后续交给运维团队维护。如果项目没有商业预算Python-MIP加上CBC或SCIP也够跑中小规模的容量配置只是遇到大规模算例时求解时间明显拉长。4.2 一套可运行的Python/Pyomo模型骨架下面是一个简化但可以直接扩展的Pyomo骨架重点展示变量的组织方式、约束循环写法和求解调用。为了篇幅考虑只保留了最核心的逻辑完整数据文件需要按你自己的微网曲线替换。import numpy as np from pyomo.environ import * # 假设T24, dt1负荷、光伏、风电、分时电价均为长度为24的数组 T 24 dt 1 load np.array([...]) pv np.array([...]) wind np.array([...]) price_buy np.array([...]) price_sell np.array([...]) # 储能基础参数 eta_ch 0.95 eta_dis 0.95 soc_min 0.1 soc_max 0.9 model ConcreteModel() model.T RangeSet(0, T-1) # 投资层变量待优化 model.E_rated Var(withinNonNegativeReals, bounds(0, 2000)) model.P_rated Var(withinNonNegativeReals, bounds(0, 1000)) # 运行层变量 model.P_ch Var(model.T, withinNonNegativeReals) model.P_dis Var(model.T, withinNonNegativeReals) model.SOC Var(model.T, withinNonNegativeReals, bounds(0, 1)) model.P_buy Var(model.T, withinNonNegativeReals) model.P_sell Var(model.T, withinNonNegativeReals) model.P_cur Var(model.T, withinNonNegativeReals) # 0-1互斥变量 model.u_ch Var(model.T, withinBinary) model.u_dis Var(model.T, withinBinary) model.u_buy Var(model.T, withinBinary) model.u_sell Var(model.T, withinBinary) # 功率平衡约束 def rule_balance(model, t): return (pv[t] wind[t] model.P_dis[t] model.P_buy[t] load[t] model.P_ch[t] model.P_sell[t] model.P_cur[t]) model.con_balance Constraint(model.T, rulerule_balance) # SOC递推与首尾耦合 def rule_soc(model, t): if t 0: return model.SOC[t] 0.5 return model.SOC[t] (model.SOC[t-1] (eta_ch * model.P_ch[t-1] - model.P_dis[t-1] / eta_dis) * dt / model.E_rated) model.con_soc Constraint(model.T, rulerule_soc) # 充放电互斥 model.con_ch_dis ConstraintList() for t in model.T: model.con_ch_dis.add(model.P_ch[t] model.P_rated * model.u_ch[t]) model.con_ch_dis.add(model.P_dis[t] model.P_rated * model.u_dis[t]) model.con_ch_dis.add(model.u_ch[t] model.u_dis[t] 1) # 目标函数 c_bat 800 # 元/kWh c_pcs 500 # 元/kW r 0.06 Y 10 CRF r * (1r)**Y / ((1r)**Y - 1) alpha_cur 0.4 # 弃电惩罚 元/kWh def rule_obj(model): annual_invest (c_bat * model.E_rated c_pcs * model.P_rated) * CRF om_cost 0.02 * (c_bat * model.E_rated c_pcs * model.P_rated) buy_cost sum(model.P_buy[t] * price_buy[t] * dt for t in model.T) * 365 sell_income sum(model.P_sell[t] * price_sell[t] * dt for t in model.T) * 365 cur_penalty alpha_cur * sum(model.P_cur[t] * dt for t in model.T) * 365 return annual_invest om_cost buy_cost - sell_income cur_penalty model.obj Objective(rulerule_obj, senseminimize) # 求解 solver SolverFactory(gurobi) solver.options[MIPGap] 0.01 solver.solve(model, teeTrue) # 输出最优配置 print(f优化结果: 容量 {model.E_rated.value:.0f} kWh, 功率 {model.P_rated.value:.0f} kW)这段代码把全年8760小时折算成了“单典型日×365”的简化形式。严格讲如果场景聚成多个典型日乘系数也要对应调整。工程上更严谨的写法是定义场景集合S每个场景s有自己的曲线和权重w_s目标函数里各个运行项都乘以w_s累加。4.3 全年8760小时规模爆炸的应对有些人一上来就把全年8760个小时全部建模觉得这样最准确。真实求解你会发现二进制变量变成8760×4个模型规模一下子上去了调参和求解时间都不可控而且不一定能拿到好的gap。我的做法是两阶段先用典型日聚类做容量优化再做全年运行回溯验证。K-means聚类把8760小时聚成6到8个典型日每个典型日覆盖不同的季节和天气模态并统计簇内天数作为权重。在典型日模型上求出储能功率和容量再把这组容量代回全年8760小时模型做运行模拟看全年弃电率、失负荷时间和储能循环次数是否越界。如果回溯发现某些极端日频繁失负荷就增加一个对应场景重新优化容量。这个流程听起来比“全年一次求解”多了一步但实际效率反而高得多。更重要的是全年回溯给业主提供了非常直观的验证材料你不是在拍脑袋选容量而是经过全年运行检验的。5. 算例结果怎么读从优化值到工程决策5.1 一个典型微网算例的参数设定用一个标准工业微网来讲结果。假设微网负荷峰值500kW年电量约180万kWh光伏装机400kWp年发电约50万kWh风电装机200kW年发电约30万kWh。并网点最大交换功率600kW。电价采用分时电价峰段1.0元/kWh、平段0.6元/kWh、谷段0.35元/kWh。储能系统综合成本取电池800元/kWh、PCS及辅机500元/kW寿命10年折现率6%运维成本按初投资2%计算。这个参数组合比较接近国内分布式微网项目的平均水平。新能源渗透率大致44%如果不配储能午间光伏大发时段和夜间风电大发时段都会出现一定弃电。配储能的核心目的就是把弃掉的绿电挪到负荷高峰时段。5.2 三种方案对比不配储能、优化配置、配得太满为了把问题讲清楚我把三个方案摆在一起对比方案储能功率/容量年投资等值年购电费用弃电率年综合费用不配储能0 kW / 0 kWh0万元约105万元约15%约105万元MILP优化配置约500 kW / 900 kWh约10万元约91万元约4%约101万元人为加大配置800 kW / 1600 kWh约18万元约87万元约2%约105万元从表里能读出两个关键信息。第一MILP给出来的不是“配得越大越好”而是综合费用最低的点。储能多了能多消纳绿电、多参与峰谷套利但每多配一度电的边际收益在递减。到800kW/1600kWh之后多花的投资已经无法通过电费节省收回来综合费用反而回到和不配储能相当的水平。第二最优方案的年综合费用虽然只比不配储能省了约4万元但账不能只这么算。储能还带来了削峰填谷、提高供电可靠性、降低变压器峰值负荷等隐性收益这些没有完全体现在购电费里。如果把需求响应的收益算进去储能的经济性会更好。5.3 敏感性分析什么变量最能改变结论配置结果对几个关键参数非常敏感做方案汇报前一定要把敏感性分析表做出来。电池价格是最敏感的变量。这几年储能电池价格下降很快模型里把单位容量成本从1200元/kWh调到600元/kWh最优容量可能从600kWh跳到1200kWh几乎翻倍。峰谷价差是第二敏感变量峰谷价差从0.4元/kWh扩大到1.0元/kWh储能循环次数会显著上升最优容量也变大。新能源渗透率决定弃电压力渗透率越高储能消纳价值越大但超过某个阈值后最优容量会呈阶梯式上涨。折现率影响投资年值折现率越高业主越不愿意一次性投入大额储能最优容量越低。我通常会给业主画一张二维热力图横轴是电池价格纵轴是峰谷价差颜色表示最优储能容量。这张图基本可以作为决策地图业主在哪条价格曲线上经营直接读出大致的储能投资区间。还有一个容易被忽略的技巧看模型输出的影子价格。也就是某个约束在最优点处的对偶变量。如果并网点600kW交换功率约束的影子价格很高说明微网的真正瓶颈是配网接入容量而不是储能容量太小。这时候第一优先级的动作应该是扩容并网点而不是继续加储能。这个结论用MILP的对偶信息就能拿到不用额外算任何东西。6. 工程落地阶段绕不开的坑与扩展思路6.1 最优解和产品型号之间的鸿沟MILP算出来一个连续最优解比如789.3kWh、432.6kW你不能直接拿着这个数去采购。电池是一簇一簇卖的PCS是一个功率等级一个功率等级卖的必须向上映射到标准产品型谱。常见做法是先对功率向上取整到PCS标准档位比如450kW档再对容量向上取整到电池簇数的整数倍比如3簇共900kWh。映射完之后不能到此为止还要把这组标准参数代回模型里跑一遍全年运行校验。如果回代发现SOC经常冲到100%、循环次数超过厂家承诺天数说明容量余量不足需要增加一簇电池。我给项目写设计报告时会把“连续优化值”和“型谱选型值”两列放在同一张表里并告诉业主差值原因。这样做既保留了优化模型的说服力又体现了工程落地的严谨性。6.2 输入曲线质量决定模型质量再好的模型喂进去的曲线不对结果都是垃圾。我见过不少团队直接用夏季最大负荷日做全年设计结果冬季储能基本闲置。反过来只用某一个阴天典型日设计容量又偏大。正确的做法我前面提过先用K-means聚成6到8个典型日保证覆盖季节差异和天气差异再做全年8760小时回溯。回溯时重点盯三类时段连续阴天导致光伏几乎为零的场景、极端高温导致负荷尖峰的时段、风大且负荷小的夜间弃风场景。只要这三类场景里失负荷时间和SOC越界次数在允许范围内配置结果基本靠谱。另外要注意数据的时间粒度。15分钟粒度和1小时粒度对容量结果的影响主要体现在瞬时功率峰值上。如果负荷曲线本身很尖1小时粒度会把峰值抹平导致储能额定功率被低估。因此我会在数据预处理阶段单独检查峰值功率是否被平滑必要时用15分钟粒度重跑一遍功率维度的约束。6.3 不确定性处理的高级用法如果工程项目对供电可靠性要求很高或者新能源占比特别大确定性的典型日模型不够用。这时可以在MILP框架上扩展成两阶段鲁棒优化第一阶段决定储能容量第二阶段在“最坏场景”下做运行调度目标是最小化最坏情况下的总成本。这个模型表达起来并不复杂在Pyomo里用鲁棒约束或者不确定性集合方式都能实现但求解时间会比确定性模型长不少。还有一种工程折中方案不做严格的随机规划而是把几个典型场景都跑一遍观察最优容量落在哪个区间。比如晴天场景最优600kWh阴天场景最优850kWh那就在区间内取一个偏向保守的值比如750kWh。这种方法虽然没有严格的概率保证但胜在快速、直观、好汇报很多项目实际采用的就是这个思路。6.4 我对这套方法边界的一点体会混合整数规划方法不是微网储能配置的唯一工具但它给出了一个非常珍贵的特性最优性可量化、过程可解释、结果可复现。当业主问“为什么是900kWh而不是800kWh”时你可以打开模型日志指着MIPGap和约束对偶值解释判断依据而不是说“经验值”。跑通这套流程之后我最大的体会是真正花时间的不是算法本身而是对业务约束的准确翻译。储能不是孤立设备它嵌在微网的功率平衡、电价机制、并网限制和新能源出力曲线里任何一个约束表达错了最优解都是空中楼阁。最后分享一个小习惯每次修改约束后先花十分钟看求解器输出日志里的gap趋势和不可行约束报告再花半小时做一组简单的参数敏感性测试最后才开始长篇分析。这看起来多花了时间实际能省掉后面好几轮返工。微网储能容量优化这个题目越往后做越明白模型只是思考工具决策质量最终取决于你对物理系统和商业模式的理解深度。