# 涌现的建模：NetLogo入门

By [jackygu's blog](https://paragraph.com/@jackygu) · 2023-09-03

---

> 这两天看关于”涌现“现象的内容，无意中发现了NetLogo这个软件，可以用它方便的为各种涌现（Emergence）现象建模，甚至还可以为进化博弈（Evolutionary Game）模型进行建模分析，因此迅速花了几小时学习了下，并把学习笔记整理如下。
> 
> 另外，Logo语言是笔者在1984年学习的第一门计算机语言，所以当40年后，再一次用到“小图龟（Turtle）”时，顿感亲切！

`NetLogo` （官网：[https://ccl.northwestern.edu/netlogo](https://ccl.northwestern.edu/netlogo) ）是一个多主体可编程的建模环境（Multi Agent-based Programmable Modeling Environment），它基于Logo绘图语言，用于模拟自然和社会现象。它由`Uri Wilensky`于1999年发起，并由西北大学`Center for Connected Learning and Computer-Based Modeling (CCL)`中心持续开发，目前最新版是6.3.0，支持Windows，MacOS和Linux桌面系统。

`NetLogo`非常适合对随时间演化的复杂系统进行建模。可以模拟成百上千个独立运行的"主体"（agent），这使得我们能够探究个体行为与`涌现`出的宏观模式之间的微观与宏观联系。

本文参考张发翻译的《NetLogo 4.0.2 用户手册》，整理了一份简易教程，以便更好地使用NetLogo来理解仿真建模。

第一部分：体验
=======

模型实例：聚会（Party）
--------------

这一部分让你了解什么是计算机建模以及如何使用它，也让你对 NetLogo 软件有所了解。

### 场景描述

你是否参加过聚会，注意过人们是怎样聚集成小组的吗？你也可能注意到人们并非一直呆在一个小组里，而是走来走去。当人们走来走去的时候，小组就发生变化。如果你长期观察这种变化，你应该注意到使用模型思维来研究这个聚会现象。

NetLogo 的“Party”模型从性别这个特殊角度考察这个问题：为什么有些小组多数是男性，或多数是女性？相同的思路可以扩展到拥有相同性格、观点、话题的人们往往更容易聚在一起。

### 初始化模型

1.  启动 NetLogo
    
2.    
    
3.  打开文件夹 "Social Science"下的"Party"模型
    
4.  点击”Setup" 按钮
    

![](https://storage.googleapis.com/papyrus_images/2415a8b04c054f669c582227c7ca3302124a9dadc38b9e4ae514736ff4b321d6.png)

这些线表示聚会上男女混合的小组。男性用蓝色表示，女性用粉色。数字是每个小组的人数。假如你邀请了 150 人参加聚会，假设人们分成了 10 组，我们使用计算机仿真，来看看人们是怎样扎堆的。

### 启动模型

1.  按下 "go" 按钮 (再次按下 "go" 会停止模型运行)
    
2.  观察人们的移动直到模型停止
    
3.  看图形输出了解发生了什么？每组有多少人？
    

开始时你可能认为将 150 人分成 10 组的结果是每组大约 10 人。从模型运行得知，人们并没有均等的分成 10 组，相反，有些组人数特别少，而有些组人数却特别多。更有意思的是，随着时间发展，所有小组男女都转变为均由同性组成。

### 如何解释这个现象？

对这个问题有很多可能的解释。本模型的设计者认为聚会上的小组不是完全按随机方式形成的。小组如何形成取决于个体的行为。模型设计者关注一个特殊变量“tolerance”（容忍度）：

![](https://storage.googleapis.com/papyrus_images/6e8cb4a55c9d408c8745e8804cea8a97a9b1afc40507ed96a332425c0ca99097.png)

这里将容忍度定义为个体感到舒服的异性的比例。如果小组中异性比例超过容忍度，他们就觉得不舒服，因此离开这一组去寻找别的小组。

例如，如果容忍度水平设为 25%，那么一个男性只有在女性比例少于25%的小组里才会感到舒服。同样女性只有在男性少于 25%的小组里才会感到舒服。当个体变得不舒服时会选择离开，转移到别的小组，这可能又让这个组中的某些人不舒服。这种链式反应不断进行，直到聚会上的所有人都感到舒服。

### 重启模型

在这个模型中，容忍度可以用滑动条改变，重新运行模型，再看看结果会如何。

1.  如果 "go" 按钮已按下 (黑色)，说明模型还在运行。再次按下该按钮停止运行。
    
2.  通过拖动滑块调整 "tolerance" 值
    
3.  按下"setup"按钮重设模型
    
4.  按下"go"按钮再次启动模型
    

### 挑战

1.  作为聚会的主人，你希望看到各组里都是男女混合。调整容忍度滑动条，让每组都尽量能够男女混合。 为保证 10 个小组都是男女混合，容忍度水平要设成多少? 如何达到尽量让不同人混在一起的容忍度，也许是会议或社群组织者要考虑的重要问题。
    
2.  你能想到可能影响每组中男女比例的其他因素吗？ 当你检验假设的时候，你会从数据中发现模型所产生的`涌现`(Emergence)。
    
3.  容忍度水平与混合组的比例有什么关系？
    

### 涌现

用`NetLogo`对聚会这样的情景建模使你可以对系统进行快速、灵活的试验，而在现实情况下这是很困难的。建模也给了你少受偏见的影响去观察各种情景的机会，因为你可以检查系统内部的动态。你会发现随着你建模越来越多，对许多现象的原有的想法会被改变。例如 Party模型一个令人惊讶的结果是：**即使容忍度水平相对较高，不同性别的人仍然会分开。**

这是关于`涌现`现象的一个经典例子，这里小组模式是许多个体交互的结果。`涌现`思想可以应用在几乎任何领域，`NetLogo`是研究`涌现`现象的重要工具，NetLogo 模型库里有许多模型，演示了各种不同类型的`涌现`现象，如：蚂蚁寻食，鸟类迁徙等著名`涌现现象`。

要更详细了解关于`涌现`的讨论以及`NetLogo`如何帮助学习者进行探索，参见[《Modeling Nature's Emergent Patterns with Multi-agent Languages》](https://ccl.northwestern.edu/2013/mnep9.pdf) (Wilensky, 2001).

模型实例:狼吃羊（ Wolf Sheep Predation）
-------------------------------

我们来试一个生物模型：狼吃羊，这是一个`掠食-食饵种群模型`。

*   从文件菜单打开模型库
    
*   从 Biology 部分选择"Wolf Sheep Predation" 按下"Open"
    

当你第一次打开模型时，你会看到视图是空的（全黑）。要让模型开始，你需要先设置它。

*   按下"setup"按钮
    
*   按下"go"按钮开始仿真
    
*   按下"go"按钮停止运行
    

### 控制模型：按钮

按钮按下后模型就会通过执行一个动作做出响应。按钮分为“一次性”（once）和“永久性”(forever)两种。可以通过按钮上的一个符号区分二者。

*   永久性按钮的右下角有两个箭头：
    

![](https://storage.googleapis.com/papyrus_images/5519414b4e1b062b00e11c0a747a1581342142f93caaa13d8e4039ad100ea4f6.png)

*   一次性按钮没有箭头:
    

![](https://storage.googleapis.com/papyrus_images/682d074e1695d19a364ec4e9adc2100bf4fd65edfdaaba0730d0c5e6ec4e98f8.png)

一次性按钮执行动作一次然后停止。当动作完成后，按钮弹起。

永久性按钮不断的执行一个动作。当你想让动作停止时，再次按下按钮。它会完成当前动作，然后弹起。

大多数模型，包括狼吃羊模型，有一个一次性按钮称为“setup”和一个永久性按钮称为“go”。许多模型还有一个一次性按钮称作“go once”或“step once”,它们很像go按钮，但区别在于它们只执行一步（时间步长）。使用这样的一次性按钮能让你更仔细的查 看模型的运行过程。

当模型因某种原因卡住时，可以使用Tools下的Halt强行停止模型运行。

### 控制速度:速度滑动条

![](https://storage.googleapis.com/papyrus_images/2e12bd110883ef4197e0f371ef79b922bb514c259b6abf9b54b645fab1c912e4.png)

滑块左移使模型速度变慢，右移使模型速度变快，变快可能会引起跳帧。

另外，所谓的加速，其实是视图更新更频繁。实际没有变慢，可以从时钟（tick）显示确认这一点。

### 调整设置：滑动条和开关

模型的配置给了你尝试不同场景或假设的机会。修改配置运行模型，观察这些改变所引起的反应，使你能更深入的了解所模拟的现象。

![](https://storage.googleapis.com/papyrus_images/689e6d355905c594b296ce59df378aeb74f164626a0a4fc0125165694060bda6.png)

#### 调整1：开关

我们看看如果改变下面的设置的话，羊群怎么变化。

*   打开"grass?" 开关
    
*   按下"setup" 和 "go" ，运行与上次差不多相同的时间
    

开关有与它相连的信息。这些信息采用开/关格式。开关发出特别的指令，这些指令对模型并非必要，但为模型增加了附加的维度。打开"grass?"影响模型结果。

通过设置和打开草的增长率，我们能够对三个因素建模：羊、狼和草。

#### 调整2：滑动条。

滑动条是一个可调的数值范围。例如"initial-number-sheep"滑动条最小值为0，最大值为250。模型运行时可以有 0 只羊，也可以有 250只羊，或者中间的任何一个数值。

实验：

*   关掉"grass?" 开关
    
*   设置"initial-number-sheep" 滑动条为 100.
    
*   设置"initial-number-wolves" 滑动条为 20.
    
*   按下 "setup" 和 "go".
    
*   模型运行约 100 时间步
    

实验：

*   设置 "initial-number-sheep" 为 80 ，"initial-number-wolves" 为 50.
    
*   设置"sheep-reproduce" 为 10.0%.
    
*   按下"setup" 和 "go".
    
*   模型运行约 100 时间步
    

绘图和监视器
------

建模的目的之一是对那些难以在实验室中进行研究的问题收集数据。`NetLogo`主要有两个显示数据的方式：绘图和监视器。

### 绘图（Plots）

![](https://storage.googleapis.com/papyrus_images/3de71f75933db6b2d05f043be36fe2bdf2082489a39c8c58b0ee4a2251941c2b.png)

这些线显示了随着时间推进，模型中发生了什么。要想知道每条线代表什么，在图形窗口的右上角单击”Pens”，打开画笔图例。一个关键字说明了每条线是什么。在本例中就是种群数量。 当图快被充满时，水平轴增加，以前的数据被压缩只占一部分空间，更多的空间用来绘制将来的图形。

如果你想保存图上数据以备查看或在另一个程序里进行分析，使用 File 菜单的"Export Plot"。这些数据就被保存，数据格式可以被电子表格，如 Excel,或数据库程序识别。

### 监视器（Monitors）

![](https://storage.googleapis.com/papyrus_images/2c81ea79b91d720ff73a22e5a058b28219165d545792f41894e77983d47ae232.png)

监视器"time-ticks"告诉我们仿真时间。其他的监视器告诉我们狼、羊、草的数量。（记 住，草的数量除以 4，为了别使图形太高）

当模型运行时监视器中的数值不断更新，而图形能显示模型整个运行过程中的数据。

模型库
---

模型库包括五部分：`Sample Models`, `Perspective Demos`, `Curricular Models`, `Code Examples`, `HubNet Computer Activities`.

### 模型样例（Sample Models）

Sample Models 部分是分科目组织的，目前有 210 多个模型。我们一直在增加模型，因此过段时间后能看到新加的模型。

有些文件夹下包含"(unverified)"子文件夹。这些模型是完整、可用的，但模型的内容、精度、代码质量等仍在评审之中。

### 透视演示（Perspective Demos）

这些模型在 Sample Models 中也有。但是略作修改，用来演示 NetLogo 的透视功能。

### 课程模型（Curricular Models）

这些模型是西北大学 CCL 开发的在学校使用的课程。有些模型在 Sample Models 中也有，有些没有。看看信息标签页，了解更多的信息。

### 代码例子（Code Examples ）

这是 NetLogo 特别功能的一些简单演示。当你以后扩展现存模型或新建模型时很有用。例如，你想在模型中增加直方图，可以看看"Histogram Example"，看看怎么做。

### HubNet 计算机活动(HubNet Computer Activities)

这一部分包括教室中使用的参与式仿真。要了解HubNet的更多信息，参见 HubNet Guide.

第二部分：常用命令Commands
=================

在这一部分，焦点从观察模型转换到改变模型。

模型实例: 基本交通模型(Traffic Basic)
---------------------------

*   在"Social Science"部分，找到并打开 Traffic Basic 模型
    

在这个模型里，你会注意到一系列`蓝车`里有一辆`红车`，车流同向移动。这些车时不时的会挤成一堆，无法移动。这是关于幽灵式阻塞的模型，即有时交通流会出现阻塞，但却找不到任何明显的原因。通过改变模型的参数：加速与刹车，会发现堵塞是由驾驶习惯导致的。

这个模型的配置太简单，黑色背景、白色街道、一些蓝车和一辆红车。现在需要对模型做点改变：改变车的形状和颜色、加上房子或路灯、新建信号灯、或者再创建一条车道。这些建议有些是装饰性的，只是改善模型的观感，另外一些是行为性的。在本教程里我们主要关注较简单的、装饰性的改变。

### 命令中心（The Command Center）

命令中心位于界面最下方，在这里可以向模型发出指令。

例如：输入下面所示的文本

    ask patches [set pcolor yellow]
    ask turtles [set color brown]
    

可以看到背景色和图龟颜色都变了。

`NetLogo`是由海龟(turtles)、瓦片(patches)和观察者(observers)组成的二维世界。瓦片构成背景，海龟在背景上移动，观察者（observer）是观察着所有事情的一个生命体。

### 颜色名称

![](https://storage.googleapis.com/papyrus_images/e3143d9413c82209faca2e4bd516e3cc3febb7bf343a4ee7ae9683d2ccc257ff.png)

为得到一个没有名字的颜色，你需要使用一个数值，或者在颜色名上加上或减去一个数。

例如，输入`set color red`与输入`set color 15`效果完全一样。要得到一个更浅或更深的颜色，只需使用一个比该颜色更小或更大的一个数。如

*   `set pcolor red - 2` ("-" 两侧的空格很重要)
    
*   `set pcolor red + 2`，通过在 red 上加上一个数, 得到更浅的颜色。
    

主体监视器（Agent Monitors）和主体命令器（Agent Commanders）
---------------------------------------------

### 修改单个图龟属性

![](https://storage.googleapis.com/papyrus_images/2e224aa6aeac218c44b22e4c4e4e08201bf4c75fa50cf76da315ddea312fb6ce.png)

修改属性：

*   方法一：在`Agent Monitors`最下方直接输入`set color green`
    
*   方法二：直接在`Turtle Monitor`中改属性
    

![](https://storage.googleapis.com/papyrus_images/44c1e8fdbd7b14e4f396f27e30a65ef72ec717808faee04f1bf57fccb6ac97a3.png)

*   方法三：在命令菜单中改
    

![](https://storage.googleapis.com/papyrus_images/f5a795484592747318121f891acb7b726898ea2a76cce0e744d68eac68356853.png)

### 修改单个patch属性

![](https://storage.googleapis.com/papyrus_images/2145d58c72b6bf7302a321119e678a813bc019f0fb03066833ec856bf7bf82ea.png)

修改patch的属性方式同修改图龟属性。

第三部分：程序（Procedures）
===================

NetLogo 中可以执行命令的主体类型有：

*   瓦片（patch）
    
*   海龟（turtle）
    
*   链（link）
    
*   观察者（observer） 瓦片是静止的，组成网格。海龟在网格上移动，链（link）接两个海龟。观察者俯视在进行的所有事情， 做那些海龟、瓦片和链自己不能做的事情。
    

所有这四种主体都能执行 NetLogo 命令。前三种主体还能运行程序（procedures）。

一个程序包括一系列 NetLogo 命令，你将它们定义为一个单一的新命令，让海龟移动、进食、繁殖和死亡。还将学习如何制作监视器、滑动 条和绘图。我们要建立一个简单的生态系统模型，与教学#1 的狼吃羊模型部分相似。

### 制作 setup 按钮

*   新建一个模型。
    
*     
    
*   切换到代码code窗口，输入`setup`按钮的代码：
    

    to setup 
     clear-all 
     create-turtles 100 
     ask turtles [ setxy random-xcor random-ycor ] 
    end
    

### 制作 go 按钮

*   同上，在桌面上创建go按钮，注意：需要选择`forever`，持续执行。
    
*   在代码窗口添加代码：
    

    to go 
     move-turtles 
    end
    
    to move-turtles
      ask turtles [
        right random 360  ;向右随机旋转一个角度
        forward 1
      ]
    end
    

### 为海龟添加属性

    turtles-own [energy]
    

添加一个叫`energy`的属性。

另外，在添加一个`eat-grass`的方法，并在`go`方法中调用`eat-grass`：

    to eat-grass
      ask turtles [
        if pcolor = green [
          set pcolor brown
          set energy energy + 10
        ]
      ]
    end
    

在`move-turtles`方法中，增加走一步需要消耗能量的代码：

    to move-turtles
      ask turtles [
        right random 360 ;随机旋转角度
        forward 1
        set energy energy - 1
      ]
    end
    

### 添加监视器

*   添加海龟数量的监视器，在`reporter`中输入：`count turtles`
    
*   添加绿地数量的监视器，在`reporter`中输入：`count patches with [pcolor = green]`
    

### 添加开关

*   添加“开关”组件，注意变量名需要加问号？，然后重写`eat-grass`方法
    

    to eat-grass
      ask turtles [
        if pcolor = green [
          set pcolor black
          set energy energy + 10
        ]
        ifelse show-energy? [
          set label energy
        ] [
          set label ""
        ]
      ]
    end
    

### 添加养小海龟的方法

    to reproduce 
      ask turtles [ 
        if energy > 50 [ ;如果能量值＞50，则生小海龟
          set energy energy - 50 ;生完小海龟，能量值减50
          hatch 1 [ set energy 50 ] ;hatch为孵化一个新的turtle（属性同母体），后面[]中写初始化设置
        ] 
      ] 
    end
    

### 添加海龟死亡的方法

    to check-death
      ask turtles [
        if energy <= 0 [ die ] ;die为默认方法，销毁一个turtle
      ]
    end
    

### 添加草场生长的方法

    to regrow-grass
      ask patches [
        if random 100 < 3 [ set pcolor green ] ;3%概率会生长出草地
      ]
    end
    

### 将上述方法添加到`go`方法中，供每一步都调用

    to go 
      move-turtles 
      eat-grass
      reproduce 
      check-death 
      regrow-grass 
    end
    

![](https://storage.googleapis.com/papyrus_images/4520786c99c974dcefe413590399967ff449b038be89da63d37bc505f36e0d72.png)

### 跟踪数据变化，并绘图

![](https://storage.googleapis.com/papyrus_images/2fa840320e79a392e3470099ea32641c156dda8f17e7add2a5c2d1327bcf6678.png)

添加个一个绘图的方法：

    to do-plots 
      set-current-plot "Totals" 
      set-current-plot-pen "turtles" 
      plot count turtles 
      set-current-plot-pen "grasses" 
      plot count patches with [pcolor = green] 
    end
    

然后分别在`setup`和`go`方法中调用`do-plots`方法。

### 控制时间与步数 tick

在`setup`方法中添加：`reset-ticks`;

修改`go`方法：

    to go 
      if ticks >= 500 [ stop ]
      ...
      tick ;步进一次
    end
    

### 添加草场生长速度的参数输入框

添加一个`输入框`，并命名为`regrow-grass-speed`，数量范围为0-100。

然后将`regrow-grass`方法改为如下：

    to regrow-grass
      ask patches [
        if random 100 < regrow-grass-speed [ set pcolor green ]
      ]
    end
    

### 添加初始化海龟数量的滑块

添加一个`滑块`，命名为`initial-turtles`，然后将`set-turtles`方法改为：

    to set-turtles
      create-turtles initial-turtles
      ask turtles [
        setxy random-xcor random-ycor
      ]
    end
    

![](https://storage.googleapis.com/papyrus_images/57878cb1d5c0e28bbd36970b9aa0f82a423d01457d8acf4ff7ab792c91b8c0e3.png)

---

*Originally published on [jackygu's blog](https://paragraph.com/@jackygu/netlogo)*
