9 - 通过人工鸟群Boid模型学习List的使用
该系列笔记基于集智乐园的《Netlogo多主体建模入门》课程,感谢张江老师与各位志愿者的辛勤付出。
三条简单的规则
当鸟偏离鸟群的时候,能自动靠拢鸟群。
鸟的飞行方向取决于 周围鸟的飞行方向。具体来说是:周围鸟的飞行方向 的平均矢量。
当鸟距离另一只鸟过近的时候,会自动远离。
实现思路:
首先,我们需要划定 规则的生效范围:
构造分力所需要的一些参数:
公式:
Pi 指的是 鸟 的邻居节点 的向量,在这个示例中,邻居节点有5个。
邻居的平均位置矢量 指的就是 将这5个向量相加,然后除以邻居节点数 5 ,从而得到的矢量(图中红线)。【为了避免看起来乱,只画了两条蓝线】
公式:
将邻居节点的速度相加,然后除以邻居节点数 5,即可得到。
因为速度是矢量,有方向和标量。所以能通过邻居节点的飞行方向来决定这只鸟的飞行方向。
计算方法同
开始构造分力
通过受力分析,能得出F1的公式:
但是我们到后面合成合力的时候,会给F1 多乘上一个系数,后面的两个分力也是如此。
F2这力等于 邻居节点的平均速度矢量 。
因为是斥力,所以要 远离碰撞节点的重心(浅紫色的点),方向为矢量( P- C平均) 的方向。
因为考虑到这个碰撞节点的重心可能会和本节点重合,导致矢量( P- C平均) 的模变为0,所以会加上一个很小的系数 ε 。
分母 | P- C平均 | 平方的原因:
第一次除以 | P- C平均 | ,是因为 | P- C平均 | 是 矢量( P- C平均)的模,除以它就能实现向量归一化。
第二次除以 | P- C平均 | ,是为了实现: 当这只鸟离碰撞重心越近,斥力越大。
最终将分力以加权的形式合成 合力F ,我们可以在模型中调整 w1、w2、w3 这三个参数。
如何让鸟运动起来?
在正式开始写代码之前,我们需要知道一些语法知识:
list的使用
创建:
set lst [0 0 1]
直接用方括号
set lst ( list (sqrt 4) val1 val2 )
调用 list 对象的构造函数
访问:
取出list对象 lst 的第一个元素
取出list对象 lst 的最后一个元素
取列表中的第4个元素
MAP的用法-矢量的数乘
用法一 单个形参:
[? -> ? * 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” 这三个参数就是代表了形参,可以理解为中间变量。你的输入是多少,它就是多少,而且可以不断更新,直到运算完毕。看图就能懂了。
了解了这些之后,我们可以开始建立模型了:
添加按钮和滑块
从上往下每个滑块的意思:
w_coh,F1 聚集拉力 的系数
w_ali , F2 对齐拉力 的系数
w_col , F3 分离斥力 的系数
然后开始敲代码啦!
以下代码是看课程视频照着敲的,就是有些变量的名字改了。我这里写的代码就不写注释了,大家跟着视频看一遍就懂啦。
ps:千万要记得每一个变量名是代表了什么,而且起名要能明显看出对应其意义。
还有就是,建议各位抽点时间把代码敲一遍,加深理解。
示例: 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 模型运行界面: