RoboCup救援仿真和代理开发框架手册

1 介绍

灾难可以定义为一种造成广泛的人员,物质,经济和环境破坏的危机情况,超过了系统自身的恢复能力。主要有两种处理灾难的方法:

  1. 预防发生,这可以通过确定原因并防止发生来实现。
  2. 一旦发生,通过先前建立的政府政策和策略来减少其影响

通常更希望使用前一种方法,因为它非常有效且几乎没有破坏。但是,这并非总是可能的,因为某些灾难事件过于强烈以至于无法避免,即使事前预测到也是如此,而另一些事件则是不可预测的。因此,这种政府管理选项对于减轻灾难的影响变得必要。
本手册的目的是促进首次接触的人更好理解RoboCup Rescue Simulation服务器,并帮助有兴趣参加RoboCup Rescue Agent Simulation竞赛的人们。

2 安装

本手册假定模拟器和代理程序将在Linux机器上运行,即使它也是可以在Microsoft Windows或Apple macOS中运行。我们建议使用Linux,因为它是开源的,并且大多数发行版都得到了用户社区的良好支持。如果您以前从未打算且使用过Linux,建议您从用户友好的发行版开始,例如Ubuntu或Fedora。

2.1 软件要求

  • Java OpenJDK 8+
  • Git
  • Gradle 3.4
  • 实用工具像是 wget, bash, xterm, tar, gzip, 等等.
    如果您使用的是Ubuntu,则所有这些软件都位于默认软件存储库中。

2.2 安装RoboCup救援仿真(RCRS)服务器

  1. 从https://github.com/roborescue/rcrs-server中克隆下载仿真服务器;
  2. 切换目录到“rcrs-server”;
    (a) 如果你是使用macOS系统,修补文件“boot/functions.sh”如
    ‘sed –i –e “/readlink/s/^/#/” boot/functions.sh’
  3. 使用gradle clean和gradle completeBuild命令编译仿真服务器。
  4. 检查安装结束时的消息。如果安装成功完成,您将收到消息BUILD SUCCESSFUL; 否则,您将得到BUILD FAILED。
    如果您使用的是Ubuntu,则根据以下命令进行安装:
