由于Anylogic使用的是java代码,因此对java没有任何基础的同学可能会在学习时有些吃力。所以本文的目的在于教会大家如何在Anylogic中使用java代码来编写程序。
个人站点:https://whl1207.github.io/,记录一些学习笔记,主要写点基于anylogic的仿真和web开发方面的知识点。有兴趣的同学可以探讨一下!
在正式开始学习之前先给大家介绍一个函数:
System.out.print();
这个函数用于在控制台输出消息,那么该函数应该在哪里输入呢?
- 1、打开Anylogic软件。
- 2、在任意位置新建一个模型。
- 3、建好后,单击Main智能体的空白位置。
- 4、在右侧Main的属性面板中找到智能体行动的启动时的输入栏。
- 5、在其中输入System.out.print(1);。
- 6、启动模型并运行。
- 7、观察到控制台中输出了1,说明代码输入成功。
该函数将在学习的过程中经常使用到,并且该函数也经常用于在开发中寻找错误。下面我们将主要使用该函数对基于Anylogic的java入门予以介绍。
一、变量
(一)基本的变量类型
在java中变量有很多种类型,也可以自己进行创建。但基础的数据类型就这样几个:
数值型:
整型(整数):
- byte:范围为(-27,27-1)
- short:范围为(-215,215-1)
- int:范围为(-231,231-1)
- long:范围为(-263,263-1)
浮点型(小数): - float:有效位数15位
- double:有效位数15位
字符型:
- char:Unicide字符集,只能储存单个字符。
- String:存储一串字符。
布尔型:
- boolean:true/false
在Anylogic常用的基本变量类型主要是:
- boolean
- int
- double
- String
(二)Anylogic中常用的其他变量类型
除了java常用的一些变量类型,Anylogic还会经常使用到一些其他的变量类型,如:
- double []:由小数构成的数组
- double [][]:由小数构成的二维数组
- Agent:通用的智能体类型
- 自建的智能体类型
- object
(三)变量的声明和使用
1、声明变量
基础的声明变量可以在代码中直接声明。
语法:数据类型 变量名
如:
double score; //声明双精度浮点型变量score存储分数
String name; //声明字符串型变量name存储姓名
char sex; //声明字符型变量sex存储性别
在Anylogic中还有另一种声明变量的方式,就是在面板中拖入“参数”或“变量”并指定数据类型来声明该变量,这种方式声明的变量在该智能体的任何位置均可直接使用。
2、变量赋值
变量赋值即将数据存储至对应的内存空间中。
语法: 变量名 = 值
如:
score = 98.5; //存储98.5
name = "张三"; //存储“张三”
sex = '男'; //存储‘男’
注意在赋值前需要先声明变量。另外在代码中我们注意到所有String类型的变量赋值需要在值的两侧输入"才不会报错。当然也有一种更简单的方法如下:
3、即声明变量又赋值
语法: 数据类型 变量名 = 值
如:
double score = 98.5;
String name= "张三";
char sex = '男';
注意这里的代码后都跟了一个';',代表一句话的结束,也是java的语法规则。
(四)调用变量
使用System.out.print()函数来调用变量,当然在实际使用过程中其他函数也都是可以直接调用变量的。
System.out.println(score); //从控制台输出变量score存储的值
System.out.println(name); //从控制台输出变量name存储的值
System.out.println(sex); //从控制台输出变量sex存储的值
现在我们完整的操作一边,在启动时中输入:
String name;
name = "张三";
System.out.println(name);
这样我们就能够看到前面声明的变量在控制台输出“张三”二字了。
(四)变量的命名规范
- 在Anylogic中一般使用英文单词或字母来命名变量。
- 多个英文单词组成的变量可在每个单词的首字母大写,中间不留空格
- 在Anylogic中还有特殊的命名方法,可以使用中文命名变量和类型,在不报错的前提下可以使用,方便辨识。
二、数组的使用
(一)定义数组
直接定义数组中元素的个数,代码如下:
int data[] = new int[3]; /*开辟了一个长度为3的数组*/
但有时需要根据实际情况定义数组中元素的个数。可分步开辟空间,代码如下:
int data[] = null;
data = new int[3]; /*开辟了一个长度为3的数组*/
(二)定义数组中元素的值
data[0] = 10; // 第一个元素
data[1] = 20; // 第二个元素
data[2] = 30; // 第三个元素
当然也可以采用循环语句来定义值。
(三)静态数组的初始化
有时我们一开始就知道数组中元素的值,我们可以使用如下的代码来定义数组:
int data[] = {1, 2, 4, 545, 11, 32, 13131, 4444};
(四)二维数组的初始化
定义二维数组我们可以采用如下的语法来定义,具体代码如下:
int[][] arr = new int[3][2]; /*动态初始化*/
int data[][] = new int[][] {{1, 2, 3}, {4, 5}, {6, 7, 8, 9}}; /*可以定义不是等列数组的数组*/
二维数组的调用可以使用如下语法来调用:
- 语法: 数组名称[所在行][所在列]
三、基础函数
(一)常用的数学函数
1、数学计算
- Math.E:产生自然常数e=2.7182818284590452354。
- Math.exp(x):计算e的x次方。
- Math.pow(a,b):计算a的b次方。
- Math.sqrt(a):计算a的开方。
- Math.abs(a):计算a的绝对值。
- Math.log(e):计算以e为底的对数
- Math.log10(100):计算以10为底的对数
- Math.max(x, y):返回x、y中较大的那个数
- Math.min(x, y):返回x、y中较小的那个数
- Math.ceil(12.3):返回最近的且大于该值的整数13.0
- Math.ceil(-12.3):返回-12.0
- Math.floor(12.3):返回最近的且小于该值的整数12.0
- Math.floor(-12.3):返回-13.0
- Math.hypot(x, y):返回√(x²+y²)
2、产生随机值
产生随机数的函数有如下一些:
- Math.random():产生0-1之间的double类型的小数。
- (int)(Math.random()*10):产生0-9之间的整数。
(二)常用的数组函数
1、数组长度
- 一维数组的长度
arg.length;
- 二维数组的长度
String[][] data = new String[2][5];
System.out.println("第一维数组长度: " + data.length);
System.out.println("第二维数组长度: " + data[0].length);
2、数组排序
Arrays.sort(array);
3、数组反转
使用 Collections.reverse(ArrayList) 可以将数组进行反转。
4、数组填充
通过 Java Util 类的 Array.fill(arrayname,value) 方法和 Array.fill(arrayname ,starting index ,ending index ,value) 方法向数组中填充元素。
5、删除数组元素
使用ArrayList.remove()方法删除数组元素。
对应的ArrayList.remove()也有两种。
objArray.remove(1);/*移除序号*/
objArray.remove("第 0 个元素");/*移除值*/
6、判断数组是否相等
Arrays.equals(ary, ary1);
(三)建立自己的函数
Anylogic中可以直接在代码里写自己的函数,具体格式如下:
/*无返回值的函数*/
void add(){
}
/*有返回值的函数*/
double getDifference(int a, int b){
double result = a - b;
return result;
}
注意在有返回值的函数中需要定义返回值的数据类型。
当然Anylogic还提供了几种可视化的函数编写元素。
1、函数元素
- 直接将智能体面板的函数元素拖到主界面。
- 命名该函数。
- 选择参数的输入。
- 定义返回值的类型。
- 在函数体中输入代码。
通过这几部就能完成一个函数的编写,然后即可在该智能体中进行引用。
2、行动图元素
可打开行动图面板,通过其中的代码块拼凑出函数,优点是可视化程度高,缺点是编辑起来较为繁琐。
四、判断及循环语句
(一)判断语句
if语句
if(关系表达式1) {
语句体1;
}else if (关系表达式2) {
语句体2;
}…
else {
语句体n+1;
}
switch语句
switch(表达式) {
case 值1:
语句体1;
break;
case 值2:
语句体2;
break;
…
default:
语句体n+1;
break;
}
(二)循环语句
do…while循环语句
do {
循环体语句;
}while((判断条件语句);
for循环语句
for(初始化语句;判断条件语句;控制条件语句) {
循环体语句;
}
其他说明
- break:退出当前循环
- continue:退出本次循环
五、智能体
在Anylogic最常用的就是智能体的概念,下面讲解以下智能体的相关知识。
(一)多层智能体之间的调用
如果在Main中建立了新的智能体,现在要在Main中调用智能体a中的val变量,可以按以下格式进行调用:
a.val
如果需要在a中调用Main中的val1变量,可以按以下格式进行调用:
get_Main().a.val1
(二)智能体
1、创建一个智能体
- 在工程面板创建一个智能体类型。
- 命名这个智能体类型。
- 选择该智能体的动画,为示例函数的运行,可选一个人作为动画。
- 创建完成后,将创建好的智能体类型拖到Main中即可完成智能体的创建。
2、智能体的移动函数
- setXY(double x,double y):设置智能体位置。
- getX()、getY()、getZ():返回智能体在连续空间中的X、Y、Z坐标。
- Position getPosition():以Position类型的形式返回连续空间中智能体最新的X、Y、Z坐标。
- void moveTo(double x,double y):向x、y位置移动
- void moveTo(double x,double y,double z):向x、y、z位置移动
- void moveTo(Agent agent):向agent这个智能体位置移动
- boolean isMoving():判断智能体是否在移动。
- double timeToArrival():返回智能体到达目标位置的时间。
- void stop():停止智能体的移动。
- void moveToInTime(Point location, double tripTime):在给定目标位置的方向移动。更改智能体的速度,使在tripTime的时间内到达目标。
- void moveToNearestAgent (java.lang.Iterable<? extends Agent> agents, double tripTime):移动到给定的智能体群中最近的智能体。停止任何当前的移动。更改智能体的速度,使在tripTime的时间内到达目标。
- void moveToInTime(double x, double y, double tripTime):在连续的三维或GIS空间的给定目标位置的方向移动。更改智能体的速度,使在tripTime的时间内到达目标。
- void moveToInTime(double x, double y, double z, double tripTime):在连续3D的目标位置开始移动。改变智能体速度,使在tripTime的时间内到达目标。
- void moveToInTime(Node node, Point location, double tripTime):开始向给定的网络节点移动。更改智能体的速度,使在tripTime的时间内到达目标。
- void moveToInTime(Attractor attractor, double tripTime):开始移动到给定吸引子。更改智能体的速度,使在tripTime的时间内到达目标。
- void jumpTo(double x, double y):在连续的3D空间中,立即将智能体移动到给定位置。终止任何移动。不调用“到达时”的代码。
- void jumpTo(double x, double y, double z):立即将智能体移动到给定位置。终止任何移动。不调用“到达时”代码。
- void jumpTo(Node node, Point location):立即将智能体移动到给定的网络位置。终止任何移动。不调用“到达时”的代码。
- void jumpTo(Point location):立即将智能体移动到给定的位置。终止任何移动。不调用“到达时”代码。
- double getTargetX():智能体在移动时返回目标位置的X坐标,否则返回当前的X坐标。
- double getTargetY():智能体在移动时返回目标位置的Y坐标,否则返回当前的Y坐标。
- double getTargetZ():智能体在移动时返回目标位置的Z坐标,否则返回当前的Z坐标。
- double distanceTo(Agent other):计算这个智能体到另一个智能体的距离。
- double distanceTo(double x, double y):计算这个智能体到给定点的距离。
- double distanceTo(double x, double y, double z):在连续空间中计算该智能体到给定点(x,y,z)的距离。
- double distanceTo(Point p):在连续空间中计算该智能体到给定点(x,y,z)的距离。
- double getSpeed():在连续空间中返回智能体速度的当前值(以模型时间单位行走的像素数来计算)。注意非零速度并不意味着智能体正在移动,智能体只在调用moveTo()函数后才开始移动。
- void setSpeed(double v):在连续空间中更改智能体的速度(以像素为单位)。如果智能体正在移动,它将继续以新的速度移动。注意非零的速度并不意味着智能体正在移动,智能体只在调用moveTo()函数后才开始移动。
- double getRotation():在连续三维空间中返回智能体的旋转角度(弧度制)如果旋转不被冻结,每次旋转时,旋转就会改变,在GIS空间的移动中也会发生多次变化。
- void setRotation(double rotation):在连续三维空间中设置智能体动画的旋转角度(弧度制)。根据自动旋转设置,在下一个调用moveTo()函数或在当前移动中(如果存在的话),这种旋转值可能会被重写。
- void setAutomaticHorizontalRotation (boolean yes):设置智能体在移动过程中的自动旋转。
- boolean isAutomaticHorizontalRotation():智能体在移动过程中被旋转返回true,否则false。
- void setVerticalRotation(double rotation):设置垂直旋转(弧度制),沿三维空间中智能体动画的Z轴。根据自动垂直旋转设置,在下一个调用moveTo()函数或在当前移动(如果存在的话)沿着路径/折线中,这个旋转值可能会被重写。
- double getVerticalRotation():在三维空间中返回智能体的当前垂直旋转角度。如果设置自动垂直旋转模式,则每次旋转时角度改变,并在移动中多次改变。
- void setAutomaticVerticalRotation(boolean yes):设置智能体是否自动垂直旋转。
- boolean isAutomaticVerticalRotation():智能体在三维的移动中被旋转(在垂直方向上,沿着Z轴)返回true,否则返回false。
3、智能体的交互函数
第一种函数:发送消息send()/推荐/
- void sendToAll(Object msg):将消息发送到智能体所在的同一环境中的所有智能体。
- void sendToRandom(Object msg):向同环境中的一个随机智能体发送消息。
void sendToAllConnected (Object msg):向所有连接的智能体发送消息。 - void sendToRandomConnected (Object msg):向随机选择的一个已连接的智能体发送消息。
- void sendToAllNeighbors (Object msg):向所有邻居发送信息,只有当智能体处于离散空间中时才可用。
- void sendToRandomNeighbor (Object msg):向随机选择的邻居发送消息,只有当智能体处于离散空间环境中时才可用。
- void send(Object msg, Agent dest):向给定的智能体发送消息。
参数:msg——信息;dest——目标智能体 - void send(Object msg, MessageDeliveryType mode):将消息发送给智能体或智能体群,以给定的mode参数。
第二种函数:及时消息传递:deliver()
- void deliverToAllAgentsInside (Object msg):立即向环境中的所有智能体传输信息。
- void deliverToRandomAgentInside (Object msg):如果存在任何一个智能体,立即向环境中的随机智能体传递消息。
- void deliverToAllConnected(Object msg):向所有已连接的智能体传递消息。
- void deliverToRandomConnected (Object msg):向随机选择的已连接的智能体传递消息。
- void deliverToAllNeighbors(Object msg):向所有邻居传递信息。
- void deliverToRandomNeighbor (Object msg):向随机选择的邻居传递信息。
- void deliver(Object msg, Agent dest):立即向给定的智能体传递消息。
- void deliver(Object msg, MessageDeliveryType mode):用指定模式将消息传递给智能体(群)。
4、函数的输入格式
当然智能体的函数还有很多,具体可参照其官方手册。
在智能体内部只需按以下格式输入函数即可完成行为:
this.函数名(参数,参数);
在Main中需按以下格式进行输入:
智能体名称.函数名(参数,参数);
(三)智能体群
1、统计数据
Anylogic能够方便的收集统计智能体群的数据,包含计数、求和、求平均值、求最小值和最大值这几个常见的统计类型。
- Count:确定符合指定条件的智能体。
- Sum:迭代所有智能体并评估每个智能体的指定表达式,返回和。
- Average:迭代所有智能体并评估每个智能体的指定表达式,返回平均值。
- Min:迭代所有智能体并评估每个智能体的指定表达式,返回最小值。
- Max:迭代所有智能体并评估每个智能体的指定表达式,返回最大值。
为智能体群定义新的统计函数的步骤
- 在图形编辑器或工程视图中选择群。
- 打开属性视图的统计部分。
- 单击添加统计按钮,创建一个定义统计的新项目。
- 在“名称”输入统计函数的名称,用于识别和访问这个统计函数。
- 在“类型”中选择统计函数的类型。
- 在“表达式”/“条件“中指定统计条件和函数。当在这些字段中编写代码时,可以使用item作为局部变量访问单个元素(智能体)。
- 要添加更多的统计项目,请重复3-6的步骤。如果不再需要一些统计数据,可以点击按钮来删除相应的项目。
如需使用函数访问统计,可以使用统计名称+括号的方式输入。即在上面的示例中,如果要计算统计数据,可用peopleStat()函数来获得值。
如何收集智能体群的统计数据
Anylogic提供了收集智能体群中的智能体的统计数据。这些函数在UtilitiesCollection类中定义,并且是全局的(即从任何地方都可以访问)。可以从列表中调用任何函数,只输入它的名字而不需要前缀,如:
count( people, p -> p.income > 10000 );
计算满足条件的智能体个数
- int count( population, condition ):计算智能体满足指定条件的个数。
例子:
count(people,p->p.income>10000);
在这个例子中将计算income(收入)超过10000的人数。作为第一个函数参数要指定要遍历的群名。第二个参数指定为每个智能体检查的条件。这种情况下,首先要定义局部变量(它以p命名,但它可以以任何其他名称命名)。可以在指定条件下使用这个局部变量来引用目前正在检查的智能体。然后输入箭头运算符->(它由两个符号组成),并定义了它们的条件p.income>10000。count()函数遍历了所有people群中的智能体,并检查每个智能体的收入。如果智能体的收入大于10000(定义条件为true)就计算这个特定的智能体。
count(people,p->p.sex==MALE&&p.age>=18);
这里检查两个条件(使用逻辑和操作符&&告诉Anyogic第一个和第二个条件都应为真),只计算满足两个条件的智能体。
为了获得目前位于people的智能体的个数,可以使用:people.size();
在群中获得平均值(智能体的属性等)
- double average( population, value ):返回给定智能体群的平均值。例:
average( people, p -> p.income );
这里计算people群中智能体的平均收入。
- double averageWhere(population,value,condition):在给定条件下返回特定人群的平均值。例:
average(people,p->p.income,p->p.age>18);
这里计算了这些人的平均收入,他们年龄需要超过18。函数第一个参数指定了群,第二个参数定义了需要计算的值,第三个参数是条件。这里只对满足这个条件的智能体计算平均值。注意在指定条件之前,需再次定义局部变量p。
在智能体群中获得最小(最大值)值
- double max(population,value ):返回给定群最大值。
- double maxWhere(population,value,condition):返回给定条件下的智能体中最大值。
- double min(population,value ):返回给定群最小值。
- double minWhere(population,value,condition):返回给定条件下的智能体中最小值。例:
maxWhere(people,p->p);
这里计算了所有人的最大收入。
maxWhere(people,p->p.income,p->p.sex==FEMALE);
这里计算了智能体群中的所有女性的最大收入。
计算智能体值的总和
- double sum( population, value ):返回在给定群中值的总和。例:
sum( people, p -> p.income );
这里计算了智能体群中的所有人的总收入。
- double sumWhere( population, value, condition ):返回给定条件、给定群值的总和。例:
sumWhere( people, p -> p.income, p -> p.age > 18 );
这里计算了年龄超过18岁的成人的总收入。
随机选择智能体群中的智能体
AnyLogic提供了从给定群中选择随机智能体的函数。这些函数在Utilities类中定义,并且是全局的(可从模型代码的每个位置访问)。可以通过输入其名称来调用下面列表中的任何函数,而不使用带有people名称的前缀:randomWhere(people,p->p.income>10000);
- Agent randomWhere(population,condition):返回满足指定条件的给定群中随机选择的一个智能体。如果群为空或者没有智能体满足指定的条件应返回null。例子:
Person wealthyPerson=randomWhere(people,p->p.income>10000);
Person wealthyMan=randomWhere(people,p->p.income>10000&&p.sex==MALE);
这里检查两个条件(使用逻辑和运算符&&告诉AnyLogic第一个和第二个条件都应为真)。该函数返回一个随机选择的智能体,满足这两个条件。
Person wealthyYoungMan=randomWhere( people , p->p.income>10000 && p.sex == MALE &&p.age<21);
还有另一个函数,使用户能够使用指定的自定义随机数生成器而不是默认值:
- Agent randomWhere(population, condition, java.util.Random r):与randomWhere(population, condition )类似,使用指定的自定义随机数生成器来选择元素。
选择处于特定状态的智能体
要从智能体群中选择一个智能体群的子集,可用使用filter()函数。函数接受原始智能体群作为第一个参数,然后指定过滤条件。
举个例子:有两种智能体,客户和卡车。要从客户那里呼叫最近的卡车,下一行代码需要从卡车智能体群(main.trucks)中获得最近的卡车,可以输入:
Truck truck = getNearestAgent( filter( main.trucks, t -> t.inState( Truck.Idle ) );
定义了局部变量t,指定筛选条件:t.inState( Truck.Idle )。卡车状态图有空闲、繁忙两种状态,将从目前处于闲置状态的卡车放入子集,并寻找最近的卡车。
从智能体群中选择特殊的智能体
- Agent top(population, value):在给定智能体群中返回某字段最大的智能体。例:
Person person = top( people, p -> p.age );
这里从这个群中得到了一个年龄最大的人。如果群为空函数将返回null。
- List filter(population, condition):返回给定群的子集,获得满足条件智能体的新列表。例:
List women = filter( people, p -> p.gender == FEMALE );
List idleTrucks = filter( trucks, t -> t.inState(Idle) );
- List findAll(population,condition):这个函数和filter(population,condition)一样。
- Agent findFirst(population, condition):从智能体群中返回满足条件的第一个智能体。如果没有元素满足条件或集合为空返回null。
例:
Person person = findFirst( people, p -> p.age > 20 );
在智能体群中排序智能体
当需对群做一些操作时,可能需要用一些值来排序。在群中不需要重新排序智能体,您可以使用以下Anylogic函数来获取有序的列表,然后在代码中使用它。
<T> java.util.List<T> sortAscending(population, value):返回一个由给定值升序排列的智能体列表。
<T> java.util.List<T> sortDescending(population, value):返回一个由给定值降序排列的智能体列表。例:
List sortedByAgeAsc = sortAscending( people, p -> p.age );
List sortedByIncomeDesc = sortDescending( people, p -> p.income );