11. Reward 구현

date
2월 28, 2026
status
완료
tags
unrealRL

1. 소개#

강화학습에서 Reward는 에이전트가 어떤 행동을 더 배워야 하는지 알려주는 기준이다. 맨 처음 강화학습을 알게 된 계기였던 그네를 잘타게 하는 법을 학습 시키는 영상에 나오는 것(유전적 알고리즘으로 그네 타는 법을 학습시켰다)이 처음에는 ‘와 저게 바로 강화학습이구나!’ 했는데 둘 다 시도해보고 더 잘한 걸 더 남긴다라는 건 비슷하지만 꽤나 다양한 방면으로 차이가 있는 걸 알게 되었다.

가장 큰 차이점은 유전 알고리즘은 전체 움직임 패턴의 진화라면 강화학습 방식은 하나의 정책이 계속 경험을 쌓는 것의 차이라고 해야할 듯 하다. 보상이라는 것도 유전 알고리즘에서는 개체를 선별하기 위한 Fitness Score 라면, 강화학습에서는 행동을 학습시키기 위한 Reward니 둘의 쓰임이 꽤나 차이가 나는 것을 알 수있다. 차이점을 표로 작성해보면 다음과 같다

구분유전 알고리즘강화학습
기본 단위여러 후보 개체하나의 에이전트/정책
개선 방식잘한 개체를 선택, 교배, 변이행동 후 받은 보상으로 정책 업데이트
학습 기준전체 결과 점수매 순간의 보상
대표 흐름세대 단위 진화경험 단위 학습
예시자동차 100대를 돌려서 잘 달린 자동차의 신경망을 남김자동차가 코너마다 어떤 조향/가속을 해야 보상이 커지는지 학습
느낌“잘한 놈끼리 섞어서 다음 세대 만들기”“이 상황에서는 이 행동이 좋았구나 배우기”

1.1. 초기 Reward 구현 목표#

이번 거미 로봇 프로젝트에서는 처음부터 복잡한 Reward를 설계하지 않고, 걷기 학습에 필요한 가장 기본적인 보상 구조부터 구현해보자. 사실 바로 아크 레이더스에 나오는 이 격자형 맵을 만들어서 진행해보고 싶은 마음은 굴뚝 같지만, 이럴 경우 내 로봇이 잘 못 걸을 경우, 어떤 문제로 인해 못 걷는 지 판별을 못하기 때문에 우선은 평지에서 걷는 것 부터 우선 완성되어야 한다

초기 목표는 완벽한 보행이 아니라, 로봇이 다음 세 가지를 먼저 학습하게 만드는 것이다.

목표의미Reward 방향
앞으로 이동제자리에서 버둥거리지 않고 목표 방향으로 이동전방 이동 시 보상
균형 유지몸체가 과하게 기울거나 넘어지는 행동 방지Pitch/Roll이 커지면 패널티
엎드려 미끄러지기 방지바닥에 붙어서 앞으로 가는 편법 행동 방지몸체 높이가 낮아지면 패널티

여기서 중요한 점은 Reward를 처음부터 많이 넣지 않는 것이다. 발 접지, 관절 에너지, 지형 Raycast, 장애물 회피 보상까지 한 번에 넣으면 학습이 실패했을 때 원인을 파악하기 어렵다. 따라서 현재 단계에서는 평지에서 앞으로 이동하려는 경향이 생기는지를 먼저 확인하고, 이후 단계에서 지형 정보와 장애물 대응을 추가한다.

2. Reward 설계#

우선적으로 실제 보상을 줄 항목을 하나하나 작성해보면,

2.1. 전방 이동 보상#

  • 전방 이동 보상은 로봇이 목표 방향으로 이동할수록 증가하는 보상

  • 로봇이 아무 움직임 없이 서 있거나 제자리에서 다리만 흔들면 학습이 진행되지 않기 때문에, 가장 먼저 “앞으로 이동하는 행동”에 보상을 줘야 한다.

    Forward Reward = 전방 속도 × 보상 가중치

    이 보상은 로봇이 앞으로 움직이는 행동을 찾도록 유도한다.


