ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

基于蒙特卡洛与Copula的多类型电动汽车随机优化调度

基于蒙特卡洛与Copula的多类型电动汽车随机优化调度

1. 项目背景:多类型电动汽车调度为什么需要随机优化

1.1 多类型电动汽车与分时电价调度的核心痛点

这套模型是我最近完成的一个配电网调度项目核心部分,项目标题一眼看上去很长:蒙特卡洛、Copula、模糊K-means、6个典型场景、多类型电动汽车、分时电价、随机优化,七个关键词叠在一起,但拆开以后就是一个非常经典的“场景生成 + 随机调度”解决不确定性的工程范式。我前后跑了将近一个月,把整个链路完整复现并调稳,今天就把这套东西从原理到细节一次性讲清楚,后续想往配电网、虚拟电厂、微网调度方向做的朋友,可以直接拿去填空。

先说说这个项目要解决什么问题。现在电动汽车已经不只是单一类型的“夜间慢充”了,私家车、网约车、公交车、物流车在充电功率、充电时间、电池容量、可调度能力上差异非常大。私家车通常晚上回家后长时慢充,白天大部分时间停在单位;网约车和出租车白天频繁补电,晚上也有一批需要快充;电动公交车则按照固定路线排班,充电窗口比较固定;物流车往往在午间和夜间两个低谷时段集中充电。如果调度系统只是简单地把它们当成“同一类可平移负荷”,那实际执行时根本控制不住,功率越限、电压越限、用户不满意都来了。

分时电价的作用就是给这些负荷一个价格信号,鼓励电动汽车往低谷时段走。但这里有一个隐蔽的坑:电动汽车充电负荷本身就是移动的、随机的,它的到达时间、初始SOC、需要充入的电量,都带有很强的随机性。再加上风电、光伏出力也在波动,于是整个调度模型变成了一个“随机变量叠加”的问题。这个时候如果还拿确定性优化去算,算出来的计划往往过于理想,遇到实际运行偏差就可能需要频繁调整,甚至无法执行。

所以这个项目的核心思路是:先把不确定性显式建模出来,再生成少数几个能代表全天运行情况的典型场景,最后在典型场景上做随机优化。这样得到的调度方案,不是一个“完美预测”下的最优解,而是一组在不同概率场景下整体期望代价最小的可执行计划。这个思路在工程中特别实用,尤其是电网侧考核越来越细,峰谷差、网损、上级联络线功率这些指标都会被计入成本,随机优化比确定性优化抗风险能力强得多。

1.2 风光出力随机性到底怎么影响调度结果

风电和光伏出力不能只取一个预测值,尤其是光伏在中午可能出现短时高发,而风电往往夜间更大。预测不可能完全准确,比如前一天给出某时刻风电预测功率是20 MW,实际可能只有12 MW,也可能是28 MW。这种偏差在日内会让给定调度计划失衡。

更麻烦的是,风光出力之间往往存在相关性。同一个区域里,光伏强的时段可能风速也小,阴雨天光伏弱但风机可能满发。如果蒙特卡洛抽样时把它们当作独立变量处理,采样得到的一些组合可能根本不符合实际相关结构,那么典型场景就不“典型”,后续优化的成本期望也会失真。比如本来风光互补性很强,独立抽样却可能生成“风电和光伏同时达到极值”的场景,这些场景虽然会拉高备用成本,却在现实里极少出现,最后方案就会偏保守。

因此,蒙特卡洛抽样只是第一步,还必须用Copula函数把风光相关性以及EV负荷的尾部依赖结构刻画出来。这样才能生成一组“统计上站得住脚”的原始场景。之后再通过聚类缩减,从成千上万个场景里提炼出最有代表性的6个典型场景,给随机优化使用。

1.3 场景生成到随机优化的闭环怎么设计

