☰
主从博弈与共享储能的综合能源微网优化调度程序实战
2026/10/2 9:10:57 网站建设 项目流程

这两年做能源系统优化调度,我碰到最多的题目就是“主从博弈 + 综合能源微网 + 共享储能”这个组合。标题里的“程序”不是流程图,而是一套能跑通、能出最优解、能画图的优化调度程序;“主从博弈”解决的是多个利益主体博弈的问题;“综合能源微网”提供了电、热、冷多能耦合的物理场景;“共享储能”则让储能从“谁投资谁独占”变成“按需租赁、多方共享”的资源池。这几样叠在一起,模型会变成带均衡约束的双层优化,写程序时比普通单层调度麻烦不少,但只要思路理清,反而比集中式调度更贴近真实的能源市场。

这篇文章适合刚接触双层优化、想用MATLAB+YALMIP或者Python+Pyomo做能源调度程序的人。我会把项目背后的建模逻辑、程序实现步骤、双线性项处理方法和实操中容易踩的坑全部摊开讲,尽量用我实际跑过的经验说话,不绕弯。

1. 项目概述:主从博弈 + 综合能源微网 + 共享储能想解决什么问题

1.1 从标题拆出的三个核心要素

先拆标题。“主从博弈”在数学上就是 Stackelberg 博弈:有一个领导者先做决策,然后若干跟随者在领导者决策的基础上做自己的最优决策,领导者再根据跟随者的反应调整最初的决策。放到微网场景里,微网运营商通常是领导者,用户是跟随者。运营商的决策变量包括内部售电价格、购电计划、燃气轮机出力、共享储能充放电计划等等;用户的决策变量则是在给定电价下,选择自己的用电量或者可转移负荷。用户不会听运营商说“你要用多少电”,运营商也不能直接控制每一个用户,所以只能通过价格信号来引导。这就需要一个博弈模型,而不是一个纯粹的物理调度模型。

“综合能源微网”是物理侧的关键词。它处在配电网的末端,内部往往包含光伏、风电、燃气轮机、燃气锅炉、储能、电负荷、热负荷、冷负荷等多元设备。综合能源微网的优势在于多种能源可以互相转换、互相补充,比如燃气轮机同时发电和产热,余热还能通过吸收式制冷机变成冷,这部分多能互补会体现在约束方程里,特别是同时含有电网络平衡、热网络平衡和冷网络平衡,约束数量比纯电力系统多很多。

“共享储能”是近几年的热点。传统微网配储能,要么微网自己买一大块电池,要么每个用户各自买一块。自己买一块,投资大、利用小时数低,很多时段那块电池是闲置的;而共享储能的思路是把储能容量拆开,按需出租给微网或用户,谁在某一段时间需要灵活性,谁就租一部分容量。这样一来,储能的使用率提高,微网也不必承担全额初始投资。问题也来了:储能容量怎么分、租赁价格怎么定、充放电功率怎么协调、多个用户同时租用会不会冲突,这些问题就成了模型里需要重点解决的决策。

1.2 主从博弈和共享储能在同一个模型里怎么分工

把两个概念放到同一个程序里,角色分工大概是这样的:微网运营商作为领导者在博弈中占据优先决策位置,先根据预测数据制定零售电价,同时租用共享储能来弥补光伏风电出力和负荷时间不匹配的问题;用户作为跟随者,在电价给定的情况下调整用电行为,甚至可以决定自己要不要租用一部分共享储能容量来“低价充电、高峰少买电”。共享储能并不是一个独立玩家,它更像被所有参与主体共同使用的支撑设施,由运营商统一调度充放电功率,租赁成本和分配约束则被写到上层的运行成本里。

这样设计的直接好处是,模型有了明确的“两层”结构。上层是微网运营商的利润最大化问题,结算售电收入和购电、购气、储能租赁费用的差额;下层是用户群的能量管理问题,用户追求自身效用最大,即在舒适度和用电费用的平衡中做出最优反应。运营商不能替用户做选择,只能预测用户会按什么样的需求响应,然后把这个响应关系带入自己的优化问题。整套程序的输出就能回答三件事:电价怎么定、储能怎么用、用户最终怎么用电。

