手上有个做了一半的研究项目,刚好跟最近很多人在后台问的方向对上了——可再生能源发电与电动汽车的协同调度。这篇文章我把整个思路、建模过程、Python实现代码和踩过的坑一次性写完,包含了完整的可复现示例,不是那种只贴一段代码就完事的。项目场景设定为含风电场、光伏电站、传统火电机组和三种类型电动汽车(私家车、公交车、出租车)的区域微电网,调度周期24小时、步长1小时。目标是让总运行成本最低,同时兼顾电动汽车用户的充电需求。代码用PuLP库求解混合整数线性规划,完整代码在2.3节和2.4节,可以直接跑,跑完用matplotlib画出来的图就是3.2节那种效果。
先说清楚这篇文章适合谁看:正在写电力系统优化调度方向毕业论文或者课程设计的学生,做园区微电网/充电站运营的技术人员,以及想入门"用Python做优化调度"但是不知道从哪下手的同行。文章不要求你有很深的电力系统背景,但是最好懂一点线性规划的基本概念,比如什么叫决策变量、什么叫约束条件,行话不用全懂,知道大概意思就行。
1. 问题定义与调度场景假设
1.1 为什么要做协同调度
先聊一个很现实的问题:为什么可再生能源发电和电动汽车充电不能各管各的,非要"协同"?
风电和光伏的出力是波动的,中午光伏大发、晚上几乎没有,风电则看天气脸色。电动汽车充电负荷也有明显的峰谷特性,早晚高峰扎堆充。如果这两者各管各的,很容易出现一个尴尬场景:中午光伏出力最高的时候,电网调度的火电机组不能停(因为要保证供电可靠性,停了下午负荷上来就来不及开机),光伏电只能弃掉;晚上下班回家一堆电动车同时开始充电,用电负荷又冲上一个高峰,火电被迫快速爬坡顶上,成本飙升。
协同调度要解决的核心问题就是:在时间维度上把"可再生能源出力曲线"和"电动汽车充电负荷曲线"进行匹配和错峰。本质是让一部分灵活性负荷(电动汽车的充电时间、充电功率)去跟随波动性的发电出力,削峰填谷,降低对传统机组的依赖。这篇博文里我把问题简化成一个典型的日前调度(day-ahead scheduling)问题,生成一个未来24小时的调度计划,告诉调度员每个时刻火电发多少、电动汽车充多少、弃风弃光多少。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
1.2 系统结构与调度场景设定
系统的物理结构其实很简单:一条母线上挂着火电机组、风电场、光伏电站、电动汽车充电站和常规基础负荷(居民、商业用电)。这里"协同"的体现在于:电动汽车的充电计划不是固定不变的,而是作为决策变量,由优化模型统一调度,在满足用户充电需求的前提下灵活安排各时段的充电功率。
具体假设如下:
- 调度周期为24小时,时间步长1小时,即 t = 1, 2, ..., 24。
- 日前风速和光照预测数据已知(模拟生成),对应风电和光伏的可用出力上限已知。
- 火电机组作为平衡机组,负责填补缺口,有出力上下限和爬坡速率限制。
- 电动汽车按用途分为3类:私家车、公交车、出租车,每类有各自的充电需求曲线和可用充电时段。
- 目标函数是最小化总运行成本,包括火电发电成本、弃风弃光惩罚成本、电动汽车充电成本/收益。
这里有个很重要的建模决策:电动汽车充电成本如何计算?如果只看电网侧总成本,那EV充电费只是内部转移支付(电费从EV用户转移给电网),不改变总社会成本。但如果加入"弃风弃光惩罚"或者"碳排放成本",就可以通过调整EV充电时段来减少弃风和火电出力,从而实现真正的协同效益。我在模型中加入了弃风弃光惩罚项,用经济手段引导EV在可再生能源富余时段充电。
2. 数学模型与Python代码实现
2.1 决策变量与目标函数设计
先定义决策变量。我把问题建模为混合整数线性规划(MILP),主要原因下面细说。核心决策变量有以下几组:
- P_g(t):t时段火电机组的出力(连续变量,MW)。
- P_ev_k(t):第k类电动汽车在t时段的总充电功率(连续变量,MW)。
- P_curtail_wind(t)、P_curtail_pv(t):t时段的弃风、弃光功率(连续变量,MW)。
- u_g(t):火电机组的启停状态(0/1整数变量),1表示运行,0表示停机。
- u_soc_k(t):第k类EV集群在t时段的充电状态切换标志(部分模型中用,用于描述"不能同时充放"或"最小充电时长"约束)。
目标函数是最小化总成本:
min ∑_t [ a * P_g(t)^2 + b * P_g(t) + c * u_g(t) + λ_wind * P_curtail_wind(t) + λ_pv * P_curtail_pv(t) ]
其中第一项是火电的发电成本,我用了二次函数近似,在MILP里需要做分段线性化处理。第二项和第三项是弃风弃光惩罚成本,λ_wind和λ_pv是惩罚系数(单位:元/MWh)。
这里有一个非常关键的设计:火电成本用二次函数。如果不做线性化,直接丢给求解器(比如Gurobi或COPT),求解器会把它识别为二次约束规划(QP)来解,也能解,但速度慢。如果全部转成线性约束(MILP),求解速度会快很多。我在这篇文章里用了分段线性化(Piecewise Linearization),这也是实际工业调度软件里最常用的做法。
2.2 约束条件梳理
约束条件是好几个维度叠加的,我按物理对象一个一个列清楚。
系统功率平衡约束:
P_g(t) + P_wind(t) + P_pv(t) = P_load_base(t) + ∑_k P_ev_k(t)
其中P_wind(t) = P_wind_forecast(t) - P_curtail_wind(t),光伏同理。这个约束是整个模型中最重要的约束,它保证任何时刻系统发电和用电严格相等。实际电力系统调度中还会有备用约束,但为了简化我先不加。
火电机组约束:
- 出力上下限:P_g_min ≤ P_g(t) ≤ P_g_max
- 爬坡约束:-RD ≤ P_g(t) - P_g(t-1) ≤ RU,RD和RU分别表示向下/向上爬坡速率。
- 最小启停时间约束:这里先不加,加了这个会让模型规模膨胀很多,而且对于1小时步长的日前调度来说,机组组合问题(UC)的最小启停时间约束虽然重要,但对结果的影响在单机场景下没那么明显。
电动汽车集群充电约束:
- 充电功率上限:0 ≤ P_ev_k(t) ≤ P_ev_k_max * u_soc_k(t),其中P_ev_k_max是第k类EV集群的最大总充电功率。
- 日充电需求约束:∑_t P_ev_k(t) * Δt = E_k_demand,即一天下来每类EV必须充入指定的电量。
- 电池SOC约束:EV集群的SOC不直接出现在模型中,而是用"充电需求总量+功率上限"来等效描述。如果想精细化建模,需要加入SOC状态变量:SOC_k(t+1) = SOC_k(t) + η * P_ev_k(t) * Δt / B_k,并限制SOC_k_min ≤ SOC_k(t) ≤ SOC_k_max。
这里我掉过一次坑:一开始只加了"充电功率上限"和"总充电量需求",没加SOC约束,结果模型给出了一个非常不合理的充电曲线——部分EV在十几个小时内以极低功率涓流充电,虽然经济上最优,但实际电池管理系统根本不允许这种操作。所以后来我加上了SOC的逐时段递推约束。完整约束代码如下。
可再生能源出力上限约束:
0 ≤ P_wind(t) ≤ P_wind_forecast(t),0 ≤ P_pv(t) ≤ P_pv_forecast(t)。
实际上这个约束我直接用弃风弃光变量来等价表达:P_wind(t) = P_wind_forecast(t) - P_curtail_wind(t), 且 P_curtail_wind(t) ≥ 0,这样更简洁,也方便在目标函数里加惩罚系数。
2.3 完整可运行的Python代码实现
环境依赖很简单:pulp(求解器接口)、pandas(数据处理)、numpy(数值计算)、matplotlib(可视化)。用到的求解器我默认是CBC,PuLP自带的,装好pulp就有。如果你想用Gurobi或者COPT,把pulp.PULP_CBC_CMD换成pulp.GUROBI_CMD或pulp.CPLEX_CMD即可。完整代码如下:
python复制import pulp
impor
