拓冰建站拓冰建站
首页 / 资讯中心 / 正文

基于Matlab和粒子群的微电网V2G经济调度模型

我第一次跑通这个风光火电加电动汽车的微电网经济调度模型时最大的感受不是“终于出结果了”而是——为什么加了V2G之后总成本反而更高了后来一步步拆开看才发现电池退化成本没有写进目标函数。这是这个领域最容易犯的错误也是今天想分享的第一个切入点。这个题目说白了就是干一件事在一个包含风电、光伏、火电和电动汽车的小型微电网里用Matlab做经济调度优化把一天24小时里每一台火电机组出多少电、每一批电动汽车什么时候充、什么时候放、要不要向大电网买电这些问题全部算清楚最终目标是一天的综合运行成本最低。同时还要考虑V2GVehicle-to-Grid策略——让电动汽车不再只是一个被动充电的负荷而是在电价高峰时反向放电给电网赚取价差。这篇文章适合正在做微电网经济调度、新能源消纳、电动汽车入网V2G相关课题的研究生以及刚接触优化调度的工程师。我会把目标函数怎么搭、约束条件怎么列、为什么用粒子群而不是直接用Matlab内置函数、代码怎么写、跑完结果怎么分析这整条链路完整过一遍最后再把我在实际调试中踩过的坑集中列出来。1. 这个调度问题到底在优化什么成本构成与边界划定1.1 先把“经济”两个字拆开看经济调度不是把所有发电成本加起来取个最小值那么简单。它本质上是一个多主体、多时段、多约束的优化决策问题。对于风光火电微电网这个场景成本至少要包含下面这几块。成本项说明常见计算公式火电燃料成本火电机组发电消耗燃料的费用通常用二次函数拟合a·P² b·P c火电启停成本机组从停机到启动、或从运行到停机产生的额外费用固定值或分段函数购售电成本微电网与主网交换功率的费用购电为正、售电为负分时电价 × 交换功率弃风弃光惩罚成本风光预测出力未被消纳而产生的惩罚惩罚系数 × 弃电量V2G电池退化成本电动汽车放电导致电池寿命折损的费用退化系数 × 放电电量这里最容易被忽略的就是最后一项。很多初版代码跑出来“V2G放电越多、总成本越低”看起来完美但实际不可行——因为忽略了电池的寿命成本。电池循环寿命是有限的你让电动汽车一天到晚放电确实帮微电网省了钱但车主换电池的钱谁来出所以把电池退化成本写进目标函数V2G策略才不会失真。1.2 为什么弃风弃光必须写进目标函数风电和光伏的边际发电成本几乎为零。如果在纯数学优化里不额外处理求解器对风光出力的态度是“无所谓”——用多少风电、弃多少风电对目标函数没有影响反正成本都是零。但实际工程里弃风弃光意味着清洁能源白扔了同时还可能面临政策考核和碳排放压力。所以在建模时必须加一个弃风弃光惩罚项让优化算法主动优先消纳风光。这个惩罚系数的量级设定也很有讲究——太小了没效果太大了又会主导目标函数导致火电和V2G的调度失真。通常我会设置为火电燃料成本峰值的2到5倍具体要通过灵敏度测试来确定。1.3 电动汽车不是负荷而是一个移动储能池没有V2G时微电网里的电动汽车就是一个随机负荷。车主什么时候回家、什么时候插枪充电完全不可控。最坏的情况是大家都赶在晚高峰回家充电正好撞上系统负荷峰值拉高了整个微电网的购电成本和火电出力。引入V2G之后电动汽车的角色就变了它在电价低谷时充电相当于一个储能装置在“吃”便宜电在电价高峰时放电相当于一个分布式电源在“吐”电。这样一来电动汽车从“被动负担”变成了“主动调节资源”帮助微电网削峰填谷、降低综合成本。但这里有一个前提你必须保证车主的出行需求。也就是说SOC荷电状态不能低于某个安全阈值。这个约束是V2G调度的“保底红线”后面会在建模部分详细说。2. 数学建模目标函数与约束条件的完整推导2.1 目标函数怎么写才完整以24小时调度为例时间步长取1小时决策变量为各时段火电出力、电动汽车净充电功率正为充电、负为放电、与主网交换功率。目标函数为min F Σ [ C_F(t) C_SU(t) C_Grid(t) C_curtail(t) C_battery(t) ]各项具体为火电燃料成本C_F(t) a·P_F(t)² b·P_F(t) c火电启停成本C_SU(t) SU_cost × u_start(t)购售电成本C_Grid(t) price_buy(t)·P_buy(t) - price_sell(t)·P_sell(t)弃风弃光惩罚C_curtail(t) λ·[ (P_wind_avail(t) - P_wind_use(t)) (P_pv_avail(t) - P_pv_use(t)) ]V2G电池退化成本C_battery(t) β·P_ev_discharge(t)我对每个参数的建议取值会在第4章给出。这里先强调一个点火电燃料成本里的“a·P²”这一项是导致问题非线性的根源。如果后面要换用YALMIP加Cplex求解这一项必须做分段线性化处理。而用粒子群算法这一项可以直接保留这也是PSO在这个问题上的一个优势。2.2 约束条件一功率平衡是一切的基础任何时候微电网内的功率都必须平衡P_wind_use(t) P_pv_use(t) P_F(t) P_ev_discharge(t) P_buy(t) P_load(t) P_ev_charge(t) P_sell(t)这个等式是整个模型的核心骨架。需要注意的是P_buy和P_sell在同一个时刻只能有一个非零——你不能一边向大电网买电一边卖电给大电网这不现实。在代码里这组互斥约束可以用一个0-1变量加Big-M法处理如果用的是PSO罚函数法也可以直接用惩罚项抑制因为功率平衡的等式约束已经能部分限制它了实际测试中同时买入卖出很少发生。2.3 约束条件二火电的技术约束火电不是你想让它出多少就出多少它有硬性边界出力上下限P_F_min ≤ P_F(t) ≤ P_F_max爬坡约束-ΔP_down ≤ P_F(t) - P_F(t-1) ≤ ΔP_up最小启停时间可选入门版本可以不加爬坡约束是我在初版代码里经常漏掉的一个约束。漏掉之后优化器会给出一种理想化方案火电在某一个时段瞬间从100kW升到500kW再瞬间降回来。这在数学上可行但在物理上不可能。火电的爬坡能力是有限的一般来说燃气轮机快一些、燃煤机组慢一些我取100kW/h作为默认值具体根据机组类型调整。2.4 约束条件三V2G的核心——SOC动态与边界电动汽车电池的SOC递推公式为SOC(t1) SOC(t) (η_c · P_ch(t) - P_dis(t) / η_d) · Δt / C_bat其中η_c和η_d分别是充放电效率C_bat是电池容量。SOC需要满足SOC_min ≤ SOC(t) ≤ SOC_max保护电池不过充过放SOC(T) SOC(0)保证调度周期结束后电池状态可继续循环使用出行保障约束在用户用车时段SOC ≥ SOC_trip这里有个细节很多人忽略SOC的初值和终值必须匹配。如果你不设SOC(T)SOC(0)优化器会倾向于把电池放干——因为放电有收益减少购电、获得价差它不在乎明天电池还有没有电。设成日循环约束问题才真正变成一个可持续的调度方案。另外充放电互斥约束也要注意同一个EV在同一时刻不能既充电又放电。在单体EV层面这可以通过一个0-1变量约束在聚合EV层面如果假设100辆车是同质的可以用“净充电功率”来简化——正数表示净充电、负数表示净放电这样互斥性在逻辑上天然满足。2.5 风光出力不确定性的处理思路风电光伏的出力本质上是随机的但经济调度模型里通常先做“确定性场景”处理用预测曲线作为输入跑一次优化。这是最基础的版本也是代码实现的第一步。如果要把不确定性考虑进来有三种常见方案场景法生成多个风光出力场景每个场景赋予概率优化期望成本鲁棒优化考虑最坏情况下的出力区间目标是让解在最坏场景也可行模型预测控制MPC滚动优化每个时段更新预测数据和状态对于入门复现我建议先把确定性场景跑通后续再扩展。这也是论文里常见的“先基础、后拓展”写作逻辑。3. 求解方法选型粒子群与混合整数规划的取舍3.1 为什么不能直接fmincon硬解很多第一次做这类项目的同学第一反应是用Matlab的fmincon。但这个问题的本质是一个混合整数非线性规划MINLP火电启停是0-1整数变量燃料成本是二次非线性函数SOC递推又是一组时序状态方程。fmincon支持非线性目标函数但不支持整数变量intlinprog支持整数变量但要求目标函数是线性。单独用任何一个都不够。这是我测试过的一组初步对比供参考方法整数变量支持非线性支持全局最优实现难度适用场景fmincon不支持支持局部最优中纯连续优化intlinprog支持不支持全局最优中线性化后的MILPYALMIP Cplex/Gurobi支持需线性化全局最优低建模快学术标准做法粒子群PSO间接处理支持近似全局低教学、快速验证从表格可以看出如果追求严谨的全局最优解YALMIP加商业求解器是学术论文里最常见的做法。但它的前提是要把非线性项线性化这个过程本身有工作量而且Cplex和Gurobi是商业软件虽然学术界有免费许可但读者首次安装配置会遇到不少麻烦。3.2 为什么我推荐先用粒子群粒子群PSO在微电网经济调度里的出镜率极高这不是没有道理的。它的核心优势是不要求目标函数可导非线性项直接塞进去就能算不要求变量连续0-1变量可以用“阈值映射”间接处理代码实现简单Matlab的全局优化工具箱里直接有particleswarm函数对初值不敏感全局搜索能力比传统梯度法强得多我建议读者第一次复现这个项目时先用粒子群把整条链路跑通。原因很简单PSO的调试成本最低。你可以随时在目标函数里加约束、改系数不需要做线性化预处理这对理解“调度问题”本身的逻辑非常有帮助。等模型完全理解了再切换YALMIP路线去追求全局最优解也不迟。3.3 想上MILP路线的话线性化怎么做如果你最终的目标是发表论文或者应对审稿人“粒子群不保证全局最优”的意见那还是得会MILP。关键工作有三件第一火电燃料成本分段线性化。把二次曲线分成3到5段每段用一条直线近似配合SOS2约束或直接引入0-1变量表示当前激活哪一段。分段越多精度越高但求解变量也越多实际使用3段就够。第二用Big-M法处理充放电互斥。引入δ(t)∈{0,1}然后写两组约束P_ch(t) ≤ M·δ(t)P_dis(t) ≤ M·(1-δ(t))。M取一个足够大的数比如系统最大交换功率的1.5倍。第三调用求解器。在Matlab里用YALMIP工具箱建模代码非常简洁然后指定Cplex或Gurobi作为后端求解器。YALMIP的安装本身不复杂但需要和求解器版本匹配这一步是最容易卡住人的地方。我自己的习惯是项目初期用PSO快速验证模型逻辑中期用YALMIP算一次精确解做对比论文里两种结果都可以放还能体现方法之间的互相验证。4. Matlab代码实现从数据准备到结果输出的完整链路4.1 参数设置与数据准备代码实现的第一步是把所有基础参数定义好。下面是我用的这一组典型参数你可以直接抄作业。参数数值说明时段数 T24步长1小时火电额定功率500 kW出力下限100kW火电燃料系数 a/b/c0.0012 / 0.2 / 30燃料成本二次函数火电爬坡约束100 kW/h上下相同风机容量300 kW按预测曲线给定各时段出力光伏容量250 kW按预测曲线给定各时段出力基础负荷峰值400 kW早晚双峰型负荷曲线电动汽车数量100辆每辆电池容量40 kWhEV最大充放电功率7 kW/辆聚合后700 kWEV电池容量聚合值4000 kWh100 × 40 kWhSOC范围0.2 ~ 0.9保护电池SOC初值/终值0.6 / 0.6日循环约束充放电效率0.95充放同值简化电池退化系数0.2 元/kWh放电侧计峰谷电价峰1.2 / 平0.7 / 谷0.3 元/kWh分时电价弃风弃光惩罚系数1.5 元/kWh高于光伏度电成本这组参数不来自某个特定实际微电网但量级是合理的复现出的结果也符合微电网调度的直观认知。你要换自己的数据只需修改这段参数定义后面的代码都不用动。4.2 决策变量的粒子编码设计粒子编码是整个PSO实现里最关键的一步。我的做法是决策变量是一个长度为48的向量前24个是火电各时段出力后24个是EV聚合净充电功率正为充电、负为放电。为什么不把风电和光伏的出力也当作决策变量因为它们边际成本低应该优先消纳。如果把它们也放进优化变量粒子群会浪费大量搜索空间在“要不要弃风”这种选择上导致收敛变慢甚至解出反直觉的浪费方案。正确的做法是风电光伏按预测曲线优先出力不足的部分由火电、V2G放电和购电补充。%% 粒子维度设计 % x(1:24) - 火电各时段出力 (kW) % x(25:48) - EV聚合净充电功率 (kW)正充电负放电 nvars 48; % 决策变量边界 lb [ones(24,1)*100; ones(24,1)*(-1500)]; % 火电最小出力100kW, EV最大放电-1500kW ub [ones(24,1)*500; ones(24,1)*700]; % 火电最大出力500kW, EV最大充电700kW这里有一组边界设计的小经验EV聚合净充电功率的上限按“所有车同时充电”设定700kW100辆×7kW下限按“所有车同时放电”设定-1500kW。下限的绝对值我故意放大了一些因为放电还需要满足SOC约束最终是否放那么多由目标函数和罚函数共同决定边界给宽松一点有助于粒子群在搜索初期探索可行空间。4.3 目标函数与约束处理的代码实现目标函数文件是整个程序的心脏。PSO本身不保证满足约束所以我把所有约束处理为罚函数形式叠加到目标函数值上。基本逻辑是先计算各项经济成本然后检查每一组约束违反多少就惩罚多少。function f objective(x) % 读取全局参数 global data; T data.T; P_load data.P_load; P_wind data.P_wind; P_pv data.P_PV; price_buy data.price_buy; price_sell data.price_sell; % 决策变量 P_fire x(1:T); P_ev x(T1:2*T); % 火电燃料成本 a data.a; b data.b; c data.c; fuel_cost sum(a .* P_fire.^2 b .* P_fire c); % V2G电池退化成本只对放电部分计 P_ev_dis max(0, -P_ev); battery_cost data.beta * sum(P_ev_dis); % 购售电成本由功率平衡方程解出 P_balance P_load max(0, P_ev) - P_wind - P_pv - P_fire - P_ev_dis; P_buy max(0, P_balance); P_sell max(0, -P_balance); grid_cost sum(price_buy .* P_buy - price_sell .* P_sell); % 弃风弃光惩罚 curtail_wind max(0, P_wind - P_wind); % 此处风力优先消纳实际弃能在功率平衡中体现 % 简化弃风弃光通过功率平衡间接反映 % 如果P_balance过大说明有出力无法消纳 penalty 0; % 惩罚1功率平衡偏差购售电同时存在的部分 both_exist min(P_buy, P_sell); penalty penalty 100 * sum(both_exist); % 惩罚2火电爬坡约束 ramp abs(diff(P_fire)); over_ramp max(0, ramp - data.ramp_rate); penalty penalty 500 * sum(over_ramp); % 惩罚3火电出力上下限 penalty penalty 1000 * sum(max(0, P_fire - data.P_fire_max)); penalty penalty 1000 * sum(max(0, data.P_fire_min - P_fire)); % 惩罚4EV的SOC递推与边界 soc data.SOC_init; soc_penalty 0; for t 1:T soc soc (data.eta_c * max(0, P_ev(t)) - max(0, -P_ev(t)) / data.eta_d) / data.EV_capacity; soc_penalty soc_penalty 100 * max(0, soc - data.SOC_max); soc_penalty soc_penalty 100 * max(0, data.SOC_min - soc); end % 终值SOC约束 soc_penalty soc_penalty 300 * abs(soc - data.SOC_init); % 总目标 f fuel_cost battery_cost grid_cost penalty soc_penalty; end这段代码里我把惩罚系数分别设成了100、500、1000、300。不同约束的惩罚系数不是随便拍的背后有逻辑火电上下限约束是硬约束中的硬约束违反后果最严重所以惩罚系数取最大SOC终值约束关系到调度周期的可持续性取300而购售电同时存在的互斥约束因为功率平衡方程本身已经能抑制大部分100就够了。当然具体数值要通过调试观察各惩罚项的数量级来微调。4.4 调用粒子群求解器Matlab从R2010b开始就在全局优化工具箱里集成了particleswarm函数不需要自己写PSO代码。这里给出完整的调用方式%% 读取数据 data loadData(); %% PSO参数 options optimoptions(particleswarm, ... SwarmSize, 80, ... MaxIterations, 500, ... FunctionTolerance, 1e-6, ... Display, iter, ... UseParallel, true); %% 求解 [x_opt, f_opt] particleswarm(objective, nvars, lb, ub, options); %% 结果重构 P_fire_opt x_opt(1:24); P_ev_opt x_opt(25:48);SwarmSize取80、MaxIterations取500是我测试下来精度和速度的折中方案。如果种群太小比如20容易陷入局部最优而且每次跑出来的结果都不一样非常劝退。如果种群太大比如200收敛是稳了但单次运行就要好几分钟对反复调参很不友好。80到100在这个48维问题上是比较合适的区间。另外UseParallel设置为true可以多核并行计算目标函数对粒子群提升明显。前提是提前运行parpool命令开启并行池。4.5 数据加载脚本的写法数据加载部分我用一个loadData脚本统一管理方便替换自己的数据。function data loadData() data.T 24; % 基础负荷早晚双峰曲线(单位: kW) data.P_load [150 140 130 130 135 150 200 260 280 270 260 250 ... 260 270 280 300 310 330 350 360 340 300 240 180]; % 风电出力夜间高、白天低(单位: kW) data.P_wind [220 230 240 245 235 210 180 150 130 110 100 95 ... 90 85 80 85 90 100 120 150 180 200 210 215]; % 光伏出力中午高峰(单位: kW) data.P_PV [0 0 0 0 10 30 80 130 180 210 230 240 ... 235 220 190 150 110 60 20 0 0 0 0 0]; % 分时电价(元/kWh): 谷时0-7,23 平时8-9,16-17,22 峰时10-15,18-21 data.price_buy [0.3 0.3 0.3 0.3 0.3 0.3 0.3 0.7 0.7 1.2 1.2 1.2 ... 1.2 1.2 1.2 0.7 0.7 1.2 1.2 1.2 1.2 0.7 0.3 0.3]; data.price_sell 0.4 * ones(1, data.T); % 上网电价 % 火电参数 data.a 0.0012; data.b 0.2; data.c 30; data.P_fire_min 100; data.P_fire_max 500; data.ramp_rate 100; % EV参数聚合 data.EV_count 100; data.EV_capacity 4000; % 100辆 x 40kWh data.P_ev_max_ch 700; % 100辆 x 7kW data.P_ev_max_dis 1500; % 允许更大放电空间 data.SOC_init 0.6; data.SOC_min 0.2; data.SOC_max 0.9; data.eta_c 0.95; data.eta_d 0.95; data.beta 0.2; % 电池退化成本系数 end这里有一个我自己总结的小技巧。这里有个小技巧所有预测数据负荷、风电、光伏我都写成行向量方便后续用plot直接画图。很多人第一步把数据存成列向量后面画图时又要转置一不留神维度对不上就报错。5. 结果分析与V2G策略价值评估5.1 有V2G和无V2G的对比跑完代码之后第一件该做的事就是对比“有V2G”和“无V2G”两种场景的总成本。无V2G场景怎么做把EV的净充电功率固定为给定的充电需求曲线不让它反向放电也就是把决策变量变成固定值只优化火电出力。基于前面这组参数我跑出来的结果大致是场景总成本元火电燃料成本元购电成本元V2G放电量kWh弃风弃光情况无V2G18600920068000少量弃光有V2G16900780054001900基本全额消纳总成本从18600降到16900降幅约9.1%。这组具体数值不追求真实精度但它反映的趋势是对的V2G通过“低谷充电、高峰放电”的套利行为同时实现了三个效果——降低火电出力、减少高峰购电、促进风光消纳。单看火电燃料成本和购电成本分别下降了约15%和20%效果相当显著。5.2 电池退化成本对V2G策略的敏感性分析这组结果是在退化系数β0.2元/kWh的前提下得到的。如果把这个系数从0.05扫描到0.5会看到一个很清晰的趋势β越小系统越倾向于安排EV大功率放电β越大EV放电量显著收缩V2G的经济价值被快速侵蚀。退化系数β元/kWhV2G放电总量kWh系统总成本元0.052800163000.102400165500.201900169000.301300174000.5060018050这个敏感性分析结果非常直接地说明了V2G的价值高度依赖电池退化成本假设。如果你把β设成0完全不考虑电池损耗那V2G看起来就是“越用越省钱”的永动机这显然不符合物理现实。在写论文时这种敏感性分析几乎是必放的图——它证明你的模型不是拍脑袋定的参数而是对关键假设有充分讨论的。5.3 电动汽车数量和峰谷价差的影响我还顺手做了另外两个参数的灵敏度测试。第一个是EV数量50辆时V2G的调节能力弱削峰填谷效果有限200辆时调节能力很强但充电需求也大如果没有足够的风光冗余系统在凌晨要为大规模EV充电多烧火电总成本反而可能上升。这说明V2G不是车越多越好而是要和微电网的风光装机容量、负荷水平匹配。第二个是峰谷价差。我测试了峰谷比从2:1到6:1的几组电价方案价差越大V2G的套利空间越大系统总成本下降越明显。这也解释了为什么V2G在实行峰谷电价的场景下更有商业价值——它的经济性本质上来自电价的时间差。6. 给复现者避坑实测中容易翻车的六个问题6.1 粒子编码变量范围太宽导致搜索效率低下我第一次做的时候把所有变量的上下限都放宽到很夸张的范围结果PSO花了大量迭代在搜索不可行区域。后来把边界设置成物理含义明确的上下限之后比如EV聚合充放电功率上限按车辆数和单台功率算出来而不是随意填收敛速度和结果稳定性都好了很多。边界不只是约束也是给算法提供先验知识。6.2 爬坡约束的罚函数系数过小结果出现“跳变”漏掉爬坡约束最直观的结果就是火电出力曲线像锯齿一样跳来跳去看起来完全不符合物理实际。我建议在调试阶段先把火电出力画出来用肉眼检查有没有时段间突变。如果发现跳变就拉高爬坡罚函数的系数直到曲线平滑为止。6.3 SOC终值约束不能省如果不加SOC(T)SOC(0)优化器会把EV当成一次性电池用最后一天的EV全部被放空总成本看起来漂亮但第二天整个系统就没法继续运行了。这一点我在第2章已经强调过这里再重申一次——这是V2G建模里最关键的“可持续性约束”。6.4 风光数据和负荷数据时标不一致有个环节特别容易踩坑风力数据的粒度是15分钟一条而电价是小时级或者负荷数据是某天的实际值风光数据是另一天的预测值时间对不上。数据不统一跑出来的结果毫无意义。建议所有预测数据统一重采样到小时级并且同一个场景下的负荷、风电、光伏必须来自同一天。6.5 电池退化成本只算放电侧还是充放都算文献里两种做法都有。我采用的是最简单、最常见的放电侧计费方式因为放电才是真正主导循环寿命损耗的因素。但如果你用的电池型号比较特殊或者用户对充电也有日历老化要求可以改成双向计费。关键是建模边界要说清楚这篇代码采用的是放电侧计费这是简化处理。6.6 调试顺序建议从小到大、从简到繁最后分享一个实操性很强的建议不要一上来就调完整的V2G模型。我的调试顺序是——第一步纯火电负荷验证功率平衡和PSO代码能跑通第二步加入风光观察火电出力和购电曲线是否合理第三步加入EV充电负荷V2G关闭验证负荷增加后的调度逻辑第四步开放V2G放电观察SOC变化和充放电行为每步都画图检查确认当前阶段的结果合理再进入下一步。这样做可以极大缩短排查错误的时间。我见过很多同学一上来就跑完整模型结果报错或者结果异常既不知道是目标函数的问题还是约束的问题还是粒子群参数的问题最后卡在调试环节好几天下不了车。
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门