整个技术路线并不复杂,大概分四步:

  1. 建立随机变量模型:风电出力、光伏出力、EV充电需求都有自己的边缘分布和相关性结构。
  2. 蒙特卡洛抽样:生成足够多的原始场景,我是按5000组样本量来做的,每个样本都是全天24小时的一条随机曲线。
  3. Copula相关抽样 + 边缘分布逆变换:先用Copula生成符合相关性的标准均匀分布数,再通过逆变换得到具有真实分布特征的风、光、EV场景。
  4. 模糊K-means聚类缩减:把5000个场景聚成6类,取每个类的中心作为典型场景,同时把各类样本数占比作为该典型场景的概率权重。

最后再把这6个典型场景带入一个混合整数随机优化模型,目标里同时考虑上级电网出力成本、峰谷差惩罚费用、风光调度费用、电动汽车负荷调度费用和网损费用。为了兼顾效率和精度,决策变量分为日前阶段和日内阶段:日前先定各时段的EV充放电价格信号、可调度EV的功率计划、上级联络线计划;日内再根据典型场景的偏差做调整。这样一套流程跑下来,调度方案的鲁棒性和经济性都能落到一个可量化的目标函数上。

2. 场景生成:从大量蒙特卡洛样本到6个典型场景

2.1 随机变量建模的一些前提

场景生成不是随便抽几个数,关键是要先定义每个随机变量的物理范围。我这边主要考虑三类变量。

风电出力:风速预测误差常用正态分布近似,实际功率受风机出力曲线限制。风速误差的方差会随时间尺度变化,我习惯先对历史预测误差做统计,按不同季节分开建模。光伏出力:由于光伏存在夜间为零、午间到峰值这类强非线性,直接用正态分布容易抽出负值,所以常用正态分布加截断处理,或者先把光伏出力归一化到[0,1]再用Beta分布拟合。EV充电需求:这个变量最麻烦,因为它不是单个连续量,而是由“车辆数量、到达时间、离开时间、初始SOC、需求电量、充电功率”多个子变量共同决定。我采用的是分模块抽样:先抽车辆规模,再抽每个EV的出行参数,最后聚合得到全天的充电需求曲线。

实际项目里,我并没有把EV需求当作一条假设的正态曲线,而是用一个“单体仿真聚合”的方式:对每辆EV,抽取到达时刻、初始SOC、目标SOC和充电功率,逐辆计算充电时段,最后叠加出总负荷曲线。这样能保留不同类型EV的峰谷差异,聚类出来的典型场景也更容易解释。

2.2 蒙特卡洛抽样:样本规模与采样技巧

蒙特卡洛抽样的核心是样本数量。太少则分布形状不稳定,太多则后续聚类和优化计算量失控。按我的经验,原始场景数量取3000到5000就能获得比较稳定的概率分布特征。再往上增加,场景缩减后的典型场景不会发生质变,但计算时间明显增长。

采样时有一个必须注意的细节:如果只是用随机数生成风光曲线,很可能把时间相关性丢掉。比如光伏并不是每个时刻独立变化,上午到中午是平滑上升的,蒙特卡洛抽样应该基于“误差增量”或者“持续性强”的思想来生成,而不是每时刻独立抽一个点。我采用的方式是先抽全天24小时的风光电预测误差向量,利用协方差矩阵反映时间相关性,再用Cholesky分解生成相关的高斯样本。这样生成的曲线更接近真实波动。

另外,不要小看随机种子。同一套算法、同一组数据,如果随机种子不同,聚类结果可能略有差异。为了结果可复现,我在抽样前固定了随机种子,并且把抽样代码单独保存成文件,方便后续审查和重新生成。

2.3 Copula函数的作用:相关性不能忽略

Copula这个词初学者容易觉得高深,其实就是“把多个变量的边缘分布粘合在一起的那个连接函数”。单独蒙特卡洛抽样只能得到每个变量的边缘分布,但变量之间的相关结构需要Copula来描述。做法是先用概率积分变换把样本转成均匀分布,再选择合适类型的Copula来拟合相关结构。

常见Copula有几类:Gaussian Copula适合对称相关关系;Clayton Copula擅长刻画下尾相关性;Gumbel Copula擅长上尾相关性;Frank Copula适合弱相关的对称结构。对于风电和光伏经常出现的负相关、互补关系,我优先试用Frank Copula,因为它能较好处理中等强度下的负相关。如果数据里存在明显的极端同时性,比如阴雨大风天风大光伏弱,可能需要混合Copula或者非对称的Copula。

