由于Anylogic所使用的代码为java代码,因此对java没有任何基础的同学可能会在学习时有些吃力。所以本文的目的在于教会大家如何在Anylogic中使用java代码来编写程序。

在正式开始学习之前先给大家介绍一个函数:

1
System.out.print();

这个函数用于在控制台输出消息,那么该函数应该在哪里输入呢?

  • 1、打开Anylogic软件。
  • 2、在任意位置新建一个模型。
  • 3、建好后,单击Main智能体的空白位置。
  • 4、在右侧Main的属性面板中找到智能体行动的启动时的输入栏。
  • 5、在其中输入System.out.print(1);。
  • 6、启动模型并运行。
  • 7、观察到控制台中输出了1,说明代码输入成功。

该函数将在学习的过程中经常使用到,并且该函数也经常用于在开发中寻找错误。下面我们将主要使用该函数对基于Anylogic的java入门予以介绍。

一、变量

(一)基本的变量类型

在java中变量有很多种类型,也可以自己进行创建。但基础的数据类型就这样几个:

数值型:

整型(整数):

  • byte:范围为(-2^7,2^7-1)
  • short:范围为(-2^15,2^15-1)
  • int:范围为(-2^31,2^31-1)
  • long:范围为(-2^63,2^63-1)
    浮点型(小数):
  • float:有效位数15位
  • double:有效位数15位

字符型:

  • char:Unicide字符集,只能储存单个字符。
  • String:存储一串字符。

布尔型:

  • boolean:true/false

在Anylogic常用的基本变量类型主要是:

  • boolean
  • int
  • double
  • String

(二)Anylogic中常用的其他变量类型

除了java常用的一些变量类型,Anylogic还会经常使用到一些其他的变量类型,如:

  • double []:由小数构成的数组
  • double [][]:由小数构成的二维数组
  • Agent:通用的智能体类型
  • 自建的智能体类型
  • object

(三)变量的声明和使用

1、声明变量

基础的声明变量可以在代码中直接声明。

语法:数据类型 变量名

如:

1
2
3
double score;     //声明双精度浮点型变量score存储分数
String name; //声明字符串型变量name存储姓名
char sex; //声明字符型变量sex存储性别

在Anylogic中还有另一种声明变量的方式,就是在面板中拖入“参数”或“变量”并指定数据类型来声明该变量,这种方式声明的变量在该智能体的任何位置均可直接使用。

2、变量赋值

变量赋值即将数据存储至对应的内存空间中。

语法: 变量名 = 值

如:

1
2
3
score = 98.5;     //存储98.5
name = "张三"; //存储“张三”
sex = '男'; //存储‘男’

注意在赋值前需要先声明变量。另外在代码中我们注意到所有String类型的变量赋值需要在值的两侧输入”才不会报错。当然也有一种更简单的方法如下:

3、即声明变量又赋值

语法: 数据类型 变量名 = 值

如:

1
2
3
double score = 98.5;
String name= "张三";
char sex = '男';

注意这里的代码后都跟了一个’;’,代表一句话的结束,也是java的语法规则。

(四)调用变量

使用System.out.print()函数来调用变量,当然在实际使用过程中其他函数也都是可以直接调用变量的。

1
2
3
System.out.println(score);       //从控制台输出变量score存储的值
System.out.println(name); //从控制台输出变量name存储的值
System.out.println(sex); //从控制台输出变量sex存储的值

现在我们完整的操作一边,在启动时中输入:

1
2
3
String name; 
name = "张三";
System.out.println(name);

这样我们就能够看到前面声明的变量在控制台输出“张三”二字了。

(四)变量的命名规范

  • 在Anylogic中一般使用英文单词或字母来命名变量。
  • 多个英文单词组成的变量可在每个单词的首字母大写,中间不留空格
  • 在Anylogic中还有特殊的命名方法,可以使用中文命名变量和类型,在不报错的前提下可以使用,方便辨识。

二、数组的使用

(一)定义数组

直接定义数组中元素的个数,代码如下:

1
int data[] = new int[3]; /*开辟了一个长度为3的数组*/

但有时需要根据实际情况定义数组中元素的个数。可分步开辟空间,代码如下:

1
2
int data[] = null; 
data = new int[3]; /*开辟了一个长度为3的数组*/

(二)定义数组中元素的值

1
2
3
data[0] = 10; // 第一个元素
data[1] = 20; // 第二个元素
data[2] = 30; // 第三个元素

当然也可以采用循环语句来定义值。

