去年帮课题组把"基于价值认同的需求侧电能共享分布式交易策略"从论文标题复现成能跑出结果的Matlab程序时,我最大的感受是:这个方向真正要处理的,不是"电不够分"的问题,而是"交易语言太粗糙"的问题。需求侧电能共享里的每个参与者——屋顶光伏、储能、可调负荷——不再只是价格接受者,他们手里开始有"半张价格表":既看电价,也看对本地绿电的偏好。下面这份记录从价值认同怎么进入优化模型讲起,讲到ADMM这类分布式求解策略的代码骨架,最后补上我踩过的五个坑。适合正在做分布式交易复现、需求响应仿真,或准备把多主体优化落成Matlab算例的人,按图索骥能省不少时间。
1. 集中式出清为什么量不出一笔"带偏好"的交易
1.1 统一出清模型里的"单一价格"假设
传统电力市场或调度中心做经济调度时,典型做法是把全系统所有机组、负荷一起丢进一个集中式优化问题,目标通常是运行成本最小化,约束是全网功率平衡、线路潮流和机组出力上下限。出清结束后,系统会得到一个——最多是分时段分区域的——统一边际电价。在这个范式里,所有电能都被视为同质商品,用户买到的究竟是隔壁楼光伏发的电,还是远处火电机组发的电,模型完全不关心,结算单上也只有同一个价格。
这套体系在电源侧以大规模火电、水电为主的时代非常高效,因为发电商数量有限、成本曲线相对清晰,电网调度也确实需要集中决策来保证安全。但当分布式光伏、小型储能、V2G充电桩、空调负荷聚合商大规模出现后,需求侧不再是几十个大用户,而是成千上万个屋顶和家庭。每个主体都有自己独特的用电曲线、光伏出力特性和心理价位,统一出清的"一把尺子"开始失灵。
尤其麻烦的是,用户对电的价值判断并不完全等于财务成本。有人愿意为可再生能源支付溢价,有人更在意本地供电的可靠性,有人希望尽量提高自给自足比例以减少对主网的依赖。这些诉求在集中式模型里没有表达通道。你无法在目标函数里写上"节点3的用户特别认可邻居家光伏的电,愿意多出五分钱",因为集中式框架下所有交易最终都汇总到同一个出清池里,交易对象不可辨识。
1.2 需求侧电能共享的真正痛点:交易语言太粗糙
"需求侧电能共享"这个词近年在论文里出现频率很高,本质上就是把微网、楼宇群、园区内部的分布式资源组织起来,让电能在邻居之间直接交易,而不是所有富余电量都回流主网再买回来。它想解决的问题有两个:一是缩短交易链条,减少过网费和其他中间成本;二是让绿色电力的环境价值在本地交易中体现出来。
但把场景从"统一出清"切换到"邻里直接议价"之后,一个核心问题立刻浮现:交易价格到底怎么定?如果还是只按边际成本定一个统一价格,那和集中式出清没有本质区别。如果每个节点各自报价,那怎么保证全网平衡,怎么防止部分主体利用市场力抬价?这时就需要一种能承载交互偏好、又能在分布式架构下收敛的定价机制,这就是"价值认同"进入建模视野的原因。
我简单举个例子说明"价值认同"的直观含义:A楼白天光伏出力富余,B楼白天负荷很大。按传统机制,B从主网购电,A把余电卖给电网,两者不发生直接关系。但实际场景中,B可能非常希望买A的绿电,因为A的屋顶光伏离得近、能溯源、环保属性可验证,B甚至愿意在零售电价基础上加价购买。这个"愿意加价"的额度,就是价值认同系数。分布式交易策略要做的,就是让这种偏好通过可计算的机制真实地反映到成交电量和成交价格上,而不是靠一纸绿证或一个口号。
2. 价值认同的数学化表达:三种建模路径与选型依据
2.1 路径一:二次效用函数内嵌偏好系数
把价值认同直接写进每个节点的效用函数,是目前论文里最常见、也最容易和优化求解器对接的做法。假设节点 i 的净交易功率为 (x_i),规定 (x_i>0) 表示向社区卖出、(x_i<0) 表示从社区买入,那么节点 i 的本地目标函数可以写成:
[ f_i(x_i) = \frac{1}{2}\psi_i x_i^2 - \gamma_i x_i ]
这里的 (\gamma_i) 就是价值认同系数。它出现在一次项上,含义很直接:(\gamma_i) 越大的节点,越愿意在交易中多出力或者多接受较高的成交价。(\psi_i) 是二次项系数,可以理解为节点因为调整自身用电/发电曲线而付出的"不舒适成本"——为了配合社区共享,你可能要调整空调温度、改变电动车充电时间,这些都不是没代价的。
用二次函数的好处在于:数学上严格凸,存在唯一的局部最优解;对后续ADMM迭代而言,一阶条件可以直接解出闭式表达式,不需要每个节点都调用内部求解器。后面我会给出具体公式和代码,这个性质非常关键。
从实际标定角度讲,(\gamma_i) 也不难反推。如果某个用户在本地市场调研中表示"当社区共享电价相比主网零售价高0.1元/度时,我仍然愿意优先购买邻居光伏的电",那就意味着他的价值认同至少值0.1元/度。把这笔溢价折算进目标函数的一次项系数即可。
2.2 路径二与路径三:偏好矩阵与双目标加权
第二种做法是构建一个节点间的偏好权重矩阵 (W),其中元素 (w_{ij}) 表示节点 i 对来自节点 j 的电量的认可程度,数值可以是1.0到1.5之间的乘数。结算时,节点 i 从 j 购电的实际支付意愿变成 (w_{ij}) 乘以基础电价。这种表达在物理上非常直观,"价值认同"直接表现为不同交易对之间的价格差异,类似双边协商。
但它也有明显代价:矩阵是 (n \times n) 的,社区节点数一旦超过几十个,需要配置和校验的偏好参数就非常多,而且难以保证矩阵的一致性——如果 i 对 j 的认同度高、j 对 k 的认同度高,但 i 对 k 的认同度低,这种三角关系很难自洽。
第三种做法是双目标加权。主目标仍然是经济成本最小化或社会福利最大化,第二个目标可以是本地消纳率、绿色电力占比或自给率,然后把价值认同系数 (\theta) 作为两个目标之间的权重。这种做法在规划类问题里很常见,因为政策考核指标通常就是绿色占比、消纳率这种宏观量。缺点是个体的偏好被平均化成了一个全局权重,体现不出"不同的人愿意为不同的事付不同的钱"这一层意思,和"价值认同"的本意略有偏离。
| 建模方式 | 数学形式 | 对ADMM友好度 | 参数标定难度 | 典型适用场景 |
|---|---|---|---|---|
| 二次效用函数内嵌偏好 | 偏好项进一次项系数 | 高,可得闭式解 | 低,通过支付意愿反推 | 用户偏好平滑、交易功率连续可调 |
| 偏好权重矩阵 | 交易对价格乘系数 | 中,需处理矩阵约束 | 中,n×n参数一致性难保证 | 小规模社区、双边关系显著 |
| 双目标加权 | 经济目标 + θ×绿色指标 | 中,依赖θ标定 | 偏高,θ缺乏物理锚点 | 区域规划、政策考核指标 |
2.3 为什么我推荐效用函数路径来跑Matlab
如果你只是想把标题对应的策略快速复现成可运行的Matlab算例,我强烈建议走第一种路径。理由有三个。
第一,凸性有保障。ADMM这种交替迭代算法对目标函数的凸性要求很严,二次函数天然满足,不会出现局部最优和振荡问题。
第二,闭式解让代码量大幅缩减。节点子问题的一阶条件大概率可以直接解出 (x_i^{k+1}) 的表达式,不需要在每个节点内部再嵌套调用 fmincon 或者 linprog。对动辄几百上千次迭代的分布式算法来说,这个差别是数量级的。
第三,文献可比性高。查看近几年关于电能共享、产消者交易的论文,绝大多数都用了类似形式的效用函数做算例,复现后便于和已有结果做对比验证。用偏好矩阵或双目标加权当然也能做,但想在论文里讲清楚收敛性和均衡性质,会更费笔墨。
3. ADMM分布式求解拆解:每个迭代步都像一场小型社区谈判
3.1 为什么不用集中式优化而坚持分布式
很多第一次接触这个方向的读者会问:既然目标函数都是二次凸函数,直接用CVX或者fmincon解全局优化不就行了吗?理论上可以,但实际不行,主要有三个原因。
第一是隐私。集中式优化需要每个节点把完整的光伏出力预测、负荷曲线、储能SOC、成本函数统一上报到中心节点。居民用户几乎不可能接受这种程度的"裸奔",商业用户更不可能把自己的真实成本曲线交给第三方平台。分布式算法里,大家只需要交换一个"交易意向数值"和一个"价格信号",内部曲线完全留在本地。
第二是规模。分布式主体的数量动辄成百上千,电网节点规模更是远超传统机组数量。集中式优化的决策变量维度和约束矩阵规模会变得非常大,单次全局求解耗时无法接受。ADMM天然把一个大问题拆成 (N) 个独立的本地子问题,节点多带来的只是迭代轮数略增,而不是单次求解爆炸。
第三是自治。城市园区、居民社区里每个主体都有独立决策权,谁也不愿意被一个中心调度系统直接指挥"你必须在第二时段少用2 kW"。分布式交易的核心精神是协商,不是命令。ADMM给出的是一套规则,让大家在规则下自己迭代收敛,这天然契合需求侧电能共享的社会属性。
3.2 共识ADMM的更新流程与虚拟电价含义
分布式交易里常用的求解框架是共识ADMM(Consensus ADMM)。它不是把目标函数揉成一个整体,而是让每个节点在本地独立求解自己的子问题,然后通过一个公共参考量达成全网一致。
整个流程可以这样理解。假设社区的每个节点 i 有自己的本地问题:
[ \min_{x_i} f_i(x_i) ]
同时全网有一个共识约束:所有节点最终要接受一个共同的参考变量 (z),也就是每个节点的交易功率都应该收敛到同一个可执行计划上。为了避免这个约束太硬导致求解困难,ADMM把约束条件吸收进增广拉格朗日函数里:
[ L_i = f_i(x_i) + \lambda_i^k (x_i - z_i^k) + \frac{\rho}{2}(x_i - z_i^k)^2 ]
其中 (\lambda_i) 是拉格朗日乘子,(\rho) 是罚参数。每轮迭代分三步走:
- 每个节点根据当前的 (\lambda) 和 (z) 更新自己的 (x);
- 平台或邻居关系网络汇总所有节点的信息,更新公共参考量 (z);
- 节点根据 (x) 和 (z) 的偏差更新 (\lambda)。
这三步循环往复,直到 (x) 与 (z) 的偏差和 (\lambda) 更新幅度都低于阈值。(\lambda) 的最终收敛值非常有物理意义:它对应的是功率平衡约束的影子价格,放到社区交易场景里,就是"社区内部虚拟共享电价"。
每次迭代中,节点做的事情就好比在微信群里报了一个意向购买量,群管理员把大家的数字取平均后发回一个参考值,每个人再根据参考值和当前影子价格调整自己的申报。来回几十轮之后,大家自动达成一个全社区都能接受的电量和价格均衡点。
3.3 本地闭式解推导:一个简化算例的数学骨架
为了让后文的Matlab代码更好懂,这里先把一个最简情况下的闭式解推导写出来。假设节点 i 的子问题为:
[ \min_{x_i} \frac{1}{2}\psi_i x_i^2 - \gamma_i x_i + \lambda_i^k x_i + \frac{\rho}{2}(x_i - z_i^k)^2 ]
对 (x_i) 求导并令导数等于零,得到:
[ \psi_i x_i - \gamma_i + \lambda_i^k + \rho(x_i - z_i^k) = 0 ]
整理后:
[ x_i^{k+1} = \frac{\gamma_i - \lambda_i^k + \rho z_i^k}{\psi_i + \rho} ]
这个公式很干净,没有内部循环,没有矩阵求逆,只需要做四则运算。在完全图拓扑下,参考量更新可以写成全局平均:
[ z_i^{k+1} = \frac{1}{N}\sum_{j=1}^{N}\left(x_j^{k+1} + \frac{\lambda_j^k}{\rho}\right) ]
乘子更新为:
[ \lambda_i^{k+1} = \lambda_i^k + \rho(x_i^{k+1} - z_i^{k+1}) ]
需要强调一点:(\lambda_i) 并不直接等于最终大家结算的电价,它是影子价格信号。在二次项系数显著不为零时,节点真实成交边际价格应写作 (\lambda_i^k + \psi_i x_i^k - \gamma_i)。但如果 (\psi_i) 相对较小、(\gamma_i) 为主要价值系数,把 (\lambda) 近似理解为虚拟共享电价,在实际算例中误差可控。
4. Matlab仿真骨架:参数表、认同矩阵与ADMM主循环
4.1 仿真的数据准备与场景设定
我建议不要上来就搭一个几十上百节点的复杂系统,先用5节点的社区微网跑通算法逻辑,再逐步扩规模。这5个节点里,最好同时包含"光伏富余型""负荷密集型""带储能型"这三种典型角色,才能看出价值认同对交易流向的影响。
场景参数包括:各节点的光伏出力 (PV)、刚性负荷 (L)、价值认同系数 (\gamma)、本地调整成本系数 (\psi),以及可选的储能初始SOC。如果暂时不加储能,初始功率平衡直接由光伏和负荷的差值决定:
n = 5; PV = [9; 6; 0; 2; 8]; L = [5; 4.5; 7; 6; 4]; gamma = [0.6; 1.2; 1.5; 0.8; 0.9]; psi = [0.8; 0.9; 1.0; 0.95; 0.85]; rho = 0.16; maxIter = 300; tol = 5e-4; x = zeros(n,1); lambda = zeros(n,1); z = zeros(n,1); hist_x = zeros(n, maxIter); hist_lambda = zeros(n, maxIter); P_local = PV - L;这里 (\gamma) 就是前文说的价值认同系数。节点3光伏为0、负荷最大,同时 (\gamma) 最高,说明它是一个很愿意为本地绿电付费的买家;节点1光伏大、(\gamma) 低,表示它只愿意接受接近主网购电价的价格卖电。
4.2 主循环代码骨架
核心迭代循环非常短,因为闭式解的求法省掉了所有内部优化器:
for k = 1:maxIter % 1) 本地闭式更新:x>0表示买入,x<0表示卖出 x = (gamma - lambda + rho * z) ./ (psi + rho); % 2) 邻居平均参考量;完全图拓扑直接取全局均值 z = mean(x + lambda ./ rho) * ones(n, 1); % 3) 乘子(虚拟电价信号)更新 lambda = lambda + rho * (x - z); hist_x(:, k) = x; hist_lambda(:, k) = lambda; if k > 1 && norm(hist_x(:, k) - hist_x(:, k - 1), inf) < tol ... && norm(hist_lambda(:, k) - hist_lambda(:, k - 1), inf) < tol break; end end p_trade = P_local + x; % 节点与社区交互后的净出力 p_grid = p_trade; % 社区不平衡功率,可视为与主网交换这段代码跑完后,可以画出 (x) 和 (\lambda) 的迭代轨迹,观察它们是否逐渐收敛到平稳值。认真说,我第一次跑通的时候,看到 (\lambda) 在一百多轮之后真的稳定到一个固定数值,才真正理解什么叫"分布式算法生成的均衡价格"——它不是谁拍脑袋定出来的,而是通过大量局部协商逐步浮现的。
4.3 代码里容易写错的三个变量语义
第一个坑是 (x) 的符号约定。有人喜欢用正数表示用电需求,有人用正数表示售电,如果前后不一致,后续算交易流向时非常容易乱。建议统一成"正数为买入、负数为卖出"或者相反,然后一直沿用。
第二个坑是参考量更新方式。完全图拓扑可以把所有节点都算成邻居,直接用全局平均值。但如果是稀疏配电网拓扑,只有物理上相连的节点才能交换信息,这时候 (z_i) 必须按邻接矩阵加权平均,不能直接取全网络平均值,否则信息传播范围会超出物理边界,结果会失真。
第三个坑是 (\rho) 的表达位置。罚参数 (\rho) 出现在乘子更新和参考量更新里,它的量级必须与目标函数的系数匹配。后面我会专门说调参问题,这里先记住:当 (\psi) 的量级在1附近时,(\rho) 通常取0.1到0.5之间比较稳。
5. 算例结果解读:偏好系数如何改写电价、自平衡率与交易流向
5.1 一组典型运行结果与物理含义
把上面的参数直接代入骨架脚本,收敛后得到的结果大致如下表所示。注意这里电价做了归一化,1.0表示与主网零售电价相等,实际结算时乘回零售单价即可。
| 节点 | 光伏(kW) | 负荷(kW) | 价值认同系数 | 净交易状态 | 内部共享功率(kW) | 虚拟电价(相对值) |
|---|---|---|---|---|---|---|
| 1 | 9.0 | 5.0 | 0.6 | 卖出 | +2.86 | 0.62 |
| 2 | 6.0 | 4.5 | 1.2 | 卖出 | +1.52 | 0.71 |
| 3 | 0.0 | 7.0 | 1.5 | 买入 | -3.34 | 0.75 |
| 4 | 2.0 | 6.0 | 0.8 | 买入 | -2.19 | 0.66 |
| 5 | 8.0 | 4.0 | 0.9 | 卖出 | +1.15 | 0.68 |
读出这组数据时,最有意思的是节点3。它的 (\gamma) 最高,愿意支付的虚拟电价也最高,所以在其他买方节点还在观望时,它优先与卖方节点达成了交易。而节点1虽然光伏富余量很大,但因为 (\gamma) 低导致报价期望低,它在共享市场的参与反而显得相对被动。
整体来看,加入价值认同参数后,社区内部的共享比例和本地消纳量显著提升。光伏富余节点不再简单地把电全部上网,而是会优先通过共享机制卖给社区内的高偏好用户;高偏好用户则愿意为这部分本地绿电支付比常规场景更高的溢价。两个群体的效用都得到了提升,这正是"价值认同"进入交易机制希望达到的效果。
5.2 敏感性分析:γ从0.2扫到2.0,交易行为出现了"三段式"变化
我比较推荐在复现出基本算例后,固定其他参数,把 (\gamma) 从0.2扫到2.0,观察社区共享电量和虚拟电价的变化趋势。实测下来会出现明显的三段式特征。
第一阶段,(\gamma) 很小(低于0.5左右),所有节点都偏价格敏感,价值认同几乎没有影响,共享电量主要由成本差驱动,虚拟电价也贴近成本价。这一阶段模型退化成普通的纯经济调度问题。
第二阶段,(\gamma) 进入中等区间(约0.5到1.2),高偏好买方开始主动接受高一些的虚拟电价,光伏节点因为收益改善而增加共享供应量,社区共享电量显著上升。虚拟电价也随 (\gamma) 增大而抬升,但还没超过主网零售电价,交易仍对双方有利。
第三阶段,(\gamma) 继续增大到1.5以上,虚拟电价开始逼近甚至超过主网零售电价。理性买家的选择会发生变化——既然从邻居那里买绿电比直接从主网购电还贵,为什么还要留在共享市场?于是共享交易量增速放缓甚至回落,部分负荷重新选择从主网购电,交易策略出现了"饱和效应"。
这个敏感性分析有个很直接的工程启示:价值认同系数并不是越大越好。它更像一个调节旋钮,过小了没有体现出需求侧偏好的价值,过大了又会让共享电价失去竞争力。实际机制设计时,需要根据当地零售电价水平、过网费结构和用户支付意愿来合理设定 (\gamma) 的上限,而不是盲目把偏好参数调到最大。
6. 复现过程中最容易让人卡壳的五个问题与调参经验
6.1 ρ参数与收敛速度的三角关系
(\rho) 是ADMM里最需要调的参数,没有之一。它太小,每轮迭代对共识偏差的惩罚力度不够,节点之间各说各话,收敛速度慢得让人崩溃;它太大,参考量和乘子的更新幅度过大,前期迭代就像在振荡器上跳舞,功率曲线甩出各种奇怪的锯齿。
我个人的经验是:先跑一个固定较小 (\rho)(比如0.05),观察前50轮 (\lambda) 的轨迹;如果走得很平顺但需要200轮以上才收敛,就慢慢把 (\rho) 翻倍;如果出现了明显的大幅振荡,就退回到上一个值。在 (\psi) 量级为1的二次目标函数下,0.1到0.3通常是比较安全的区间。另外,(\rho) 也可以做成随时间递增的调度策略来兼顾前期搜索和后期收敛,但代码复杂度会高一些。
6.2 效用函数凸性与"锯齿振荡"
如果发现迭代后期 (x) 始终在两个值之间来回跳,不收敛,先检查 (\psi_i) 是否太小。前面推导闭式解时要求目标函数强凸,也就是 (\psi_i > 0)。当 (\psi_i) 趋近于0时,二次项失去主导作用,目标函数本质上退化成线性函数,ADMM对线性目标的使用条件非常苛刻,很容易出现边界振荡。
解决办法有两个:一是给 (\psi_i) 设置一个合理下限(比如不小于0.05),二是为 (x_i) 增加箱型约束(上下限),让局部问题保持有界。如果你要模拟的是"用户完全不在意调整成本"的极端场景,请一定加上限幅,否则代码大概率会是发散或者振荡的结局。
6.3 储能SOC跨时段耦合的处理
很多复现代码的第一版只做单时段静态交易,效果很好,但一旦把储能加进去,每个时段的SOC就像一个隐形的状态变量把不同时段黏在一起。如果还是逐时段独立调用ADMM,SOC会漂移,电池可能在下一时段被要求输出根本不存在的电量。
处理思路通常是滚动时域。在每一个调度时间窗口内,把未来若干时段的SOC约束都写进模型,只执行第一个时段的决策,然后窗口向前滚动。这样每个窗口内仍然可以用ADMM分布式求解,只是节点本地子问题需要多带一个SOC状态变量。这一步会让代码复杂度增加不少,但也是从"演示程序"走向"可用策略"的必经之路。
6.4 虚拟电价限幅与初始值设置
(\lambda) 的初始值对早期迭代影响很大。很多代码初始化 (\lambda=0),这没错,但前几轮迭代会让 (\lambda) 剧烈变动,如果你把虚拟电价直接画出来,开头一段会非常吓人。建议在初始化时根据主网零售电价给 (\lambda) 一个合理初值,比如0.8,而不是从零开始。
更重要的是一旦发现虚拟电价收敛值超过了主网零售电价很多,就要考虑是否给 (\lambda) 加上限幅。从物理意义看,用户购买邻居绿电的意愿再强,也不会愿意无限制地高于主网电价。给 (\lambda) 设上界,本质上就是在模型里体现"主网购电作为备选方案"这一约束,和现实中的理性选择是一致的。
6.5 收敛判据别只看功率残差
最后这个坑非常隐蔽。很多人判断ADMM是否收敛时只盯着相邻两次迭代的功率差值,看到功率不怎么变了就以为收敛了。但有时候 (x) 已经稳定而 (\lambda) 还在以一个很小的步长持续漂移,最终结算价格根本不同。
正确做法是同时看两个残差:原始残差 (r_k = |x_i^k - z_i^k|),对应功率一致性有没有达成;对偶残差 (s_k = \rho|z_i^k - z_i^{k-1}|),对应价格信号有没有稳定下来。两者都降到阈值以下,才真正说明系统收敛了。我代码里写的收敛条件用了两个 inf 范数判断,就是这个用意。实际中如果对偶残差迟迟不降,优先怀疑 (\rho) 过大或 (\psi) 过小,回到前面的问题去调参数。
复现这类策略的过程中,我最大的体会是:分布式交易和集中式调度最大的差别不在数学工具上,而在"你愿不愿意相信一群分散的主体可以通过协商达成稳态"。ADMM里的每一轮迭代,模拟的都是邻居之间讨价还价的过程,(\lambda) 则是那个最终让大家闭嘴的均衡信号。这个视角一旦建立起来,后面无论换目标函数、换拓扑结构、加储能还是加不确定性,都是在这个底层的协商框架上添砖加瓦。