2. 为什么用主从博弈而不是常见的集中式优化

2.1 三种建模思路的对比

同一个问题,常见的建模范式有三种。集中式优化把微网内所有设备、负荷看作完全受控对象,求解一个整体成本最小化问题,这种方式计算简单、全局最优,但前提是掌握所有用户的真实偏好数据,实际应用时数据都拿不到。多主体博弈则把各用户都当作平等主体,用一般纳什均衡来描述,但收敛性和均衡唯一性比较难保证。主从博弈正好卡在中间:上下级结构明确,领导者的需求和跟随者的反应不完全对称,模型更贴近真实电价形成过程,也兼顾了数据隐私。

对比起来可以看这张表:

建模思路用户角色决策顺序隐私保护计算复杂度适用场景
集中式优化受控对象同时低低单主体配电系统规划
一般纳什博弈平等参与主体同时决策中较高多微网竞争市场
主从博弈跟随者,做最优响应领导者先,跟随者后较高中高微网定价、共享储能、需求响应

主从博弈还有一个额外优势,它天然适合初始化电价。运营商的收益来自用户买电,而用户买多少又由电价决定。这就好像商场里的超市定价,超市先给一个零售价,顾客根据价格决定买多少东西,超市再根据总销量反推自己的利润。如果价格太高,顾客买得少,利润反而很低;价格太低,销量大但每单利润被压缩。主从博弈就是把这个“联动关系”用数学方式显式建模,最后找到双方都能接受且领导者最优的定价点。

2.2 Stackelberg博弈的数学骨架

从数学角度看,主从博弈可以用一个两阶段问题进行描述。假设领导者的决策变量是 u,跟随者的决策变量是 v,领导者的目标函数是 F(u,v),跟随者的目标函数是 G(u,v)。博弈过程分两步:

第一步,领导者先给出 u。第二步,跟随者看到 u 后,选择自己最优的反应 v*(u),满足 v*(u) = argmax G(u,v)。领导者在做决策时就已经预见到这个反应,所以领导者要解决的是 max F(u, v*(u))。

这个表达式看起来只有两个函数,但程序实现时并不简单。因为 v*(u) 不是一个显式函数,而是另一个优化问题的解。如果不做变换,程序里就会嵌套两层优化,外层调用一次内层求解,这在变量规模不大的时候还能勉强跑,但一旦有 24 个时段、几十个设备、多个用户,嵌套求解的计算量会急剧膨胀。常用的办法是,把下层优化问题用 KKT 最优性条件替代,写成一堆带互补约束的方程组,再并入上层问题,得到一个数学规划带均衡约束(MPEC)问题;如果下层是线性凸规划或者二次凸规划,KKT 条件还是线性或二次的,可以进一步通过二元变量线性化互补约束,最终交给混合整数线性规划求解器处理。

2.3 共享储能让博弈模型更贴近现实

没有共享储能时,微网内部储能是运营商的私有资产,只有一条储能约束和一条成本回收通道,建模上更像“设备”。共享储能加入之后,它同时具有商品属性和物理属性。商品属性体现在租赁费用上,上层利润要扣除储能租赁费用,用户也要支付自己的租赁费用;物理属性体现在容量分配和充放电协调上,多个用户共享同一组电池,总充放电功率和总SOC不能超过上限。

于是主从博弈模型里多了一层“租赁市场”的互动。运营商既可以自己租储能来套利,也可以把一部分储能容量再转租给用户。用户在低电价时段把储能充满,在高电价时段释放给自身负荷使用,围绕储能容量产生收益分配关系。程序里面容量分配约束、租赁价格变量、SOC约束以及能量守恒约束必须同时存在,否则共享储能很容易变成“名义共享、实际独占”,算出来的结果也就失去创新点了。

3. 双层优化模型怎么建:变量、目标与约束

3.1 角色设定与决策变量

写程序之前,先把集合和变量定清楚。时间集合通常取 T 个时段,典型是典型日 24 时段,或者按 15 分钟一个点取 96 时段。用户集合取 I,第 i 个用户有自己的负荷、自己的储能租赁上限。能源种类包括电力、天然气、热力,如果是冷热电联供,还要加上冷能。

微网运营商(领导者)的决策变量主要有:每个时段的零售电价 p_t、向上级电网购电功率 P_buy(t)、购买天然气 G_fuel(t)、燃气轮机发电功率 P_gt(t)、燃气锅炉产热 Q_gb(t)、共享储能充电功率 P_ch(t)、共享储能放电功率 P_dch(t)、共享储能的荷电状态 SOC(t),以及储能租赁容量分配变量 C_i(t)。用户(跟随者)的决策变量主要是自己的可调电负荷 d_i(t)、可调热负荷 h_i(t),以及自己租用储能的充放电功率 P_i_es(t)。

外部输入参数则是:上级电网分时电价 ρ_buy(t)、光伏出力 P_pv(t)、风电出力 P_wt(t)、不可切除的刚性负荷 D_fix(t)、天然气价格 λ_gas、燃气轮机发电效率与热电比、储能容量 E_cap、储能最大充放电功率 P_max、充放效率 η_ch 和 η_dch,以及储能的租赁单价租金 r_es。

建程序的第一步,把所有变量用求解器的数据结构定义好。我习惯先列一张变量表,再写代码,不然写到约束的时候容易漏变量。

3.2 上层微网运营商的优化模型

上层目标函数通常取运营商的日利润最大化,也可以写成运行成本最小化,二者等价但符号不同:

max Σ_t [ p_t · D_t − ρ_buy(t) · P_buy(t) − λ_gas · G_fuel(t) − C_es_cost − C_om(m) ]

其中 D_t 是运营商卖给用户的电量,包含多个用户的合计。 C_es_cost 通常是共享储能租金,可以是固定的容量费用减去放电收入等,C_om 是设备运行维护成本。

上层约束中最核心的是电平衡。每一时段,购电、光伏出力、风电出力、燃气轮机出力和储能放电加在一起,必须等于用户总负荷、储能充电功率和其他内部电耗:

P_buy(t) + P_pv(t) + P_wt(t) + P_gt(t) + P_dch(t) = Σ_i d_i(t) + P_ch(t)

热平衡则反映燃气锅炉和燃气轮机余热供热,等于用户热负荷:

Q_gb(t) + Q_gt_heat(t) = Σ_i h_i(t)

燃气轮机输入天然气和输出电热之间有关联,常见为一个线性效率模型:P_gt(t) = η_gt · G_gt(t),Q_gt_heat(t) = COP_heat · P_gt(t)。如果程序里加入吸收式制冷机,类似再拉一条冷平衡。

共享储能自身的动态方程是重点,也是容易写错的地方:

SOC(t) = SOC(t−1) + η_ch · P_ch(t) / E_cap − P_dch(t) / (η_dch · E_cap)

同时要保证 SOC 不越界、充放不能同时进行、功率不超过限值。最后一个等式约束是调度周期首末状态一致,即 SOC(0) = SOC(T),否则储能会把全部电量“滚雪球”式放光,结果失真。

3.3 下层用户响应模型与需求弹性

下层用户模型要足够简单,同时又能合理反映用户对电价的反应。最常见的做法,是给用户定义一个用电效用函数。效用函数取二次型比较顺手:

U_i(d_i(t)) = θ_i · d_i(t) − (γ_i / 2) · d_i(t)^2

θ_i 是用户对用电的偏好系数,γ_i 是满意度的递减系数。用户自己的优化问题就是在给定零售电价 p_t 的情况下,最大化“用电效用减去购电费用”,同时还要满足总用电量的下限约束,保证一天内不能把电用得太少。