2.2. 몸체 기울기 패널티#

  • 로봇이 앞으로 이동하더라도 몸체가 심하게 기울거나 넘어지면 보행이라고 보기 어렵다.

    따라서 몸체의 Pitch와 Roll 값을 확인해서, 기울기가 커질수록 패널티를 부여한다.

    Tilt Penalty = (Pitch 절댓값 + Roll 절댓값) × 패널티 가중치

    Yaw는 방향 회전에 해당하므로, 초기 단계에서는 기울기 패널티에 포함하지 않는다.


2.3. 낮은 높이 패널티#

  • 강화학습에서는 에이전트가 사람이 의도하지 않은 방식으로 Reward를 얻는 경우가 많다.

    예를 들어 앞으로 이동 보상만 주면, 로봇이 정상적으로 걷는 대신 몸체를 바닥에 붙이고 미끄러져 이동하는 방법을 찾을 수도 있다. 이를 막기 위해 몸체 높이가 일정 기준보다 낮아지면 패널티를 준다.

    Low Height Penalty =
      몸체 높이가 기준 이하이면 감점

    이 항목은 로봇이 최소한의 자세를 유지한 상태로 이동하도록 유도한다.

3. 구현#

3.1. BP_SpiderTrainingEnvironment 생성#

  • 콘텐츠 브라우저에서 우클릭 → Blueprint Class → All Classes 검색(LearningAgentsTrainingEnvironment) 이름은 BP_SpiderTrainingEnvironment

3.1 Gather Agent Reward 오버라이딩#

1. Gather Agent Reward 함수 오버라이딩#

  • 풀 블루프린트 에디터 열기 →왼쪽에 함수, 오버라이드 목록에서 Gather Agent Reward 선택

  • 입력 변수 추가

    변수명타입기본값
    ForwardRewardScaleFloat0.01
    TiltPenaltyScaleFloat-0.005
    MinBodyHeightFloat50.0
    LowHeightPenaltyFloat-1.0
  • Agent Id → Get Agent 노드 연결 후, Agent Class→BP_SpiderRobot

  • Get Agent의 Return Value에서 드래그 → Spider Mesh 노드 추가(실제 컴포넌트 참조)

  • Spider Mesh/Spider Mesh → Get Physics Linear Velocity 노드 추가

  • 여기까지하면 현재 학습 중인 BP_SpiderRobot을 찾아서 해당 로봇의 스파이더 매쉬를 가져와서 실제 물리 속도를 가져올 수 있다.

2. Spider Mesh에서 Get Forward Vector 만들기#

  • Spider Mesh 파란 핀에서 한 번 더 드래그 → Get Forward Vector 노드 생성

    속도만 보면 로봇이 어느 방향으로 움직이는지 알 수는 있지만, 그게 앞으로 간 건지, 옆으로 미끄러진 건지, 뒤로 간 건지 구분이 안 되기 때문에, Forward Vector로 로봇이 바라보는 방향을 가져온다.


3. Dot Product 만들기#

  • 빈 곳 우클릭해서 검색 →Dot Product, Vector용 Dot Product 노드를 생성

    연결은 이렇게.

    Get Physics Linear Velocity Return Value
    → Dot Product A
    
    Get Forward Vector Return Value
    → Dot Product B

  • Dot Product를 쓰냐면, 이게 속도 방향이 전방 방향과 얼마나 같은지 계산해주기 때문이다.

    상황Dot Product 결과
    앞으로 이동양수
    정지0 근처
    옆으로 이동0 근처
    뒤로 이동음수

    즉, “앞으로 잘 갔는가?”를 숫자로 바꾸는 노드


4. ForwardRewardScale 곱하기#

  • Dot Product 결과에서 드래그 → Multiply 노드를 생성 ,

  • ForwardRewardScale 가져와서 두번째 핀에 연결

0.01을 곱하냐면, Unreal 속도는 보통 cm/s 단위라 값이 너무 크게 나올 수 있기 때문에, 예를 들어 속도가 100이면 Reward가 100이 되어버리니까, 0.01을 곱해서 1.0 정도로 줄이는 것이 좋다. 가능하면 나중에는 이 0.01ForwardRewardScale 변수로 빼주도록 하자.


5. Return Node의 OutReward 에 연결#

  • 마지막으로 곱한 값을 Return NodeOut Reward에 연결

  • 최종 흐름

    flowchart LR
        A[Agent Id] --> B[Get Agent]
        B --> C[Spider Mesh]
        C --> D[Get Physics Linear Velocity]
        C --> E[Get Forward Vector]
        D --> F[Dot Product]
        E --> F
        F --> G[* 0.01]
        G --> H[Out Reward]

3.2. 몸체 기울기 패널티 추가#

  • 현재 Reward는 로봇이 앞으로 이동하면 보상을 주는 구조이다. 하지만 이 상태만으로는 로봇이 정상적으로 걷는 것이 아니라, 몸체를 굴리거나 바닥에 비비면서 앞으로 이동하는 방식도 보상받을 수 있기 때문에, 다음 단계에서는 몸체가 과하게 기울어질 경우 감점하는 Tilt Penalty를 추가해서 해당 방식을 방지해보도록 하자.

    Tilt Penalty =
    (Pitch 절댓값 + Roll 절댓값) × TiltPenaltyScale

    여기서 Pitch는 앞뒤 기울기, Roll은 좌우 기울기이다. Yaw는 방향 회전에 해당하므로, 넘어짐 판단에는 사용하지 않는다.

1. Spider Mesh에서 Get World Rotation 생성#

  • 기존에 사용한 Spider Mesh 핀에서 드래그 → Get World Rotation 노드 생성

    이 노드는 Spider Mesh가 현재 월드에서 어떤 방향으로 회전되어 있는지 가져온다.


2. Break Rotator 생성#

  • Get World Rotation의 Return Value에서 드래그 → Break Rotator 생성

    Break Rotator는 회전값을 Pitch, Yaw, Roll로 나누기 위해 사용한다.


3. Pitch와 Roll에 Absolute 적용#

  • Pitch에서 드래그 → Absolute (Float) 생성
  • Roll에서 드래그 → Absolute (Float) 생성

기울기는 방향보다 크기가 중요하다. 왼쪽으로 기울었는지 오른쪽으로 기울었는지가 아니라, 얼마나 많이 기울었는지가 중요하므로 절댓값을 사용한다.


4. Pitch와 Roll 더하기#

  • Abs(Pitch)Abs(Roll)Float + Float으로 더한다.

    TiltAmount = Abs(Pitch) + Abs(Roll)

    이 값이 클수록 로봇 몸체가 많이 기울어진 상태이다.


5. TiltPenaltyScale 곱하기#

  • 이제 이 합산 값을 패널티 값으로 변경해야 한다

  • TiltAmountTiltPenaltyScale 변수를 곱하도록 하자

    TiltPenalty = TiltAmount × TiltPenaltyScale
  • TiltPenaltyScale이 음수인 이유는 기울기가 커질수록 Reward를 줄이기 위해서이다.

    예를 들어 Pitch가 10도, Roll이 5도라면

    TiltAmount = 10 + 5 = 15
    TiltPenalty = 15 × -0.005 = -0.075

    즉, 약간 기울어진 정도는 작은 감점만 주고, 심하게 기울어질수록 감점이 커진다.


6. 기존 Forward Reward와 더하기#

  • 현재 만들어둔 전방 이동 Reward 결과와 Tilt Penalty를 더하도록 하자

    TotalReward = ForwardReward + TiltPenalty
  • 블루프린트에서는 기존 ForwardRewardScale 곱하기 결과와 TiltPenalty 결과를 Float + Float으로 더한 뒤, 그 값을 Return NodeOut Reward에 연결한다.

  • 이는 총 보상 = 앞으로 간 보상 + 기울기 패널티의 뜻이다.


