주요 콘텐츠

여러 개의 에이전트가 협업해서 작업을 수행하도록 훈련시키기

이 예제에서는 Simulink® 환경에서 다중 에이전트 분산식 또는 중앙 집중식 훈련을 설정하는 방법을 보여줍니다. 이 예제를 통해 2개의 에이전트가 협업해서 물체를 옮기는 작업을 훈련해 보겠습니다.

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

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

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

previousRngState = rng(0,"twister");

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

환경 설명

이 예제의 환경은 원으로 표현된 요소들이 들어있는 마찰 없는 2차원 표면입니다. 타깃 물체 C는 반지름이 2m인 파란색 원이고, 로봇 A(빨간색)와 로봇 B(녹색)는 각각 반지름이 1m인 더 작은 원입니다. 이들 로봇은 충돌을 통해 힘을 가하여 물체 C를 반지름이 8m인 원의 테두리 밖으로 옮기려고 합니다. 환경 내에 있는 모든 요소들은 질량을 가지며 뉴턴의 운동 법칙을 따릅니다. 또한, 요소와 환경 경계 사이의 접촉력은 스프링과 질량 댐퍼 시스템으로 모델링됩니다. 요소들은 외부적으로 가해진 힘의 적용을 통해 표면에서 X와 Y 방향으로 움직일 수 있습니다. 요소는 3차원에서 움직이지 않으며 시스템의 총 에너지가 보존됩니다.

rlCollaborativeTaskParams 스크립트를 사용하여, 이 예제에 필요한 변수를 만듭니다.

rlCollaborativeTaskParams

Simulink 모델을 엽니다.

mdl = "rlCollaborativeTask";
open_system(mdl)

이 환경의 경우 다음이 적용됩니다.

  • 2차원 공간은 X와 Y 방향에서 모두 –12m부터 12m까지 경계가 지정되어 있습니다.

  • 접촉 스프링 경직성과 감쇠 값은 각각 100N/m 및 0.1N/m/s입니다.

  • 에이전트들은 A, B, C의 위치와 속도에 대해 동일한 관측값을 공유하고 마지막 시간 스텝의 행동 값을 공유합니다.

  • 물체 C가 원의 테두리 밖으로 나가면 시뮬레이션이 종료됩니다.

  • 각 시간 스텝에서 에이전트는 다음 보상을 받습니다.

rA=rglobal+rlocal,ArB=rglobal+rlocal,Brglobal=0.001dcrlocal,A=-0.005dAC-0.008uA2rlocal,B=-0.005dBC-0.008uB2

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

  • rArB는 각각 에이전트 A와 B가 받는 보상입니다.

  • rglobal은 물체 C가 테두리 경계 쪽으로 더 가까이 움직였을 때 두 에이전트가 모두 받는 팀 보상입니다.

  • rlocal,Arlocal,B는 물체 C와의 거리 및 마지막 시간 스텝의 행동 크기에 기반하여 에이전트 A와 B가 받는 국소 벌점입니다.

  • dC는 원의 중심에서 물체 C까지의 거리입니다.

  • dACdBC는 각각 에이전트 A와 물체 C 사이, 그리고 에이전트 B와 물체 C 사이의 거리입니다.

  • 에이전트가 로봇에 외부 힘을 가하고 그 결과 로봇이 움직입니다. uAuB는 마지막 시간 스텝에서 두 에이전트 A와 B의 행동 값입니다. 행동 값의 범위는 -1과 1 사이입니다.

환경 객체 만들기

다중 에이전트 환경을 만들려면 string형 배열을 사용하여 에이전트의 블록 경로를 지정하십시오. 또한, 셀형 배열을 사용하여 관측값과 행동 사양 객체를 지정하십시오. 셀형 배열에서 사양 객체의 순서는 블록 경로 배열에 지정된 순서와 일치해야 합니다. 환경을 생성하는 시점에 MATLAB® 작업 공간에 사용 가능한 에이전트가 있는 경우, 관측값 및 행동 사양 배열은 선택 사항입니다. 다중 에이전트 환경을 만드는 방법에 대한 자세한 내용은 rlSimulinkEnv 항목을 참조하십시오.

환경에 대한 I/O 사양을 만듭니다. 이 예제에서 에이전트는 동일한 유형이며 동일한 I/O 사양을 가집니다.

% Number of observations
numObs = 16;

% Number of actions
numAct = 2;

% Maximum value of externally applied force (N)
maxF = 1.0;

