В этом примере показано, как узнать оптимальную политику планирования насоса для системы распределения воды с помощью обучения с подкреплением (RL).
На следующем рисунке показана система распределения.
Здесь:
количество воды, подаваемой в бак с водой из резервуара.
количество воды, вытекающей из бака для удовлетворения потребности в использовании.
Цель агента обучения с подкреплением состоит в том, чтобы запланировать количество работающих насосов, чтобы минимизировать энергопотребление системы и удовлетворить потребность в использовании (). Диафрагма системы бака определяется следующим уравнением.
Вот, и . Потребность в течение 24-часового периода является функцией времени, заданного как
где является ожидаемым спросом и представляет неопределенность спроса, которая выбирается из равномерного случайного распределения.
Подача определяется количеством работающих насосов, согласно следующему отображению.
Чтобы упростить задачу, потребление степени определяется как количество работающих насосов, .
Следующая функция является вознаграждением для этого окружения. Чтобы избежать переполнения или опорожнения бака, добавляются дополнительные затраты, если высота воды близка к максимальному или минимальному уровню воды. или , соответственно.
Чтобы сгенерировать и профиль потребности в воде, основанный на количестве рассматриваемых дней, используйте generateWaterDemand
функция, заданная в конце этого примера.
num_days = 4; % Number of days
[WaterDemand,T_max] = generateWaterDemand(num_days);
Просмотрите профиль потребности.
plot(WaterDemand)
Откройте распределение систему Simulink модели.
mdl = "watertankscheduling";
open_system(mdl)
В дополнение к агенту обучения с подкреплением в блоке Control law MATLAB Function задан простой контроллер базовой линии. Этот контроллер активирует определенное количество насосов в зависимости от уровня воды.
Задайте начальный уровень воды.
h0 = 3; % m
Задайте параметры модели.
SampleTime = 0.2; H_max = 7; % Max tank height (m) A_tank = 40; % Area of tank (m^2)
Чтобы создать интерфейс окружения для модели Simulink, сначала задайте спецификации действий и наблюдений, actInfo
и obsInfo
, соответственно. Действие агента является выбранным количеством насосов. Наблюдение агента - этот уровень воды, которая эмазируется как сигнал непрерывного времени.
actInfo = rlFiniteSetSpec([0,1,2,3]); obsInfo = rlNumericSpec([1,1]);
Создайте интерфейс окружения.
env = rlSimulinkEnv(mdl,mdl+"/RL Agent",obsInfo,actInfo);
Задайте пользовательскую функцию сброса, которая задана в конце этого примера, которая рандомизирует начальный уровень воды и потребность в воде. Это позволяет обучать агента на различных начальных уровнях воды и функциях спроса на воду для каждого эпизода.
env.ResetFcn = @(in)localResetFcn(in);
Агент DQN аппроксимирует долгосрочное вознаграждение, заданные наблюдения и действия, используя представление функции Q-значения критика. Чтобы создать критика, сначала создайте глубокую нейронную сеть. Для получения дополнительной информации о создании представления функции ценности глубокой нейронной сети, смотрите, Создают политику и представления функции ценности.
% Fix the random generator seed for reproducibility.
rng(0);
Создайте глубокую нейронную сеть для критика. В данном примере используйте непериодическую нейронную сеть. Чтобы использовать рецидивирующую нейронную сеть, задайте useLSTM
на true
.
useLSTM = false; if useLSTM layers = [ sequenceInputLayer(obsInfo.Dimension(1),"Name","state","Normalization","none") fullyConnectedLayer(32,"Name","fc_1") reluLayer("Name","relu_body1") lstmLayer(32,"Name","lstm") fullyConnectedLayer(32,"Name","fc_3") reluLayer("Name","relu_body3") fullyConnectedLayer(numel(actInfo.Elements),"Name","output")]; else layers = [ featureInputLayer(obsInfo.Dimension(1),"Name","state","Normalization","none") fullyConnectedLayer(32,"Name","fc_1") reluLayer("Name","relu_body1") fullyConnectedLayer(32,"Name","fc_2") reluLayer("Name","relu_body2") fullyConnectedLayer(32,"Name","fc_3") reluLayer("Name","relu_body3") fullyConnectedLayer(numel(actInfo.Elements),"Name","output")]; end
Задайте опции для создания представления критика.
criticOpts = rlRepresentationOptions('LearnRate',0.001,'GradientThreshold',1);
Создайте представление критика с помощью заданных глубокой нейронной сети и опций.
critic = rlQValueRepresentation(layerGraph(layers),obsInfo,actInfo,... 'Observation',{'state'},criticOpts);
Чтобы создать агента, сначала задайте опции агента. Если вы используете сеть LSTM, установите длину последовательности равной 20
.
opt = rlDQNAgentOptions('SampleTime',SampleTime); if useLSTM opt.SequenceLength = 20; else opt.SequenceLength = 1; end opt.DiscountFactor = 0.995; opt.ExperienceBufferLength = 1e6; opt.EpsilonGreedyExploration.EpsilonDecay = 1e-5; opt.EpsilonGreedyExploration.EpsilonMin = .02;
Создайте агента с помощью заданных опций и представления критика.
agent = rlDQNAgent(critic,opt);
Чтобы обучить агента, сначала укажите опции обучения. В данном примере используйте следующие опции.
Запустите обучение для 1000 эпизодов с каждым эпизодом, длящимся в ceil(T_max/Ts)
временные шаги.
Отображение процесса обучения в диалоговом окне Диспетчер эпизодов (установите Plots
опция)
Задайте опции для обучения с использованием rlTrainingOptions
объект.
trainOpts = rlTrainingOptions(... 'MaxEpisodes',1000, ... 'MaxStepsPerEpisode',ceil(T_max/SampleTime), ... 'Verbose',false, ... 'Plots','training-progress',... 'StopTrainingCriteria','EpisodeCount',... 'StopTrainingValue',1000,... 'ScoreAveragingWindowLength',100);
В то время как вы не делаете это для этого примера, можно сохранить агентов в процессе обучения. Например, следующие опции сохраняют каждый агент со значением вознаграждения, большим или равным -42
.
Save agents using SaveAgentCriteria if necessary trainOpts.SaveAgentCriteria = 'EpisodeReward'; trainOpts.SaveAgentValue = -42;
Обучите агента с помощью train
функция. Обучение этого агента является интенсивным в вычислительном отношении процессом, который занимает несколько часов. Чтобы сэкономить время при запуске этого примера, загрузите предварительно обученного агента путем установки doTraining
на false
. Чтобы обучить агента самостоятельно, установите doTraining
на true
.
doTraining = false; if doTraining % Train the agent. trainingStats = train(agent,env,trainOpts); else % Load the pretrained agent for the example. load('SimulinkWaterDistributionDQN.mat','agent') end
Следующий рисунок показывает процесс обучения.
Чтобы подтвердить производительность обученного агента, моделируйте его в среде бака с водой. Для получения дополнительной информации о симуляции агента смотрите rlSimulationOptions
и sim
.
Чтобы симулировать эффективность агента, соедините блок RL Agent путем переключения ручного блока switch.
set_param(mdl+"/Manual Switch",'sw','0');
Установите максимальное количество шагов для каждого симуляйтона и количество симуляций. В данном примере выполните 30 симуляций. Функция сброса окружения устанавливает разный начальный уровень воды, и потребность в воде различна в каждой симуляции.
NumSimulations = 30; simOptions = rlSimulationOptions('MaxSteps',T_max/SampleTime,... 'NumSimulations', NumSimulations);
Чтобы сравнить агент с контроллером базовой линии при тех же условиях, сбросьте входный случайный seed, используемое в функции сброса окружения.
env.ResetFcn("Reset seed");
Симулируйте агент против окружения.
experienceDQN = sim(env,agent,simOptions);
Чтобы сравнить агента DQN с базовым контроллером, вы должны симулировать базовый контроллер с помощью тех же опций симуляции и начального случайного seed для функции сброса.
Включите базовый контроллер.
set_param(mdl+"/Manual Switch",'sw','1');
Чтобы сравнить агент с контроллером базовой линии при тех же условиях, сбросьте случайный seed, используемое в функции сброса окружения.
env.ResetFcn("Reset seed");
Симулируйте контроллер базовой линии относительно окружения.
experienceBaseline = sim(env,agent,simOptions);
Инициализируйте векторы совокупного результата вознаграждения как для агента, так и для базового контроллера.
resultVectorDQN = zeros(NumSimulations, 1); resultVectorBaseline = zeros(NumSimulations,1);
Вычислите совокупные вознаграждения как для агента, так и для базового контроллера.
for ct = 1:NumSimulations resultVectorDQN(ct) = sum(experienceDQN(ct).Reward); resultVectorBaseline(ct) = sum(experienceBaseline(ct).Reward); end
Постройте график совокупных вознаграждений.
plot([resultVectorDQN resultVectorBaseline],'o') set(gca,'xtick',1:NumSimulations) xlabel("Simulation number") ylabel('Cumulative Reward') legend('DQN','Baseline','Location','NorthEastOutside')
Совокупное вознаграждение, получаемое агентом, постоянно составляет около -40. Это значение намного больше, чем среднее вознаграждение, полученное базовым контроллером. Поэтому агент DQN последовательно превосходит контроллер базового уровня с точки зрения экономии энергии.
Функция потребности в воде
function [WaterDemand,T_max] = generateWaterDemand(num_days) t = 0:(num_days*24)-1; % hr T_max = t(end); Demand_mean = [28, 28, 28, 45, 55, 110, 280, 450, 310, 170, 160, 145, 130, ... 150, 165, 155, 170, 265, 360, 240, 120, 83, 45, 28]'; % m^3/hr Demand = repmat(Demand_mean,1,num_days); Demand = Demand(:); % Add noise to demand a = -25; % m^3/hr b = 25; % m^3/hr Demand_noise = a + (b-a).*rand(numel(Demand),1); WaterDemand = timeseries(Demand + Demand_noise,t); WaterDemand.Name = "Water Demand"; end
Функция сброса
function in = localResetFcn(in) % Use a persistent random seed value to evaluate the agent and the baseline % controller under the same conditions. persistent randomSeed if isempty(randomSeed) randomSeed = 0; end if strcmp(in,"Reset seed") randomSeed = 0; return end randomSeed = randomSeed + 1; rng(randomSeed) % Randomize water demand. num_days = 4; H_max = 7; [WaterDemand,~] = generateWaterDemand(num_days); assignin('base','WaterDemand',WaterDemand) % Randomize initial height. h0 = 3*randn; while h0 <= 0 || h0 >= H_max h0 = 3*randn; end blk = 'watertankscheduling/Water Tank System/Initial Water Height'; in = setBlockParameter(in,blk,'Value',num2str(h0)); end