4.flocking(netlogo)

一、简单叙述

这个模型也是netlogo自带的模型,是模拟鸟群行为的,位置在biology下,叫做flocking

样子如下图所示

模型假设每只鸟的视野范围是以自身为圆心,vision为半径的一个圆

有3条转角规则

1、在视野范围内,有鸟和他靠的太近,他就会主动远离那只鸟(separate原则)

如果没有鸟和它很近,就会执行对齐(align原则)和靠近(cohere原则)

2、它会尽量模仿视野范围内其他鸟的飞行方向,得到他们的平均方向,往那个方向偏一定角度(align原则)

3、它会根据视野内其他鸟的位置,得到一个平均方向,往那个方向偏一定角度(cohere原则)

这些规则在此模型中都只是调整鸟的运动方向,调整完之后往前走一个单位

所有鸟都是平等的,没有什么leader

模型原来的代码有点绕来绕去的,我这里调整了一下,再稍作分析

二、代码分析

1.鸟拥有的属性

在建模过程中,最重要的就是想清楚全局变量怎么设置

主体应该有哪些额外属性

这里每只鸟有两个属性

第一个属性是flockmates,表示在视野范围内,除自己以外所有的邻居鸟

第二个属性是nearest-neighbor,表示离这只鸟最近的邻居

;;1.鸟拥有的属性
turtles-own[
  flockmates ;;视野内的鸟
  nearest-neighbor ;;最近的邻居
]

2.初始化按钮 setup

这里主要的工作就是创建鸟,另外就是国际惯例,清空界面,重置时间步

;;2.初始化按钮 setup 代码
to setup
  clear-all ;;清空界面
  create-turtles birdNum[ ;;创建鸟群,设置每只鸟的大小,颜色,位置,还有自带的属性
    set size 7
    set color yellow - 2 + random 5 ;;棕色鸟,颜色稍有差异
    setxy random-xcor random-ycor ;;每只鸟随机分布
    set flockmates no-turtles ;;刚开始没有邻居 集合需要初始化,最近邻一只鸟貌似就不用初始化? nobody和空集合意义不同
  ]
  reset-ticks ;;重置时间步
end

3.循环迭代按钮 go

每一个时间步,都要调整每只鸟的方向,调整完之后,每只鸟往前1个单位

这里由于我们设置成按时间步更新,直接fd 1貌似问题也不大,至少我看不出来区别

所以整个程序的重点就是怎么转向

;;3. go按钮
to go
  ask turtles[
    adjust-heading ;;给每只鸟调整一下方向
  ]
  repeat 5[ ;;每只鸟向前移动1个单位 5*0.2,为了更流畅
    ask turtles[
      fd 0.2
    ]
    display
  ]
  tick
end

4.adjust-heading 调整方向函数

首先按照视野范围,找到范围内的邻居鸟

只要有一只鸟的话,就找到最近的那只

最近那只靠的太近的话,就远离

不然就执行对齐和靠近


;;4.adjust-heading 调整方向函数
to adjust-heading
    set flockmates other turtles in-radius vision ;;按照视野vision,找到这只鸟的邻居
    if any? flockmates[ ;;如果有一只鸟的话
      set nearest-neighbor min-one-of flockmates [distance myself] ;;找到最近的邻居
      ifelse distance nearest-neighbor < minimum-separation[ 
        ;;separate远离
        let neighbor-heading [heading] of nearest-neighbor ;;得到邻居的方向
        change-heading (subtract-headings heading neighbor-heading) max-separate-turn
      ]
      [
        ;;align 对齐
        change-heading (subtract-headings average-flockmate-heading heading) max-align-turn
        ;;cohere 靠近
        change-heading (subtract-headings average-heading-towards-flockmates heading) max-cohere-turn
      ]
    ]
end

源代码这里都采用函数封装的形式,把远离,对齐,靠近三个行为封装成函数

感觉有点多余,这里就拆开来更加直观,反正3个行为都是以某种方式调整了方向

change-heading函数传入两个值,一个是两种方向的差值,一个是偏转最大值,在主界面可以调整

所有偏转都有最大值的限制,不能乱来

5.change-heading 函数

这个函数用最大偏转角度max-turn来规范turn这个转角

turn里面包含了转角方向(正负情况)和转角大小


;;5.change-heading 在最大偏角范围内调整角度
to change-heading [turn max-turn]
  ifelse abs turn > max-turn[
    ifelse turn > 0[
      rt max-turn
    ]
    [
      lt max-turn
    ]
  ]
  [
    rt turn ;;正数向右转,不然向左转,turn里已经包含正负号了可以直接rt
  ]
end

6.average-flockmate-heading

这是一个reporter函数,可以返回一个值

先看代码

;;6.average-flockmate-heading
to-report average-flockmate-heading
  let x-component sum[dx] of flockmates
  let y-component sum[dy] of flockmates
  ifelse x-component = 0 and y-component = 0 ;;总体方向相互抵消,不产生影响
  [
    report heading
  ]
  [
    report atan x-component y-component
  ]
end

这里的dx,和dy是这样定义的

某只鸟,朝现在的方向,向前走一个单位,和刚在坐标比较,得到一个横坐标差值就是dx,纵坐标差值就是dy

由一只鸟的dx和dy可以使用函数 atan,得到当前的方向

atan dx dy

计算所有邻居鸟的dx和dy,再通过atan函数得到所有鸟的平均方向

有人可能会问,每只鸟作为主体都有heading属性,代表着方向,为什么不把所有方向加起来,平均一下

那么如果一只鸟方向是1度,一只鸟方向是359度,应该得到的平均方向是?

应该不是 (1+356)/2  = 180度吧,应该是0度。

方向是一个矢量,我们在暴力平均heading的时候,把那个方向条件给弄没了