% I/O specifications for each agent
oinfo = rlNumericSpec([numObs,1]);
ainfo = rlNumericSpec([numAct,1], ...
    UpperLimit= maxF, ...
    LowerLimit= -maxF);
oinfo.Name = "observations";
ainfo.Name = "forces";

Simulink 환경 객체를 만듭니다.

blks = ["rlCollaborativeTask/Agent A", "rlCollaborativeTask/Agent B"];
obsInfos = {oinfo,oinfo};
actInfos = {ainfo,ainfo};
env = rlSimulinkEnv(mdl,blks,obsInfos,actInfos);

환경에 대한 재설정 함수를 지정합니다. 함수 resetRobots는 각 에피소드가 시작할 때마다 로봇이 임의의 초기 위치에서 시작할 수 있게 해줍니다. 이 함수는 이 예제의 끝부분에 제공됩니다.

env.ResetFcn = @(in) resetRobots(in,RA,RB,RC,boundaryR);

디폴트 PPO 에이전트 만들기

이 예제에서는 연속 행동 공간을 갖는 4개의 PPO(근위 정책 최적화) 에이전트를 사용합니다(구체적으로 두 개의 에이전트는 분산식으로 훈련되고, 다른 두 개의 에이전트는 중앙 집중식으로 훈련됨). PPO 에이전트에 대한 자세한 내용은 PPO(근위 정책 최적화) 에이전트 항목을 참조하십시오.

에이전트가 로봇에 외부 힘을 가하고 그 결과 로봇이 움직입니다.

에이전트는 미니 배치 크기 300과 경험 지평 600을 사용하여 수집된 궤적에서 훈련됩니다. 훈련 안정성을 높이기 위해 목적 함수 자르기 인자 0.2를 사용하고, 장기 보상을 장려하기 위해 감가 인자 0.99를 사용합니다.

이 예제에 대한 에이전트 옵션을 지정합니다.

agentOptions = rlPPOAgentOptions(...
    ExperienceHorizon=600,...
    ClipFactor=0.2,...
    EntropyLossWeight=0.01,...
    MiniBatchSize=300,...
    NumEpoch=4,...
    SampleTime=Ts,...
    DiscountFactor=0.99);

액터와 크리틱에 대한 학습률을 1e-4로 설정합니다.

agentOptions.ActorOptimizerOptions.LearnRate  = 1e-4;
agentOptions.CriticOptimizerOptions.LearnRate = 1e-4;

에이전트를 생성하면 액터 신경망과 크리틱 신경망의 초기 파라미터가 난수 값으로 초기화됩니다. 에이전트가 항상 동일한 파라미터 값으로 초기화되도록 난수 스트림을 고정합니다.

rng(0,"twister");

디폴트 에이전트 생성 구문을 사용하여 에이전트를 만듭니다. 자세한 내용은 rlPPOAgent 항목을 참조하십시오.

dcAgentA = rlPPOAgent(oinfo, ainfo, ...
    rlAgentInitializationOptions(NumHiddenUnit= 200), ...
    agentOptions);
dcAgentB = rlPPOAgent(oinfo, ainfo, ...
    rlAgentInitializationOptions(NumHiddenUnit= 200), ...
    agentOptions);
cnAgentA = rlPPOAgent(oinfo, ainfo, ...
    rlAgentInitializationOptions(NumHiddenUnit= 200), ...
    agentOptions);
cnAgentB = rlPPOAgent(oinfo, ainfo, ...
    rlAgentInitializationOptions(NumHiddenUnit= 200), ...
    agentOptions);

에이전트 훈련시키기

여러 개의 에이전트를 훈련시키려면 에이전트로 구성된 배열을 train 함수에 전달할 수 있습니다. 배열에서 에이전트의 순서는 환경 생성 중에 지정된 에이전트 블록 경로의 순서와 일치해야 합니다. 이렇게 하면 agent 객체가 환경에서 적절한 I/O 인터페이스에 연결됩니다.

분산식 또는 중앙 집중식으로 여러 개의 에이전트를 훈련시킬 수 있습니다. 분산식 훈련에서 에이전트는 에피소드 중에 자신만의 경험 세트를 수집하고 이러한 경험으로부터 독립적으로 학습합니다. 중앙 집중식 훈련에서 에이전트는 수집한 경험을 공유하고 이러한 경험으로부터 함께 학습합니다. 궤적이 완성된 후에 에이전트 간에 액터와 크리틱 함수가 동기화됩니다.