(三)静态数组的初始化

有时我们一开始就知道数组中元素的值,我们可以使用如下的代码来定义数组:

1
int data[] = {1, 2, 4, 545, 11, 32, 13131, 4444};

(四)二维数组的初始化

定义二维数组我们可以采用如下的语法来定义,具体代码如下:

1
2
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、数组长度

  • 一维数组的长度

    1
    arg.length;
  • 二维数组的长度

    1
    2
    3
    String[][] data = new String[2][5];
    System.out.println("第一维数组长度: " + data.length);
    System.out.println("第二维数组长度: " + data[0].length);

2、数组排序

1
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()也有两种。

1
2
objArray.remove(1);/*移除序号*/
objArray.remove("第 0 个元素");/*移除值*/

6、判断数组是否相等

1
Arrays.equals(ary, ary1);

(三)建立自己的函数

Anylogic中可以直接在代码里写自己的函数,具体格式如下:

1
2
3
4
5
6
7
8
9
/*无返回值的函数*/
void add(){

}
/*有返回值的函数*/
double getDifference(int a, int b){
double result = a - b;
return result;
}

注意在有返回值的函数中需要定义返回值的数据类型。

当然Anylogic还提供了几种可视化的函数编写元素。

1、函数元素

  • 直接将智能体面板的函数元素拖到主界面。
  • 命名该函数。
  • 选择参数的输入。
  • 定义返回值的类型。
  • 在函数体中输入代码。

通过这几部就能完成一个函数的编写,然后即可在该智能体中进行引用。

2、行动图元素

可打开行动图面板,通过其中的代码块拼凑出函数,有点是可视化程度高,缺点是编辑起来较为繁琐。

四、判断及循环语句

(一)判断语句

if语句

1
2
3
4
5
6
7
8
if(关系表达式1) {
语句体1;
}else if (关系表达式2) {
语句体2;
}…
else {
语句体n+1;
}

switch语句

1
2
3
4
5
6
7
8
9
10
11
12
switch(表达式) {
case 值1:
  语句体1;
  break;
case 值2:
  语句体2;
  break;

default:
  语句体n+1;
  break;
}

(二)循环语句

do…while循环语句

1
2
3
do {
循环体语句;
}while((判断条件语句);

for循环语句

1
2
3
for(初始化语句;判断条件语句;控制条件语句) {
循环体语句;
}

其他说明

  • break:退出当前循环
  • continue:退出本次循环

五、智能体

在Anylogic最常用的就是智能体的概念,下面讲解以下智能体的相关知识。

(一)多层智能体之间的调用

如果在Main中建立了新的智能体,现在要在Main中调用智能体a中的val变量,可以按以下格式进行调用:

1
a.val

如果需要在a中调用Main中的val1变量,可以按以下格式进行调用:

1
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、函数的输入格式

当然智能体的函数还有很多,具体可参照其官方手册。

在智能体内部只需按以下格式输入函数即可完成行为:

1
this.函数名(参数,参数);

在Main中需按以下格式进行输入:

1
智能体名称.函数名(参数,参数);

(三)智能体群

1、统计数据

Anylogic能够方便的收集统计智能体群的数据,包含计数、求和、求平均值、求最小值和最大值这几个常见的统计类型。

  • Count:确定符合指定条件的智能体。
  • Sum:迭代所有智能体并评估每个智能体的指定表达式,返回和。
  • Average:迭代所有智能体并评估每个智能体的指定表达式,返回平均值。
  • Min:迭代所有智能体并评估每个智能体的指定表达式,返回最小值。
  • Max:迭代所有智能体并评估每个智能体的指定表达式,返回最大值。

为智能体群定义新的统计函数的步骤

  • 在图形编辑器或工程视图中选择群。
  • 打开属性视图的统计部分。
  • 单击添加统计按钮,创建一个定义统计的新项目。
  • 在“名称”输入统计函数的名称,用于识别和访问这个统计函数。
  • 在“类型”中选择统计函数的类型。
  • 在“表达式”/“条件“中指定统计条件和函数。当在这些字段中编写代码时,可以使用item作为局部变量访问单个元素(智能体)。
  • 要添加更多的统计项目,请重复3-6的步骤。如果不再需要一些统计数据,可以点击按钮来删除相应的项目。

如需使用函数访问统计,可以使用统计名称+括号的方式输入。即在上面的示例中,如果要计算统计数据,可用peopleStat()函数来获得值。

如何收集智能体群的统计数据

Anylogic提供了收集智能体群中的智能体的统计数据。这些函数在UtilitiesCollection类中定义,并且是全局的(即从任何地方都可以访问)。可以从列表中调用任何函数,只输入它的名字而不需要前缀,如:

1
count( people, p -> p.income > 10000 );

计算满足条件的智能体个数

  • int count( population, condition ):计算智能体满足指定条件的个数。
    例子:
    1
    count(people,p->p.income>10000);
    在这个例子中将计算income(收入)超过10000的人数。作为第一个函数参数要指定要遍历的群名。第二个参数指定为每个智能体检查的条件。这种情况下,首先要定义局部变量(它以p命名,但它可以以任何其他名称命名)。可以在指定条件下使用这个局部变量来引用目前正在检查的智能体。然后输入箭头运算符->(它由两个符号组成),并定义了它们的条件p.income>10000。count()函数遍历了所有people群中的智能体,并检查每个智能体的收入。如果智能体的收入大于10000(定义条件为true)就计算这个特定的智能体。
1
count(people,p->p.sex==MALE&&p.age>=18);

这里检查两个条件(使用逻辑和操作符&&告诉Anyogic第一个和第二个条件都应为真),只计算满足两个条件的智能体。

为了获得目前位于people的智能体的个数,可以使用:people.size();
在群中获得平均值(智能体的属性等)

  • double average( population, value ):返回给定智能体群的平均值。例:

    1
    average( people, p -> p.income );

    这里计算people群中智能体的平均收入。

  • double averageWhere(population,value,condition):在给定条件下返回特定人群的平均值。例:

1
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):返回给定条件下的智能体中最小值。例:
1
maxWhere(people,p->p);

