주요 콘텐츠

강화 학습을 사용하여 PI 제어기 조정하기

이 예제에서는 TD3(Twin-Delayed Deep Deterministic: 트윈 지연 심층 결정적) 정책 경사 강화 학습 알고리즘을 사용하여 PI 제어기의 2가지 이득을 조정하는 방법을 보여줍니다. 조정된 제어기의 성능은 제어 시스템 조정기 앱을 사용하여 조정된 제어기의 성능과 비교됩니다. Simulink®에서 제어 시스템 조정기 앱을 사용하여 제어기를 조정하려면 Simulink Control Design™이 필요합니다.

조정 가능형 파라미터 수가 적어 비교적 간단한 제어 작업의 경우 모델 기반 조정 기법을 사용하면 모델이 주어지지 않은(model-free) RL 기반 방법보다 조정 처리가 더 빠르며 좋은 결과를 얻을 수 있습니다. 하지만 상당히 비선형적인 시스템이나 적응형 제어기 조정 작업에는 RL 방법이 더 적절할 수 있습니다. 제어기 비교가 용이하도록 두 조정 방법에는 선형 2차 가우스(LQG) 목적 함수가 사용됩니다. DDPG 에이전트를 사용하여 LQR 제어기를 구현하는 예제는 Compare DDPG Agent to LQR Controller 항목을 참조하십시오.

이 예제에서는 강화 학습(RL) 에이전트를 사용하여 PI 제어기에 대한 이득을 계산합니다. PI 제어기를 신경망 제어기로 바꾸는 예제는 DDPG 에이전트를 사용하여 탱크의 수위 제어하기 항목을 참조하십시오.

재현이 가능하도록 난수 스트림 고정하기

예제 코드의 다양한 단계에서 난수 계산이 포함될 수 있습니다. 예제 코드에 있는 다양한 섹션의 시작 부분에서 난수 스트림을 고정하면 매 실행 시에 섹션의 난수열이 유지되며 결과를 재현할 가능성이 높아집니다. 자세한 내용은 결과 재현성 항목을 참조하십시오.

시드값 0과 난수 알고리즘인 메르센 트위스터를 사용하여 난수 스트림을 고정합니다. 난수 생성에 사용되는 시드값을 제어하는 방법에 대한 자세한 내용은 rng 항목을 참조하십시오.

previousRngState = rng(0,"twister");

출력값 previousRngState는 스트림의 이전 상태에 대한 정보를 포함하는 구조체입니다. 이 예제의 끝부분에서 그 상태를 복원할 것입니다.

환경 모델

이 예제의 환경 모델은 물탱크 모델입니다. 이 제어 시스템의 목표는 탱크에 있는 물의 수위를 기준 값과 일치하도록 유지하는 것입니다.

open_system('watertankLQG')

모델에는 분산 E(n2(t))=1을 갖는 공정 잡음이 포함되어 있습니다.

물의 수위를 유지하면서도 제어 노력 u를 최소화하기 위해 이 예제의 제어기에는 다음 LQG 기준이 사용됩니다.

J=limTE(1T0T((Href-y(t))2+0.01u2(t))dt)

이 모델에서 제어기를 시뮬레이션하기 위해, 시뮬레이션 시간 Tf와 제어기 샘플 시간 Ts를 초 단위로 지정합니다.

Ts = 0.1;
Tf = 10;

물탱크 모델에 대한 자세한 내용은 watertank Simulink 모델 (Simulink Control Design) 항목을 참조하십시오.

제어 시스템 조정기를 사용하여 PI 제어기 조정하기

Simulink에서 제어 시스템 조정기를 사용하여 제어기를 조정하려면, 제어기 블록을 조정 블록으로 지정하고 조정 프로세스의 목표를 정의해야 합니다. 제어 시스템 조정기 사용 방법에 대한 자세한 내용은 Tune a Control System Using Control System Tuner (Simulink Control Design) 항목을 참조하십시오.

