01、需求分析
“火星探矿机器人”旨在要开发若干个自主机器人,将其送到火星上去搜寻和采集火星上的矿产资源。
火星环境对于开发者和自主机器人而言事先不可知,但是可以想象火星表面会有多样化的地形情况,如河流、巨石、凹坑等,机器人在运动过程中会遇到各种障碍;
另外,火星上还可能存在一些未知的动态因素(如风暴等),会使得环境的状况发生变化。
概括起来,火星环境具有开放、动态、不可知、难控等特点。
为了简化案例的开发和演示,可以将机器人探矿的区域(即机器人的运动环境)简化和抽象成M×M的单元格,每个单元格代表某个火星区域,火星矿产分布在这些单元格中,同时这些单元格中还存在阻碍机器人运行的障碍物。
探矿机器人在这些网格中运动,根据感知到的网格环境信息自主地决定自身的行为。
如果所在的单元格有矿产,则采集矿产;如果探测到附近的单元格存在矿产,则移动到该单元格;如果周围的单元格存在障碍物,则避开这些障碍物。
环境网格有两个特殊的单元格:一个是矿产堆积单元格,用于存放机器人采集到的矿产,如图1中的左下角;另一个是能量补充单元格,机器人可以从该单元格获得能量。
图1火星探矿机器人案例的环境示意图
“火星探矿机器人”的设计目标是要采集火星矿产,并将其带到预定的区域。
为此,该机器人具有以下一组基本能力。
移动。它能够在火星表面移动,能够从一个单元格移动到其上、下、左、右的相邻单元格。
探测。它配备了多种传感器,具有一定程度的环境感知能力,具体包括探测周围一定区间(如相邻多少个单元格)的矿产分布情况以及障碍物情况。
采集。它能够采集所在单元格中的火星矿产。
卸载。它能够卸载其采集的、放置在其体内的矿产。
交互。它能够与其他的机器人进行交互和协同,以更高效地采集矿产。例如,一个机器人探测到大面积的矿产,它可以将该矿产信息告知其他机器人,以便他们能够来该区域采集矿产。
为了充分反应探矿机器人的实际情况,我们对机器人作了以下假设:
①每个机器人存储矿产的容量都有一定的限度,即机器人内部只有有限的空间存放矿产,一旦机器人采集的矿产超出其存储容量,它必须将这些矿产卸载到特定的位置区域,以便能够再次采集矿产。
②每个机器人的能量都有一定的限度,机器人在移动、探测、采集、卸载等过程中会消耗能量,为此机器人必须在其能量消耗殆尽之前补充能量(如充电)。
③每个机器人的感知能力都是有限度的,它只能够感知其周围一定范围内的环境状况,如邻近两个单位的单元格。
下面通过多个场景描述机器人如何在上述火星环境下采集火星矿产,这些场景分别描述了机器人采矿的不同工作模式,反映了实现这些自主机器人的不同难易程度。
场景一:独立采集矿产。
在该场景中,有多个自主机器人参与到火星矿产的采集工作中,每个机器人都有移动、探测、采集、卸载的能力,它们在火星表面随机移动,根据其所在位置探测到的矿产信息和障碍物等环境信息自主地实施行为。
但是,这些机器人都是单独工作,它们之间没有任何交互与合作。
因此,可以将本场景中的每个机器人都抽象和设计为自主的Agent。
场景二:合作采集矿产。
在该场景中,有多个自主机器人参与到火星矿产的采集工作中,每个机器人都有移动、探测、采集、卸载的能力,它们在完成各自矿产采集任务的同时,相互之间还进行交互和合作,以更高效地开展工作。
例如,某个机器人探测到大片的矿产信息,那么它可以将该信息告诉给其他机器人,或者请求其他机器人来该区域采矿。
因此,可以将本场景中的机器人抽象和设计为由多个自主Agent所构成的多Agent系统。
该系统的设计和实现不仅要考虑到各个自主Agent,还要考虑到这些Agent之间的交互和协同。
场景三:多角色合作采集矿产。
在该场景中,有多个具有不同职责、扮演不同角色的机器人参与到火星矿产的采集工作中,每类机器人承担矿产采集中的某项工作(如探测、采集),它们之间通过交互和合作共同完成矿产采集任务,即该场景有多种类型的机器人,包括:
①采矿机器人,采集矿产并将其运送到指定区域;
②探测机器人,负责探测矿产并将其探测到的矿产信息通知给采矿机器人。
因此,可以将本场景中的机器人抽象和设计为由多个自主Agent所构成的多Agent系统。
该系统的设计和实现不仅要考虑到各个自主Agent,还要考虑到这些Agent之间的交互和协同。
显然,该场景比前一个场景更复杂,它涉及的Agent类型和数量、交互和合作关系等更多。
02、设计与实现
下面介绍如何基于多Agent系统的开发框架JADE开发“火星探矿机器人”案例。
为了简化设计,聚焦于Agent的构造和实现,“火星探矿机器人”案例中的环境被设计为一个M×M的网格,每个网格单元代表了一个地理位置,不同网格单元具有不同的地形信息,可能存在影响机器人移动的障碍物,火星矿产非均匀地分布在网格单元格中。
机器人驻留在网格环境中,可以在不同的网格中移动,感知网格周围的环境信息,如矿产、障碍物等,如果它发现所在的网格中存在矿产,那么它就挖掘矿产。
整个应用的界面如图2所示。
界面左部显示了机器人所在的环境(用网格来表示),它提供了多样化的图符以及数字信息表示环境中的机器人、矿产、障碍物等及其在环境中的分布情况。
机器人运行在网格中,因而任何时刻机器人都有其所处单元格的位置。
界面的右部显示了各种图符信息的说明以及系统和环境中机器人、矿产等数量的变化。
界面的下部提供整个系统运行过程中的各种动态信息,如某个机器人探测到矿产、机器人从一个位置移动到另一个位置等。
图2火星探矿多Agent系统的运行界面
系统在初始化时将自动生成机器人的运行环境,包括矿产、障碍物等的分布。
用户可以根据需要配置系统运行时的机器人信息,包括机器人的类型、数目等,设置机器人的基本属性,如机器人的观测范围、机器人的初始能量值等。
在实际开发中,我们具有以下的基本假设:Agent从初始位置出发在地图上随机单步移动,遇到障碍能够自动避开,能自动探测到其周边是否有矿产,一次只能采集一个矿产并将其运送到指定的矿产仓库。
1●环境的设计与实现
我们设计了一个环境类(对应于environment.java文件)表示和处理应用中的环境。
该类封装了以下一组属性和行为。
环境中的矿产,定义一个一维动态数组存放矿产的位置ArrayList<Coordinate>MinePositions,其中Coordinate是一个类,定义了网格的坐标。
环境中的障碍物,定义一个一维动态数组存放障碍物的位置ArrayList<Coordinate>ObstaclePositions。
环境中的机器人,ArrayList<BasicRobot>robots,该属性定义了处于环境中的一组机器人。
InitEnv()方法,该方法生成网格环境并随机产生环境中的矿产和障碍物。
2●系统中的Agent和行为
根据应用案例描述,我们设计了如图3所示的一组Agent和行为,以支持场景一至场景三的实现。
图3系统中的Agent及其行为示意图
BasicRobot是一个基本的Agent机器人,它能够在环境中行走,具有随机行走RandomWalkBev的行为。
在本案例中,BasicRobot无须生成具体的Agent实例,而是被其他Agent所继承。
ExploreRobot是一个专门为场景一设计的Agent机器人,它继承了“BasicRobot”Agent的属性、方法和行为,具有探测矿产DetectMineBev、采集矿产DigMineBev、转存矿产DumpMineBev3个行为。
也就是说,该Agent可以独立完成探矿、采矿和存矿的功能。
但是,ExploreRobot不具有与其他Agent交互和协同的能力。
在场景一,系统可能会产生一个或者多个“ExploreRobot”Agent机器人。
ExploreCoopRobot是一个专门为场景二设计的Agent机器人,它继承了“ExploreRobot”Agent的属性、方法和行为,同时具有交互协同CooperationBev行为,能够与其他Agent机器人进行协同,以告知所探测的矿产信息。
在场景二中,系统可能会产生一个或者多个“ExploreCoopRobot”Agent机器人。
场景三包含两类不同的Agent机器人:
一类是专门探矿的机器人DetectMineRobot,它具有探测矿产DetectMineBev、交互协同CooperationBev两个行为,可以实现探矿并将所探测到的矿产信息告诉给其他的Agent机器人;
另一类是DigMineRobot,它具有采集矿产DigMineBev、转存矿产DumpMineBev、交互协同CooperationBev3个行为,能够实施采矿、存矿等功能,处理其他Agent发送过来的消息,并将采矿信息告诉给环境中的其他
3●Agent类的设计与实现
Agent类的设计与实现需要注意以下几点:
①继承Agent类或者其子类;
②在构造函数中初始化Agent的基本属性;
③在Setup()方法中通过addBehaviour()语句给Agent增加相应的行为,以便Agent创建后就可执行这些行为。
下面以“ExploreRobot”Agent为例介绍如何设计和实现Agent。
任何Agent类都要继承JADE的Agent类或者其子类,如BasicRobot继承了Agent类,其代码框架如下所示。
import jade.core.Agent;
public class BasicRobot extends Agent{…}
“ExploreRobot”Agent则继承了“BasicRobot”Agent,而“BasicRobot”Agent是Agent类的子类,该部分的代码框架如下所示。
import jade.core.Agent;
public class ExploreRobot extends BasicRobot{…}
在每个Agent的构造函数部分,程序员需要针对Agent的一些属性完成初始化工作。
例如,对于每一个“ExploreRobot”Agent而言,当其实例化之后,需要为其随机生成该Agent机器人在环境(即网格)中的位置,同时需要初始化该机器人所获得环境中矿产的信息。
该部分的代码框架描述如下。
import jade.core.Agent;
public class ExploreRobot extends BasicRobot{
Coordinate position;
ArrayList<Coordinate>FoundMine;
…
public ExploreRobot(){
…;
position=env.CreateRandomPosition();
FoundMine=new ArrayList<Coordinate>();
}
…
}
其中,position是一个类型为Coordinate的属性,它定义了Agent机器人在网格中的坐标;FoundMine是一个类型为Coordinate的动态数组,定义了Agent机器人探测到的矿产位置信息。
在上述语句中,CreateRandomPosition()是一个产生随机环境位置的方法,FoundMine=new ArrayList<Coordination>()语句则产生一个类型为Coordinate的动态数组。
Agent类的设计通常需要实例化setup()方法。
针对本案例,我们需要在该方法中增加一组行为,以便让ExploreRobot在创建之后就可执行这些行为,其代码框架描述如下。
import jade.core.Agent;
public class ExploreRobot extends BasicRobot{
…
public void setup(){
addBehaviour(new DetectMineBehaviour(this));
addBehaviour(new DigMineBehaviour(this));
addBehaviour(new DumpMineBev(this));
}
…
}
其中,语句addBehaviour(new DetectMineBehaviour(this));旨在增加一个探测矿产的行为,语句addBehaviour(new DigMineBehaviour(this));旨在增加一个挖矿的行为,语句addBehaviour(new DumpMineBev(this));旨在增加一个卸载矿产到矿产仓库的行为。
4●行为类的设计与实现
行为类的设计与实现需要注意以下几点:
①分析待实现行为的特点,确定该行为类应继承什么样的基类行为;
②在publicvoidaction()方法中编写具体的行为代码。
下面以DetectMineBehaviour行为为例介绍如何设计和实现行为。
首先,DetectMineBehaviour行为旨在探测Agent周边是否存在矿产,这种探测需要不断地进行,因而该行为属于一类周期性行为,需要继承JADE中的CyclicBehaviour类,其代码框架如下所示。
import jade.core.Agent;
import jade.core.behaviours.CyclicBehaviour;
…
public class Detect MineBev extends CyclicBehaviour{
…
public DetectMineBev(BasicRobot robot){
…
}
public void action(){
…
}
}
其中,DetectMineBev()是构造函数,可以完成一些初始化的工作;public void action()定义了行为体。
其次,需要在public void action()方法中定义行为的程序代码。
对于DetectMineBev行为而言,其行为部分主要是要获得其当前所在位置的周围是否存在矿产,如果存在,则将这些矿产信息(即矿产所在的坐标)加入到Agent机器人的FoundMine动态数组中。
5●Agent间交互的设计与实现
场景二和场景三都涉及Agent机器人之间的交互和协同。
下面以场景三中的DetectRobot为例,介绍如何实现Agent之间基于FIPA ACL的交互和协同。
一旦某个DetectRobot探测到某些矿产,它需要将它所探测到的矿产信息通知给环境中的“DigRobot”Agent机器人。
下面的程序代码描述了Agent机器人在DetectMineBehaviour行为中如何给其他的Agent发送所探测到的矿产信息。
首先,通过newACLMessage(ACLMessage.INFORM)语句产生一个“通知”类型的ACL消息,其次将矿产坐标作为该消息的内容,然后将其他Agent加入到消息的接收者列表中,最后发送该ACL消息。
public class DetectMineBehaviour extends CyclicBehaviour {
…
//行为体
public void action(){
Coordinate target =RandomWalkBev.RandomNextpoint(ui.myGrid);
int RobotNum =en.getrobotArryList().size();
…
//生成消息类型
ACLMessage msg =new ACLMessage(ACLMessage.INFORM);
//生成消息内容
for(int i=0;i<en.getMinePositions().size();i++){
targetposi.x= en.getMinePositions().get(i).x;
targetposi.y= en.getMinePositions().get(i).y;
try{
msg.setContentObject((Serializable) targetposi);
} catch(IOException e){
//TODO Auto-generated catch block
e.printStackTrace();
}
}
//生成消息的接收者
for(int i=0;i<en.getrobotArryList().size();i++){
msg.addReceiver(new AID("Dig"+i,AID.ISLOCALNAME));
}
thisRobot.send(msg);//发送消息
}
…
}