但我们有一个逻辑链条

由当前方向heading向前一个单位,得到dx和dy,这两个值本身就是自带的属性

而dx和dy是有方向性的矢量,能够很好的表达方向,自由地加减

又有atan函数可以把 dx 和 dy 反向表示成一个方向

所以可以通过累加每只鸟的dx和dy,得到邻居鸟的平均方向

7.average-heading-towards-flockmates

先上代码

;;7.average-heading-towards-flockmates
to-report average-heading-towards-flockmates
  let x-component mean [sin (towards myself + 180)] of flockmates
  let y-component mean [cos (towards myself + 180)] of flockmates
  ifelse x-component = 0 and y-component = 0[
    report heading
  ]
  [
    report atan x-component y-component
  ]
end

先来看看普通坐标系和方向坐标系的不同

普通坐标系东面是0度西面是180度,是顺时针的

而方向坐标系如下所示,是逆时针的

下图右侧假设一个物体做圆周运动,在四个位置就应该是相应的度数

所以rt 20,这个指令是给heading加20

lt 20,则是给heading减去20

在访问每只邻居鸟的时候,调用 towards myself得到的方向就是

邻居鸟朝向自身的方向,下图红色球表示当前鸟,蓝色球表示邻居鸟

这个方向加上180度,就是自身指向邻居鸟的方向

现在我们只有一个方向,没有dx,dy,因为这是一个逻辑上的方向,不是任何主体的方向

所以我们现在要拿到dx和dy

假设我们现在的方向是方向坐标系下的90°,其实就是直角坐标系下的0度,那么我们向前一个单位,dx = 1,dy = 0

我们用方向坐标系下的90°做三角运算,得到

sin 90° = 1 cos 90° = 0

所以我们使用sin(方向坐标系角度)来表示dx

用cos(方向坐标系角度)来表示dy

再来实证一下

方向坐标系下的角度直角坐标系下的角度
90°
90°
180°270°
270°180°

首先建立一个函数关系 设左边的度数为x1,右边的度数为x2

在2π为周期的函数中有 2π + 90° - x1 等价于 x2

有sin(2π + 90° - x1 ) = sin (x2)

而sin(2π + 90° - x1 ) 的图像如下

所以sin(2π + 90° - x1 ) 等价于cos(x1)

同理

cos(2π + 90° - x1 ) = cos (x2)

cos(2π + 90° - x1 ) 等价于 sin(x1)

所以sin(x1) = cos (x2)

但是这里为什么要用mean取平均值,有点问题,讲道理用sum应该也行?源程序用的的mean,我用了一波sum也看不出什么问题

三、全部代码

;;1.鸟拥有的属性
turtles-own[
  flockmates ;;视野内的鸟
  nearest-neighbor ;;最近的邻居
]

;;2.初始化按钮 setup 代码
to setup
  clear-all ;;清空界面
  create-turtles birdNum[ ;;创建鸟群,设置每只鸟的大小,颜色,位置,还有自带的属性
    set size 7
    set color yellow - 2 + random 5 ;;棕色鸟,颜色稍有差异
    setxy random-xcor random-ycor ;;每只鸟随机分布
    set flockmates no-turtles ;;刚开始没有邻居 集合需要初始化,最近邻一只鸟貌似就不用初始化? nobody和空集合意义不同
  ]
  reset-ticks ;;重置时间步
end

;;3. go按钮
to go
  ask turtles[
    adjust-heading ;;给每只鸟调整一下方向
  ]
  repeat 5[ ;;每只鸟向前移动1个单位 5*0.2,为了更流畅
    ask turtles[
      fd 0.2
    ]
    display
  ]
  
  tick
end

;;4.adjust-heading 调整方向函数
to adjust-heading
    set flockmates other turtles in-radius vision ;;按照视野vision,找到这只鸟的邻居
    if any? flockmates[ ;;如果有一只鸟的话
      set nearest-neighbor min-one-of flockmates [distance myself] ;;找到最近的邻居
      ifelse distance nearest-neighbor < minimum-separation[ 
        ;;separate远离
        let neighbor-heading [heading] of nearest-neighbor ;;得到邻居的方向
        change-heading (subtract-headings heading neighbor-heading) max-separate-turn
      ]
      [
        ;;align 对齐
        change-heading (subtract-headings average-flockmate-heading heading) max-align-turn
        ;;cohere 靠近
        change-heading (subtract-headings average-heading-towards-flockmates heading) max-cohere-turn
      ]
    ]
end

;;5.change-heading 在最大偏角范围内调整角度
to change-heading [turn max-turn]
  ifelse abs turn > max-turn[
    ifelse turn > 0[
      rt max-turn
    ]
    [
      lt max-turn
    ]
  ]
  [
    rt turn ;;正数向右转,不然向左转,turn里已经包含正负号了可以直接rt
  ]
end

;;6.average-flockmate-heading
to-report average-flockmate-heading
  let x-component sum[dx] of flockmates
  let y-component sum[dy] of flockmates
  ifelse x-component = 0 and y-component = 0 ;;总体方向相互抵消,不产生影响
  [
    report heading
  ]
  [
    report atan x-component y-component
  ]
end

;;7.average-heading-towards-flockmates
to-report average-heading-towards-flockmates
  let x-component mean [sin (towards myself + 180)] of flockmates
  let y-component mean [cos (towards myself + 180)] of flockmates
  ifelse x-component = 0 and y-component = 0[
    report heading
  ]
  [
    report atan x-component y-component
  ]
end

四、小结

最后那个三角转换应该还有更合理的解释,为什么用mean不用sum,这里不深究了

模型中主要是用一定的逻辑改变鸟的方向,速度都恒定不变都是每个时间步向前一个单位

应该还有更好的表达方式

  • 5
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值