이 예제에서는 저장된 세션 ControlSystemTunerSession.mat제어 시스템 조정기를 사용하여 엽니다. 이 세션에는 watertankLQG의 PID Controller 블록이 조정 블록으로 지정되고 LQG 조정 목표가 포함되어 있습니다.

controlSystemTuner("ControlSystemTunerSession")

제어기를 조정하려면 조정 탭에서 조정을 클릭합니다.

조정된 비례 이득과 적분 이득은 대략적으로 각각 9.8과 1e-6입니다.

Kp_CST = 9.80199999804512;
Ki_CST = 1.00019996230706e-06;

에이전트 훈련을 위한 환경 만들기

RL 에이전트 훈련을 위한 모델을 정의하려면 다음 단계에 따라 물탱크 모델을 수정하십시오.

  1. PID 제어기를 삭제합니다.

  2. RL Agent 블록을 삽입합니다.

  3. 관측값 벡터 [e dte]T 를 만듭니다. 여기서 e=Href-h이고, h는 탱크의 수위이며, Href는 기준 수위입니다. RL Agent 블록에 관측값 신호를 연결합니다.

  4. RL 에이전트의 보상 함수를 LQG 비용의 음수로 정의합니다. 즉, Reward=-((Href-h(t))2+0.01u2(t))입니다. RL 에이전트는 이 보상을 최대화하므로 LQG 비용을 최소화합니다.

결과로 생성되는 모델은 rlwatertankPIDTune.slx입니다.

mdl = 'rlwatertankPIDTune';
open_system(mdl)

rlSimulinkEnv를 사용하여 물탱크 RL 환경 객체를 만듭니다. 환경에 대한 재설정 함수를 설정하기 위해 이 예제의 끝에 정의된 localResetFcn을 사용합니다.

% Define the observation specification obsInfo 
% and the action specification actInfo.
obsInfo = rlNumericSpec([2 1]);
obsInfo.Name = 'observations';
obsInfo.Description = 'integrated error and error';

actInfo = rlNumericSpec([1 1]);
actInfo.Name = 'PID output';

% Build the environment object.
env = rlSimulinkEnv(mdl,[mdl '/RL Agent'],obsInfo,actInfo);

% Set a custom reset function that randomizes 
% the reference values for the model.
env.ResetFcn = @(in)localResetFcn(in,mdl);

이 환경에 대한 관측값 및 행동 차원을 추출합니다. prod(obsInfo.Dimension)prod(actInfo.Dimension)을 사용하여 행 벡터, 열 벡터 또는 행렬로 정렬되는지에 관계없이 각각 관측값 공간과 행동 공간의 차원 수를 얻습니다.

numObs = prod(obsInfo.Dimension);
numAct = prod(actInfo.Dimension);

재현이 가능하도록 난수 생성기 시드값을 고정합니다.

rng(0)

TD3 에이전트 만들기

재현이 가능하도록 난수 스트림을 고정합니다.

rng(0,"twister");

PI 제어기를 액터로 모델링할 수 있습니다. 액터는 오차 적분과 오차 관측값을 입력값으로 사용합니다.

u=[edte]*[KiKp]T

여기서 각 요소는 다음과 같습니다.

  • u는 액터의 출력값입니다.

  • KiKp는 액터의 가중치입니다.

  • e=Href-h(t)에서 h(t)는 탱크의 수위이며, Href는 기준 수위입니다.

액터를 만들려면 rlContinuousDeterministicActor과 기저 함수 myBasisFcn을 사용합니다. 이는 예제 폴더에 별도의 파일로 제공됩니다. 자세한 내용은 rlContinuousDeterministicActor 항목을 참조하십시오.

