ADF框架开发教程

该教程说明如何安装和执行 RoboCup 救援模拟代理开发框架 (ADF) 示例代理,以及如何使用 ADF 示例代理实现新的代理团队

1.目的

该教程说明如何安装和执行 RoboCup 救援模拟代理开发框架 (ADF) 示例代理,以及如何使用 ADF 示例代理实现新的代理团队


2.安装

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

2.1 软件要求
2.2 下载

您可以通过克隆 https://github.com/roborescue/rcrs-adf-sample 存储库来下载带有 ADF 的示例代理。

使用此命令克隆存储库

1
git clone https://github.com/roborescue/rcrs-adf-sample.git
2.3. 目录

rcrs-adf-sample 包含多个目录。 重要的目录是:

  • config/: 配置文件
  • src/: 源代码
  • precomp_data: 每种类型代理的预计算结果
  • build/: 构建结果
  • library/: 使用的库
2.4 编译

执行以下步骤来编译 ADF 示例代理:

  1. 切换到目录 rcrs-adf-sample
  2. 使用命令编译项目
1
2
$ ./gradlew clean
$ ./gradlew build
2.5 示例

在 Ubuntu 中,安装根据以下命令进行:

1
2
3
4
$ git clone https://github.com/roborescue/rcrs-adf-sample.git
$ cd rcrs-adf-sample
$ ./gradlew clean
$ ./gradlew build

3.运行

模拟服务器和 ADF 示例代理有两种执行模式:预计算模式和正常模式。

3.1 预计算模式

在预计算模式下,模拟器连接每种类型的一个代理,并允许它们写入计算结果。

在预计算模式下运行模拟服务器的命令序列是:

1
2
3
$ cd rcrs-server
$ cd boot
$ bash start-precompute.sh

在为预计算运行模拟服务器后,移动到另一个终端窗口上的 ADF 目录并运行执行以下命令:

1
$ bash launch.sh -t 1,0,1,0,1,0 -h localhost -pre 1 & APID=$! ; sleep 120 ; kill $APID

预计算完成后,按下 Ctrl + C 并键入 sh kill.sh 以停止运行的模拟服务器。

1
$ bash kill.sh
3.2 正常模式

在正常模式下,模拟器连接场景中定义的所有代理,并允许它们使用预计算输出。

在正常模式下运行模拟服务器的命令序列是:

1
2
3
$ cd rcrs-server
$ cd boot
$ bash start-comprun.sh

运行模拟服务器后,移动到另一个终端窗口上的 ADF 目录并使用以下命令运行代理:

1
$ bash launch.sh -all

4.使用 ADF 开发您自己的代理

本节介绍如何使用 ADF 示例实现您的代理。

4.1 开发代理的文件

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

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

注意!!! 您不得对 src/adf/sample/tactics 中的文件进行任何更改。 这是我们目前的竞争规则的限制。

您应该从根本上复制示例代码,而不是编辑它们。 原因是如果 ADF 找不到您自己的代码,将使用示例代码。 您可以通过修改 src/adf/config/module.cfg 轻松更改对模块的引用。

4.2 为您的代理编码的工作流程

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

  • 复制与您要创建的代理相关的示例代码
  • 编辑拷贝的文件
  • 根据编辑的文件编辑src/adf/config/module.cfg
  • 编译和运行
4.3 模块的配置文件

模块配置文件 src/adf/config/module.cfg 指示哪些代码将用作代理模块。

以下显示模块配置文件的一部分。 冒号左边是模块名,右边是类名。 在大多数情况下,目标问题相同的模块应该针对所有代理类型引用相同的类

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

在该例子中, TacticsAmbulanceTeam.Search TacticsFireBrigade.Search 两个模块都引用 adf.sample.module.complex.SampleSearch

4.4 路径规划算法 A* 算法的示例
4.4.1 复制 ADF 示例代码

首先,您应该复制路径规划的示例代码,即 SamplePathPlanning.java,示例如下所述。

1
2
$ mkdir -p src/myteam/module/algorithm
$ cp src/adf/sample/module/algorithm/SamplePathPlanning.java src/myteam/module/algorithm/AStarPathPlanning.java
4.4.2 编辑 ADF 示例代码

以下是具有 Dijkstra 算法的 SamplePathPlanning.java 的代码。 您应该编辑第 1 行、第 18 行和第 27 行。 您将在方法 calc() 中实现自己的代码,并删除仅由 calc() 使用的方法 isGoal()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
package adf.sample.module.algorithm; // Edit this line

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 { // Edit this line

  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 algorithm here)
    List<EntityID> open = new LinkedList<>();
    Map<EntityID, EntityID> ancestors = new HashMap<>();
    open.add(this.from);
    EntityID next;
    boolean 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);
  }
}

以下代码显示了编辑这些行的结果。

您必须实现方法 calc() 才能通过方法 getResult() 获取其计算结果。 getResult() 返回的类型是 List<EntityID>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package myteam.module.algorithm; // Position of the file

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 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();
  }

以下代码指示方法 calc() 的内容。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
  @Override
  public PathPlanning calc() {
    List<EntityID> open = new LinkedList<>();
    List<EntityID> close = new LinkedList<>();
    Map<EntityID, Node> nodeMap = new HashMap<>();
    open.add(this.from);
    nodeMap.put(this.from, new Node(null, this.from));
    close.clear();

    while (true) {
      if (open.size() < 0) {
        this.result = null;
        return this;
      }
      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;
        }
      }
      if (targets.contains(n.getID())) {
        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());

      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);
        }
      }
    }
  }

此外,您应该编写新的私有类 Node,它被方法 calc() 使用。 代码如下所示。它必须放在文件 AStarPathPlanning.java 中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
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;
  }
}
4.4.3 编辑模块的配置文件

您必须编辑与路径规划相关的模块配置文件 src/adf/config/module.cfg 才能使用您的代码。 下列显示了默认 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

修改后:

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