max Σ_t U_i(d_i(t)) − p_t · d_i(t)

s.t. d_i_min ≤ d_i(t) ≤ d_i_max(功率上下限),Σ_t d_i(t) ≥ D_i_total_need

如果不考虑上下限和总量约束,这个二次凸规划的最优条件非常漂亮:对 d_i(t) 求偏导,令其等于 0,得到:

θ_i − γ_i · d_i(t) − p_t = 0

于是 d_i(t) = (θ_i − p_t) / γ_i。这是一条典型的线性需求曲线:电价越低,用户用越多;电价越高,用户用越少。把这条曲线代回上层目标,上层中的 p_t · d_i(t) 就变成 p_t 的二次函数,避免了双线性项。不过这只适用于无约束情况,实际模型里一旦加入 d_i_min 和总量约束,KKT 条件里就会多出互补松弛项,这部分必须用二元变量处理。

3.4 KKT转换和双线性项处理

写程序时我习惯把整段推理写清楚,而不是直接堆代码。先把下层用户问题写成拉格朗日函数,列出四类条件:对决策变量求一阶导数的平稳性条件、原始可行性约束、对偶可行性约束、互补松弛条件。下层是凸二次规划时,KKT 条件是充要条件,可以用这些条件完美等价一个用户的最优决策。

常见的目标函数中存在 p_t · d_t 双线性项,这是主从博弈程序最大的麻烦。处理有两条经验路径:

第一条,用强对偶条件。当用户问题为凸,强对偶成立,下层原始问题的最优目标值等于对偶问题最优目标值。通过这个等式,往往能把上层目标里的双线性项替换成只含对偶变量和常数的线性项。我在程序里优先用这种方式,因为它不仅数值稳定,还能少引入很多整数变量。

第二条,用代入法消去。如果能像上面提到的那样显式写出 d_t 关于 p_t 的表达式,直接把 d_t(p_t) 带进上层目标,双线性项就自动变成凸二次项。缺点是只在用户问题没有太多复杂约束时成立,一旦用户问题带离散变量或者强非凸约束,这条路就断了。

互补约束的处理又是一层麻烦。KKT 中会出现形如 0 ≤ λ ⊥ g(x) ≥ 0 的条件,意思是 λ 和 g(x) 不能同时大于 0。程序里要把这种关系变成线性表达式,常见套路是引入二元变量 δ,写成:

g(x) ≤ M1·δ,λ ≤ M2·(1−δ)

这样就把互补条件打散成两组不等式,代价是增加整数变量。求解器看到整数变量后,问题的复杂度立刻上升,所以大M的取值非常敏感,这一点我在第5节会专门展开。

4. 程序实现:从双层到单层的落地细节

4.1 求解器和语言选型

主从博弈程序最常见的实现环境是 MATLAB 配合 YALMIP,求解器用 Gurobi 或者 CPLEX。YALMIP 的好处是建模接近数学语言,约束和变量一目了然,调试也方便。缺点是商业求解器通常要license,不过高校一般都有。

Python 生态这些年也很成熟。Pyomo 做建模非常优美,JuMP 在 Julia 里也很好用。我的实际体会是,如果只是跑一个典型日场景,MATLAB+YALMIP 最省心;如果一个批次要跑几十个场景或者做参数敏感性分析,Python+Pyomo 的批处理能力更强。选型不是死规矩,关键是要保证最终能交给 Gurobi 这样的求解器去解。

用 Gurobi 时要注意模型类别。下面用户问题被 KKT 替代,加上 0-1 变量处理互补约束后,整个模型通常变成一个混合整数线性规划(MILP)或混合整数二次规划(MIQP)。Gurobi 对这两类模型都有专门的优化算法,在给模型的时候尽量不要留下非凸二次项,非凸会导致求解器运行时间爆炸。

4.2 用MATLAB+YALMIP搭主从博弈程序