在Ubuntu上安装
$ git clone https://github.com/roborescue/rcrs-server.git
$ cd rcrs-server
($ sed –i –e “/readlink//s/^/#” boot/functions.sh # Only required for macOS)
$ gradle clean
$ gradle completeBuild

如果安装成功完成,将出现以下消息。

安装完成
BUILD SUCCESSFUL in 2s
1 actionable task: 1 executed

2.3 编译代理开发框架(ADF)示例代理

通过克隆https://github.com/roborescue/rcrs-adf-sample.git资料库下载样例代理框架ADF。然后,移动至rcrs-adf-sample目录,并使用脚本compile.sh编译样例代理框架。
如果您使用的是Ubuntu,则可以使用以下命令获取并编译ADF:

在Ubuntu中下载ADF
$ git clone https://github.com/roborescue/rcrs-adf-sample.git
$ cd rcrs-adf-sample
$ java –jar ./library/rescue/adf/adf-core.jar –compile

3 在RCRS服务器上运行ADF示例代理

3.1 无预计算运行

要运行样例代理,必须打开两个终端窗口。一个用于运行仿真服务器(即模拟器),另一个用于运行代理(ADF)。

3.1.1 运行仿真服务器

使用一个终端窗口,移动至仿真器文件夹(rcrs-server)内的引导(boot)目录。然后,键入bash start-comprun.sh。整体执行命令顺序为:

运行仿真服务器
$ cd rcrs-server
$ cd boot
$ bash start-comprun.sh

当仿真服务器正确运行时,将出现如图1中的窗口。

3.1.2 运行ADF样本代理

运行仿真服务器后,移动至另一个终端窗口上的rcrs-adf-sample目录,并通过以下命令运行代理:

运行样例代理(ADF)
$ sh launch.sh –all
[FINISH] Done connecting to server (3 agents)

如果代理(agents)可以连接到仿真器,代理状态和城市将显示在图1窗口中的左侧部分。然后,仿真如图2所示。

3.2 有预计算运行

代理可以通过在仿真开始之前对其执行预计算来查看仿真模拟情景。在比赛中,预计算的时间预定义为2分钟。预计算也同样需要两个终端窗口。

3.2.1 运行Simulation Server(仿真服务器)进行预计算

打开一个终端窗口,移动至模拟器文件夹(rcrs-server)内的引导(boot)目录。 然后,键入bash start-precompute.sh。这些命令是:

运行仿真服务器
$ cd rcrs-server
$ cd boot
$bash start-precompute.sh

3.2.2 运行ADF样本代理进行预计算

运行用于预计算的仿真服务器后,在另一个终端窗口上移至ADF目录,并执行以下命令运行代理:

Running Sample Agents
$sh launch.sh –t 1,0,1,0,1,0 –h localhost –pre 1 & APID=$!; sleep 120; kill $SPID
[START ] Connect to server (host:localhost, port:7000)
[INFO ] Connected - adf.agent.platoon.PlatoonFire@756ec19c (PRECOMPUTATION)
[INFO ] Connected - adf.agent.platoon.PlatoonPolice@366bbbe (PRECOMPUTATION)
[INFO ] Connected - adf.agent.platoon.PlatoonAmbulance@2a453513 (PRECOMPUTATION)
*******************
[FINISH] Connect PoliceForce (success:1)
[FINISH] Connect AmbulanceTeam (success:1)
[FINISH] Connect FireBrigade (success:1)
[FINISH] Done connecting to server (3 agents)

3.2.3 运行服务器

预计算完成后,按Control-C并键入sh kill.sh以停止仿真服务器的运行。然后,键入bash start-comprun.sh以再次启动仿真服务器以运行模拟方案。这些命令是:

Running Simulation Server
Control –C
$ sh kill.sh
$ bash start-comprun.sh

3.2.4 运行ADF样本代理

运行仿真服务器后,在另一个终端窗口上移至ADF目录,并使用以下命令运行代理:

Running Sample Agents
$ sh lauch.sh –all
[FINISH] Done connecting to server (3 agents)

4 仿真服务器选项

4.1 目录

仿真服务器的重要目录为:

  • boot/: 运行仿真服务器的脚本
    - boot/config/: 仿真服务器的配置文件
    - boot/logs/: 日志文件
  • build/: 仿真服务器的Java类
  • jars/: 仿真服务器的JAR文件
  • lib/: 模拟服务器使用的库
  • map/:在仿真服务器中可以运行的地图
  • modules/: 仿真服务器的源代码
  • oldsims/: 仿真服务器旧版本的源代码

4.2 参数

可以使用以下参数来运行模拟服务器:

  • -m MAPDIR or --map MAPDIR, 其中MAPDIR是包含要运行的地图的目录的路径(默认是 …/maps/gml/Kobe2013/map)
  • -c CONFIGDIR or --config CONFIGDIR, 其中CONFIGDIR是包含与地图关联的配置的目录 (default is ./config).
  • -l LOGDIR or --log LOGDIR, 其中LOGDIR是存储日志文件的目录 (default is ./logs).

这些参数可用于运行预计算和仿真。您必须使用与MAPDIR和CONFIGDIR相同的参数来运行带有或没有预计算的仿真服务器。如何使用这些参数运行模拟服务器的示例是:

Running Simulation Server with Options
$ bash start-precompute.sh –m ../maps/gml/berlin –l log2
(完成代理预计算后)
Control –C
$ sh kill.sh
$ bash start-comprun.sh –m ../maps/gml/berlin –l log2

5 如何使用ADF创建自己的代理

本节说明如何使用ADF示例实现代理。

5.1 重要目录

ADF(rcrs-adf-sample)的重要目录为:

  • config/: 代理程序的配置文件
  • src/: 代理的源代码
  • precomp_data/: 每类代理的预计算结果
  • build/: 代理的Java类
  • library/: 代理使用的库。

5.2 创建你的Agent文件

您可以仅使用目录中的文件来开发自己的代理代码:

  • src/adf/sample/centralized: 中心代理的源代码。这种类型的代理与世界的唯一交互是通过无线电通信。中心代理共有三种类型:救护中心,消防局和警察局,它们在仿真服务器中作为建筑物存在。
  • src/adf/sample/extraction: 目录中描述的联合动作的代码
  • src/adf/sample/module: 具体的算法代码,例如路径规划,聚类,目标检测等。该目录包含两个目录:
    -src/adf/samp;e/module/algorithm
    -src/adf/sample/module/complex

注意
您不得在src /adf/sample/tactics中对文件进行任何更改。 这是我们当前竞争规则的限制。
您应该从根本上复制示例代码,而不是对其进行编辑。原因是如果ADF无法找到您自己的代码,则将使用示例代码。您可以通过修改src/adf/config/module.cfg轻松更改对模块的引用。该文件的用法如下所述。

5.3 编码代理的工作流程

编写自己的代理所需的步骤是:

  1. 复制与您要创建的代理相关的示例代码
  2. 编辑复制的文件
  3. 根据编辑的文件编辑src/adf/config/module.cfg
  4. 编译并运行

5.4 模块配置文件

模块配置文件src/adf/config/module.cfg指示哪些代码用作代理的模块。图3显示了模块配置文件的一部分。冒号的左侧表示模块名称,右侧是类名称。在大多数情况下,目标问题相同的模块应当为所有类型的代理引用相同的类。图3中的示例在TacticsAmublanceTeam.Search和TacticsFireBrigade.Search中,指示这两个模块均引用adf.sample.module.complex.SampleSearch。一个使用示例展示在5.5.3节中。

TacticsAmbulanceTeam.HumanDetector : adf.sample.module.complex.SampleHumanDetector
TacticsAmbulanceTeam.Search : adf.sample.module.complex.SampleSearch
TacticsAmbulanceTeam.ActionTransport : adf.sample.extaction.ActionTransport
TacticsAmbulanceTeam.ActionExtMove : adf.sample.extaction.ActionExtMove
TacticsAmbulanceTeam.CommandExecutorAmbulance : adf.sample.centralized.CommandExecutorAmbulance
TacticsAmbulanceTeam.CommandExecutorScout : adf.sample.centralized.CommandExecutorScout
TacticsFireBrigade.BuildingDetector : adf.sample.module.complex.SampleBuildingDetector
TacticsFireBrigade.Search : adf.sample.module.complex.SampleSearch
TacticsFireBrigade.ActionFireFighting : adf.sample.extaction.ActionFireFighting
TacticsFireBrigade.ActionExtMove : adf.sample.extaction.ActionExtMove
…
Figure 3: Modules Configuration File

5.5 路径规划算法的A *算法实现示例

5.5.1 复制示例代码

首先,您应该复制示例代码进行路径规划,即SamplePathPlanning.java。该示例如下所述。请注意,由于空间限制,第二条命令分为两行,但应将其作为一行输入。

Copy the Sample Path Planning
$ mkdir –p srcmyteam/module/algorithm
$ cp src/adf/sample/module/algorithm/SamplePathPlanning.java 
Src/myteam/module/algorithm/AStarPathPlanning.java

5.5.2 编辑示例代码

Listing1是SamplePathPlanning.java的代码,其中包含Dijkstra的算法。您应该编辑第一行,第18行和第27行(这些行用红色注释表示)。您将在calc()方法中实现自己的代码,并删除仅由calc()中使用的isGoal()方法。Listing2显示了编辑这些行的结果。
您必须实现方法calc()才能通过方法getResult()获得其计算结果。返回的getResult()的类型为List 。
Listing3指出了方法calc()的内容。另外,您应该编写新的私有类节点方法(Node),该类由calc()方法使用。清单4中显示了代码。必须将其放在文件AStarPathPlanning.java中。

Listing 1: SamplePathPlanning.java
package adf.sample.module.algorithm;

import adf.agent.communication.MessageManager;
import adf.agent.develop.DevelopData;
import adf.agent.info.AgentInfo;
import adf.agent.info.ScenarioInfo;
import adf.agent.info.WorldInfo;
import adf.agent.module.ModuleManager;
import adf.agent.precompute.PrecomputeData;
import adf.component.module.algorithm.PathPlanning;
import rescuecore2.misc.collections.LazyMap;
import rescuecore2.standard.entities.Area;
import rescuecore2.worldmodel.Entity;
import rescuecore2.worldmodel.EntityID;

import java.util.*;

public class SamplePathPlanning extends PathPlanning {

    private Map<EntityID, Set<EntityID>> graph;

    private EntityID from;
    private Collection<EntityID> targets;
    private List<EntityID> result;
    // Edit the following line*
    public SamplePathPlanning(AgentInfo ai, WorldInfo wi, ScenarioInfo si, ModuleManager moduleManager, DevelopData developData) {
        super(ai, wi, si, moduleManager, developData);
        this.init();
    }

    private void init() {
        Map<EntityID, Set<EntityID>> neighbours = new LazyMap<EntityID, Set<EntityID>>() {
            @Override
            public Set<EntityID> createValue() {
                return new HashSet<>();
            }
        };
        for (Entity next : this.worldInfo) {
            if (next instanceof Area) {
                Collection<EntityID> areaNeighbours = ((Area) next).getNeighbours();
                neighbours.get(next.getID()).addAll(areaNeighbours);
            }
        }
        this.graph = neighbours;
    }

    @Override
    public List<EntityID> getResult() {
        return this.result;
    }

    @Override
    public PathPlanning setFrom(EntityID id) {
        this.from = id;
        return this;
    }

    @Override
    public PathPlanning setDestination(Collection<EntityID> targets) {
        this.targets = targets;
        return this;
    }

    @Override
    public PathPlanning updateInfo(MessageManager messageManager) {
        super.updateInfo(messageManager);
        return this;
    }

    @Override
    public PathPlanning precompute(PrecomputeData precomputeData) {
        super.precompute(precomputeData);
        return this;
    }

    @Override
    public PathPlanning resume(PrecomputeData precomputeData) {
        super.resume(precomputeData);
        return this;
    }

    @Override
    public PathPlanning preparate() {
        super.preparate();
        return this;
    }

    @Override
    public PathPlanning calc() { //Renew this method (implement your algrithm here)*
        List<EntityID> open = new LinkedList<>();
        Map<EntityID, EntityID> ancestors = new HashMap<>();
        open.add(this.from);
        EntityID next;
        boolean found = false; //found为false
        ancestors.put(this.from, this.from);
        do {
            next = open.remove(0);
            if (isGoal(next, targets)) {
                found = true;
                break;
            }
            Collection<EntityID> neighbours = graph.get(next);
            if (neighbours.isEmpty()) {
                continue;
            }
            for (EntityID neighbour : neighbours) {
                if (isGoal(neighbour, targets)) {
                    ancestors.put(neighbour, next);
                    next = neighbour;
                    found = true;
                    break;
                }
                else {
                    if (!ancestors.containsKey(neighbour)) {
                        open.add(neighbour);
                        ancestors.put(neighbour, next);
                    }
                }
            }
        } while (!found && !open.isEmpty());
        if (!found) {
            // No path
            this.result = null;
        }
        // Walk back from goal to this.from*
        EntityID current = next;
        LinkedList<EntityID> path = new LinkedList<>();
        do {
            path.add(0, current);
            current = ancestors.get(current);
            if (current == null) {
                throw new RuntimeException("Found a node with no ancestor! Something is broken.");
            }
        } while (current != this.from);
        this.result = path;
        return this;
    }
    // Remove the method ( it is only used by calc ())*
    private boolean isGoal(EntityID e, Collection<EntityID> test) {
        return test.contains(e);
    }
}
Listing 2:Part of AStarPlanning.java
package myteam.module.algorithm; // Position of the file

import adf.agent.develop.DevelopData;
import adf.agent.info.AgentInfo;
import adf.agent.info.ScenarioInfo;
import adf.agent.info.WorldInfo;
import adf.agent.module.ModuleManager;
import adf.agent.precompute.PrecomputeData;
import adf.component.module.algorithm.PathPlanning;
import rescuecore2.misc.collections.LazyMap;
import rescuecore2.standard.entities.Area;
import rescuecore2.worldmodel.Entity;
import rescuecore2.worldmodel.EntityID;

import java.util.*;

public class AStarPathPlanning extends PathPlanning { // Same as the file name

   private Map<EntityID, Set<EntityID>> graph;

   private EntityID from;
   private Collection<EntityID> targets;
   private List<EntityID> result;
   // Same as the file name
   public AStarPathPlanning(AgentInfo ai, WorldInfo wi, ScenarioInfo si, ModuleManager moduleManager, DevelopData developData) {
       super(ai, wi, si, moduleManager, developData);
       this.init();
   }
Listing 3: calc()
@Override
   public PathPlanning calc() {
       //  1
       List<EntityID> open = new LinkedList<>();
       List<EntityID> close = new LinkedList<>();
       Map<EntityID, Node> nodeMap = new HashMap<>();
    
       //  3
       open.add(this.from);
       nodeMap.put(this.from, new Node(null, this.from));
       close.clear();
    
       while (true) {
           //  4
           if (open.size() < 0) {
               this.result = null;
               return this;
           }
    
           //  5
           Node n = null;
           for (EntityID id : open) {
               Node node = nodeMap.get(id);
    
               if (n == null) {
                   n = node;
               } else if (node.estimate() < n.estimate()) {
                   n = node;
               }
           }
    
           //  6
           if (targets.contains(n.getID())) {
               //  9
               List<EntityID> path = new LinkedList<>();
               while (n != null) {
                   path.add(0, n.getID());
                   n = nodeMap.get(n.getParent());
               }
    
               this.result = path;
               return this;
           }
           open.remove(n.getID());
           close.add(n.getID());
    
           //  7
           Collection<EntityID> neighbours = this.graph.get(n.getID());
           for (EntityID neighbour : neighbours) {
               Node m = new Node(n, neighbour);
    
               if (!open.contains(neighbour) && !close.contains(neighbour)) {
                   open.add(m.getID());
                   nodeMap.put(neighbour, m);
               }
               else if (open.contains(neighbour) && m.estimate() < nodeMap.get(neighbour).estimate()) {
                   nodeMap.put(neighbour, m);
               }
               else if (!close.contains(neighbour) && m.estimate() < nodeMap.get(neighbour).estimate()) {
                   nodeMap.put(neighbour, m);
               }
           }
       }
   }
Listing 4: Node Class
private class Node {
	    EntityID id;
	    EntityID parent;
	 
	    double cost;
	    double heuristic;
	 
	    public Node(Node from, EntityID id) {
	        this.id = id;
	 
	        if (from == null) {
	            this.cost = 0;
	        } else {
	            this.parent = from.getID();
	            this.cost = from.getCost() + worldInfo.getDistance(from.getID(), id);
	        }
	 
	        this.heuristic = worldInfo.getDistance(id, targets.toArray(new EntityID[targets.size()])[0]);
	    }
	 
	    public EntityID getID() {
	        return id;
	    }
	 
	    public double getCost() {
	        return cost;
	    }
	 
	    public double estimate() {
	        return cost + heuristic;
	    }
	 
	    public EntityID getParent() {
	        return this.parent;
	    }
	}

5.5.3 编辑模块配置文件

您必须编辑模块配置文件src/adf/config/module.cfg中与路径规划相关的模块去使用您的代码。图4和5显示了默认module.cfg的一部分和已编辑更改了与路径规划相关行的module.cfg的一部分。在这种情况下,文件中的所有adf.sample.module.algorithm.SamplePathPlanning都将替换为myteam.module.algorithm.AStarPathPlanning。 如果您想在某些模块中使用该代码,则可以标明那唯一的模块引用它。

SampleRoadDetector.PathPlanning : adf.sample.module.algorithm.SamplePathPlanning
SampleSearch.PathPlanning.Ambulance : adf.sample.module.algorithm.SamplePathPlanning
SampleSearch.PathPlanning.Fire : adf.sample.module.algorithm.SamplePathPlanning
SampleSearch.PathPlanning.Police : adf.sample.module.algorithm.SamplePathPlanning
ActionExtClear.PathPlanning : adf.sample.module.algorithm.SamplePathPlanning
ActionExtMove.PathPlanning : adf.sample.module.algorithm.SamplePathPlanning
ActionFireFighting.PathPlanning : adf.sample.module.algorithm.SamplePathPlanning
ActionTransport.PathPlanning : adf.sample.module.algorithm.SamplePathPlanning
CommandExecutorAmbulance.PathPlanning : adf.sample.module.algorithm.SamplePathPlanning
CommandExecutorFire.PathPlanning : adf.sample.module.algorithm.SamplePathPlanning
CommandExecutorPolice.PathPlanning : adf.sample.module.algorithm.SamplePathPlanning
CommandExecutorScout.PathPlanning : adf.sample.module.algorithm.SamplePathPlanning
CommandExecutorScoutPolice.PathPlanning : adf.sample.module.algorithm.SamplePathPlanning
Figure 4: Default module.cfg
SampleRoadDetector.PathPlanning : myteam.module.algorithm.AStarPathPlanning
SampleSearch.PathPlanning.Ambulance : myteam.module.algorithm.AStarPathPlanning
SampleSearch.PathPlanning.Fire : myteam.module.algorithm.AStarPathPlanning
SampleSearch.PathPlanning.Police : myteam.module.algorithm.AStarPathPlanning
ActionExtClear.PathPlanning : myteam.module.algorithm.AStarPathPlanning
ActionExtMove.PathPlanning : myteam.module.algorithm.AStarPathPlanning
ActionFireFighting.PathPlanning : myteam.module.algorithm.AStarPathPlanning
ActionTransport.PathPlanning : myteam.module.algorithm.AStarPathPlanning
CommandExecutorAmbulance.PathPlanning : myteam.module.algorithm.AStarPathPlanning
CommandExecutorFire.PathPlanning : myteam.module.algorithm.AStarPathPlanning
CommandExecutorPolice.PathPlanning : myteam.module.algorithm.AStarPathPlanning
CommandExecutorScout.PathPlanning : myteam.module.algorithm.AStarPathPlanning
CommandExecutorScoutPolice.PathPlanning : myteam.module.algorithm.AStarPathPlanning
Figure 5: Edited module.cfg

6 RoboCup救援仿真服务器

6.1 架构

该仿真器实际上分为许多其他子仿真器:

  • 清障仿真器,负责障碍物的清除
  • 坍塌仿真器,负责管理建筑物结构毁坏和障碍物的产生
  • 点火仿真器,负责在仿真期间在众多建筑物中随机性引燃
  • 交通仿真器,负责人类(具有human属性的Agent)的移动过程
  • 杂项仿真器,负责人类受伤和掩埋的仿真
    内核仿真器与这些仿真器建立了连接,负责协调仿真器的协作流程并集中各仿真器的仿真结果。

RoboCup Rescue模拟器旨在创建具有局部可观察性,时间离散性,动态性,随机性的多Agent环境。换句话说,在这种环境下:

  • 不能通过单个代理的感知来了解整个世界的当前状态(即使该代理具有无限的视线范围,仍然无法透过建筑物的墙壁看到)。
  • 时间被分为间隔,而不是连续时间;
  • 有一些随机因素会影响其状态转换(注:指环境会因为随机因素动态变化);
  • 存在一个以上的救援智能体代理,并且其中代理的行为动作会影响到其他代理的表现。

时间被划分为时间步长;在每个时间步长中,代理都会感知环境状态以及将要执行某种行为动作的原因。在每个时间步中,都会发生以下情况:

  1. 内核会更新所有代理的感知信息(包括视觉和交流通信信息),然后等待代理命令
  2. 代理更新其世界模型并做出决策,然后将他们的命令发送到内核
  3. 内核将代理的命令发送到子仿真器
  4. 仿真器处理代理程序的命令,并将环境所受到的改变发送回内核
  5. 内核将环境的改变发送给显示

6.2 实体

仿真器中表示了几个对象,如图7所示。

6.2.1 障碍物(封锁Blockades)

障碍物阻碍了救援智能体和平民的前进道路;它们以道路中黑色块状(多边形)物体表示。障碍物出现在仿真的开始,之后将不再产生。并且它们只能由警察部队(警察代理)清除。
属性

  • position: 障碍物所属道路的编号ID
  • repair cost: 从道路上完全清除该障碍物的成本
  • shape: 包围整个障碍物的矩形
  • X & Y: 该障碍物的中心坐标
  • apexes: 包含该障碍物顶点的向量

6.2.2 区域(Area)

区域实体代表建筑物和道路。
属性

  • blockades: 该区域的障碍物列表(list)
  • edges(边缘): 限制该区域的边缘列表(通常包括建筑物、障碍物边缘)
  • neighbours(邻居s): 可以从该区域访问到的邻接区域列表
  • X & Y: 在地图中代表该区域的坐标
    虽然建筑物和道路都具有障碍物(封锁)属性,但障碍物仅出现在道路中。

6.2.3 建筑(Building)

建筑物将仿真服务器中各位建筑物进行分组:除了常规建筑物外,如下面描述的,还有无法着火的特殊类型的建筑物(包括避难所,救护中心,消防局和警察局,如图8所示)。 它们将在本文档的后面部分中描述。

属性

  • brokenness(破损): 建筑物在结构上有多严重的损坏,在仿真中不改变
  • fireryness(火势): 建筑物中火势(着火强度)和与火有关的破坏
    - UNBURNT (not burnt at all)完全不燃烧的
    - WATER_DAMAGE (not burnt at all, but has water damage) 完全不燃烧,但有水造成的伤害
    - HEATING (on fire a bit)刚着火,着火了一点
    - BURNING (on fire a bit more) 着火多了一点
    - INFERNO (on fire a lot)着火了很多
    - MINOR_DAMAGE (extinguished but minor damage)火势熄灭,但轻微损坏
    - MODERATE_DAMAGE (extinguished but moderate damage)火势熄灭,中等损坏
    - SEVERE_DAMAGE (extinguished but major damage)火势熄灭,但损坏严重
    - BURNT_OUT (completely burnt out)完全烧光
  • floors: 建筑物的楼层数
  • ground area: 每层面积
  • ignition(点火):如果仿真器点燃了这座建筑物的标志(注:建筑物可能会被仿真器点燃或靠近燃烧的建筑物而引燃;如果建筑物在仿真的某个时候被模拟器点燃,则ignition(点火)标志将设置为“ 1”)
  • importance: (功能未知;所有建筑物具有同等价值)
  • temperature(温度): 建筑物的温度; 如果超过某个阈值,建筑物着火
  • total area: 建筑物的总面积(楼层X地面面积)

    常规建筑物根据其状态用各种颜色的多边形表示,如图9所示; 颜色越深,火灾或水的结构破坏越大。
    在仿真开始时,破碎的建筑物将人困在碎片中;这些碎片必须由救护车队清除,然后继续救援受伤人员。
    避难所是一种特殊的建筑物:它代表了旨在支持救援活动的场所,能够为伤员提供医疗护理,为消防员提供水源。在仿真器中,避难所内的人的伤害为零,这意味着他们在那儿时不会失去健康;但是,当人类实体离开避难所时,伤害将恢复。
    此外,在避难所内,消防队在每个周期的补给水量都是一定的。

6.2.4 道路(Roads)

代表道路的区域实体除了区域实体的属性外,没有其他新属性。

6.2.5 人类(Humans)

代表人类的实体。在仿真中,他们可以是市民,救护队,消防队或警察部队。他们全部由不同颜色的圆圈表示,并且如果他们死了或被掩埋(注:此处掩埋指在建筑物内被建筑碎片困住)了将无法自行移动。

  • buriedness: 人类被掩埋的程度
  • damage: 人类每个周期损失多少HP的伤害;且在到达避难所时伤害设置为零
  • direction: 人体移动的方向(推断); Y轴正半数为零,并且值增加,直到逆时针旋转129599(360 * 60 * 60-1)秒(此处为角度单位)
  • HP: 人类的健康点(生命值HP); 如果达到0,人类死亡
  • position: 人员位置所在实体的ID;可以是区域实体或人类实体(如果在救护车内)
  • position history: 人类在上一个周期中所经过的实体列表, 按时间顺序排列
  • stamina(耐力): 未实现; 每次代理商采取行动时都会减少,并且在每次循环开始时都会部分补充
  • travel distance(行程距离): (未知)
  • X & Y: 代表人类在地图中的坐标
    仿真中每个human实体的类型和健康状况由颜色来表示:其健康状况越低,颜色越深。死去的人类用黑色表示。

6.2.6 市民(Civilians)

市民是人类实体,且不属于救援队。它们由颜色绿色表示。如果没有受伤或被埋葬,他们的标准行为是前往离他们最近的避难所。否则,它们将必须由救护车队运送。(注:受伤的市民是无法自行移动的,但救援智能体仍可以继续工作,但伤害存在)

6.3 代理(Agents注:本文智能体、代理、Agent为同一意义)

这是组成救援团队的实体;换句话说,这就是您要编程的内容。代理分为两种:排代理(此处排指军队的排,应当是代表一个小组团队)和中心代理。(注:我们常按照其Agent可移动性将其分为移动智能体与中心智能体)

6.3.1 排代理(Platoon agents移动智能体)

排代理能够通过感知和在仿真中执行动作与模仿真环境进行交互。他们还可以通过语音或无线电通信与其他代理交换消息。他们分为三个不同的类别:救护团队,消防队和警察部队。
救护团队 救护团队负责营救人员(特工(即救援智能体)和平民)并将他们带到避难所。他们能够挖掘受害者和负载一个人类实体。
消防队 消防队负责扑灭着火的建筑物。此外,他们能在水箱中携带确定量的水,并且可以在避难所中补充水。
警察部队 警察部队负责清除道路上的障碍物。当被命令这样做时,他们将在每个周期里从目标障碍物中按照指定的维修花费数值清除一定数量的障碍物。但是,与救护队和消防队不同的是,让两个警察部队对同一障碍物执行清理动作并不会带来任何优势:就同只有一个警察部队在清理障碍物一样。

6.3.2 中央代理(Central agents)

中央代理是只通过无线电通信与世界的交互的一种智能体。中央代理有三种类型:救护中心,消防局(消防中心)和警察局(警察中心),并且它们作为建筑物。

6.4 感知和命令

在仿真中有两种感知模式:标准和视线。 我们将只讨论后者,因为它是比赛中使用的一种。
视线感知模式模拟了代理(智能体)的视觉感知:视觉的范围和大量的射线被定义,并且代理能够感知这些射线可以到达的任何物体。(即:以智能体本身向外发射最大长度固定的射线,射线所能够触及的物体智能体皆可感知,但射线会被建筑等不可视过物体阻拦,另外障碍物非不可视过物体)

代理(智能体)当前可见实体集合被存储在一个名为ChangeSet的结构中;存在于其中的实体会在智能体的世界模型中自动更新;就是说,如果一个代理人感知到他以前不知道的障碍物,该障碍物会自动添加到其世界模型中。相反,尽管不会发生:如果代理不再能感知到障碍物,即使他知道这里曾经存在障碍物,其世界模型也不会改变。在这种情况下,即使该障碍物已被清除,该代理仍会认为这条路上存在障碍物。因此,这由代理决定如何处理并相应地修改其世界模型。

6.5 通讯

仿真中有两种通信形式:直接通信和无线电通信。直接通信是距发射器(speak)智能体一定半径内的人类可以听见的通信方法,通过speak命令执行。仿佛发射器大喊某事。
无线电通信是通过tell命令完成的,并将信息发送到所有已签署该广播的频道的代理(智能体)。无线电通信信道的数量有限,且每个信道的带宽都有限。
在两种类型的通信中,必须先将消息编码为字节字符串,然后再发送;接收者在收到消息后必须对其进行解码。这两种通信类型都可能容易丢失消息(message),因为目标智能体没有接收到消息。无线电通信还容易出现消息故障,即接收到的消息为空。

  • 7
    点赞
  • 59
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值