《Netlogo多主体建模入门》笔记 9

387 阅读6分钟
9 - 透过人工鸟群Boid模型学习List的使用

三条简单的规则
  • 靠近规则
当鸟偏离鸟群的时候,能自动靠拢鸟群。
  • 对齐规则
鸟的飞行方向取决于 周围鸟的飞行方向。具体来说是:周围鸟的飞行方向 的平均矢量。
  • 分离规则
当鸟距离另一只鸟过近的时候,会自动远离。

实现思路:
  1. 将鸟群飞行的模型转化成一个多质点相互作用(受力)的力学模型
  2. 构建 三个分力,靠近的拉力F1 、对齐的拉力F2、 分离的斥力 F3
  3. 通过将三个力(F1 、F2、 F3) 合成 为一个力 F,并结合 简单的欧拉法来 更新 运动的方向 v 和位置 p,这样就能模拟鸟群的运动。



首先,我们需要划定 规则的生效范围:
  • 在蓝色区域内,是 靠近、对齐规则起作用的范围
  • 在红色区域内,是 分离规则起作用的范围



构造分力所需要的一些参数:
  • 邻居的平均位置矢量(矢量指向的点,又叫重心)
公式:
Pi 指的是 鸟 的邻居节点 的向量,在这个示例中,邻居节点有5个。
邻居的平均位置矢量 指的就是 将这5个向量相加,然后除以邻居节点数 5 ,从而得到的矢量(图中红线)。【为了避免看起来乱,只画了两条蓝线】
  • 邻居节点的平均速度矢量
公式:
将邻居节点的速度相加,然后除以邻居节点数 5,即可得到。
因为速度是矢量,有方向和标量。所以能通过邻居节点的飞行方向来决定这只鸟的飞行方向。
  • 碰撞者的平均位置矢量(矢量指向的点,又叫重心)
计算方法同



开始构造分力

  • 靠近的拉力F1
通过受力分析,能得出F1的公式:
但是我们到后面合成合力的时候,会给F1 多乘上一个系数,后面的两个分力也是如此。
  • 对齐的拉力F2
F2这力等于 邻居节点的平均速度矢量 。

  • 分离的斥力 F3
因为是斥力,所以要 远离碰撞节点的重心(浅紫色的点),方向为矢量( P- C平均) 的方向。
因为考虑到这个碰撞节点的重心可能会和本节点重合,导致矢量( P- C平均) 的模变为0,所以会加上一个很小的系数 ε 。
分母 | P- C平均 | 平方的原因:
第一次除以 | P- C平均 | ,是因为 | P- C平均 | 是 矢量( P- C平均)的模,除以它就能实现向量归一化。
第二次除以 | P- C平均 | ,是为了实现: 当这只鸟离碰撞重心越近,斥力越大。

最终将分力以加权的形式合成 合力F ,我们可以在模型中调整 w1、w2、w3 这三个参数。


如何让鸟运动起来?

  • 通过合力F,更新鸟的飞行速度 V 的方向和大小。
  • 通过鸟当前的速度 V,更新位置P。

list的使用
创建:
  • 如果列表中的元素都是一些数值常数
set lst [0 0 1]
直接用方括号
  • 如果列表中的元素含有变量
set lst ( list (sqrt 4) val1 val2 )
调用 list 对象的构造函数

访问:
  • first lst
取出list对象 lst 的第一个元素
  • last lst
取出list对象 lst 的最后一个元素
  • item 3 lst
取列表中的第4个元素


MAP的用法-矢量的数乘
用法一 单个形参:
  • map [? -> ? * 2] [1 3 4]
[? -> ? * 2] 有点像java里的lambda表达式,是一个匿名函数,?代表了形参。
你可以将 " -> " 这个符号当作 “变成”。 [? -> ? * 2] 的意思就是把 变量x 变成 2x 的一个匿名函数。
map [? -> ? * 2] [1 3 4] 的意思就是 把 这个匿名函数 作用于右边列表[1 3 4] 里的元素。