7. 최종 연결 형태#

ForwardReward = Dot(Velocity, ForwardVector) * ForwardRewardScale

TiltPenalty = (Abs(Roll) + Abs(Pitch)) * TiltPenaltyScale

OutReward = ForwardReward + TiltPenalty
  • 이제 Reward는 단순히 앞으로 이동하는 것만 평가하지 않고, 앞으로 이동하면서도 몸체를 안정적으로 유지하는 행동을 더 선호하게 된다.

3.3. Gather Agent Completion 오버라이딩#

  • Reward는 “좋은 행동/나쁜 행동 점수”이고, Completion은 “이번 에피소드를 끝낼지”를 판단하는 역할이다. 만약 넘어졌는데도 에피소드가 계속 진행되면, 로봇이 넘어진 상태에서 이상한 행동까지 계속 학습하기 때문이다.
  • BP_SpiderTrainingEnvironment에서 오버라이드 목록 열고 → Gather Agent Completion 선택

1. Completion 조건 입력#

  • 일단 생각날만한 종료 조건은 기울기가 너무 기울어서 로봇이 정상적으로 못 움직이는 경우가 있다. 그러니 우선 기울기를 입력받도록 하자

  • 변수 추가

    변수명타입기본값
    MaxTiltAngleForCompletionFloat70.0
  • 노드 흐름 생성

    flowchart LR
        A[Agent Id] --> B[Get Agent]
        B --> C[Spider Mesh]
        C --> D[Get World Rotation]
        D --> E[Break Rotator]
        E --> F[Abs Roll]
        E --> G[Abs Pitch]
        F --> H[Roll > MaxTiltAngle]
        G --> I[Pitch > MaxTiltAngle]
        H --> J[OR]
        I --> J
        J --> K[Out Completion]

3.4. Reset Agent Episode 오버라이딩#

  • 이제 넘어지면 종료 후에 종료된 로봇을 처음 위치/자세로 되돌리는 과정을 만들어야 한다. Reset이 없으면 episode가 끝나도 다음 학습을 할 수 없기 때문.

  • reset_agent_episode(agent_id)는 특정 Agent의 episode를 리셋하는 콜백이다.

  • reset_agent_episodes는 여러 Agent에 대해 기본적으로 단수형 reset을 호출하는 구조. 그리고 reward/completion은 action 이후 다음 상태에서 평가된다.

    unreal.LearningAgentsTrainingEnvironment — Unreal Python 5.5 (Experimental) documentation

  • 즉 여기서 할 일은 간단하다.

    1. Agent Id로 BP_SpiderRobot 가져오기
    2. 로봇 위치를 시작 위치로 이동
    3. 회전을 초기 회전으로 복구
    4. Physics Linear Velocity = 0
    5. Physics Angular Velocity = 0
  • 트레이닝 환경 → 함수 → Reset Agent Episode 오버라이드

  • 이제 마찬가지로 Agent 를 가져와서 작업을 수행하도록 하자

  • Reset Agent Episode/Agent id → Get Agent

  • Get Agent/Return Value → Get Spider Mesh → Set Physics Linear Velocity

  • Set Physics Linear Velocity → Set Physics Angular Velocity in Degrees

  • 변수 생성

    변수명타입기본값 예시의미
    ResetLocationVector(0, 0, 150)에피소드 시작 위치
    ResetRotationRotator(0, 0, 0)에피소드 시작 회전
  • 각각을 Set Actor Location And Rotation 연결

  • Set Actor Location And Rotation/Teleport 체크

  • Set Actor Location And Rotation/실행핀 → Set Physics Linear Velocity

  • Set Physics Linear Velocity/실행핀 → Set Physics Angular Velocity in Degrees

  • Set Physics Angular Velocity in Degrees/실행핀 → Wake All Rigid Bodies

