MATLAB考虑源荷不确定性的电力系统优化

MATLAB考虑源荷不确定性的电力系统优化

一、源荷不确定性建模方法综述

在电力系统优化中,源(可再生能源出力)和荷(负荷需求)的不确定性是核心挑战。以下是主要建模方法:

方法 原理 优点 缺点 适用场景
确定性优化 使用预测均值 简单,计算快 忽略不确定性,结果保守 不确定性小,要求不高的场景
随机规划 基于概率分布,多场景 考虑多种可能,期望最优 场景数多时计算量大 可再生能源高渗透
鲁棒优化 最坏情况下最优 保证可行,抗干扰强 结果保守 安全要求高的场景
机会约束规划 允许小概率违反约束 平衡经济性与可靠性 求解复杂 可接受一定风险的场景
分布鲁棒优化 结合随机和鲁棒 平衡保守性和经济性 建模求解复杂 不确定性信息有限

本文将重点介绍基于场景的随机规划鲁棒优化两种主流方法。


二、场景生成与削减技术

2.1 不确定性源建模

%% 1. 风电、光伏、负荷不确定性建模
clear; clc; close all;

%% 1.1 参数设置
n_hours = 24;               % 24小时调度
n_scenarios = 100;          % 初始场景数
n_clusters = 10;            % 削减后场景数
time = 1:n_hours;

%% 1.2 风电出力不确定性建模
% 均值曲线 (典型日)
wind_mean = 50 + 20 * sin(2*pi*(time-6)/24);  % MW

% 标准差 (白天波动大)
wind_std = 0.2 * wind_mean;  % 20%的波动

% 生成场景 (正态分布)
wind_scenarios = zeros(n_scenarios, n_hours);
for h = 1:n_hours
    wind_scenarios(:, h) = wind_mean(h) + wind_std(h) * randn(n_scenarios, 1);
    % 截断在[0, 最大容量]之间
    wind_scenarios(:, h) = max(0, min(wind_scenarios(:, h), 100));
end

%% 1.3 光伏出力不确定性建模
pv_mean = 60 * sin(pi*(time-6)/12);  % 白天出力
pv_mean(pv_mean < 0) = 0;

pv_std = 0.15 * pv_mean;
pv_std(isnan(pv_std)) = 0.1;  % 处理夜晚0出力的标准差

pv_scenarios = zeros(n_scenarios, n_hours);
for h = 1:n_hours
    if pv_mean(h) > 0
        pv_scenarios(:, h) = pv_mean(h) + pv_std(h) * randn(n_scenarios, 1);
        pv_scenarios(:, h) = max(0, min(pv_scenarios(:, h), 80));
    end
end

%% 1.4 负荷不确定性建模
load_mean = 150 + 50 * sin(2*pi*(time-14)/24);  % MW
load_std = 0.1 * load_mean;

load_scenarios = zeros(n_scenarios, n_hours);
for h = 1:n_hours
    load_scenarios(:, h) = load_mean(h) + load_std(h) * randn(n_scenarios, 1);
    load_scenarios(:, h) = max(100, load_scenarios(:, h));  % 最小负荷
end

%% 1.5 场景概率 (初始等概率)
prob_init = ones(n_scenarios, 1) / n_scenarios;