initialGain = single([1e-3 2]);
actor = rlContinuousDeterministicActor( ...
    {@myBasisFcn,initialGain'},obsInfo, actInfo);

이 예제의 에이전트는 TD3(트윈 지연 심층 결정적) 정책 경사 에이전트입니다. TD3 에이전트는 최적의 정책을 학습하기 위해 액터 및 크리틱 근사기 객체에 의존합니다.

TD3 에이전트는 관측값과 행동이 주어지면 2개의 크리틱 가치 함수 표현을 사용하여 장기 보상을 근사합니다. 크리틱을 만들려면 먼저 관측값과 행동에 해당하는 2개의 입력값 및 1개의 출력값을 갖는 심층 신경망을 만드십시오.

크리틱 신경망을 만듭니다. 두 크리틱 표현에 동일한 신경망 구조를 사용합니다.

statePath = [
    featureInputLayer(numObs,Name='stateInLyr')
    fullyConnectedLayer(32,Name='fc1')
    ];

actionPath = [
    featureInputLayer(numAct,Name='actionInLyr')
    fullyConnectedLayer(32,Name='fc2')
    ];

commonPath = [
    concatenationLayer(1,2,Name='concat')
    reluLayer
    fullyConnectedLayer(32)
    reluLayer
    fullyConnectedLayer(1,Name='qvalOutLyr')
    ];

criticNet = dlnetwork();
criticNet = addLayers(criticNet,statePath);
criticNet = addLayers(criticNet,actionPath);
criticNet = addLayers(criticNet,commonPath);

criticNet = connectLayers(criticNet,'fc1','concat/in1');
criticNet = connectLayers(criticNet,'fc2','concat/in2');

지정된 신경망과 환경 행동 및 관측값 사양을 사용하여 critic 객체를 만듭니다. 관측값 채널 및 행동 채널과 연결할 신경망 계층의 이름도 추가 인수로 전달합니다.

critic1 = rlQValueFunction(criticNet, ...
    obsInfo,actInfo,...
    ObservationInputNames='stateInLyr', ...
    ActionInputNames='actionInLyr');

critic2 = rlQValueFunction(criticNet, ...
    obsInfo,actInfo,...
    ObservationInputNames='stateInLyr', ...
    ActionInputNames='actionInLyr');

critic 객체로 구성된 벡터를 정의합니다.

critic = [critic1 critic2];

액터와 크리틱에 대한 훈련 옵션을 지정합니다.

actorOpts = rlOptimizerOptions( ...
    LearnRate=1e-3, ...
    GradientThreshold=1);

criticOpts = rlOptimizerOptions( ...
    LearnRate=1e-3, ...
    GradientThreshold=1);

rlTD3AgentOptions 객체를 사용하여 TD3 에이전트 옵션을 지정합니다. 액터와 크리틱에 대한 훈련 옵션을 포함시킵니다.

  • 제어기 샘플 시간 Ts를 사용하도록 에이전트를 설정합니다.

  • 미니 배치 크기를 256개의 경험 샘플로 설정합니다.

  • 경험 버퍼 길이를 1e6으로 설정합니다.

  • 탐색 모델과 타깃 정책 평활화 모델에서 분산 0.1의 가우스 잡음을 사용하도록 설정합니다.

agentOpts = rlTD3AgentOptions(...
    SampleTime=Ts,...
    MiniBatchSize=256, ...
    ExperienceBufferLength=1e6,...
    ActorOptimizerOptions=actorOpts,...
    CriticOptimizerOptions=criticOpts);

점 표기법을 사용하여 에이전트 옵션을 설정하거나 수정할 수도 있습니다.

agentOpts.TargetPolicySmoothModel.StandardDeviation = sqrt(0.1);

지정된 액터 표현, 크리틱 표현, 에이전트 옵션을 사용하여 TD3 에이전트를 만듭니다. 자세한 내용은 rlTD3AgentOptions 항목을 참조하십시오.

agent = rlTD3Agent(actor,critic,agentOpts);

에이전트 훈련시키기

재현이 가능하도록 난수 스트림을 고정합니다.

rng(0,"twister");

에이전트를 훈련시키려면 먼저 다음 훈련 옵션을 지정하십시오.

  • 최대 1000개의 에피소드에 대해 각 훈련을 실행하며, 각 에피소드마다 최대 100개의 시간 스텝이 지속됩니다.

  • 강화 학습 훈련 모니터에 훈련 진행 상황을 표시하고(Plots 옵션 설정) 명령줄 표시를 비활성화합니다(Verbose 옵션 설정).

  • 훈련 에피소드 50개마다 그리디 정책 성능을 평가하고, 5회 시뮬레이션의 누적 보상에 대한 평균값을 계산합니다.

  • 평가 점수가 -345에 도달하면 훈련을 중지합니다. 이 시점에서 에이전트는 탱크에 있는 물의 수위를 제어할 수 있습니다.

훈련 옵션에 대한 자세한 내용은 rlTrainingOptions 항목을 참조하십시오.

maxepisodes = 1000;
maxsteps = ceil(Tf/Ts);
trainOpts = rlTrainingOptions(...
    MaxEpisodes=maxepisodes, ...
    MaxStepsPerEpisode=maxsteps, ...
    ScoreAveragingWindowLength=100, ...
    Verbose=false, ...
    Plots="training-progress",...
    StopTrainingCriteria="EvaluationStatistic",...
    StopTrainingValue=-345);

% agent evaluator
evaluator = rlEvaluator( ...
    NumEpisodes=5,EvaluationFrequency=50,RandomSeeds=1:5);

train 함수를 사용하여 에이전트를 훈련시킵니다. 이 에이전트를 훈련시키는 것은 완료하는 데 수 분이 소요되는 계산 집약적인 절차입니다. 이 예제를 실행하는 동안 시간을 절약하려면 doTrainingfalse로 설정하여 사전 훈련된 에이전트를 불러오십시오. 에이전트를 직접 훈련시키려면 doTrainingtrue로 설정하십시오.

doTraining = false;
if doTraining
    % Train the agent.
    trainingStats = train(agent,env,trainOpts,Evaluator=evaluator);
else
    % Load pretrained agent for the example.
    load("WaterTankPIDtd3.mat","agent")
end

PI_tune.png

훈련된 에이전트 검증하기

재현이 가능하도록 난수 스트림을 고정합니다.

rng(0,"twister");

모델에 대해 훈련된 에이전트를 시뮬레이션으로 검증합니다.

simOpts = rlSimulationOptions(MaxSteps=maxsteps);
experiences = sim(env,agent,simOpts);

PI 제어기의 적분 이득과 비례 이득은 액터 표현의 가중치입니다. 이 가중치를 구하려면 먼저 액터에서 학습 가능한 파라미터를 추출합니다.

actor = getActor(agent);
parameters = getLearnableParameters(actor);

제어기 이득을 구합니다.

Ki = parameters{1}(1)
Ki = single

0.4862
Kp = parameters{1}(2)
Kp = single

8.2851

RL 에이전트로부터 구한 이득을 원래 PI Controller 블록에 적용하고 계단-응답 시뮬레이션을 실행합니다.

mdlTest = 'watertankLQG';
open_system(mdlTest); 
set_param([mdlTest '/PID Controller'],'P',num2str(Kp))
set_param([mdlTest '/PID Controller'],'I',num2str(Ki))
sim(mdlTest)

시뮬레이션의 계단 응답 정보, LQG 비용, 안정성 여유를 추출합니다. 안정성 여유를 계산하려면 이 예제 끝에 정의된 localStabilityAnalysis를 사용합니다.

rlStep = simout;
rlCost = cost;
rlStabilityMargin = localStabilityAnalysis(mdlTest);

구해진 이득을 제어 시스템 조정기를 사용하여 원래 PI Controller 블록에 적용하고 계단-응답 시뮬레이션을 실행합니다.

set_param([mdlTest '/PID Controller'],'P',num2str(Kp_CST))
set_param([mdlTest '/PID Controller'],'I',num2str(Ki_CST))
sim(mdlTest)
cstStep = simout;
cstCost = cost;
cstStabilityMargin = localStabilityAnalysis(mdlTest);

제어기 성능 비교하기

각 시스템의 계단 응답을 플로팅합니다.

figure
plot(cstStep)
hold on
plot(rlStep)
grid on
legend('Control System Tuner','RL',Location="southeast")
title('Step Response')

Figure contains an axes object. The axes object with title Step Response, xlabel Time (seconds), ylabel y contains 2 objects of type line. These objects represent Control System Tuner, RL.

두 시뮬레이션의 계단 응답을 분석합니다.

rlStepInfo = stepinfo(rlStep.Data,rlStep.Time);
cstStepInfo = stepinfo(cstStep.Data,cstStep.Time);
stepInfoTable = struct2table([cstStepInfo rlStepInfo]);
stepInfoTable = removevars(stepInfoTable,{'SettlingMin', ...
    'TransientTime','SettlingMax','Undershoot','PeakTime'});
stepInfoTable.Properties.RowNames = {'CST','RL'};
stepInfoTable
stepInfoTable=2×4 table
           RiseTime    SettlingTime    Overshoot     Peak 
           ________    ____________    _________    ______

    CST    0.77737        1.3278        0.33125     9.9023
    RL     0.95173        1.6188        0.45174     10.103

두 시뮬레이션의 안정성을 분석합니다.

stabilityMarginTable = struct2table( ...
    [cstStabilityMargin rlStabilityMargin]);
stabilityMarginTable = removevars(stabilityMarginTable,{...
    'GMFrequency','PMFrequency','DelayMargin','DMFrequency'});
stabilityMarginTable.Properties.RowNames = {'CST','RL'};
stabilityMarginTable
stabilityMarginTable=2×3 table
           GainMargin    PhaseMargin    Stable
           __________    ___________    ______

    CST      8.1616        84.123       true  
    RL       9.6843        83.828       true  

두 제어기의 누적 LQG 비용을 비교합니다. RL 조정 제어기가 조금 더 나은 최적해를 생성합니다.

rlCumulativeCost  = sum(rlCost.Data)
rlCumulativeCost = 
-375.4192
cstCumulativeCost = sum(cstCost.Data)
cstCumulativeCost = 
-376.9373

두 제어기 모두 안정된 응답을 생성하며, 응답 속도는 제어 시스템 조정기를 사용하여 조정된 제어기가 더 빠릅니다. 하지만 RL 조정 방법이 더 높은 이득 여유와 더 나은 최적해를 생성합니다.

previousRngState에 저장된 정보를 사용하여 난수 스트림을 복원합니다.

rng(previousRngState);

로컬 함수

각 에피소드가 시작할 때 탱크 내부의 초기 물 높이와 기준 신호를 무작위로 할당하는 함수입니다.

function in = localResetFcn(in,mdl)

% Randomize reference signal
blk = sprintf([mdl '/Desired \nWater Level']);
hRef = 10 + 4*(rand-0.5);
in = setBlockParameter(in,blk,'Value',num2str(hRef));

% Randomize initial water height
hInit = rand;
blk = [mdl '/Water-Tank System/H'];
in = setBlockParameter(in,blk,'InitialCondition',num2str(hInit));

end

SISO 물탱크 시스템의 안정성 여유를 선형화하고 계산하는 함수입니다.

function margin = localStabilityAnalysis(mdl)

io(1) = linio([mdl '/Sum1'],1,'input');
io(2) = linio([mdl '/Water-Tank System'],1,'openoutput');
op = operpoint(mdl);
op.Time = 5;
linsys = linearize(mdl,io,op);

margin = allmargin(linsys);
end

참고 항목

함수

객체

도움말 항목