在项目里我做了这样一个对比:独立抽样生成场景后,优化结果的弃风率偏高、备用费用偏大;而使用Copula后,风光同步高发的虚假场景被压低了,典型场景更贴近真实,最后目标成本下降了不少。这说明相关性建模不是锦上添花,而是直接影响优化方案经济性的关键一环。

2.4 Fuzzy K-means聚类:场景缩减的实际操作

蒙特卡洛生成几千个完整日曲线之后,直接用于优化求解是不现实的。随机优化里的目标函数和约束条件都要对每个场景重复计算,场景越多计算量越大。工程上常用场景缩减,也就是把相似场景合并为少数典型场景。

这里选模糊K-means而不是普通K-means,原因是场景之间的边界并不是绝对清晰。某个场景可能既像“高风电高EV负荷”场景,又像“中等风电中等EV负荷”场景,如果硬划分进某一类,会丢失概率信息。模糊C均值(FCM)给每个场景赋予对所有簇的隶属度,最后典型场景可以是每个簇的加权中心,场景权重则由所有场景隶属度之和占样本总数的比例决定。

运行FCM的主要流程是:先初始化聚类中心,我一般直接用普通K-means跑一次,把K-means结果作为FCM的初始中心,这样能显著减少迭代次数。然后反复更新隶属度矩阵和聚类中心,直到目标函数变化小于阈值。模糊指数m通常取2.0,太大会导致隶属度过度平滑,太小又退化成硬聚类。实际操作中我试过1.5到2.5,2.0左右的结果在典型场景区分度和优化结果稳定性上最好。

2.5 为什么偏偏是6个典型场景

项目标题里写了6个典型场景,这不是拍脑袋,而是工程权衡的结果。从调度角度看,最少需要覆盖这些情况:高风电高负荷、高风电低负荷、低风电高负荷、低风电低负荷、光伏尖峰、EV晚高峰。如果场景数太少,比如只有2到3个,很多极端但概率不可忽略的情况会被平均掉,优化的计划在突发场景下不够安全。如果场景数太多,比如超过10个,随机优化的整数变量和约束数量会成倍增加,求解时间从几分钟变成几十分钟甚至几小时,而且对场景概率估计的边际精度提升很有限。

我判断是否选6个,会先跑一遍不同K值的轮廓系数和DB指数。轮廓系数反映了场景内聚度和分离度,K=6时在这类24维风光EV曲线上通常能取得一个局部最优区间。同时我也会看每个典型场景的概率权重是否分布合理,如果某个场景权重超过40%,说明聚类数偏少;如果某个场景权重低于5%,说明聚类结果被极端小簇拖着走,代表性不强。在实际项目数据上,6个场景的权重落在8%到25%之间,比较均衡。

3. 随机优化调度模型:目标函数与约束

3.1 目标函数拆解:五类成本分别怎么算

随机优化模型的目标函数是所有典型场景期望成本之和。我把它写成:

min ∑(场景概率 × 该场景下的总调度成本)

其中总调度成本包括五块。第一块是上级电网出力费用,也就是通过联络线向外部电网购电的成本,这个直接按分时购电价格乘以购电功率计算。第二块是峰谷差惩罚费用,用全日24小时联络线功率的最大值与最小值之差来构造,这个差值越大惩罚成本越高,体现电网对削峰填谷的硬性要求。第三块是风光调度费用,我这里把风电和光伏的运行维护费用、以及弃风弃光惩罚费用都算进去,如果因为系统调节能力不足导致必须削减风电或光伏出力,就产生惩罚项。第四块是电动汽车负荷调度费用,包括给参与调度的EV用户的充放电补贴、电池损耗补偿和分时电价下的购电费用。第五块是网损费用,通过潮流计算求出各支路损耗电量,乘以电价折算成钱。