用法儿 多个形参:
map [[?1 ?2 ?3] -> ?1 + ?2 + ?3] [1 2 3] [2 3 4] [0 1 2]
[3 6 9]
注意:在命令中心,要用 show(命令) 的形式才能打印结果。
“ ?1” 、“?2”、“?3” 这三个参数就是代表了形参,可以理解为中间变量。你的输入是多少,它就是多少,而且可以不断更新,直到运算完毕。看图就能懂了。




了解了这些之后,我们可以开始建立模型了:

添加按钮和滑块

从上往下每个滑块的意思:
  • 鸟群中鸟的数量 --------- num_birds
  • 视野半径大小 ------ view_range
  • 碰撞距离(小于这个距离的时候,触发分离规则) ------- coll_range



  • 三个分力的 权值大小
w_coh,F1 聚集拉力 的系数
w_ali , F2 对齐拉力 的系数
w_col , F3 分离斥力 的系数

然后开始敲代码啦!

以下代码是看课程视频照着敲的,就是有些变量的名字改了。我这里写的代码就不写注释了,大家跟着视频看一遍就懂啦。
ps:千万要记得每一个变量名是代表了什么,而且起名要能明显看出对应其意义。
还有就是,建议各位抽点时间把代码敲一遍,加深理解。

  • In-radius
示例: other turtles in-radius 5 ------- 限定条件,表示半径5以内的其他 turtles 。

turtles-own [
 v
 nei_mates
 col_mates
]
to setup
  clear-all
  crt num_birds [
    setxy random-xcor random-ycor
    set v [0 0]
    set size 5
  ]
end
to go
  ask turtles [
    get_neis
    flocking
    facexy xcor + (first v)  ycor + (last v)
    setxy xcor + (first v)  ycor + (last v)
  ]
end
to get_neis
  set nei_mates (other turtles in-radius view_range)
  set col_mates (other turtles in-radius coll_range)
end
to flocking
  let corr [0 0]
  let vel [0 0]
  let nei_n count nei_mates
  ask nei_mates[
   set corr (map + corr (list xcor ycor))
    set vel (map + vel v)
  ]
  if nei_n > 0 [
    set corr (map [ ?1 -> ?1 / nei_n ] corr)
    set vel (map [ ?1 -> ?1 / nei_n ] vel )
  ]
  let dis (map - corr (list xcor ycor))
  let col_n count col_mates
  let cdis [0 0]
  if col_n > 0 [
    let  ccorr [0 0]
    ask col_mates [
      set ccorr (map + ccorr (list xcor ycor))
    ]
    set ccorr (map [ ?1 -> ?1 / col_n ] ccorr)
    set cdis (map - (list xcor ycor ) ccorr)
    set cdis (map [ ? -> ?  / (((norm cdis) * (norm cdis)) + 0.01  )] vel )
  ]
  let w1 w_coh
  let w2 w_ali
  let w3 w_col
  let f1 (map [ ? -> ? * w1 ] dis)
  let f2 (map [ ? -> ? * w2 ] vel)
  let f3 (map [ ? -> ? * w3 ] cdis)
  let force (map [ [?1 ?2 ?3] -> ?1 + ?2 + ?3 ] f1 f2 f3 )
  set v (map + v force)
  
  if (norm v) > 5 [
   set v normalize v
    set v map [ ? -> ? * 5 ] v
  ]
end
to-report normalize [xy]
  let realdis norm xy
  let out [0 0]
  if realdis > 0[
    set out  (map [ ? -> ? / realdis ] xy)
  ]
  report out
end
to-report norm [arr]
  let xx first arr
  let yy last arr
  report sqrt (xx * xx + yy * yy )
end


代码完成后,就可以跑模型啦!


  • 当我们试图调高 聚集系数的时候:
鸟群的飞行开始倾向为一条线的形状。

  • 当我们试图调高 对齐系数的时候:
鸟群的飞行方向的变得更加相同。

  • 当我们试图调高 防碰撞系数 的时候:
鸟群的结构趋于松散化。

除此之外,还可以用netlogo自带的 flocking 模型,改模型也是能模拟鸟群飞行的模型之一。

flocking 模型运行界面: