跳至内容

构建 Tag

在本教程中,我们将创建一个多代理环境,其中代理将被训练来玩一个 3v1 的追逐游戏。具体来说,我们创建一个逃跑代理,它试图避免被抓住,以及三个追逐代理,它们的目标是抓住逃跑代理。代理可以向前、向左和向右移动,并且可以感知周围的物体以及其他代理的位置。

Unreal Engine 中的环境结构

要构建游戏(以下简称环境),我们需要在 Unreal Engine 项目中创建以下内容:

  • 方向和距离观察器:一个自定义的 BlueprintBoxObserver,允许追逐者观察其他代理的方向和距离。

  • 代理蓝图Character 的一个子类,包含代理的形状和外观。

  • Trainer 蓝图BlueprintTrainer 的一个子类,包含计算训练的 rewardstatus 的逻辑。

  • 环境定义BlueprintStaticScholaEnvironment 的一个子类,包含在训练开始前 初始化 环境以及在训练的不同回合之间 重置 环境的逻辑。

  • 地图:游戏地图包括地板、四个墙壁、代理和环境。

  • 注册代理:将代理连接到环境和它们各自的 Trainer。

初始设置

请参考 Schola 初始设置 部分来设置 Unreal Engine 项目和 Schola 插件。

创建自定义方向和距离观察器

Schola 提供了多种内置观察器类,例如 RotationObserverRayCastObserver。当我们需要的特定观察结果不在内置观察器范围内时,就需要自定义观察器。在此示例中,我们将创建一个自定义的 BlueprintBoxObserverBoxObserver 的子类),以便追逐者能够观察到游戏中当前代理相对于其他代理的方向和距离。它将返回按环境大小归一化的距离以及作为单位向量的方向。GetObservationSpace 函数将返回观察空间,而 CollectObservations 函数将收集并返回观察结果。

  1. 创建一个新的蓝图类,父类为 BlueprintBoxObserver,并将其命名为 DirectionDistanceObserver
  2. 添加一个新的整数变量。将其命名为 EnvSize,并将默认值设置为 5000。这将存储环境中两个代理之间的最大可能距离。
  3. 添加一个新的 Actor 变量。将其命名为 Target。这将存储观察器将要跟踪的目标代理。
  4. 按如下方式设置 GetObservationSpaceCollectObservations 蓝图。

创建代理

创建 Tagger 类

  1. 创建一个父类为Character的新蓝图类,并将其命名为Tagger
  2. 作为代理的身体,添加任何你想要的静态网格体材质
  3. DetailsCharacter Movement: WalkingMax Walk Speed 设置为 520 厘米/秒。
  4. DetailsCharacter Movement (Rotation Settings)Orient Rotation to Movement 设置为 true。这允许代理使用Movement Input Actuator 进行旋转。
  5. DetailsPawnUse Controller Rotation Yaw 设置为 false。这允许代理使用Movement Input Actuator 进行旋转。
  6. DetailsTags 中,添加一个新标签,并将其值设置为Tagger。此标签由RayCastObserver 用于检测不同的对象。

附加射线投射观察器

  1. 添加一个Sensor 组件。
  2. DetailsSensorObserver 中,选择Ray Cast Observer
  3. DetailsSensorObserverSensor propertiesNumRays 设置为 36。
  4. DetailsSensorObserverSensor propertiesRayDegrees 设置为 360。
  5. DetailsSensorObserverSensor propertiesRayLength 设置为 2048。
  6. DetailsSensorObserverSensor propertiesTrackedTags 中,添加两个新元素,并将标签设置为TaggerRunner

附加移动输入执行器

我们将使用两个Movement Input Actuators 来移动代理。一个用于转向的横向轴执行器,一个用于向前移动代理的前进轴执行器。

  1. 添加一个Actuator 组件,并将其命名为ForwardAxisMovementInputActuator
  2. DetailsActuator ComponentActuator 中,选择Movement Input Actuator
  3. DetailsActuator ComponentActuatorActuator Settings 中,取消勾选HasYDimensionHasZDimension
  4. 添加一个Actuator 组件,并将其命名为LateralAxisMovementInputActuator
  5. DetailsActuator ComponentActuator 中,选择Movement Input Actuator
  6. DetailsActuator ComponentActuatorActuator Settings 中,取消勾选HasXDimensionHasZDimension
  7. DetailsActuator ComponentActuatorActuator Settings 中,将Minspeed 设置为 -1。

附加方向和距离观察器

  1. 添加三个Sensor 组件,并将它们命名为Teammate Sensor 1Teammate Sensor 2Runner Sensor
  2. 对于每个传感器,在DetailsSensorObserver 中,选择DirectionDistanceObserver
  3. 每个传感器的Target 变量将在注册代理部分设置。

创建 Runner 类

Runner 的构建方式与 Tagger 类似,但有一些小的改动。请重复创建 Tagger 类部分中的步骤,并进行以下更改:

  1. 向 Runner 类添加相同的RayCastObserverMovementInputActuator,但不要添加DirectionDistanceObserver
  2. DetailsCharacter Movement: WalkingMax Walk Speed 设置为 490 厘米/秒。我们将使 Runner 最初速度较慢,以便 Tagger 更容易追上 Runner,这样 Tagger 就可以在训练开始时学会追上 Runner。如果 Runner 的速度与 Tagger 一样快或更快,Tagger 可能永远无法追上 Runner,从而阻止 Tagger 学习。随着 Tagger 的改进并能持续追上速度较慢的 Runner,可以在训练期间手动增加此值。
  3. DetailsTags 中,添加一个新元素,并将其值设置为Runner。此标签由RayCastObserver 用于检测不同的对象。

创建 Trainer

我们将创建两个BlueprintTrainers,一个用于 Tagger 代理,一个用于 Runner 代理。

创建 Tagger Trainer

  1. 创建一个父类为BlueprintTrainer 的新蓝图类,并将其命名为TaggerTrainer
  2. 添加一个新的布尔变量。将其命名为CaughtTarget。它存储在当前步骤中 Tagger 代理是否抓住了 Runner 代理。它由Environment Definition 蓝图设置。
  3. 添加一个新的布尔变量。将其命名为HitWall。它存储在当前步骤中 Tagger 代理是否撞墙。它由Environment Definition 蓝图设置。
  4. 添加一个新的Tagger 变量。将其命名为Agent。它存储训练器控制的 Pawn。
  5. 启用DetailsReinforcement LearningName,并将其设置为TaggerUnifiedPolicy 或任何字符串。此字符串决定训练期间使用的策略,因此所有 Tagger 使用相同的名称,可以使所有 Tagger Trainer 实例共享相同的策略。因此,三个 Tagger 代理将训练并使用相同的模型。
  6. Details → Interaction Manager → DecisionRequestFrequency 设置为 1。这使得代理在每一步都决定一个动作,从而加快训练速度。
  7. 按照下图设置事件图。

定义 Tagger 奖励函数

当 Tagger 代理抓住 Runner 代理时,我们给予一个大的单次奖励;当 Tagger 代理撞墙时,给予 -0.015 的小额惩罚。此外,我们对 Tagger 代理的每一步都给予 -0.005 的小额惩罚,以鼓励代理尽快抓住 Runner 代理。单次奖励的计算方式为 10 - (0.0005 * DistanceFromRunner),其中 10 是抓住 Runner 的最大奖励,而-0.0005*DistanceFromRunner 会在 Tagger 离 Runner 越远时降低奖励,以确保当 Runner 被抓住时,离 Runner 较近的 Tagger 获得更高的奖励。这两个数字是根据我们的经验选择的,可以根据需要进行调整。每步奖励的计算方式为-(0.015*HitWall) - 0.005

  1. 按照下图设置 ComputeReward 函数。

定义 Tagger 状态函数

对于 tagger,当 runner 被抓住时,就达到了终止状态。我们还设置了一个最大步数,以防止一个 episode 无限期运行。有关 Step 变量和 ComputeStatus 函数的更多信息,请参阅 示例 1

  1. 添加一个新的整数变量。将其命名为 MaxSteps,并将默认值设置为 2000。这存储了一个 episode 在结束前可以运行的最大步数。如果 tagger 在 2000 步内无法抓住 runner,则可以将其设置为更高的值。
  2. 如下所示设置 ComputeStatus

创建 Runner Trainer

  1. 创建一个父类为 BlueprintTrainer 的新 Blueprint Class,并将其命名为 RunnerTrainer
  2. 添加一个新的布尔变量。将其命名为CaughtTarget。它存储在当前步骤中 Tagger 代理是否抓住了 Runner 代理。它由Environment Definition 蓝图设置。
  3. Details → Interaction Manager → DecisionRequestFrequency 设置为 1。这将使 agent 每一步都做出决定,从而实现更平滑的动作。

定义 Runner 奖励函数

当 runner agent 被抓住时,我们给予一个很大的单次惩罚 -20,并给予一个小的恒定的每步奖励 0.01,以鼓励 runner 尽可能长时间地存活。

  1. 按照下图设置 ComputeReward 函数。

定义 Runner 状态函数

runner 的状态函数与 Tagger Trainer 相同。

  1. 添加一个新的整数变量。将其命名为 MaxSteps,并将默认值设置为 2000。这存储了一个 episode 在结束前可以运行的最大步数。如果您发现训练期间 tagger 经常无法在 episode 结束前抓住 runner,则可以将其设置为更高的值。
  2. 如下所示设置 ComputeStatus

创建环境定义

我们将在环境中创建一个 SetRunnerTagged 函数,该函数在 runner 被抓住时通知所有 trainer。 InitializeEnvironmentOnActorHit 事件绑定到每个 runner,当 runner 接触到 tagger 时调用 SetRunnerTagged 函数。 ResetEnvironment 函数在每个 episode 结束时将每个 agent 移动到随机位置并重置 trainer 中的变量。

  1. 创建一个父类为 BlueprintStaticScholaEnvironment 的新 Blueprint Class,并将其命名为 TagEnvironment
  2. 添加一个名为 Agents 的新变量,类型为 Pawn (Object Reference) 数组,并使其可公开编辑(通过点击眼睛图标切换可见性)。这用于跟踪属于此环境定义的已注册 agent。
  3. 如下所示创建 SetRunnerTagged 函数。
  4. 如下所示设置 Event Graph 和 RegisterAgents 函数。

创建地图

  1. 创建一个包含地板和四面墙的关卡。
  2. 根据需要添加障碍物和装饰。
  3. 将一个 TagEnvironment 放置在地图中的任意位置。位置不重要。
  4. 在地图中心附近放置三个 Taggers
  5. 在 taggers 附近放置一个 Runner

注册代理

  1. 在地图中选择 TagEnvironment

    1. 转到 Details 面板 → DefaultAgents
    2. 添加 4 个新元素,并将值设置为地图中的四个代理。
  2. 在蓝图编辑器中打开 Tagger 类。

    1. 转到“详细信息”面板。
    2. 搜索 AIController
    3. 在下拉菜单中,选择 TaggerTrainer
  3. 在蓝图编辑器中打开 Runner 类。

    1. 转到“详细信息”面板。
    2. 搜索 AIController
    3. 在下拉菜单中,选择 RunnerTrainer
  4. 在地图中选择一个 tagger。

    1. 转到“详细信息”面板。
    2. 选择 Teammate Sensor 1 组件,将 Target 设置为另一个 tagger,并对 Teammate Sensor 2 重复此操作。
    3. 选择 Runner Sensor 组件,并将 Target 设置为 runner。
    4. 对其他两个 taggers 重复此操作。

开始训练

我们将使用 Proximal Policy Optimization (PPO) 算法训练代理 2,000,000 步。由于 SB3 不支持多代理训练,因此在本示例中我们将使用 RLlib。以下两个方法运行相同的训练。从终端运行可能更方便进行超参数调整,而从 Unreal 编辑器运行在编辑游戏时可能更方便。

  1. 在 Unreal Engine 中运行游戏(通过单击绿色三角形)。
  2. 打开终端或命令提示符,然后运行以下 Python 脚本
终端窗口
schola-rllib -p 8000 -t 2000000 --use-attention
  1. 随着 taggers 的改进,能够持续追上速度较慢的 runner,请逐渐增加 Runner 蓝图中的 runner 速度 → Character Movement: WalkingMax Walk Speed

启用 TensorBoard

要可视化训练进度,请参阅 示例 1,了解使用 TensorBoard 的详细信息。

© . This site is unofficial and not affiliated with AMD.