这里要注意峰谷差惩罚费用不是一个线性可导的简单函数,因为它是“最大值减最小值”的整体指标。工程上可以把联络线功率序列中的最大值和最小值分别引入辅助变量,再通过一组约束把它线性化。这种做法比直接在目标里写max/min函数更友好,求解器处理起来更快。

3.2 分时电价与多类型EV的响应行为建模

分时电价是调度和负荷之间最重要的连接机制。我把一天分为峰、平、谷三个时段,EV用户看到电价后会调整充电计划。私家车倾向于在晚间低谷充电,网约车因为没法长时间等待,只能在白天平段甚至峰段少量补电;公交车由运营方统一调度,对价格信号响应较弱;物流车往往在两个低谷时段集中充电。

要把这些行为写进模型,不能只用“总充电负荷等于预测值”这么简单。我对每类EV定义一个可调度比例和响应弹性。比如私家车中有40%愿意响应分时电价,允许充电时间平移,剩余60%是固定负荷。可响应部分会按电价排序安排充电,还要设置最低电量约束,确保第二天出行前一定充满。对于支持V2G放电的EV,我还加入了放电补贴和SOC下限限制,避免为了赚补贴过度放电损害电池。

EV调度费用不仅要算用户交的电费,还要算调度机构给用户的响应补偿。这里我采用分段补偿函数:平移充电量越多、放电电量越多,单位补偿越高。因为用户让渡了自己的用电自主权,这个成本必须真实反映在目标函数里,否则模型会低估EV调度的实际难度。

3.3 关键约束条件与运行边界

随机优化的约束条件决定了场景是否可执行。最基本的功率平衡约束是:每一个时段,风光出力加上级电网购电、EV放电量,要等于常规负荷加EV充电负荷和网损之和。这个等式在每个场景每个时段都必须满足。

电压约束也很重要,我按配电网实际节点电压允许范围,控制在0.95到1.05倍额定电压之间。不过如果节点特别多,逐节点加入电压约束会让模型变大,我前期会做潮流灵敏度分析,只把电压波动最明显的几个关键节点纳入优化模型,保证计算效率。

EV自身的SOC约束是一个递推关系:当前时刻SOC等于上一时刻SOC加上充电效率乘以充电功率,再减去放电功率与行驶耗电。SOC上下限要根据电池特性和用户要求设定,放电深度一般不超过80%以下。上级联络线功率约束也不能忘,购电功率不能超过合同容量,倒送功率如果允许则也要限制在安全范围内。

3.4 求解方法与初始化技巧

这个模型本质上是带混合整数变量的随机规划问题,因为EV充电状态、可调度状态往往是0/1变量。我用的求解方式是先做线性化,把非线性的潮流损耗项和峰谷差惩罚项全部转换成线性约束,然后用成熟的混合整数线性规划求解器来解。

这里的小技巧是求解顺序。直接解全模型往往很慢,我习惯先忽略整数变量,求一个连续松弛解作为热启动,把结果作为整数变量的初始可行解。这样能大幅缩短分支定界的搜索时间。另一个技巧是场景分解:因为约束条件里只有部分变量耦合所有场景,我可以先固定日前计划变量,把每个场景的子问题单独求解,再通过拉格朗日乘子逐步协调,相当于一个简单的场景分解算法。这样即使场景数量略有增加,求解时间也不会线性暴涨。

4. 实操过程:从数据准备到结果分析

4.1 数据准备与预处理要点

我这次使用的数据都是自己构造的模拟数据,但结构严格参照典型配电网工程的历史数据格式。风电和光伏用的是24小时96个时段的出力曲线,额外叠加预测误差数据;EV数据则包含车辆类型、数量、电池容量、充电功率、到达离开时间分布。

预处理阶段最耗时的是数据清洗。原始数据里经常会出现个别时段风光出力超过装机容量这类异常点,我会先做分钟级平滑,再做归一化。归一化不是简单除以最大值,因为风电光伏的最大值都等于装机容量,直接归一化会把两条曲线的相对幅值关系扭曲。我推荐按装机容量归一化后,保留每个变量的实际容量参数,在目标函数里再乘回去。

4.2 核心算法流程:从抽样到典型场景

用Python风格的伪代码描述一下整体流程。如果你是用其他数值计算环境,思路也可以完全照搬:

# 设定随机种子,保证可复现 set_seed(42) # 1. 生成5000个原始随机场景 n_samples = 5000 wind_forcast = load_wind_data("wind.csv") # 96点预测曲线 pv_forcast = load_pv_data("pv.csv") # 96点预测曲线 ev_profile = load_ev_historical("ev.csv") # 2. 蒙特卡洛抽样 + Copula相关结构 u_wind, u_pv, u_ev = copula_sample(n_samples) # 生成符合相关性的均匀数 wind_scenes = inverse_cdf(wind_forcast, u_wind) # 边缘分布逆变换 pv_scenes = inverse_cdf(pv_forcast, u_pv) ev_scenes = sample_ev_scenes(ev_profile, u_ev) # 3. 把风、光、EV曲线拼接成样本矩阵 raw_scenes = concatenate([wind_scenes, pv_scenes, ev_scenes], axis=1) # 4. 标准化后做模糊K-means聚类 standardized = zscore(raw_scenes) centers, membership = fuzzy_cmeans(standardized, k=6, m=2.0) # 5. 计算每个典型场景概率权重 weights = sum(membership, axis=0) / n_samples # 6. 输出6个典型场景及其权重,用于随机优化 save_scenarios(centers, weights, "typical_scenes.csv")

如果是在最优化的环境里,下一步就是把centers作为典型场景代入优化模型。模型读取典型场景的风光出力、EV负荷,在目标函数中乘以对应权重,然后求解。

4.3 优化模型中的关键参数怎么调

参数调优是这类项目最耗时的一环。我列几个直接影响结果的参数供参考。

分时电价时段划分:一般按峰、平、谷三段。峰段可以是8点到11点、18点到22点,平段是11点到18点、22点到24点,谷段是0点到8点。价格差距不要太小,否则EV响应意愿不足,但差距过大又会让负荷过于集中到低谷,造成新的局部峰。我倾向峰谷价比控制在3比1左右。

EV可调度比例:我设私家车40%、网约车20%、公交车0%、物流车30%作为初始值。你可以根据项目里实际合同的签约比例调整。这个参数特别影响调度费用和峰谷差结果。

峰谷差惩罚系数:这个系数没有绝对大小,需要根据电网考核力度标定。我建议先用一段历史数据做敏感性分析,观察惩罚系数从0.5倍到2倍变化时联络线功率曲线的峰谷差变化,选择一个拐点区域作为最终设置。

网损折算电价:网损费用按分时电价折算,不同时段用不同电价,峰时段网损费用更高,这样模型会更倾向于在峰时段减少重载线路上的输送功率。

4.4 结果分析与典型场景可视化

结果分析我一般分成三个层面。第一层,看6个典型场景曲线本身是否合理。比如看场景1是不是“高风电低光伏晚间EV高峰”,场景2是不是“低风电高光伏白天空调负荷较高”,如果某个场景曲线违背物理常识,大概率是抽样或聚类环节出了问题。

第二层,看随机优化解对应的联络线功率曲线。好的结果应该是峰时段从上级电网购电明显减少,谷时段适当增加充电负荷,峰谷差比不调度时缩小15%以上。这个数字不是固定的,但至少能看出削峰填谷效果。如果优化后峰谷差反而变大,就要检查峰谷差惩罚系数是不是太小,或者EV充电负荷被错误引导到了峰时段。

第三层,看费用构成占比。我读结果时最关注峰谷差惩罚费用和弃风惩罚费用的比例。如果峰谷差惩罚费用占比过高,说明价格信号引导不足;如果弃风惩罚费用很高,说明风光消纳压力大,EV充电应该更多往高风光时段安排。

5. 常见问题与避坑实录

5.1 场景缩减后优化结果不稳定

我在项目初期遇到一个典型现象:同样一组数据,聚类出来的6个典型场景每次跑都不一样,最后调度成本能差出6%到8%。检查后发现是模糊K-means对初始聚类中心太敏感,随机初始化不同,收敛到不同局部最优。