我在这里给一个启动框架,它可以作为程序骨架。框架没写完整数据,只强调结构。

T = 24; % 时段数 I = 3; % 用户数量 % 输入参数 rho_buy = 分时购电价向量(1,T); pv = 光伏预测(1,T); wt = 风电预测(1,T); E_cap = 600; % kWh P_max = 150; % kW eta_ch = 0.95; eta_dch = 0.95; % 上层决策变量 p_retail = sdpvar(1,T); % 零售电价 P_buy = sdpvar(1,T); % 购电功率 P_gt = sdpvar(1,T); % 燃气轮机发电 P_ch = sdpvar(1,T); % 储能充电 P_dch = sdpvar(1,T); % 储能放电 SOC = sdpvar(1,T); % 荷电状态 % 下层用户负荷决策变量 d = sdpvar(I,T); % 第i个用户t时段负荷 % 约束:用户最优性条件(KKT)在此处写成等式/不等式 cons = []; % 电平衡 for t = 1:T cons = [cons, P_buy(t) + pv(t) + wt(t) + P_gt(t) + P_dch(t) == sum(d(:,t)) + P_ch(t)]; end % 储能动态约束 for t = 2:T cons = [cons, SOC(t) == SOC(t-1) + eta_ch*P_ch(t)/E_cap - P_dch(t)/(eta_dch*E_cap)]; end cons = [cons, SOC(1) == 0.5 + eta_ch*P_ch(1)/E_cap - P_dch(1)/(eta_dch*E_cap)]; cons = [cons, SOC(T) == 0.5]; % 周期末回到初始值 % 下层用户最优反应(简化线性需求情况) for i = 1:I for t = 1:T cons = [cons, theta(i) - gamma(i)*d(i,t) - p_retail(t) == 0]; cons = [cons, d_min(i,t) <= d(i,t) <= d_max(i,t)]; end cons = [cons, sum(d(i,:)) >= D_total(i)]; end % 目标函数:运营商利润最大化 profit = sum(p_retail .* sum(d,1) - rho_buy .* P_buy - c_gas * P_gt); ops = sdpsettings('solver','gurobi'); optimize(cons, -profit, ops); % 输出 value(p_retail), value(d), value(P_ch), value(P_dch), value(SOC)

注意,这个框架里我把下层最优反应直接写成等式,是一种简化表达。实际带互补约束时,需要引入二元变量把互补条件线性化,再交给求解器。我当时第一次跑通的时候,就是先从一个无互补约束的线性需求关系开始,验证目标函数和各时段曲线合理后,才逐步加入更复杂的约束。

4.3 Python/Pyomo版本的关键写法

如果你偏好Python,核心结构类似,只是语法不同。用Pyomo定义变量和约束非常接近数学表达式,尤其适合后期加不确定性和场景。下面给一个关键片段,展示用户KKT条件并入模型的样子:

import pyomo.environ as pyo model = pyo.ConcreteModel() model.T = pyo.RangeSet(1, 24) model.I = pyo.RangeSet(1, 3) model.p_retail = pyo.Var(model.T, bounds=(0.2, 1.5)) model.d = pyo.Var(model.I, model.T, bounds=(0, 10)) model.P_ch = pyo.Var(model.T, bounds=(0, 150)) model.P_dch = pyo.Var(model.T, bounds=(0, 150)) model.SOC = pyo.Var(model.T, bounds=(0, 1)) def user_kkt(model, i, t): return theta[i] - gamma[i]*model.d[i,t] - model.p_retail[t] == 0 model.user_kkt = pyo.Constraint(model.I, model.T, rule=user_kkt) def objective(model): daily_profit = sum(model.p_retail[t] * sum(model.d[i,t] for i in model.I) for t in model.T) buy_cost = sum(rho_buy[t]*model.P_buy[t] for t in model.T) return daily_profit - buy_cost model.obj = pyo.Objective(rule=objective, sense=pyo.maximize)