3.5. BP_SpiderTrainingEnvironment 등록#

앞에서 BP_SpiderTrainingEnvironment를 생성하고, 그 안에 Gather Agent Reward, Gather Agent Completion, Reset Agent Episode를 구현하였다.

하지만 여기까지는 Training Environment 안에 필요한 함수들을 만든 것일 뿐, 아직 실제 학습 루프에 연결된 상태는 아니다.

Learning Agents에서는 Learning Manager가 Agent를 직접 관리하고, InteractorTraining Environment 같은 Listener를 통해 학습에 필요한 기능들을 호출한다.

즉, BP_SpiderTrainingEnvironment를 만들었다고 해서 Gather Agent Reward, Gather Agent Completion, Reset Agent Episode가 자동으로 실행되는 것은 아니다.

따라서 BP_SpiderTrainingManagerBeginPlay에서 BP_SpiderTrainingEnvironment를 생성하고, 이를 BP_SpiderLearningManager에 Listener로 등록하는 과정이 필요하다.


1. 기존 BeginPlay 흐름#

기존 BP_SpiderTrainingManagerBeginPlay에서는 먼저 BP_SpiderInteractor를 생성하고, 이를 BP_SpiderLearningManager에 Listener로 등록하는 흐름이 구성되어 있었다.

flowchart LR
    A[Event BeginPlay] --> B[Construct BP Spider Interactor]
    B --> C[Set Spider Interactor]
    C --> D[Setup Interactor]
    D --> E[Add Listener]

여기서 BP_SpiderInteractor는 Observation 수집과 Action 적용을 담당한다.

  • Observation 수집: 로봇의 현재 상태를 학습 입력값으로 전달
  • Action 적용: Policy가 출력한 관절 제어값을 실제 로봇에 적용

즉, Interactor는 로봇과 Policy 사이에서 상태와 행동을 주고받는 역할을 한다.

하지만 Reward, Completion, Reset은 Interactor가 담당하는 기능이 아니다.

이 기능들은 앞에서 만든 BP_SpiderTrainingEnvironment가 담당하므로, Training Environment도 별도의 Listener로 등록해야 한다.


2. Make Training Environment 추가#

Interactor를 등록한 뒤, Make Training Environment 노드를 추가하였다.

이 노드는 Learning Agents에서 사용할 Training Environment 인스턴스를 생성하는 역할을 한다.

여기서는 Class에 앞에서 만든 BP_SpiderTrainingEnvironment를 지정하였다.

설정은 다음과 같다.

항목설정
In ManagerBP_SpiderLearningManager
ClassBP_SpiderTrainingEnvironment
NameTrainingEnvironment

In ManagerBP_SpiderLearningManager를 연결하는 이유는, 이 Training Environment가 어떤 Learning Manager에 소속되어 동작할지 지정하기 위해서이다.

Class에는 직접 만든 BP_SpiderTrainingEnvironment를 넣는다.

그래야 앞에서 오버라이드한 Reward, Completion, Reset 함수들이 학습 과정에서 사용될 수 있다.


3. Spider Training Environment 변수 저장#

Make Training EnvironmentReturn ValueSpider Training Environment 변수에 저장하였다.

flowchart LR
    A[Make Training Environment] --> B[Set Spider Training Environment]

이렇게 변수로 저장하는 이유는 생성된 Training Environment를 이후에도 참조할 수 있도록 하기 위해서이다.

단순히 노드에서 한 번 생성하고 끝내는 것이 아니라, BP_SpiderTrainingManager가 현재 사용하는 Training Environment를 변수로 가지고 있게 만드는 구조이다.

이렇게 해두면 이후 디버깅을 하거나, Training Environment 관련 값을 확인해야 할 때 같은 인스턴스를 다시 참조할 수 있다.


4. Training Environment를 Listener로 등록#

Training Environment를 생성하고 변수에 저장한 뒤에는, Add Listener 노드를 한 번 더 사용해서 BP_SpiderLearningManager에 등록해야 한다.

