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 的示例代理。
使用此命令克隆存储库
git clone https://github.com/roborescue/rcrs-adf-sample.git
2.3. 目录
rcrs-adf-sample
包含多个目录。 重要的目录是:
config/
: 配置文件src/
: 源代码precomp_data
: 每种类型代理的预计算结果build/
: 构建结果library/
: 使用的库
2.4 编译
执行以下步骤来编译 ADF 示例代理:
- 切换到目录
rcrs-adf-sample
- 使用命令编译项目
$ ./gradlew clean
$ ./gradlew build
2.5 示例
在 Ubuntu 中,安装根据以下命令进行:
$ git clone https://github.com/roborescue/rcrs-adf-sample.git
$ cd rcrs-adf-sample
$ ./gradlew clean
$ ./gradlew build
3.运行
模拟服务器和 ADF 示例代理有两种执行模式:预计算模式和正常模式。
3.1 预计算模式
在预计算模式下,模拟器连接每种类型的一个代理,并允许它们写入计算结果。
在预计算模式下运行模拟服务器的命令序列是:
$ cd rcrs-server
$ cd boot
$ bash start-precompute.sh
在为预计算运行模拟服务器后,移动到另一个终端窗口上的 ADF 目录并运行执行以下命令:
$ bash launch.sh -t 1,0,1,0,1,0 -h localhost -pre 1 & APID=$! ; sleep 120 ; kill $APID
预计算完成后,按下 Ctrl + C 并键入 sh kill.sh
以停止运行的模拟服务器。
$ bash kill.sh
3.2 正常模式
在正常模式下,模拟器连接场景中定义的所有代理,并允许它们使用预计算输出。
在正常模式下运行模拟服务器的命令序列是:
$ cd rcrs-server
$ cd boot
$ bash start-comprun.sh
运行模拟服务器后,移动到另一个终端窗口上的 ADF 目录并使用以下命令运行代理:
$ 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
,示例如下所述。
$ 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()
。
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>
。
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()
的内容。
@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
中。
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
- 感谢你赐予我前进的力量