Pyomo写成这样的好处是,后续如果要把下层换成更复杂的状态转移模型,只需要改约束规则函数,不会牵连整个建模逻辑。

4.4 结果验证与均衡性检查

程序跑完,不等于结果正确。主从博弈程序最容易出现“人眼看循环”错误,也就是约束里少写了一个括号,最后结果诡异,却很难发现。所以我会固定做两轮验证。

第一轮验证物理合理性。把所有设备的出力曲线、储能SOC曲线、电价曲线和负荷响应曲线画在同一张图里,人工检查逻辑关系:电价高的时段,用户负荷是不是确实下来了;光伏大发时段,储能是不是在充电;燃气轮机出力高峰是不是和热负荷高峰对应。这些物理直觉是程序里最强的校验工具。

第二轮验证博弈均衡性。把上层求出的电价固定下来,重新独立求解一遍下层用户问题,看用户目标值与主从程序中下层最优值是否一致。如果差异小于 1e-6,说明上层结果是可实现的均衡点;如果差异明显,说明下层KKT约束或者目标函数写法有问题。我之前就遇到过一次,用户目标函数里忘记加入储能租赁费用项,导致上层求解的“最优策略”在下层独立求解时被轻松推翻,算出来的电价完全没有实际可执行性。

用微扰法也可以检验。把某个时段的电价稍微提高或压低 0.01 元/kWh,观察原上层总目标是否变小。如果变小,说明初始点满足局部最优条件;如果总目标还能变高,说明求解器没有找到均衡点,需要检查是否漏了整型约束或者 M 值取值不当。

5. 常见问题与排查技巧实录

5.1 大M选不好导致数值病态

互补约束线性化是大M法应用最多的地方,也是坑最多的地方。很多人图省事,直接把 M 写成 1e7,想着“足够大肯定覆盖住”。但Gurobi这种求解器对数值尺度非常敏感,M 过大会造成求解精度下降、分支定界效率骤降,甚至出现错误的整数结论。

我的建议是根据物理单位设置M。充放电功率的上限是 P_max,那么和充放电互补约束相关的 M 就取 2×P_max,留一点裕度但不要离谱。零售电价一般取 1.5元/kWh 或 2元/kWh 以内,租金单位一般取 0.5~1元/kWh,完全没必要出现 1e5 以上的量级。如果模型中有购电量和容量这类大数值变量,M 就按对应上限取,做到“刚好盖住”而不是“越盖越大”。

问题现象常见原因处理办法
求解时间暴涨,gap 一直降不下去大M选取过大,节点下界松散把 M 改成物理上限的 1.2 倍
解出来SOC跳变但储能实际做不到互补约束被错误分支检查储能约束是否有公式错误
整数变量结果不稳定,微调参数结果突跳M过小,切掉了可行域调大 M 到安全裕度

5.2 下层用户问题多解或非凸会导致KKT失效

KKT 条件等价于最优解的前提是:下层问题是凸的且约束满足规范条件。如果用户问题里引入了阶梯电价、最小运行时间、离散档位选择,问题就变成了混合整数规划,KKT 不再适用。这时候要么把用户问题拆成多场景枚举,要么把离散变量留在下层用固定点迭代法求解,但迭代法收敛速度会变慢。

另一个常见问题是下层最优解不唯一。用户效用函数是线性的,或者没有任何曲率时,用户在做决策时会在多个解之间“平移”,上层把下层KKT条件当成等式加入了模型,但实际上每个用户的不同最优反应都会影响上层目标。解决方法是给用户目标函数加一个极小权重凸正则项,比如在目标里加 −ε·d(t)^2,让解唯一。ε 取 1e-4 数量级,基本不影响博弈结果,却能显著提升数值稳定性。这个技巧我屡试不爽。

5.3 共享储能充放电不守恒