이때 연결은 다음과 같이 구성한다.

Add Listener 핀연결 대상
TargetBP_SpiderLearningManager
ListenerSpider Training Environment

여기서 중요한 점은 Add Listener가 두 번 사용된다는 것이다.

첫 번째 Add ListenerBP_SpiderInteractor를 등록하기 위한 것이고,

두 번째 Add ListenerBP_SpiderTrainingEnvironment를 등록하기 위한 것이다.

두 Listener의 역할은 다음과 같이 다르다.

Listener역할
BP_SpiderInteractorObservation 수집, Action 적용
BP_SpiderTrainingEnvironmentReward 계산, Completion 판단, Episode Reset

따라서 Training Environment를 만들기만 하고 Add Listener에 연결하지 않으면, Reward와 Completion, Reset 함수가 학습 루프에서 호출되지 않을 수 있다.


5. Agent 등록#

Training Environment까지 Listener로 등록한 뒤에는, 월드에 배치된 BP_SpiderRobot을 찾아 Agent로 등록한다.

이를 위해 Get Actor Of Class 노드에서 BP_SpiderRobot을 찾고, 그 Return ValueAdd AgentAgent 핀에 연결하였다.

Add Agent의 연결은 다음과 같다.

Add Agent 핀연결 대상
TargetBP_SpiderLearningManager
AgentGet Actor Of ClassReturn Value

여기서 Target은 반드시 BP_SpiderLearningManager가 되어야 한다.

Add Agent는 Training Environment에 Agent를 넣는 노드가 아니라, Learning Manager가 관리할 학습 대상 Agent를 등록하는 노드이기 때문이다.


6. 최종 BeginPlay 구성#

최종적으로 BP_SpiderTrainingManagerBeginPlay 흐름은 다음과 같이 구성된다.

이 구조에서 BP_SpiderLearningManager는 학습에 참여하는 Agent와 Listener들을 관리하는 중심 역할을 한다.

BP_SpiderInteractor는 Observation과 Action을 담당하고,

BP_SpiderTrainingEnvironment는 Reward, Completion, Reset을 담당한다.

마지막으로 BP_SpiderRobot은 실제 학습 대상 Agent로 등록된다.

정리하면 다음과 같다.

구성 요소역할
BP_SpiderLearningManagerAgent와 Listener를 관리하는 중심 객체
BP_SpiderInteractorObservation 수집 및 Action 적용
BP_SpiderTrainingEnvironmentReward, Completion, Reset 처리
BP_SpiderRobot실제 학습 대상 Agent

7. 현재 단계의 의미#

이번 단계에서 중요한 점은 BP_SpiderTrainingEnvironment를 단순히 생성한 것이 아니라, 실제 Learning Agents 학습 루프에 연결했다는 것이다.

앞에서 만든 Reward, Completion, Reset은 이제 다음 흐름 안에서 사용될 수 있다.

flowchart TD
    A[Observation 수집] --> B[Policy가 Action 출력]
    B --> C[Interactor가 Action 적용]
    C --> D[로봇 물리 시뮬레이션 진행]
    D --> E[Training Environment가 Reward 계산]
    E --> F[Training Environment가 Completion 판단]
    F --> G{Episode 종료 여부}
    G -- 계속 진행 --> A
    G -- 종료 --> H[Reset Agent Episode]
    H --> A

즉, 로봇은 이제 단순히 관절 Action만 받는 상태가 아니라, 행동 결과에 따라 보상을 받고, 넘어졌을 경우 에피소드가 종료되며, 다시 초기 상태에서 다음 에피소드를 시작할 수 있는 구조가 되었다.

이를 통해 강화학습에 필요한 기본 루프가 다음과 같이 연결된다.

Observation → Action → Physics Simulation → Reward → Completion → Reset

이 구조가 완성되어야 PPO Trainer를 사용한 실제 학습을 시작할 수 있다.

Be Your Own Lamp