다중 에이전트 훈련을 구성하려면 rlMultiAgentTrainingOptions 객체를 통해 에이전트 그룹을 만들고 각 그룹에 대한 학습 전략을 지정할 수 있습니다. 각 에이전트 그룹은 고유한 에이전트 인덱스를 포함할 수 있으며, 학습 전략은 "centralized" 또는 "decentralized"일 수 있습니다. 예를 들어, 다음 명령을 사용하여 서로 다른 학습 전략을 갖는 3개의 에이전트 그룹에 대한 훈련을 구성할 수 있습니다. 인덱스가 [1,2][3,4]인 에이전트는 중앙 집중식으로 학습하는 반면, 에이전트 4는 분산식으로 학습합니다.

opts = rlMultiAgentTrainingOptions(AgentGroups= {[1,2], 4, [3,5]}, LearningStrategy= ["centralized","decentralized","centralized"])

자세한 내용은 rlMultiAgentTrainingOptions 항목을 참조하십시오.

1. 분산식 훈련

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

rng(0,"twister");

이 예제에 대해 분산식 다중 에이전트 훈련을 구성하려면 다음을 수행하십시오.

  • AgentGroups=auto 옵션을 사용하여 에이전트 그룹을 자동으로 할당합니다. 이 경우 각 에이전트가 별개의 그룹에 할당됩니다.

  • "decentralized" 학습 전략을 지정합니다.

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

dcOpts = rlMultiAgentTrainingOptions(...
    AgentGroups="auto",...
    LearningStrategy="decentralized",...
    MaxEpisodes=500,...
    MaxStepsPerEpisode=600,...
    ScoreAveragingWindowLength=30,...
    StopTrainingCriteria="none");

train 함수를 사용하여 에이전트를 훈련시킵니다. 계산 성능에 따라 훈련을 완료하는 데 몇 시간이 걸릴 수 있습니다. 시간을 절약하기 위해 사전 훈련된 에이전트 세트가 포함된 MAT 파일 decentralizedAgents.mat를 불러옵니다. 에이전트를 직접 훈련시키려면 dcTrainingtrue로 설정하십시오.

dcTraining = false;
if dcTraining
    dcResults = train([dcAgentA,dcAgentB], env, dcOpts);
else
    load("decentralizedAgents.mat");
end

다음 그림은 분산식 훈련 진행의 스냅샷을 보여줍니다. 훈련 과정에서 임의성으로 인해 서로 다른 결과가 나올 수 있습니다.

2. 중앙 집중식 훈련

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

rng(0,"twister");

이 예제에 대해 중앙 집중식 다중 에이전트 훈련을 구성하려면 다음을 수행하십시오.

  • 두 에이전트(인덱스 12)를 모두 하나의 그룹에 할당합니다. 이렇게 하려면 "AgentGroups" 옵션에 에이전트 인덱스를 지정하십시오.

  • "centralized" 학습 전략을 지정합니다.

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

cnOpts = rlMultiAgentTrainingOptions(...
    AgentGroups={[1,2]},...
    LearningStrategy="centralized",...
    MaxEpisodes=500,...
    MaxStepsPerEpisode=600,...
    ScoreAveragingWindowLength=30,...
    StopTrainingCriteria="none");

train 함수를 사용하여 에이전트를 훈련시킵니다. 계산 성능에 따라 훈련을 완료하는 데 몇 시간이 걸릴 수 있습니다. 시간을 절약하기 위해 사전 훈련된 에이전트 세트가 포함된 MAT 파일 centralizedAgents.mat를 불러옵니다. 에이전트를 직접 훈련시키려면 cnTrainingtrue로 설정하십시오.

cnTraining = false;
if cnTraining
    cnResults = train([cnAgentA,cnAgentB], env, cnOpts);
else
    load("centralizedAgents.mat");
end

다음 그림은 중앙 집중식 훈련 진행의 스냅샷을 보여줍니다. 훈련 과정에서 임의성으로 인해 서로 다른 결과가 나올 수 있습니다.

환경 시뮬레이션하기

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

rng(0,"twister");

시뮬레이션이 수행되는 환경 스텝의 최대 수를 600으로 지정하는 simulation options 객체를 만듭니다.

simOptions = rlSimulationOptions(MaxSteps=600);

분산식으로 훈련된 에이전트를 환경에서 시뮬레이션합니다.

simulationChoice = "decentralized";
if strcmpi(simulationChoice,"decentralized")
    dcExp = sim(env,[dcAgentA,dcAgentB],simOptions);
end