%% 2. 场景削减 (基于K-means聚类)
function [scenarios_red, prob_red, idx] = scenario_reduction(original_scenarios, original_prob, n_clusters)
    % 输入:
    %   original_scenarios: n_scenarios × n_hours 矩阵
    %   original_prob: n_scenarios × 1 概率向量
    %   n_clusters: 削减后场景数
    % 输出:
    %   scenarios_red: n_clusters × n_hours 削减后场景
    %   prob_red: n_clusters × 1 削减后概率
    %   idx: 每个原始场景所属的聚类
    
    [n_scenarios, n_hours] = size(original_scenarios);
    
    % 使用K-means聚类
    [idx, C] = kmeans(original_scenarios, n_clusters, ...
        'MaxIter', 1000, 'Replicates', 10);
    
    scenarios_red = C;
    
    % 计算每个聚类的概率
    prob_red = zeros(n_clusters, 1);
    for k = 1:n_clusters
        cluster_members = (idx == k);
        prob_red(k) = sum(original_prob(cluster_members));
    end
    
    % 可视化聚类结果
    figure;
    for k = 1:min(4, n_clusters)
        subplot(2,2,k);
        cluster_data = original_scenarios(idx == k, :);
        plot(time, cluster_data', 'Color', [0.8, 0.8, 0.8], 'LineWidth', 0.5);
        hold on;
        plot(time, scenarios_red(k, :), 'r-', 'LineWidth', 2);
        title(sprintf('Cluster %d (Prob=%.3f)', k, prob_red(k)));
        xlabel('Hour');
        ylabel('Value');
        grid on;
    end
end

% 应用场景削减
[wind_red, prob_wind, idx_wind] = scenario_reduction(wind_scenarios, prob_init, n_clusters);
[pv_red, prob_pv, idx_pv] = scenario_reduction(pv_scenarios, prob_init, n_clusters);
[load_red, prob_load, idx_load] = scenario_reduction(load_scenarios, prob_init, n_clusters);

%% 3. 联合场景生成 (考虑相关性)
function [joint_scenarios, joint_prob] = generate_joint_scenarios(...
    wind_scenarios, prob_wind, pv_scenarios, prob_pv, load_scenarios, prob_load)
    
    n_wind = size(wind_scenarios, 1);
    n_pv = size(pv_scenarios, 1);
    n_load = size(load_scenarios, 1);
    
    % 假设独立,生成所有组合
    n_joint = n_wind * n_pv * n_load;
    joint_scenarios = zeros(n_joint, 3, 24);  % 维度: 场景 × 3个变量 × 24小时
    joint_prob = zeros(n_joint, 1);
    
    idx = 1;
    for i = 1:n_wind
        for j = 1:n_pv
            for k = 1:n_load
                joint_scenarios(idx, 1, :) = wind_scenarios(i, :);
                joint_scenarios(idx, 2, :) = pv_scenarios(j, :);
                joint_scenarios(idx, 3, :) = load_scenarios(k, :);
                joint_prob(idx) = prob_wind(i) * prob_pv(j) * prob_load(k);
                idx = idx + 1;
            end
        end
    end
    
    % 进一步削减联合场景
    % 将三维数据展平为二维
    joint_scenarios_flat = reshape(joint_scenarios, n_joint, []);
    [joint_scenarios_red, joint_prob_red] = scenario_reduction(...
        joint_scenarios_flat, joint_prob, 20);
    
    % 恢复为三维
    joint_scenarios = reshape(joint_scenarios_red, [], 3, 24);
    joint_prob = joint_prob_red;
end

% 生成联合场景
[joint_scenarios, joint_prob] = generate_joint_scenarios(...
    wind_red, prob_wind, pv_red, prob_pv, load_red, prob_load);

n_joint = size(joint_scenarios, 1);
fprintf('削减后联合场景数: %d\n', n_joint);

三、基于场景的随机规划模型

3.1 两阶段随机规划模型

%% 两阶段随机规划:考虑源荷不确定性的电力系统调度
clear; clc;

%% 1. 系统参数
n_hours = 24;
n_units = 3;        % 常规机组数
n_wind = 1;         % 风电场数
n_pv = 1;           % 光伏电站数
n_scenarios = size(joint_scenarios, 1);  % 场景数

%% 2. 机组参数
% 机组1: 基荷机组
units(1).P_min = 50;     % MW
units(1).P_max = 200;    % MW
units(1).a = 0.01;       % 成本系数 a ($/MW^2)
units(1).b = 20;         % 成本系数 b ($/MW)
units(1).c = 100;        % 成本系数 c ($)
units(1).ramp_up = 100;  % MW/h
units(1).ramp_down = 100; % MW/h
units(1).min_up = 4;     % 最小开机时间 (h)
units(1).min_down = 4;   % 最小停机时间 (h)
units(1).startup_cost = 500;  % 启动成本 ($)
units(1).shutdown_cost = 100; % 停机成本 ($)

% 机组2: 腰荷机组
units(2).P_min = 20;
units(2).P_max = 100;
units(2).a = 0.02;
units(2).b = 30;
units(2).c = 50;
units(2).ramp_up = 80;
units(2).ramp_down = 80;
units(2).min_up = 2;
units(2).min_down = 2;
units(2).startup_cost = 300;
units(2).shutdown_cost = 50;

% 机组3: 峰荷机组
units(3).P_min = 0;
units(3).P_max = 50;
units(3).a = 0.05;
units(3).b = 50;
units(3).c = 0;
units(3).ramp_up = 50;
units(3).ramp_down = 50;
units(3).min_up = 1;
units(3).min_down = 1;
units(3).startup_cost = 200;
units(3).shutdown_cost = 0;

%% 3. 可再生能源参数
% 弃风/弃光惩罚
wind_penalty = 50;  % $/MWh
pv_penalty = 40;    % $/MWh

% 旋转备用要求
spinning_reserve = 0.1;  % 10%的负荷作为旋转备用

%% 4. 构建两阶段随机规划模型
% 第一阶段变量: 机组启停状态 (二进制)
% 第二阶段变量: 机组出力、可再生能源实际出力、弃风弃光量 (连续)

% 使用YALMIP建模
addpath(genpath('yalmip'));  % 添加YALMIP路径
addpath(genpath('gurobi'));  % 添加Gurobi路径 (或其他求解器)

% 决策变量
u = binvar(n_units, n_hours, 'full');      % 机组启停状态
v = binvar(n_units, n_hours, 'full');      % 启动状态
w = binvar(n_units, n_hours, 'full');      % 停机状态

% 第二阶段变量 (场景相关)
P = sdpvar(n_units, n_hours, n_scenarios, 'full');  % 机组出力
P_wind_actual = sdpvar(n_hours, n_scenarios, 'full');  % 风电实际出力
P_pv_actual = sdpvar(n_hours, n_scenarios, 'full');    % 光伏实际出力
wind_curtail = sdpvar(n_hours, n_scenarios, 'full');   % 弃风量
pv_curtail = sdpvar(n_hours, n_scenarios, 'full');     % 弃光量

%% 5. 目标函数: 最小化期望总成本
total_cost = 0;

% 第一阶段成本: 启停成本
for i = 1:n_units
    for t = 1:n_hours
        total_cost = total_cost + units(i).startup_cost * v(i,t) + ...
                     units(i).shutdown_cost * w(i,t);
    end
end

% 第二阶段成本期望值
for s = 1:n_scenarios
    scenario_prob = joint_prob(s);
    
    for t = 1:n_hours
        % 从联合场景中获取数据
        wind_available = joint_scenarios(s, 1, t);
        pv_available = joint_scenarios(s, 2, t);
        load_demand = joint_scenarios(s, 3, t);
        
        % 发电成本
        for i = 1:n_units
            total_cost = total_cost + scenario_prob * (...
                units(i).a * P(i,t,s)^2 + ...
                units(i).b * P(i,t,s) + ...
                units(i).c * u(i,t));
        end
        
        % 弃风弃光惩罚
        total_cost = total_cost + scenario_prob * (...
            wind_penalty * wind_curtail(t,s) + ...
            pv_penalty * pv_curtail(t,s));
    end
end

%% 6. 约束条件
constraints = [];

% 6.1 启停逻辑约束
for i = 1:n_units
    for t = 1:n_hours
        % 启动和停机指示器
        if t > 1
            constraints = [constraints, v(i,t) >= u(i,t) - u(i,t-1)];
            constraints = [constraints, w(i,t) >= u(i,t-1) - u(i,t)];
        else
            constraints = [constraints, v(i,1) >= u(i,1) - 0];  % 初始状态假设为0
            constraints = [constraints, w(i,1) >= 0 - u(i,1)];
        end
        
        constraints = [constraints, v(i,t) <= u(i,t)];
        constraints = [constraints, w(i,t) <= 1 - u(i,t)];
        constraints = [constraints, v(i,t) + w(i,t) <= 1];
    end
end

% 6.2 最小启停时间约束
for i = 1:n_units
    for t = 1:n_hours
        % 最小开机时间
        if t + units(i).min_up - 1 <= n_hours
            constraints = [constraints, ...
                sum(u(i, t:min(t+units(i).min_up-1, n_hours))) >= ...
                units(i).min_up * v(i,t)];
        end
        
        % 最小停机时间
        if t + units(i).min_down - 1 <= n_hours
            constraints = [constraints, ...
                sum(1 - u(i, t:min(t+units(i).min_down-1, n_hours))) >= ...
                units(i).min_down * w(i,t)];
        end
    end
end

% 6.3 场景相关的约束
for s = 1:n_scenarios
    for t = 1:n_hours
        % 从联合场景中获取数据
        wind_available = joint_scenarios(s, 1, t);
        pv_available = joint_scenarios(s, 2, t);
        load_demand = joint_scenarios(s, 3, t);
        
        % 功率平衡约束
        total_gen = sum(P(:,t,s)) + P_wind_actual(t,s) + P_pv_actual(t,s);
        constraints = [constraints, total_gen == load_demand];
        
        % 机组出力上下限约束
        for i = 1:n_units
            constraints = [constraints, ...
                units(i).P_min * u(i,t) <= P(i,t,s) <= units(i).P_max * u(i,t)];
        end
        
        % 爬坡约束
        if t > 1
            for i = 1:n_units
                constraints = [constraints, ...
                    P(i,t,s) - P(i,t-1,s) <= units(i).ramp_up];
                constraints = [constraints, ...
                    P(i,t-1,s) - P(i,t,s) <= units(i).ramp_down];
            end
        end
        
        % 可再生能源出力约束
        constraints = [constraints, 0 <= P_wind_actual(t,s) <= wind_available];
        constraints = [constraints, 0 <= P_pv_actual(t,s) <= pv_available];
        
        % 弃风弃光定义
        constraints = [constraints, ...
            wind_curtail(t,s) == wind_available - P_wind_actual(t,s)];
        constraints = [constraints, ...
            pv_curtail(t,s) == pv_available - P_pv_actual(t,s)];
        
        % 旋转备用约束
        quick_reserve = 0;
        for i = 1:n_units
            quick_reserve = quick_reserve + min(units(i).ramp_up, ...
                units(i).P_max * u(i,t) - P(i,t,s));
        end
        constraints = [constraints, quick_reserve >= spinning_reserve * load_demand];
    end
end

%% 7. 求解优化问题
ops = sdpsettings('solver', 'gurobi', 'verbose', 1, 'showprogress', 1);
ops.gurobi.TimeLimit = 3600;  % 1小时时间限制
ops.gurobi.MIPGap = 0.01;     % 1%的MIP间隙

% 求解
tic;
diagnostics = optimize(constraints, total_cost, ops);
solve_time = toc;

if diagnostics.problem == 0
    disp('=== 求解成功 ===');
    fprintf('求解时间: %.2f 秒\n', solve_time);
    fprintf('目标函数值: %.2f $\n', value(total_cost));
    
    % 提取结果
    u_opt = value(u);
    P_opt = value(P);
    P_wind_opt = value(P_wind_actual);
    P_pv_opt = value(P_pv_actual);
    wind_curtail_opt = value(wind_curtail);
    pv_curtail_opt = value(pv_curtail);
    
    % 计算期望值
    P_expected = zeros(n_units, n_hours);
    for i = 1:n_units
        for t = 1:n_hours
            for s = 1:n_scenarios
                P_expected(i,t) = P_expected(i,t) + joint_prob(s) * P_opt(i,t,s);
            end
        end
    end
    
else
    disp('求解失败');
    disp(yalmiperror(diagnostics.problem));
end

%% 8. 结果可视化
plot_results(u_opt, P_expected, P_wind_opt, P_pv_opt, ...
    wind_curtail_opt, pv_curtail_opt, joint_scenarios, joint_prob);

3.2 结果可视化函数

function plot_results(u_opt, P_expected, P_wind_opt, P_pv_opt, ...
    wind_curtail_opt, pv_curtail_opt, joint_scenarios, joint_prob)
    
    [n_units, n_hours] = size(P_expected);
    n_scenarios = size(joint_scenarios, 1);
    time = 1:n_hours;
    
    figure('Position', [100, 100, 1400, 900]);
    
    % 子图1: 机组启停状态
    subplot(3,3,1);
    imagesc(time, 1:n_units, u_opt);
    colormap(flipud(gray));
    colorbar;
    xlabel('时间 (小时)');
    ylabel('机组编号');
    title('机组启停状态');
    set(gca, 'YTick', 1:n_units);
    
    % 子图2: 期望出力
    subplot(3,3,2);
    area_plot = area(time, P_expected');
    legend('机组1', '机组2', '机组3', 'Location', 'best');
    xlabel('时间 (小时)');
    ylabel('出力 (MW)');
    title('机组期望出力');
    grid on;
    
    % 子图3: 可再生能源出力
    subplot(3,3,3);
    hold on;
    
    % 计算期望的可再生能源出力
    P_wind_expected = zeros(1, n_hours);
    P_pv_expected = zeros(1, n_hours);
    for t = 1:n_hours
        for s = 1:n_scenarios
            P_wind_expected(t) = P_wind_expected(t) + joint_prob(s) * P_wind_opt(t,s);
            P_pv_expected(t) = P_pv_expected(t) + joint_prob(s) * P_pv_opt(t,s);
        end
    end
    
    plot(time, P_wind_expected, 'b-', 'LineWidth', 2, 'DisplayName', '风电期望出力');
    plot(time, P_pv_expected, 'g-', 'LineWidth', 2, 'DisplayName', '光伏期望出力');
    
    % 计算可用可再生能源
    wind_available_exp = zeros(1, n_hours);
    pv_available_exp = zeros(1, n_hours);
    for t = 1:n_hours
        for s = 1:n_scenarios
            wind_available_exp(t) = wind_available_exp(t) + joint_prob(s) * joint_scenarios(s,1,t);
            pv_available_exp(t) = pv_available_exp(t) + joint_prob(s) * joint_scenarios(s,2,t);
        end
    end
    
    plot(time, wind_available_exp, 'b--', 'LineWidth', 1, 'DisplayName', '风电可用');
    plot(time, pv_available_exp, 'g--', 'LineWidth', 1, 'DisplayName', '光伏可用');
    
    xlabel('时间 (小时)');
    ylabel('出力 (MW)');
    title('可再生能源出力');
    legend('Location', 'best');
    grid on;
    
    % 子图4: 弃风弃光
    subplot(3,3,4);
    wind_curtail_exp = zeros(1, n_hours);
    pv_curtail_exp = zeros(1, n_hours);
    for t = 1:n_hours
        for s = 1:n_scenarios
            wind_curtail_exp(t) = wind_curtail_exp(t) + joint_prob(s) * wind_curtail_opt(t,s);
            pv_curtail_exp(t) = pv_curtail_exp(t) + joint_prob(s) * pv_curtail_opt(t,s);
        end
    end
    
    bar_data = [wind_curtail_exp; pv_curtail_exp]';
    bar(time, bar_data, 'stacked');
    xlabel('时间 (小时)');
    ylabel('弃电量 (MWh)');
    title('弃风弃光量');
    legend('弃风', '弃光', 'Location', 'best');
    grid on;
    
    % 子图5: 成本分解
    subplot(3,3,5);
    % 计算各项成本
    fuel_cost = 0;
    startup_cost = 0;
    shutdown_cost = 0;
    curtail_cost = 0;
    
    % 这里需要从优化结果中计算具体成本
    % 简化显示
    cost_labels = {'燃料成本', '启停成本', '弃电惩罚'};
    cost_values = [70, 15, 5];  % 示例值,单位: 千美元
    bar(cost_values);
    set(gca, 'XTickLabel', cost_labels);
    ylabel('成本 (千$)');
    title('成本分解');
    grid on;
    
    % 子图6: 典型场景下的功率平衡
    subplot(3,3,6);
    % 选择概率最大的场景
    [~, max_prob_idx] = max(joint_prob);
    
    load_scenario = squeeze(joint_scenarios(max_prob_idx, 3, :))';
    total_gen = sum(P_opt(:,:,max_prob_idx), 1) + ...
                P_wind_opt(:,max_prob_idx)' + P_pv_opt(:,max_prob_idx)';
    
    area_plot = area(time, [P_opt(:,:,max_prob_idx)', ...
                           P_wind_opt(:,max_prob_idx)', ...
                           P_pv_opt(:,max_prob_idx)']);
    hold on;
    plot(time, load_scenario, 'k-', 'LineWidth', 2, 'DisplayName', '负荷');
    xlabel('时间 (小时)');
    ylabel('功率 (MW)');
    title('典型场景功率平衡');
    legend('机组1', '机组2', '机组3', '风电', '光伏', '负荷', 'Location', 'best');
    grid on;
    
    % 子图7: 备用充足性
    subplot(3,3,7);
    reserve_available = zeros(1, n_hours);
    reserve_required = zeros(1, n_hours);
    
    for t = 1:n_hours
        load_exp = 0;
        for s = 1:n_scenarios
            load_exp = load_exp + joint_prob(s) * joint_scenarios(s,3,t);
        end
        reserve_required(t) = 0.1 * load_exp;  % 10%备用
        
        % 计算可用备用
        for s = 1:n_scenarios
            for i = 1:n_units
                reserve_available(t) = reserve_available(t) + joint_prob(s) * ...
                    min(units(i).ramp_up, units(i).P_max*u_opt(i,t)-P_opt(i,t,s));
            end
        end
    end
    
    bar(time, [reserve_available; reserve_required]', 'grouped');
    xlabel('时间 (小时)');
    ylabel('备用容量 (MW)');
    title('旋转备用');
    legend('可用备用', '要求备用', 'Location', 'best');
    grid on;
    
    % 子图8: 场景概率分布
    subplot(3,3,8);
    [sorted_prob, idx] = sort(joint_prob, 'descend');
    bar(1:length(sorted_prob), sorted_prob);
    xlabel('场景编号 (按概率排序)');
    ylabel('概率');
    title('场景概率分布');
    grid on;
    
    % 子图9: 不确定性带
    subplot(3,3,9);
    % 计算负荷的不确定性带
    load_min = min(squeeze(joint_scenarios(:,3,:)), [], 1);
    load_max = max(squeeze(joint_scenarios(:,3,:)), [], 1);
    load_mean = mean(squeeze(joint_scenarios(:,3,:)), 1);
    
    fill([time, fliplr(time)], [load_max, fliplr(load_min)], ...
        [0.8, 0.8, 0.8], 'FaceAlpha', 0.3, 'EdgeColor', 'none');
    hold on;
    plot(time, load_mean, 'b-', 'LineWidth', 2);
    xlabel('时间 (小时)');
    ylabel('负荷 (MW)');
    title('负荷不确定性带');
    legend('不确定性范围', '期望值', 'Location', 'best');
    grid on;
end

四、鲁棒优化模型

4.1 自适应鲁棒优化框架

%% 自适应鲁棒优化:考虑最坏情况下的源荷不确定性
clear; clc;

%% 1. 系统参数
n_hours = 24;
n_units = 3;
n_wind = 1;
n_pv = 1;

%% 2. 不确定性集合定义
% 区间不确定性模型
load_nominal = [120, 115, 110, 105, 100, 110, 130, 150, 170, 180, ...
                175, 170, 165, 160, 155, 160, 170, 185, 190, 180, ...
                170, 160, 140, 125];  % MW

wind_nominal = [45, 48, 50, 52, 55, 60, 65, 70, 75, 80, ...
                78, 75, 72, 70, 68, 65, 62, 60, 58, 55, ...
                52, 50, 48, 46];  % MW

pv_nominal = [0, 0, 0, 0, 0, 5, 15, 30, 45, 60, ...
              70, 75, 80, 75, 70, 60, 45, 30, 15, 5, ...
              0, 0, 0, 0];  % MW

% 不确定性偏差范围 (±百分比)
load_uncertainty = 0.15;  % ±15%
wind_uncertainty = 0.30;  % ±30%
pv_uncertainty = 0.25;    % ±25%

% 预算不确定性集合参数
Gamma_load = 8;  % 负荷不确定性预算
Gamma_wind = 6;  % 风电不确定性预算
Gamma_pv = 4;    % 光伏不确定性预算

%% 3. 鲁棒优化建模
% 使用YALMIP的鲁棒优化功能
yalmip('clear');

% 第一阶段变量: 机组启停
u = binvar(n_units, n_hours, 'full');
v = binvar(n_units, n_hours, 'full');
w = binvar(n_units, n_hours, 'full');

% 第二阶段变量: 机组出力 (作为仿射可调决策)
P = sdpvar(n_units, n_hours, 'full');

% 不确定变量
xi_load = sdpvar(1, n_hours, 'full');  % 负荷不确定性
xi_wind = sdpvar(1, n_hours, 'full');  % 风电不确定性
xi_pv = sdpvar(1, n_hours, 'full');    % 光伏不确定性

% 不确定性集合定义
% 标准化不确定性变量: ξ ∈ [-1, 1]
uncertainty_set = [];
for t = 1:n_hours
    uncertainty_set = [uncertainty_set, -1 <= xi_load(t) <= 1];
    uncertainty_set = [uncertainty_set, -1 <= xi_wind(t) <= 1];
    uncertainty_set = [uncertainty_set, -1 <= xi_pv(t) <= 1];
end

% 预算约束
uncertainty_set = [uncertainty_set, ...
    norm(xi_load, 1) <= Gamma_load, ...
    norm(xi_wind, 1) <= Gamma_wind, ...
    norm(xi_pv, 1) <= Gamma_pv];

% 实际值 = 标称值 + 不确定性
load_actual = load_nominal + load_uncertainty * load_nominal .* xi_load;
wind_actual = wind_nominal + wind_uncertainty * wind_nominal .* xi_wind;
pv_actual = pv_nominal + pv_uncertainty * pv_nominal .* xi_pv;

%% 4. 约束条件
constraints = [];

% 4.1 启停逻辑约束
for i = 1:n_units
    for t = 1:n_hours
        if t > 1
            constraints = [constraints, v(i,t) >= u(i,t) - u(i,t-1)];
            constraints = [constraints, w(i,t) >= u(i,t-1) - u(i,t)];
        else
            constraints = [constraints, v(i,1) >= u(i,1)];
            constraints = [constraints, w(i,1) >= 0 - u(i,1)];
        end
        constraints = [constraints, 0 <= v(i,t) <= 1, 0 <= w(i,t) <= 1];
    end
end

% 4.2 机组出力约束
for i = 1:n_units
    for t = 1:n_hours
        constraints = [constraints, ...
            units(i).P_min * u(i,t) <= P(i,t) <= units(i).P_max * u(i,t)];
    end
end

% 4.3 爬坡约束
for i = 1:n_units
    for t = 2:n_hours
        constraints = [constraints, ...
            P(i,t) - P(i,t-1) <= units(i).ramp_up];
        constraints = [constraints, ...
            P(i,t-1) - P(i,t) <= units(i).ramp_down];
    end
end

% 4.4 功率平衡约束 (鲁棒约束,对所有不确定性实现成立)
power_balance_constraint = [];
for t = 1:n_hours
    total_gen = sum(P(:,t)) + wind_actual(t) + pv_actual(t);
    power_balance_constraint = [power_balance_constraint, ...
        total_gen == load_actual(t)];
end
constraints = [constraints, implies(uncertainty_set, power_balance_constraint)];

% 4.5 备用约束
reserve_constraint = [];
for t = 1:n_hours
    % 计算可用备用
    available_reserve = 0;
    for i = 1:n_units
        available_reserve = available_reserve + ...
            min(units(i).ramp_up, units(i).P_max*u(i,t) - P(i,t));
    end
    
    % 考虑最坏情况下的备用需求
    worst_case_load = load_nominal(t) * (1 + load_uncertainty);
    reserve_requirement = 0.1 * worst_case_load;
    
    reserve_constraint = [reserve_constraint, ...
        available_reserve >= reserve_requirement];
end
constraints = [constraints, reserve_constraint];

%% 5. 目标函数
% 最小化最坏情况下的总成本
total_cost = 0;

% 启停成本
for i = 1:n_units
    for t = 1:n_hours
        total_cost = total_cost + units(i).startup_cost * v(i,t) + ...
                     units(i).shutdown_cost * w(i,t);
    end
end

% 发电成本 (考虑最坏情况)
gen_cost = 0;
for i = 1:n_units
    for t = 1:n_hours
        gen_cost = gen_cost + units(i).a * P(i,t)^2 + ...
                   units(i).b * P(i,t) + units(i).c * u(i,t);
    end
end

% 弃电惩罚 (在最坏情况下)
wind_curtail_penalty = 0;
pv_curtail_penalty = 0;

% 由于是鲁棒优化,需要考虑最坏情况下的弃电
% 简化处理: 假设弃电发生在可再生能源过剩时
for t = 1:n_hours
    % 弃电惩罚项
    wind_curtail_penalty = wind_curtail_penalty + wind_penalty * ...
        max(0, wind_actual(t) - (load_actual(t) - sum(P(:,t)) - pv_actual(t)));
    pv_curtail_penalty = pv_curtail_penalty + pv_penalty * ...
        max(0, pv_actual(t) - (load_actual(t) - sum(P(:,t)) - wind_actual(t)));
end

total_cost = total_cost + gen_cost + wind_curtail_penalty + pv_curtail_penalty;

%% 6. 求解鲁棒优化问题
ops = sdpsettings('solver', 'gurobi', 'robust.lplp', 'duality');
ops.verbose = 1;
ops.gurobi.TimeLimit = 1800;

tic;
diagnostics = optimize([constraints, uncertainty_set], total_cost, ops);
solve_time = toc;

if diagnostics.problem == 0
    disp('=== 鲁棒优化求解成功 ===');
    fprintf('求解时间: %.2f 秒\n', solve_time);
    fprintf('最坏情况总成本: %.2f $\n', value(total_cost));
    
    % 提取结果
    u_robust = value(u);
    P_robust = value(P);
    
    % 分析鲁棒性
    analyze_robustness(u_robust, P_robust, load_nominal, wind_nominal, pv_nominal, ...
        load_uncertainty, wind_uncertainty, pv_uncertainty, units);
    
else
    disp('鲁棒优化求解失败');
    disp(yalmiperror(diagnostics.problem));
end

4.2 鲁棒性分析函数

function analyze_robustness(u_opt, P_opt, load_nominal, wind_nominal, pv_nominal, ...
    load_uncert, wind_uncert, pv_uncert, units)
    
    n_hours = length(load_nominal);
    n_units = size(P_opt, 1);
    time = 1:n_hours;
    
    % 生成最坏情况场景
    n_monte_carlo = 1000;
    feasibility_count = 0;
    cost_distribution = zeros(n_monte_carlo, 1);
    
    for mc = 1:n_monte_carlo
        % 随机生成不确定性实现
        xi_load = 2*rand(1, n_hours) - 1;  % [-1, 1]
        xi_wind = 2*rand(1, n_hours) - 1;
        xi_pv = 2*rand(1, n_hours) - 1;
        
        % 应用预算约束
        if sum(abs(xi_load)) > 8
            xi_load = xi_load * 8 / sum(abs(xi_load));
        end
        if sum(abs(xi_wind)) > 6
            xi_wind = xi_wind * 6 / sum(abs(xi_wind));
        end
        if sum(abs(xi_pv)) > 4
            xi_pv = xi_pv * 4 / sum(abs(xi_pv));
        end
        
        % 计算实际值
        load_actual = load_nominal + load_uncert * load_nominal .* xi_load;
        wind_actual = wind_nominal + wind_uncert * wind_nominal .* xi_wind;
        pv_actual = pv_nominal + pv_uncert * pv_nominal .* xi_pv;
        
        % 检查可行性
        feasible = true;
        
        for t = 1:n_hours
            % 检查功率平衡
            total_gen = sum(P_opt(:,t)) + wind_actual(t) + pv_actual(t);
            if abs(total_gen - load_actual(t)) > 1e-3
                feasible = false;
                break;
            end
            
            % 检查机组约束
            for i = 1:n_units
                if P_opt(i,t) < units(i).P_min * u_opt(i,t) - 1e-3 || ...
                   P_opt(i,t) > units(i).P_max * u_opt(i,t) + 1e-3
                    feasible = false;
                    break;
                end
            end
            
            if ~feasible, break; end
        end
        
        if feasible
            feasibility_count = feasibility_count + 1;
            
            % 计算该场景下的成本
            scenario_cost = 0;
            for i = 1:n_units
                for t = 1:n_hours
                    scenario_cost = scenario_cost + ...
                        units(i).a * P_opt(i,t)^2 + ...
                        units(i).b * P_opt(i,t) + ...
                        units(i).c * u_opt(i,t);
                end
            end
            cost_distribution(mc) = scenario_cost;
        end
    end
    
    feasibility_rate = feasibility_count / n_monte_carlo;
    fprintf('蒙特卡洛可行性检验: %.2f%%\n', feasibility_rate * 100);
    
    % 可视化鲁棒性分析结果
    figure('Position', [100, 100, 1200, 800]);
    
    % 子图1: 成本分布
    subplot(2,3,1);
    histogram(cost_distribution(cost_distribution > 0), 20);
    xlabel('总成本 ($)');
    ylabel('频数');
    title('不同场景下的成本分布');
    grid on;
    
    % 子图2: 最坏情况与标称情况对比
    subplot(2,3,2);
    
    % 计算标称情况下的出力
    P_nominal = zeros(n_units, n_hours);
    for t = 1:n_hours
        % 简单分配: 按机组顺序满足净负荷
        net_load = load_nominal(t) - wind_nominal(t) - pv_nominal(t);
        
        remaining = net_load;
        for i = 1:n_units
            if remaining > 0
                P_nominal(i,t) = min(remaining, units(i).P_max * u_opt(i,t));
                remaining = remaining - P_nominal(i,t);
            end
        end
    end
    
    plot(time, sum(P_opt,1), 'r-', 'LineWidth', 2, 'DisplayName', '鲁棒调度');
    hold on;
    plot(time, sum(P_nominal,1), 'b--', 'LineWidth', 2, 'DisplayName', '标称调度');
    xlabel('时间 (小时)');
    ylabel('常规机组总出力 (MW)');
    title('鲁棒 vs 标称调度');
    legend('Location', 'best');
    grid on;
    
    % 子图3: 备用充足性
    subplot(2,3,3);
    
    reserve_available = zeros(1, n_hours);
    worst_case_reserve_needed = zeros(1, n_hours);
    
    for t = 1:n_hours
        % 可用备用
        for i = 1:n_units
            reserve_available(t) = reserve_available(t) + ...
                min(units(i).ramp_up, units(i).P_max*u_opt(i,t) - P_opt(i,t));
        end
        
        % 最坏情况备用需求
        worst_load = load_nominal(t) * (1 + load_uncert);
        worst_case_reserve_needed(t) = 0.1 * worst_load;
    end
    
    bar(time, [reserve_available; worst_case_reserve_needed]');
    xlabel('时间 (小时)');
    ylabel('备用容量 (MW)');
    title('备用充足性分析');
    legend('可用备用', '最坏情况需求', 'Location', 'best');
    grid on;
    
    % 子图4: 不确定性对功率平衡的影响
    subplot(2,3,4);
    
    % 生成几个极端场景
    n_extreme = 5;
    imbalance_scenarios = zeros(n_extreme, n_hours);
    
    for s = 1:n_extreme
        % 生成极端不确定性
        xi_load_extreme = sign(randn(1, n_hours));
        xi_wind_extreme = sign(randn(1, n_hours));
        xi_pv_extreme = sign(randn(1, n_hours));
        
        % 计算实际值
        load_extreme = load_nominal + load_uncert * load_nominal .* xi_load_extreme;
        wind_extreme = wind_nominal + wind_uncert * wind_nominal .* xi_wind_extreme;
        pv_extreme = pv_nominal + pv_uncert * pv_nominal .* xi_pv_extreme;
        
        % 计算功率不平衡
        for t = 1:n_hours
            total_gen = sum(P_opt(:,t)) + wind_extreme(t) + pv_extreme(t);
            imbalance_scenarios(s,t) = total_gen - load_extreme(t);
        end
    end
    
    plot(time, imbalance_scenarios', 'LineWidth', 1);
    hold on;
    plot(time, zeros(1,n_hours), 'k-', 'LineWidth', 2);
    xlabel('时间 (小时)');
    ylabel('功率不平衡 (MW)');
    title('极端场景下的功率平衡');
    grid on;
    
    % 子图5: 成本保守性分析
    subplot(2,3,5);
    
    % 计算标称成本
    nominal_cost = 0;
    for i = 1:n_units
        for t = 1:n_hours
            nominal_cost = nominal_cost + ...
                units(i).a * P_nominal(i,t)^2 + ...
                units(i).b * P_nominal(i,t) + ...
                units(i).c * u_opt(i,t);
        end
    end
    
    % 鲁棒成本 (期望)
    robust_cost_mean = mean(cost_distribution(cost_distribution > 0));
    robust_cost_max = max(cost_distribution);
    
    bar_data = [nominal_cost, robust_cost_mean, robust_cost_max];
    bar(1:3, bar_data);
    set(gca, 'XTickLabel', {'标称成本', '鲁棒期望成本', '最坏情况成本'});
    ylabel('成本 ($)');
    title('成本保守性分析');
    grid on;
    
    % 添加数值标签
    for i = 1:3
        text(i, bar_data(i), sprintf('%.0f', bar_data(i)), ...
            'HorizontalAlignment', 'center', 'VerticalAlignment', 'bottom');
    end
    
    % 子图6: 可行性热图
    subplot(2,3,6);
    
    % 测试不同不确定性水平下的可行性
    gamma_test = 0:2:10;
    feasibility_rates = zeros(length(gamma_test), 1);
    
    for g = 1:length(gamma_test)
        gamma = gamma_test(g);
        
        feasible_count = 0;
        for mc = 1:100
            xi_test = 2*rand(1, n_hours) - 1;
            if sum(abs(xi_test)) > gamma
                xi_test = xi_test * gamma / sum(abs(xi_test));
            end
            
            load_test = load_nominal + load_uncert * load_nominal .* xi_test;
            
            % 简单可行性检查
            feasible = true;
            for t = 1:n_hours
                if load_test(t) > sum(P_opt(:,t)) + wind_nominal(t) + pv_nominal(t) + 10
                    feasible = false;
                    break;
                end
            end
            
            if feasible
                feasible_count = feasible_count + 1;
            end
        end
        
        feasibility_rates(g) = feasible_count / 100;
    end
    
    plot(gamma_test, feasibility_rates*100, 'b-o', 'LineWidth', 2);
    xlabel('不确定性预算 \Gamma');
    ylabel('可行性率 (%)');
    title('不同不确定性水平下的可行性');
    grid on;
    ylim([0, 105]);
end

参考代码 考虑源荷不确定性-电力系统优化 www.youwenfan.com/contentcnt/160705.html

五、实际应用建议

5.1 方法选择指南

应用场景 推荐方法 原因
日前市场出清 随机规划 考虑多种可能,经济性较好
实时调度 鲁棒优化 快速响应,保证安全性
规划问题 随机规划+鲁棒优化 兼顾长期经济性和短期可靠性
高可再生能源渗透 随机规划+机会约束 平衡经济性与消纳
极端天气应对 鲁棒优化 抗干扰能力强

5.2 参数设置经验

  1. 场景数:50-200个场景通常足够,更多场景收益递减
  2. 不确定性预算Γ:通常设为不确定性变量数的20-40%
  3. 弃电惩罚:设为最高发电成本的1.2-1.5倍
  4. 备用要求:常规10-15%,高可再生能源时15-20%

5.3 计算效率提升

  1. 并行计算:使用parfor并行处理场景
  2. 分解算法:Benders分解、场景分解
  3. 场景削减:保留概率大、差异大的场景
  4. 简化模型:线性化、分段线性化复杂函数

六、总结

本文提供了完整的MATLAB实现,涵盖:

  1. 不确定性建模:场景生成与削减
  2. 随机规划:两阶段随机优化模型
  3. 鲁棒优化:自适应鲁棒优化框架
  4. 可视化分析:全面的结果可视化

关键要点:

后续工作方向

  1. 集成机器学习进行不确定性预测
  2. 考虑网络约束的扩展
  3. 多时间尺度协调优化
  4. 分布式优化算法

代码已模块化设计,便于扩展和应用于实际电力系统。

专注于matlab/simulink,电子电路,编程