这里计算了所有人的最大收入。

1
maxWhere(people,p->p.income,p->p.sex==FEMALE);

这里计算了智能体群中的所有女性的最大收入。

计算智能体值的总和

  • double sum( population, value ):返回在给定群中值的总和。例:
1
sum( people, p -> p.income );

这里计算了智能体群中的所有人的总收入。

  • double sumWhere( population, value, condition ):返回给定条件、给定群值的总和。例:
1
sumWhere( people, p -> p.income, p -> p.age > 18 );

这里计算了年龄超过18岁的成人的总收入。

随机选择智能体群中的智能体

AnyLogic提供了从给定群中选择随机智能体的函数。这些函数在Utilities类中定义,并且是全局的(可从模型代码的每个位置访问)。可以通过输入其名称来调用下面列表中的任何函数,而不使用带有people名称的前缀:randomWhere(people,p->p.income>10000);

  • Agent randomWhere(population,condition):返回满足指定条件的给定群中随机选择的一个智能体。如果群为空或者没有智能体满足指定的条件应返回null。例子:
1
2
Person wealthyPerson=randomWhere(people,p->p.income>10000);
Person wealthyMan=randomWhere(people,p->p.income>10000&&p.sex==MALE);

这里检查两个条件(使用逻辑和运算符&&告诉AnyLogic第一个和第二个条件都应为真)。该函数返回一个随机选择的智能体,满足这两个条件。

1
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)中获得最近的卡车,可以输入:

1
Truck truck = getNearestAgent( filter( main.trucks, t -> t.inState( Truck.Idle ) );

定义了局部变量t,指定筛选条件:t.inState( Truck.Idle )。卡车状态图有空闲、繁忙两种状态,将从目前处于闲置状态的卡车放入子集,并寻找最近的卡车。

从智能体群中选择特殊的智能体

  • Agent top(population, value):在给定智能体群中返回某字段最大的智能体。例:

    1
    Person person = top( people, p -> p.age );

    这里从这个群中得到了一个年龄最大的人。如果群为空函数将返回null。

  • List filter(population, condition):返回给定群的子集,获得满足条件智能体的新列表。例:

    1
    2
    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。
    例:

    1
    Person person = findFirst( people, p -> p.age > 20 );

    在智能体群中排序智能体

当需对群做一些操作时,可能需要用一些值来排序。在群中不需要重新排序智能体,您可以使用以下Anylogic函数来获取有序的列表,然后在代码中使用它。

  • java.util.List sortAscending(population, value):返回一个由给定值升序排列的智能体列表。

  • java.util.List sortDescending(population, value):返回一个由给定值降序排列的智能体列表。例:

    1
    2
    List sortedByAgeAsc = sortAscending( people, p -> p.age );
    List sortedByIncomeDesc = sortDescending( people, p -> p.income );