Figure Multi Agent Collaborative Task contains an axes object. The axes object with xlabel X (m), ylabel Y (m) contains 5 objects of type rectangle, text.

중앙 집중식으로 훈련된 에이전트를 시뮬레이션하기 위해 simulationChoicecentralized로 설정합니다.

if strcmpi(simulationChoice,"centralized")
    cnExp = sim(env,[cnAgentA,cnAgentB],simOptions);
end

에이전트가 물체를 테두리 밖으로 밀어낼 수 있습니다.

에이전트 시뮬레이션에 대한 자세한 내용은 rlSimulationOptions 항목과 sim 항목을 참조하십시오.

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

rng(previousRngState);

로컬 함수

환경을 재설정하는 헬퍼 함수입니다.

function in = resetRobots(in,RA,RB,RC,boundaryR)
% Reset the environment.

% Random initial positions
valid = false;
while ~valid
    xA0 = -10 + 20*rand;
    yA0 = -10 + 20*rand;
    xB0 = -10 + 20*rand;
    yB0 = -10 + 20*rand;
    r = 0.5*boundaryR*rand;
    th = -pi + 2*pi*rand;
    xC0 = r*cos(th);
    yC0 = r*sin(th);
    dAB = norm([xA0-xB0;yA0-yB0]);
    dBC = norm([xB0-xC0;yB0-yC0]);
    dAC = norm([xA0-xC0;yA0-yC0]);
    dA = norm([xA0;yA0]);
    dB = norm([xB0;yB0]);
    valid = dA > boundaryR && ... 
        dB > boundaryR && ... 
        dAB > (RA+RB) && ... 
        dBC > (RB+RC) && ... 
        dAC > (RA+RC);
end

% Set the variables in the simulation input object
in = setVariable(in,'xA0',xA0);
in = setVariable(in,'xB0',xB0);
in = setVariable(in,'yA0',yA0);
in = setVariable(in,'yB0',yB0);

환경을 플로팅하는 메인 헬퍼 함수입니다.

% Set a post sim function for visualization
in = setPostSimFcn(in,@(out) localPostSim(out,RA,RB,RC,boundaryR));
end

function out = localPostSim(out,RA,RB,RC,boundaryR)
tsqA = localGetState(out,'qA');
tsqB = localGetState(out,'qB');
tsqC = localGetState(out,'qC');
xA = tsqA.Data(:,1);
xB = tsqB.Data(:,1);
xC = tsqC.Data(:,1);
yA = tsqA.Data(:,2);
yB = tsqB.Data(:,2);
yC = tsqC.Data(:,2);
t = tsqA.Time;
for i = 1:numel(xA)
    plotEnvironment( ...
        [xA(i);xB(i);xC(i)], ...
        [yA(i);yB(i);yC(i)], ...
        [RA;RB;RC], boundaryR, t(i));
    drawnow();
end
end

function ts = localGetState(out,name)
stateObj = out.logsout.get(name);
ts = stateObj.Values;
end

환경을 플로팅하는 2차 헬퍼 함수입니다(setPostSimFcn에서 호출됨).

function plotEnvironment(x,y,R,boundaryR,t)
% Plot the environment.

persistent f;
if isempty(f) || ~isvalid(f)
    f = figure(...
        NumberTitle="off",...
        Name="Multi Agent Collaborative Task",...
        MenuBar="none",...
        Position=[500,500,300,300],...
        Visible="on");
    ha = gca(f);
    axis(ha,"equal");
    ha.XLim = [-12 12];
    ha.YLim = [-12 12];
    xlabel(ha,"X (m)");
    ylabel(ha,"Y (m)");
    
    D = [-1 -1 2 2]*boundaryR;
    rectangle(ha,Position=D,Curvature=[1 1]);
    
    hold(ha,'on');
end
% plot elements
ha = gca(f);
N = numel(R);
colors = ["r","g","b"];
for i = 1:N
    D = [(x(i)-R(i)) (y(i)-R(i)) 2*R(i) 2*R(i)];
    tagstr = sprintf("rect_%u",i);
    r = findobj(ha,Tag=tagstr);
    if isempty(r)
        r = rectangle(ha,Position=D,Curvature=[1 1],Tag=tagstr);
        r.FaceColor = colors(i);
    else
        r.Position = D;
    end
end
% Display time string
timestr = sprintf("Time: %0.1f s",t);
time = findobj(ha,Tag="time");
if isempty(time)
    text(ha,5,10,timestr,Tag="time");
else
    time.String = timestr;
end
end

참고 항목

함수

객체

블록

도움말 항목