ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

微网优化调度与粒子群算法:从风光燃储建模到约束求解实战

微网优化调度与粒子群算法:从风光燃储建模到约束求解实战 我刚接触“微网优化调度”这个词时以为它就是把负荷需求往几台发电机上一分那么简单。真正动手做基于粒子群算法的含风光燃储微网优化调度之后才发现风光出力是随机波动的燃气轮机要花钱还得看爬坡速率储能充放必须受荷电状态上下限约束而这一堆变量又必须在每个时间断面里同时满足功率平衡——这时候你已经不是在“分负荷”而是在解一个有约束、非线性、多目标的优化问题。这篇博文把我入门时的完整思路整理出来问题怎么建模、粒子群算法PSO怎么套上去、代码逻辑怎么落地、结果怎么看、哪些坑我踩过以后不想你再踩。适合正在学微网调度、智能优化算法或者准备用这类题目做毕设、课题的本科生和研究生也适合想快速上手优化调度的工程师。1. 为什么把“风光燃储”作为微网调度入门案例1.1 微网调度的本质给每一个时段找一个可行的功率组合微网说简单点就是一个小型的发配电系统里面有分布式电源、储能装置和本地负荷既可以并网运行也可以孤岛运行。调度的核心任务是在满足负荷需求的前提下决定每一台设备在每一个时刻到底出多少力、充多少电、从电网买多少电。这里有一个关键词每一个时刻。以最常用的日前调度为例把一天分成24个时段每个时段都有一个决策点。于是问题从“某个时刻怎么分配”变成了“24个时刻串联起来怎么分配”。串联这个词很关键因为储能会让相邻时段互相影响——你今天下午多充一度电晚上就能多放一度电所以不能只看单一断面。风光燃储这个组合刚好覆盖了微网调度里最典型也最头疼的四类对象随机性电源风电、光伏、可控但昂贵的传统机组燃气轮机、以及可以双向调节的缓冲设备储能。把这四个单元玩明白了后面的需求响应、电动汽车入网、多微网协同本质上都是往这套框架里加东西。1.2 四种电源的“性格”决定了问题难度我把这四个单元的性格给你捋一遍你会发现它们天生就是来“制造难度”的单元可控性成本特性主要约束调度中的角色风电不可控几乎是零边际成本取决于风速预测能发多少是多少多余的要弃掉光伏不可控几乎是零边际成本白天有晚上没有同上弃光有惩罚燃气轮机可控燃料成本随出力变化出力上下限、爬坡速率主要调节手段但不能乱动储能半可控有充放电损耗成本SOC范围、充放功率限制削峰填谷、平抑波动风电和光伏像“别人给你寄的快递”你能预测它大概什么时候到但决定不了它今天来不来、来多少。燃气轮机则是那种“随时能叫得动但每次都要付钱”的帮手而且它不能瞬移——上个小时还在50kW这个小时直接跳到500kW是不行的得有个爬坡过程。储能则像一个缓冲水池既能蓄水又能放水但池子容积有限也不能一边蓄水一边放水。这四类设备凑在一起才让调度问题有了研究价值。如果全是燃气轮机线性规划就能轻松解决如果只有风光加储能又不一定有24小时连续可控的调节手段。混合之后问题就变成了真正的优化难题。1.3 为什么入门先用粒子群算法接触过微网调度的人应该听过混合整数线性规划MILP、内点法、动态规划这些大名鼎鼎的方法。它们很严谨但对初学者并不友好。MILP需要把问题建模成标准形式动态规划要处理状态爆炸很多教材讲着讲着就变成数学课了。粒子群算法不一样。它的原理极其朴素就是模拟一群鸟在觅食每只鸟记住自己飞过的最好位置群里共享一个全局最好位置大家反复向这两个方向调整飞行速度最后逼近食物。实现起来只需要位置更新和速度更新两个公式二三十行代码就能跑起来。更关键的是PSO做的是“直接搜索”不需要目标函数可导不需要问题有凸性哪怕你的成本函数里带着分段函数、甚至查表数据它照样能算。这对微网调度这种约束多、非线性的问题来说非常合适。而且它的搜索过程能可视化每一代适应度怎么下降、粒子往哪个方向移动全看得见对建立直觉特别有帮助。2. 把调度问题翻译成数学语言目标、约束与罚函数2.1 目标函数不是只算发电成本做调度之前先要回答一个问题让算法“优化”的到底是什么最常见的答案是总运行成本最小但实际操作中我建议你把弃风弃光惩罚也写进去否则算法为了省钱会疯狂放弃新能源得到一个“技术上正确但完全没意义”的解。以一个与主网相连的微网为例决策变量是每个时段的燃气轮机出力 (P_{gt}(t))、储能充放电功率 (P_{bat}(t))、以及与主网的交互功率 (P_{grid}(t))。目标函数可以写成[ \min C \sum_{t1}^{T} \left[ \lambda_{gt} P_{gt}(t) \lambda_{grid}(t) P_{grid}(t) c_{bat} |P_{bat}(t)| \lambda_{curtail}(P_w^{ava}(t) - P_w(t) P_{pv}^{ava}(t) - P_{pv}(t)) \right] ]这里每一项都有实际对应。 (\lambda_{gt} P_{gt}(t)) 是燃气轮机的燃料加维护成本 (\lambda_{grid}(t)) 是分时电价峰时贵谷时便宜所以算法会自动倾向于在谷时购电、峰时用储能放电 (c_{bat}|P_{bat}(t)|) 是储能充放电带来的损耗成本系数通常很小主要是防止储能频繁无意义地动作最后一项里 (P_w^{ava}(t)) 是风电可用功率 (P_w(t)) 是实际消纳功率两者之差就是弃风量乘上一个较大的惩罚系数 (\lambda_{curtail})让算法宁可用贵一点的燃气轮机也不愿意轻易放弃新能源。2.2 约束条件每条约束都对应一个物理事实优化问题光有目标函数还不够还得有约束否则算法会给出“燃气轮机超额定功率硬扛”或者“储能一天充放一百次”这种离谱方案。下面这几类约束是跑不掉的第一是功率平衡约束。这是一个等式约束也是最核心的约束它要求每个时段所有电源出力加上储能放电、加上购电正好等于负荷[ P_w(t) P_{pv}(t) P_{gt}(t) P_{bat}^{dis}(t) P_{grid}(t) P_L(t) ]如果储能处于是充电状态 (P_{bat}) 就是负值挪到等式右边就成了负荷增加。这里我建议统一约定放电为正、充电为负代码里不容易搞混。第二是机组出力约束。燃气轮机有最小稳定出力也有额定上限[ P_{gt}^{min} \le P_{gt}(t) \le P_{gt}^{max} ]第三是爬坡约束。这是最容易在初学者代码里被忽略的一条[ -\Delta_{down} \le P_{gt}(t) - P_{gt}(t-1) \le \Delta_{up} ]它限制了燃气轮机在两个相邻时段之间出力变化的幅度。注意这里涉及 (t) 和 (t-1) 两个时段我第一次写代码时就用成了绝对功率上限结果算法给出的调度曲线像锯齿一样乱跳后来才发现是索引搞错了。第四是储能约束。储能是最复杂的单元它至少包含三层限制充放电功率不能超过额定值任意时刻的荷电状态SOC不能超出 ([SOC_{min}, SOC_{max}])SOC本身满足递推关系[ SOC(t) SOC(t-1) \eta_{ch} P_{ch}(t) \Delta t - \frac{1}{\eta_{dis}} P_{dis}(t) \Delta t ](\eta_{ch}) 是充电效率 (\eta_{dis}) 是放电效率。给数据时别把这俩设成1真实储能一定有损耗否则仿真结果会过于乐观。另外理论上储能不能同时充电和放电这一条在一些入门代码里是用“充放互斥”约束或直接加一个二进制变量处理的但用PSO做连续变量编码时可以先不加我实际跑下来发现算法为了节省成本基本不会同时充放罚函数也能兜住。2.3 罚函数让PSO知道“哪些样子不能长”上面这些约束尤其是功率平衡等式和SOC边界PSO本身并不认识。它只认一个东西适应度值。所以标准做法是罚函数法把约束违反量变成惩罚项塞进目标函数[ F C \sigma_{eq} \cdot |g(x)| \sigma_{ineq} \cdot \sum \max(0, h_i(x)) ]等式约束看绝对误差不等式约束看超出边界的量两者都乘以对应的罚系数。罚系数的取值直接影响算法行为太小了算法发现违反约束的代价不痛不痒最后给你一个功率不平衡的解太大了又会让所有粒子都挤在可行域边缘不敢越界试探收敛变慢。我自己的经验是先把目标函数各类成本归一化到同一数量级再给等式约束设一个目标函数量级10到50倍的罚系数不等式约束设5到20倍。具体值要和你的数据量级一起调后面我会专门说这个坑。3. 粒子群算法原理拆解它凭什么能解决调度问题3.1 从鸟群觅食说起三个记忆决定行动PSO的灵感来源是鸟群觅食。想象你是一只在空中飞的鸟面前有一片看不见边界的地方你不知道食物在最北边还是最南边。你手里有三种信息自己现在飞的位置自己这辈子飞过的最食物密集的位置个体记忆 pbest以及鸟群里所有鸟到目前为止发现的全局最食物密集的位置群体记忆 gbest。下一步怎么飞正常人都会这么做先朝着自己记忆中食物最多的方向飞同时听说某个同伴发现了更好的地方也分一点力气朝那边飞最后保留一小部分速度作为惯性。PSO就是把这个朴素行为抽象成了数学公式。3.2 两个核心公式和一个速度限幅PSO的更新由下面两个公式完成[ v_{id}^{k1} w v_{id}^{k} c_1 r_1 (p_{id} - x_{id}^{k}) c_2 r_2 (p_{gd} - x_{id}^{k}) ][ x_{id}^{k1} x_{id}^{k} v_{id}^{k1} ]第一个公式是速度更新第二项是位置更新。 (w) 是惯性权重控制粒子沿原来方向飞多远 (c_1) 是个体学习因子控制粒子飞向自己历史最优位置的力度 (c_2) 是社会学习因子控制粒子飞向全局最优位置的力度 (r_1)、 (r_2) 是[0,1]之间的随机数给搜索引入随机性。还有一个容易被忽略的操作是速度限幅。每个粒子的速度不能无限大否则粒子会在搜索空间里“咻”地一下飞出去再也回不来。工程上常用[ v_{id} \in [-v_{max}, v_{max}], \quad v_{max} \alpha (x_{max} - x_{min}) ](\alpha) 一般取0.1到0.2比如某个决策变量范围是[0, 500]那速度最大不要超过100。3.3 参数设置常用范围和我踩出来的经验值很多刚开始学PSO的人喜欢问“标准参数是什么”但实际调参像调音响得靠听。给你一份我试过的常用范围和推荐值你可以在这个基础上按自己问题调整参数常用范围我推荐理由种群规模 N20~603024时段问题维度约72维30个粒子足够覆盖加大到50收益有限惯性权重 w0.4~0.90.9递减到0.4前期大权重全局搜索后期小权重局部精修个体学习因子 c10.5~2.52.5递减到0.5前期多探索自己后期更多跟随群体社会学习因子 c20.5~2.50.5递增到2.5与c1配合前期探索后期收敛最大迭代次数100~500200200代以后收益明显下降还要跑对照实验求快为主速度上限系数 α0.1~0.20.15太小搜索慢太大致发散这里特别强调惯性权重线性递减。我最早用固定权重 w0.8连续跑了十次找出八次的最优成本都是几千块但彼此之间波动很大。改成线性递减之后解的质量和稳定性同时提升。原理也很好理解搜索前期粒子需要在广阔的区域内到处找权重小了容易一开始就被某个局部好解“吸住”搜索后期粒子已经找到了不错的区域权重太大又会在最优解附近来回震荡始终落不下去。3.4 PSO在这个问题上的真实优势我不是说PSO是微网调度里最牛的算法严格说MILP在有成熟求解器比如Gurobi、Cplex的情况下能给出有最优性证明的解这是PSO做不到的。但PSO有它独特的价值首先它对目标函数的要求极低。微网调度一旦加入启停成本、阶梯式购电价格或者设备寿命损耗等非连续、非线性项传统方法建模会非常痛苦PSO只需要把这些项写进适应度函数不需要任何求导或线性化处理。其次它对初学者的认知构建特别友好。你能直观地看到粒子群从散乱到收敛的过程理解什么是“探索”和“利用”之间的平衡。我至今觉得把PSO跑通一遍后再回去学MILP你对“为什么需要凸松弛”“为什么要求对偶间隙为0”这些概念会更有体感。4. 从数学公式到能跑的程序编码、迭代与落地细节4.1 粒子编码把72个变量装进一个粒子有了数学描述接下来要思考“粒子到底是什么”。一个粒子本质上就是一个候选调度方案。最直接的编码方式是把所有可控变量按时间顺序排成一行[ x [P_{gt}(1), P_{gt}(2), \ldots, P_{gt}(24), P_{bat}(1), P_{bat}(2), \ldots, P_{bat}(24), P_{grid}(1), \ldots, P_{grid}(24)] ]这是一个72维的向量。第1到第24维度控制燃气轮机逐时出力第25到第48维度控制储能逐时充放电功率正为放电负为充电第49到第72维度控制购电功率。每个维度都有上下限初始化时用这些边界随机生成一批粒子就得到了第一代候选方案。我在实际编码时做了个小改动不一定把 (P_{grid}) 也放进编码。因为功率平衡约束是个等式给定前三组变量后 (P_{grid}) 其实可以直接解出来[ P_{grid}(t) P_L(t) - P_w(t) - P_{pv}(t) - P_{gt}(t) - P_{bat}(t) ]如果算出来的购电功率超出联络线容量就记一笔约束违反量。这样编码维度从72降到了48搜索效率明显更高而且功率平衡约束天然满足了一大半。这个“靠等式反解变量”的思路在优化里很常用值得记下来。4.2 适应度函数与约束处理的代码逻辑适应度函数是整个PSO的核心它是粒子和问题之间的唯一接口。我的写法是输入一个粒子的位置向量输出该方案的成本加惩罚。流程是——把72/48维向量按规则切片还原成燃气轮机、储能、购电三个24时段的序列。根据功率平衡反解或校验购电功率。计算燃气轮机燃料成本、购电费用、储能损耗、弃风弃光惩罚。逐时段检查爬坡约束、SOC范围、出力上下限、联络线功率限制累加违反量。总适应度 原始成本 惩罚项。这里有个经验值得分享约束违反量最好是“连续”的。比如SOC超过上限0.1和超过上限0.01罚函数反馈应该是成比例的而不是一旦越界就给一个固定惩罚。连续性的违反量能让粒子知道“稍微越界一点点比严重越界要好”搜索方向才不至于完全无序。4.3 一个可运行的核心循环Python这里给一个不依赖任何优化库、纯PSO核心逻辑的Python示意代码重点展示主循环怎么组织import numpy as np # 参数设置 T 24 # 时段数 N 30 # 种群大小 max_iter 200 # 迭代次数 dim 48 # 编码维度P_gt(24) P_bat(24)P_grid反解 # 边界每台设备出力上下限按24小时重复 gt_max, gt_min 500, 20 # 燃气轮机出力上下限 bat_max, bat_min 100, -100 # 储能充放电边界正放负充 lb np.tile([gt_min] * T [bat_min] * T, (N, 1)) ub np.tile([gt_max] * T [bat_max] * T, (N, 1)) # PSO 参数 w_max, w_min 0.9, 0.4 c1_max, c1_min 2.5, 0.5 c2_max, c2_min 0.5, 2.5 # 初始化位置和速度 x lb np.random.rand(N, dim) * (ub - lb) v np.random.uniform(-0.15, 0.15, (N, dim)) * (ub - lb) pbest x.copy() pbest_fitness np.array([fitness(x[i]) for i in range(N)]) gbest x[pbest_fitness.argmin()] gbest_fitness pbest_fitness.min() for k in range(max_iter): w w_max - (w_max - w_min) * k / max_iter c1 c1_max - (c1_max - c1_min) * k / max_iter c2 c2_max - (c2_max - c2_min) * k / max_iter for i in range(N): r1, r2 np.random.rand(dim), np.random.rand(dim) v[i] w * v[i] c1 * r1 * (pbest[i] - x[i]) c2 * r2 * (gbest - x[i]) # 速度限幅 v[i] np.clip(v[i], -0.15 * (ub[i] - lb[i]), 0.15 * (ub[i] - lb[i])) x[i] x[i] v[i] x[i] np.clip(x[i], lb[i], ub[i]) fi fitness(x[i]) if fi pbest_fitness[i]: pbest[i] x[i] pbest_fitness[i] fi gbest_idx pbest_fitness.argmin() if pbest_fitness[gbest_idx] gbest_fitness: gbest pbest[gbest_idx] gbest_fitness pbest_fitness[gbest_idx] print(fiter {k1}: best fitness {gbest_fitness:.2f})这段代码没有给出fitness的具体实现因为它随你的成本参数和约束定义变化需要你按自己数据填充。但主循环结构就是这样先算适应度再更新个体最优和全局最优然后速度位置更新循环往复。我用Python而不是MATLAB写示例是因为Python更容易让初学者读。真做课题写论文用MATLAB也完全没问题逻辑一模一样把循环和矩阵操作换一下语法而已。4.4 跑仿真前需要准备的数据很多人一头扎进算法代码结果跑到一半发现没数据。我建议开局就准备这五类数据网上很多论文附录或者公开数据集都能找到典型日负荷曲线24个点单位kW/时段。风电和光伏的预测出力曲线各24个点。入门阶段可以用一条平滑的单峰曲线代替光伏一条有波动的曲线代替风电。燃气轮机参数额定出力上限和下限、爬坡速率、单位发电成本。储能参数容量kWh、SOC上下限、充放电效率、最大充放电功率、初始SOC和末尾SOC。末尾SOC建议设成回到初始值否则相当于白白多用了一天能量很多没经验的代码会在这个细节上吃亏。分时电价峰、平、谷三个时段的购电价单位元/kWh。5. 仿真结果怎么看收敛曲线、参数敏感性与方案对比5.1 收敛曲线不会骗人但你得会读收敛曲线是每一代迭代里全局最优适应度的连线。正常情况是前几十代下降很快然后逐渐变缓最后几乎水平。如果曲线在100代后才突然跳出一个小台阶说明算法之前卡在局部最优后来偶然逃逸如果曲线从头到尾都几乎不动要么你的初始种群已经离最优很近要么罚系数或速度上限设置出了问题。我个人判断收敛质量的标准不是只看最终值而是看十次独立运行的最好值、平均值和标准差。PSO是随机算法单次运行有很大运气成分。十次跑下来平均值低、标准差小才说明算法稳定。很多论文里只给一条最好收敛曲线那是不严谨的。5.2 参数敏感性我亲手试出来的差异我拿公开的微网算例做过一组小实验其他条件不变只改一个参数结果很能说明问题实验设置10次平均最优成本标准差固定权重w0.8, c1c22236742.8线性递减权重w: 0.9→0.4231821.6线性递减w 递减c1递增c2前表推荐230113.2种群30改为50w/c策略不变229812.5可以看到参数策略对解的质量影响很大而单纯加大种群到50的收益反而不如改进参数策略。这组数据给了我一个很深印象调参之前先调策略先让算法的“行为逻辑”符合搜索规律。5.3 有储能和没储能成本差多少储能的作用可以用一组对照实验看得非常清楚。还是同一组负荷和风光数据储能参数设置为容量100kWh、效率90%、SOC范围0.1到0.9不含储能的微网使用PSO优化后日运行成本约2450元。加入储能后日运行成本约2240元下降了大约8.6%。成本下降主要来自两个途径。一是低存高放套利储能会在谷电时段充电把能量攒到峰电时段放电减少高价购电。二是新能源消纳中午光伏出力大而负荷小时储能把多余的光伏电量存下来避免弃光惩罚。看储能SOC曲线时有一件事值得留意正常的SOC曲线应该是“早晚低、中间起伏”并且最终回到初始值。如果你的SOC曲线在一段时间内反复触顶触底说明储能容量相对负荷需求偏大或者充放电效率设得过高。5.4 和“拍脑袋策略”的对比只跟“无储能”对比还不够我建议再做一组与人工策略的对比它能直观展示优化的价值。所谓拍脑袋策略就是光伏风电全额消纳燃气轮机固定一个中低出力剩余缺口全由电网补充。还是同一组数据简单策略下日成本大约2630元而PSO调度的结果是2240元少了将近15%。这15%的差距不是靠某个“神奇操作”而是靠几百次迭代把每个时段该用燃气轮机、该买网电、该充不该放给一点点抠出来的。这也是为什么学微网调度一定要亲手写一次优化算法的原因你会真的相信这些决策变量之间存在耦合关系。6. 实战踩坑记录与几个值得做的改进方向6.1 早熟收敛粒子们被“带着一起跑偏”PSO最常见的毛病就是早熟收敛。现象是迭代才三四十代所有粒子就已经集中在某个位置附近再迭代下去适应度完全不动。这在微网调度问题里尤其容易发生因为目标函数存在大量局部最优——比如某个解里燃气轮机出力整体偏大但恰好满足约束罚函数又不高它就会像一个陷阱把周围粒子都吸进去。我吃过一次大亏。有一版代码用固定权重跑无论怎么调参结果永远卡在一个“看似合理但成本偏高”的方案上。后来我把惯性权重改成线性递减问题基本解决。如果改了还在早熟建议再试两个方法一个是在迭代中期给粒子位置加小随机扰动另一个是隔几十代把一部分粒子重新随机初始化模拟“新鸟加入鸟群”。6.2 罚系数调起来很烦两个极端都不行罚函数法看起来简单实际调起来最耗时间。罚系数太小和太大各有各的灾难罚系数太小算法会得到一个“成本很低但功率不平衡”的解甚至储能在连续多小时同时充放电违反SOC约束——因为罚得轻它发现违规比守规矩更“划算”。罚系数太大粒子一旦越界就会被拉回但问题是你在可行域边缘的搜索被彻底限制粒子就像一个被绳子拴住的狗只能在很小区域里转悠收敛极慢而且找不到真正好的边界解。我的解决办法是分两步走。第一步不看最终成本先看违反量总和是否降到接近零把罚系数从小到大扫一遍找到一个“连续三次运行违反量都为零”的最小罚系数。第二步在这个基础上把罚系数稍微再放大一倍左右。这样既不会大量出现违规解也不会限制边界探索。6.3 初始种群质量决定前100代的走势PSO初始种群是完全随机生成的这没问题但我发现完全随机容易让前几十代都在“试错”——粒子们大量违反爬坡约束或SOC约束适应度全是惩罚项搜索方向混乱。改进办法有两个。一是约束感知初始化生成初始粒子时让储能功率序列先满足“每时段充放电不超限且SOC递推始终在范围内”再生成燃气轮机序列。这相当于在初始化阶段就帮粒子排掉了一部分绝对不可能的解。二是注入优质解先把“光伏满发、燃气轮机定出力、电网补差额”的人工策略作为第一个粒子放进去其余粒子再随机生成。这个做法不需要额外算力但能让收敛曲线起点就低一截。6.4 改进方向从PSO到混合算法入门之后可以沿着几个方向深入。最直接的改进是参数自适应比如根据种群多样性动态调整惯性权重多样性高就减小权重让粒子集中多样性低就增大权重重新散开。其次是混合算法典型做法是先跑遗传算法GA利用交叉和突变产生多样性较好的种群再用PSO在这个种群里精搜两者互补在微网调度文献里效果很稳定。如果你进一步想逼近理论最优解我建议学一下MILP求解器的用法用YalmipGurobi解同一个模型对比小规模算例下MILP给的最优解和PSO给的近似解你会发现两件事问题规模小的时候MILP又快又准问题变复杂比如加了整数变量、不确定性之后MILP求解时间暴涨而PSO虽然不保证最优但解够用、时间可控。这才是优化调度里算法选择的真实逻辑。最后说点个人体会。学这个内容别一上来就冲最复杂的模型。先跑通一个24时段、四五个决策变量的最小案例把SOC曲线和燃气轮机出力曲线画出来用眼睛看住每一个变量的行为合不合理。很多细节会在调试中自然理解比如爬坡约束的索引错位、末尾SOC不回初值、罚系数压不住等式约束——这些都是好事踩过一次以后你再也不会犯。粒子群算法本身简单但它帮你建立的“约束意识”和“搜索思维”会一直跟着你走到后面更复杂的调度方法里。
返回列表