Aimbot-ParallelEnv/README.md
2024-01-24 17:02:48 +09:00

144 lines
20 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Aimbot-ParallelEnv
这是一个基于Unity ML-Agents的基于FPS游戏的多智能体训练环境用于生成指定目标和对应难度并且根据Agent的Action反馈Reward来训练强化学习Agent的环境。
[![Chinese badge](https://img.shields.io/badge/简体中文-Simplified_Chinese-blue)](./README.md)
[![Japanese badge](https://img.shields.io/badge/日本語-Japanese-blue)](./README-JP.md)
## Description
项目基于[ML-Agents 2.0.1](https://github.com/Unity-Technologies/ml-agents/tree/develop)使用Unity 2021.3.14f1开发。
Python侧则使用[mlagents-envs 0.30.0](https://pypi.org/project/mlagents-envs/)与环境进行通信。
## 快速开始
在项目[Aimbot-PPO](https://koha9.asuscomm.com/yurugit/Koha9/Aimbot-PPO)中使用了本环境进行了PPO算法的训练其中在[AimbotEnv.py](https://koha9.asuscomm.com/yurugit/Koha9/Aimbot-PPO/src/branch/OffP-FullMNN-V2/Aimbot-PPO-Python/Pytorch/AimbotEnv.py)中给出了本环境的使用方法。
## 游戏简介
这是一个模仿初代Doom的基本模式的FPS游戏环境Agent将会被给到不同的目标Agent需要根据目标的类型和位置以及自身的状态来决定是否开枪射击射击的方向以及移动方向。环境内包含两种模式和3种目标分别是训练模式和测试模式以及`FreeTarget``GotoTarget`和`AttackTarget`。`FreeTarget`需要Agent使用武器击倒区域内所有的敌人`GotoTarget`和`AttackTarget`会在区域内生成一个目标区块Agent需要对目标区块进行不同的对应来完成任务。同时`GotoTarget`和`AttackTarget`中根据生成目标区块的构造不同分为了5种不同的难度。
## 模式介绍
训练模式和测试模式都需要与Python侧进行通信根据从本环境中观测到的Observation生成Action并传递回本环境。在环境启动时需要使用者在游戏内进行操作来选择模式由于ML-Agents存在Time limit所以需要在45s内进行模式选择超过45s后将自动退出游戏环境。
### 训练模式
在进入训练模式前使用者需要从Start界面中只训练单个模式的`Train-Free``Train-Goto``Train-Attack`和训练所有模式的`Train-Mix`中进行选择。在选择后,将会进入训练模式,并且注意该选择不能在之后进行更改。对于拥有多种难度的目标区块的模式`Train-Goto`和`Train-Attack`在Start界面将会有一个用来调整生成不同难易度目标区块的概率的面板默认的生成概率为平均。面板为了保证该模式各难易度生成概率总和为1在调整某一难度的生成概率时其他难度的生成概率将会被自动调整用户可以通过点击右侧锁定按钮以锁定或解锁某一难度的生成概率。该面板将在训练模式执行中也可见用户在训练模式执行中对概率面板进行调整后将会在应用在下次目标区块生成时。
<br>![TargetLevelProbabilityPanel](./ReadmeImages/LevelProbabilityPanel.jpg)
### 测试模式
测试模式中用户需要手动来对Agent下达命令用户可以通过点击右上角的菜单来执行生成目标区块或敌人切换目标模式等。在该模式中比训练模式多出一个新的Target为StayTarget当用户未指定目标或者清空游戏时将会以将该Target指派给Agent。通过点击右上角的菜单可以将鼠标的模式切换为生成Enemy或者生成对应难度Target区块的模式此时鼠标移动到游戏区域内后将会出现生成物件的预览内点击鼠标右键则可以在对应的位置生成对应的Enemy或者区块。当生成Target区块时会自动指派对应target给agent需要指派`FreeTarget`时则需要点击`FreeMode`按钮。通过点击`ClearGame`可以清空所有enemy和区块并将Agent的目标指派为`StayTarget`,通过点击`StayMode`则可以强制将Agent目标指派为`StayTarget`。
## 目标介绍
### FreeTarget
在Agent被指派到`FreeTarget`时Agent需要使用武器击倒区域内所有的敌人。在该模式中一个回合开始时将会在区域内随机生成一定数量的敌人默认为6Agent将会生成在区域的随机位置Agent朝向继承上一回合如果是第一回合则是默认角度为0。Agent在成功击倒所有敌人后被判断为胜利或者超过30s未能成功击倒所有敌人则被判断为失败。
### GotoTarget
在Agent被指派到`GotoTarget`时Agent的目标是移动到指定目标区块的指定位置。在该模式中一个回合开始时Agent的位置总是继承上一个回合如果是第一回合将会默认生成在地图的左下角。Goto的目标区块将会根据难度概率在区域内随机生成对应难度的预设区块不同难易度拥有多个已保存好的预设区块这些预设区块通过[`SceneBlocksSet`](https://koha9.asuscomm.com/yurugit/Koha9/Aimbot-ParallelEnv/src/branch/main/Assets/Script/TargetContainer/SceneBlocksSet.cs)保存在[Prefab文件夹](https://koha9.asuscomm.com/yurugit/Koha9/Aimbot-ParallelEnv/src/branch/main/Assets/Prefeb)中。所有目标区块的总大小为10*10生成目标区块时区块将会避开Agent的所在位置。指定目标区域是名为`FireBase`的圆形区域该区域所覆盖的范围不会超过目标区块但是它的位置和大小会根据预设区块的不同而不同。Agent在成功移动到目标区块中心位置后被判断为胜利或者超过30s未能成功移动到目标区块中心位置则被判断为失败。
### AttackTarget
在Agent被指派到`AttackTarget`时Agent的目标是对目标区域进行火力压制同时主要目标是击倒区块内所有敌人。在该模式中Agent与目标区块的生成策略还有`FireBase`的构成与`GotoTarget`相同。但是在`AttackTarget`的`FireBase`区域内必定会有Enemy生成。Agent在成功击倒区块内所有敌人后被判断为胜利或者超过30s未能成功击倒区块内所有敌人则被判断为失败。
---
# Environment
## Observation Space
在本强化学习训练设置中,观测数据是理解和交互环境的关键。观测环境由三个主要部分组成:`TargetState`(目标状态)、`AgentState`(自机状态)和`RaycastState`射线探测状态。这些观测数据通过ML-Agents的`VectorSensor`类进行收集并发送给Python侧为决策提供必要的信息。Observation的Source Code位于[`MLAgentsCustomController.cs`](https://koha9.asuscomm.com/yurugit/Koha9/Aimbot-ParallelEnv/src/branch/main/Assets/Script/GameScript/MLAgentsCustomController.cs)中被override的`CollectObservations`函数中。
### TargetState目标状态
`TargetState`包含了Agent接收的目标指令和相关信息,总大小为6。其中包含Target的类型Target的x,y,z坐标TargetArea的直径以及Agent是否处于TargetArea和本回合剩余时间内这些信息。
| Num | Observation | Size | State Space | Description |
|-----|------------------|------|-------------|-------------------------------------------------------------------------------------------------------------|
| 0 | Target类型 | 1 | 0,1,2,3,4 | 描述被指派的Target类型0=FreeTarget1=GotoTarget2=AttackTarget3=DefenceTarget未使用4=StayTarget |
| 1~3 | Target坐标 | 3 | 0~47 | 描述Target中Firebase的连续空间坐标取值范围为0到47之间的连续值 |
| 4 | FireBaseDiameter | 1 | 1~10 | 描述FireBase的直径取值范围为在1到10之间的连续值 |
| 5 | InFireBaseState | 1 | 0,1 | 描述Agent是否处于FireBase中0=False1=True |
| 6 | RemainTime | 1 | 0~30 | 描述本回合剩余时间取值范围为在0到30之间的连续值 |
### AgentState自机状态
`AgentState`包含了Agent的武器可攻击状态Agent的x,y,z坐标和Agent的朝向角度。为了避免直接使用Agent GameObject朝向角度时0到360度变化时值的大幅度跳变同时为了实现更好的周期性表示这里使用了Agent GameObject旋转角度的余弦和正弦值来表示Agent的朝向。
| Num | Observation | Size | State Space | Description |
|------|-------------|------|-------------|---------------------------------------------------------------------|
| 7 | GunState | 1 | 0,1 | 描述Agent武器是否处于可使用状态。0=False1=True |
| 8~10 | Agent坐标 | 3 | 0~47 | 描述Agent的连续空间坐标取值范围为0到47之间的连续值 |
| 11~12 | Agent朝向 | 2 | -1~1 | 通过计算Agent GameObject旋转角度的余弦和正弦以描述Agent的正面朝向。 |
### RaycastState射线探测状态
`RaycastState`记录了视野内发射的射线探测到的Object的Tag及其距离、其中Tag可以通过Label Encoding和OneHot Encoding两种方式来记录默认使用Label Encoding可以在Object`CommonParameterContainer`中进行调整。`RaycastState`由[`RaySensors`](https://koha9.asuscomm.com/yurugit/Koha9/Aimbot-ParallelEnv/src/branch/main/Assets/Script/GameScript/RaySensors.cs)类管理。它实现了射线的非均匀分布默认视野中间15%的区域射线分布更密集两侧则较为稀疏默认中间密集部分共射出5条两侧稀疏部分各7条。 每个射线可探测的对象最远距离为100个单位超过探测距离则返回0。其中视野中间区域的覆盖范围区域内RayCast数量以及可探测距离均可以在每个`Agent`GameObject的Inspector中进行调整。
<br><img src="./ReadmeImages/RayCastLayout.png" alt = "射线分布方式" width="500" height = "auto">
| Num | Observation | Size | State Space | Description |
|------------------------------------------------------------------|-----------------|--------------------|---------------|----------------------------------------------------------------------------------|
| 11~Raynum+11 | TagType(Label) | Number of Raycasts | 0, 1, 2 | 描述Raycast所探测到物体的Tag, 0=Wall, 1=Enemy, 2=Player, -1=Nothing |
| 11~Raynum * 3+11 | TagType(Onehot) | Raynum * 3 | 0, 1 | 描述Raycast所探测到物体的Tag, 使用Onehot编码记录 |
| Raynum+12~2* Raynum+12(Label),<br>3* Raynum+12~4* Raynum+12(OneHot) | Distance | Number of Raycasts | 0~MaxDistance | 描述Raycast所探测到物体的距离取值范围在0~MaxDistance其中MaxDistance默认为100 |
*注n为每个Agent的RayCast数量MaxDistance为每个Agent的RayCast最大探测距离*
## Action Space
为了模拟FPS游戏中玩家对于键盘和鼠标的同时操作本环境中的Action Space分为两个部分分别是`Discrete Action`和`Continuous Action`。其中`Discrete Action`用于模拟玩家对于键盘的离散操作,`Continuous Action`用于模拟玩家对于鼠标的连续操作。两个Action Space的对应操作可以参考于[`MLAgentsCustomController.cs`](https://koha9.asuscomm.com/yurugit/Koha9/Aimbot-ParallelEnv/src/branch/main/Assets/Script/GameScript/MLAgentsCustomController.cs)中被override的`Heuristic`函数。具体实现则于[`AgentController.cs`](https://koha9.asuscomm.com/yurugit/Koha9/Aimbot-ParallelEnv/src/branch/main/Assets/Script/GameScript/Character/AgentController.cs)中的`MoveAgent`和`CameraControl`函数中。
### Discrete Action
在`Discrete Action`中Agent可以对各方向的移动和攻击进行离散操作其中移动操作包括垂直移动操作和水平移动操作攻击操作可以对武器进行操。
| Num | Action | Action Space | Description |
|-----|-----------------|--------------|---------------------------------------------------------------------------|
| 0 | Vertical Move | 0,1,-1 | 用于控制Agent在其正面方向的垂直轴上移动。0=停止1=向前移动,-1=向后移动 |
| 1 | Horizontal Move | 0,1,-1 | 用于控制Agent在其正面方向的水平轴上移动。0=停止1=向右移动,-1=向左移动 |
| 2 | Attack | 0,1 | 用于控制Agent的攻击操作。0=不攻击1=执行攻击 |
### Continuous Action
在`Continuous Action`中可以对Agent的视角进行操作。由于本环境是模拟早期Doom的FPS环境所以Agent的视角被设定为仅能在水平方向上进行旋转。
| Num | Action | Action Space | Description |
|-----|-------------------|--------------|---------------------------------------------------------------------------------|
| 0 | Vertical Rotation | -Inf~Inf | 控制Agent在水平方向左右上的视角旋转。正值使视角向右负值则使视角向左旋转。 |
## Reward
在各给定的目标中为了让Agent注重于完成不同的任务Reward的设计也有所不同。在训练模式中Reward由共同Reward`Common Reward`和各个目标的专用Reward组成。
### Common Reward
为了引导Agent在训练过程中执行对完成目标有帮助的操作和避免无意义的动作在`Common Reward`中环境为Agent设计了以下的Reward和Penalty。其中由于ML-Agents不能向Python直接传递回合结束的结果同时为了避免其他Reward数值过小或者过大导致无法判断胜利或者失败因此在回合结束的Reward中添加了极大数和极小数来表示胜利和失败。
| Reward/Penalty | Value/Symbol | Description |
|----------------|---------------|-----------------------------------------------------|
| NonTargetHit | 3 | 当Agent击中或者击倒不作为Target的敌人时所获得Reward |
| TargetHit | 25 | 当Agent击中或者击倒作为Target的敌人时所获得Reward |
| MovePenalty | -0.5 | 当Agent在场地中移动时给到的Penalty |
| SpinPenalty | $P_s$ | 当Agent进行视角旋转时给到的Penalty |
| WinReward | 999 | 当回合结束并且本回合Agent达成目标时给到的Reward |
| LoseReward | 999 | 当回合结束并且本回合Agent失败时给到的Reward |
为了防止Agent在训练中出现无意义的旋转和旋转过快这里用下列函数来给到Agent一个`SpinPenalty`。当40个Action的旋转记录未达到阈值时会给到一个较小的Penalty而当超过阈值时我们可以知道Agent正在向同一方向持续旋转则给到一个较大的Penalty。其中<i>P<sub>s<sub>t</sub></sub></i>为在`t`时刻时的旋转的Penalty<i>mouseX<sub>t</sub></i>为`t`时刻Agent的`Contunuouse Action`中的旋转Action数值。
$$
P_{st} =
\begin{cases}
-|mouseX_t| \cdot 0.06 &, & \left| \sum_{t=0}^{-40} mouseX_t \cdot 0.08 \right| < 10 \\
-\left| \sum_{t=0}^{-40} mouseX_t \cdot 0.08 \right| & , & \left| \sum_{t=0}^{-40} mouseX_t \cdot 0.08 \right| \geq 10
\end{cases}
$$
### FreeTarget
`FreeTarget`主要目标是消灭所有敌人因此追踪敌人的能力非常关键本模式设置了两种奖励机制当视野中心探测到敌人时给到Agent一个为2的Reward当未探测到敌人时则根据探测到的敌人距离视野中心的距离 $D$ 给到一个小于`2`的Reward距离的计算仅考虑通过 Raycast 探测到的敌人且当距离越接近0.5即Enemy半径则Reward越接近`2`。例如在图中只计算了与`Enemy1``Enemy3`的距离并且取用最短距离 $D_1$ 来计算Reward未探测到的`Enemy2``Enemy4`被排除在外在这个模式下所有敌人都被视为目标当敌人被击败或击中时Agent 将获得`Common Reward`中的`TargetHit Reward`。
<br><img src="./ReadmeImages/FreeTargetFacingreward.png" alt = "FacingReward" width="500" height = "auto">
| Reward/Penalty | Value/Symbol | Description |
|----------------|----------------------|----------------------------------------|
| FacingReward | $R_f$ | 视野中心与所探测到最近敌人距离的Reward |
$$
R_{f} =
\begin{cases}
2 &, &min(D) \leq 0.5 \\
\frac{1}{\sqrt{\frac{min(D)}{2}}} &,& min(D) \gt 0.5
\end{cases}
$$
### GotoTarget
在`GotoTarget`中,主要目标是向指定的`FireBase`移动。根据在`t`时刻和`t-1`时刻的玩家与目标间的距离 $D_t$ 和 $D_{t-1}$ 的差值,设置了每个 Step 可获得的距离奖励 $R_d$ 。当Agent接近目标时将获得正向Reward远离目标时则获得小于0的Penalty。当玩家处于`FireBase`内时,可以获得更高的固定奖励,然而此时`DistanceReward`将不会生效。在此模式中,所有敌人均不被视为攻击目标,但在击中或击败敌人时,可以获得`Common Reward`中的`NonTargetHit`。
| Reward/Penalty | Value/Symbol | Description |
|----------------|--------------|-----------------------------------------|
| DistanceReward | $R_d$ | Agent不在目标内时和目标的距离差Reward |
| InAreaReward | 5 | Agent处于目标内时的持续奖励 |
$$
R_{d} = (D_{t-1} - D_{t}) \cdot 20
$$
### AttackTarget
在 Attack Mode 中主要目的是消灭目标地点的敌人并鼓励Agent对目标地点进行设计。因此与`FreeTarget`类似将视线移动到目标地点同样重要。当Agent面对目标地点并且进行攻击时将会被视为压制行为并获得较低的`SuppressiveReward`。面对目标地点但未进行攻击时,获得持续获得更低的`FacingAreaReward`。处于`FireBase`中的Enemy将会被视为目标当敌人被击败或击中时Agent 将获得`Common Reward`中的`TargetHit Reward`。而`FireBase`外的敌人则不会被视为目标当敌人被击败或击中时Agent 将只能获得`Common Reward`中的`NonTargetHit Reward`。
| Reward/Penalty | Value/Symbol | Description |
|-------------------|--------------|-----------------------------------------|
| SuppressiveReward | 5 | Agent对`FireBase`进行压制攻击时获得奖励 |
| FacingAreaReward | 2 | Agent面朝目标时获得的持续奖励 |
## Side Channel
为了方便对于环境的调试和实现Unity与Python间非实时通信本环境中使用了ML-Agents提供的Side Channel用于向Python侧传递一些额外的信息。这里我参考了huggingface的[Custom Side Channels](https://github.com/huggingface/ml-agents-patch/blob/develop/docs/Custom-SideChannels.md)。在Unity端Side Channel的实现位于[`AimbotSideChannel.cs`](https://koha9.asuscomm.com/yurugit/Koha9/Aimbot-ParallelEnv/src/branch/main/Assets/Script/GameScript/SideChannel/AimbotSideChannel.cs)和[`AimBotSideChannelController.cs`](https://koha9.asuscomm.com/yurugit/Koha9/Aimbot-ParallelEnv/src/branch/main/Assets/Script/GameScript/SideChannel/AimBotSideChannelController.cs)中。每个Log信息将会以`|`来分隔字段其中第一个字段为Log的类型随后字段为自定义Log信息。在本环境中Unity的SideChannel将会发送所有`LogType.Warning`和`LogType.Error`类型的Log到Python侧。其中`LogType.Warning`会传递一回合结束后的的胜败信息和从Unity发送至Python的指令。胜败信息将类似于`Warning|Result|Win`和`Warning|Result|Lose`。而指令则传递了在下一训练结束后保存模型的命令,它的内容为`Warning|Command`。