共享储能模型里,最常见的物理错误是能量不守恒。有些程序只加了 SOC 动态方程,忘了加“初始状态等于末尾状态”的约束,导致储能像用不完的宝矿一样,在周期末还有剩余电量,把储能的反向套利空间算得过大,最后微网运营商的利润虚高。将这个约束加上后电价曲线才会正常。

另一种不守恒出现在容量分配层面。用户各自的储能租赁容量加起来超过物理储能容量,结果每个用户都以为自己能用 100kWh 储能,但物理电池只有 200kWh,全局SOC却仍然满足要求,程序表面可行实际失控。必须强调一个全局约束:Σ_i C_i(t) ≤ C_es_total(t),其中 C_es_total 是共享储能可出租的总容量上限。

还有一个细节:储能充电效率损失的功率没有在电平衡中体现。实际计算 SOC 时,η_ch·P_ch(t) 进入能量,但电平衡里 P_ch(t) 还是按充电功率算,物理上没问题,但如果你在电平衡里写的是 η_ch·P_ch(t),那就把充电效率算了两次,会低估实际充电功率。建议统一口径,写代码时注释清楚每个变量的物理含义,最好把储能充放电功率写在两个物理端口,不把效率提前乘到电平衡里。

5.4 大规模求解卡住:整数变量爆炸

24时段的小模型跑起来很快,但如果把时间步长加密到 96 个点,或者把用户数放大到 50 个,每个用户都有多个0-1约束,整数变量会迅速上升到几千个,Gurobi会卡得很厉害。这时候有几个实用策略。

第一个策略是减少互补约束的二元变量个数。仔细观察互补约束,很多二元变量其实存在冗余。比如储能充放电互斥条件 P_ch·P_dch=0,在大多数情况下可以不加这个0-1约束,因为目标函数和SOC动态方程天然会让储能避免同时充放电;强行加互补只会增加求解负担。我在程序里会试探性移除这类约束,算完检查是否存在异常同时充放,再决定要不要加回来。

第二个策略是固定电价或储能租赁价格的分段。把连续电价离散成几段,虽然会损失部分精度,但整个MILP规模会大幅缩小,适合大规模场景预筛选。最后再用一小段范围重新精细优化。

第三个策略是滚动时域。把一天切成几个小的优化窗口,窗口间通过SOC状态衔接,省掉一天内所有时段一起求解的压力。代价是最优性会略降,但对日前的滚动调度来说完全可接受。

关于剪枝,我实际跑过一个对比:同样96时段、10用户的问题,不做任何整数变量削减时Gurobi求解需要12分钟,剔除互斥冗余 + 压缩M边界后,只花了40秒。模型尺度的调整,效果远大于盲目堆算力。

6. 一些个人体会

我从这个模型里学到的第一课,是先摆清楚“谁先决策、谁跟随决策”,再写任何公式。程序里很多bug实际不是代码bug,而是角色定位没分清楚。比如一开始写共享储能约束时,我习惯把储能完全当成运营商资产,没有考虑用户按需租赁的容量限制,算出来的电价总是偏高,因为运营商觉得储能是自己的,敢于低价套利再高价卖电,但真实共享储能不可能让运营商独占全部灵活容量。把容量分配约束加进去以后,结果才合理。

还有一点想特别提:主从博弈程序的调试,可以先用一个迷你案例验证,比如只取三个时段、一个用户、一台燃气轮机、储能容量取50kWh,把程序跑通后再扩展到24时段和多个用户。我在迷你案例里能一眼看出电价和负荷之间的联动关系,但扩展到完整案例后,数据一多,反而不容易判断哪里出了问题。迷你案例和完整案例的结果数值会差很多,但角色逻辑、约束结构是能被验证的,这是省时间最有用的做法。

如果还在写这个方向的新手,我给的最后一条建议是:别看别人论文里的公式多完美,自己亲手把双层约束写成一封能交给求解器的代码,比看十篇论文都有效。第一次跑通的时候,你自然就会明白“主从博弈”这几个字到底在程序里长什么样子了。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询