解决方案是在聚类前先用普通K-means跑20次,选目标函数最小的那组作为FCM的初始中心。同时固定随机种子。之后再重复实验,典型场景几乎一致。如果你的数据量很小,聚类结果可能天生不稳定,这时建议把样本量加到3000以上,或者延长蒙特卡洛抽样的时间序列长度。

5.2 Copula拟合效果不好怎么办

Copula不是只要选一个函数就完事。我踩过的坑是直接用Gaussian Copula处理风电和光伏的负相关,结果模拟出的联合分布尾部不足,极端低风电叠加高负荷的场景被低估。后来改用Frank Copula并且加入拟合优度检验,情况才改善。

拟合检验我推荐两步。第一步,把原始样本转换到均匀分布空间,计算Empirical Copula作为参照;第二步,比较候选Copula和Empirical Copula的欧氏距离,或计算AIC、BIC,选距离最小的那种。如果几种Copula差距不大,优先选参数少的,避免过拟合。

另外要注意Copula拟合必须基于“去趋势”后的残差。直接拿包含日周期性的原始风光数据去拟合相关性,会把昼夜规律误当成变量相关性,导致Copula参数失真。我处理方式是把每日典型出力曲线先作为确定性基准,只对预测误差部分做Copula拟合。

5.3 求解器收敛慢或者目标函数震荡

随机优化模型规模一大,常见问题就是求解效率低。先不要急着加更多的精细化约束。我建议先跑一个确定性优化,也就是只用6个典型场景中的1个,验证模型约束本身没有互相冲突、目标函数数值合理。然后再扩展到全部6个场景。

如果求解器在目标值附近来回震荡,大概率是约束不够紧或者整数变量出现对称性。比如多辆同类EV在不同时段充电,如果不加对称性破坏约束,求解器会在等价解之间反复切换。我会给每类EV加一个按用户编号排序的充电开始时刻约束,这是一种常见的对称性破缺技巧。

还有一个小技巧是把时间尺度从96点聚合到24个时段,先快速看整体趋势。确认方案经济性合理后再加密到96点,这样效率能提高很多。96点模型是最终项目需要的精度,但调试阶段用24点完全足够发现问题。

5.4 分时电价与EV行为耦合的隐性坑

分时电价虽然能引导EV充电到低谷,但如果所有用户都响应,低谷负荷可能被抬得很高,在原本的负荷低谷处又形成一个局部高峰。这是“负荷反弹”现象。我在仿真中发现,如果谷段电价设置过低,EV充电负荷会从原来的22点集中迁到0点到2点,联络线功率从原本的平滑曲线变成两头尖的形状,峰谷差指标反而变差。

要避免这个问题,不能在建模时假设EV响应是无限制的。我给每个时段设置了最大充电参与率,比如单个低谷时段最多允许60%的可调度EV同时充电,并且给EV充电设置爬坡速率限制,让它们分批进入。这样价格引导和系统安全之间才能平衡。

另外,目标函数中的EV调度费用不能只看总电费。V2G放电如果补贴不足,用户不参与;补贴过高,系统可能会为了降低峰谷差而不合理地让EV频繁放电,导致电池损耗成本被低估。我在模型里加入放电深度与电池寿命折损费用的映射关系,把这些成本也计入EV调度费用,这样优化出来的方案才具有实际可操作性。

我个人在实际操作中的体会是:这套“蒙特卡洛 + Copula + 模糊聚类 + 随机优化”的框架,真正的价值不在某一个算法有多高级,而在于它把一个高度不确定的调度问题拆成了两个工程上可控的阶段。场景生成阶段做好统计代表性,优化阶段做好经济性。只要你舍得花时间在数据清洗和场景合理性校验上,后面的优化求解其实比想象中顺利。最后再分享一个小技巧:每次调整模型参数后,把典型场景和成本构成一起输出到同一张表里,保存成带时间戳的版本。我靠这个习惯回看了十几组实验,很快就锁定了最优参数区间,希望也能帮你少走弯路。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进