【Julia】Agents.jlを利用したマルチエージェントシミュレーション③鳥の群れ
はじめに
今回はAgents.jlの鳥の群れ(flocking)例題について見ていきます。各個体が3つのルールに基づいて周囲から影響を受けて行動をすることで、最終的には群れになっていく様子をコンピュータ上でモデル化していきます。こちらもABMの古典的な例題として親しまれています。
3つのルール
・衝突回避のための最小距離を維持する
・近隣の鳥の平均位置に向かって移動する
・近隣の鳥の平均方向に向かって移動する
▼URL
モデルの定義
using Agents, LinearAlgebra
mutable struct Bird <: AbstractAgent
id::Int
pos::NTuple{2,Float64}
vel::NTuple{2,Float64}
speed::Float64
cohere_factor::Float64
separation::Float64
separate_factor::Float64
match_factor::Float64
visual_distance::Float64
end
連続空間を用いるため、鳥の属性としてposとvel、speedを用います。1stepごとに、velの方向にspeedの分だけposが更新されます。以下、移動に関わるその他の属性です。
・cohere_factor:近隣鳥の平均位置
・separation:衝突回避のための最小距離
・separate_factor:最小距離維持の動きの重み
・match_factor:近隣鳥の軌道に沿う動きの重み
・visual_distance:鳥の視界の範囲(半径)
function initialize_model(;
n_birds = 100,
speed = 1.0,
cohere_factor = 0.25,
separation = 4.0,
separate_factor = 0.25,
match_factor = 0.01,
visual_distance = 5.0,
extent = (100, 100),
spacing = visual_distance / 1.5,
)
space2d = ContinuousSpace(extent, spacing)
model = ABM(Bird, space2d, scheduler = Schedulers.randomly)
for _ in 1:n_birds
vel = Tuple(rand(model.rng, 2) * 2 .- 1)
add_agent!(
model,
vel,
speed,
cohere_factor,
separation,
separate_factor,
match_factor,
visual_distance,
)
end
return model
end
こちらの関数で初期化されたmodelを用意します。それぞれの鳥(agent)の属性の初期値を決め、vel(速度)のみランダムに定義してagentを100体生成します。また、100x100の連続空間を定義します。
step関数
function agent_step!(bird, model)
# Obtain the ids of neighbors within the bird's visual distance
neighbor_ids = nearby_ids(bird, model, bird.visual_distance)
N = 0
match = separate = cohere = (0.0, 0.0)
# Calculate behaviour properties based on neighbors
for id in neighbor_ids
N += 1
neighbor = model[id].pos
heading = neighbor .- bird.pos
# `cohere` computes the average position of neighboring birds
cohere = cohere .+ heading
if edistance(bird.pos, neighbor, model) < bird.separation
# `separate` repels the bird away from neighboring birds
separate = separate .- heading
end
# `match` computes the average trajectory of neighboring birds
match = match .+ model[id].vel
end
N = max(N, 1)
# Normalise results based on model input and neighbor count
cohere = cohere ./ N .* bird.cohere_factor
separate = separate ./ N .* bird.separate_factor
match = match ./ N .* bird.match_factor
# Compute velocity based on rules defined above
bird.vel = (bird.vel .+ cohere .+ separate .+ match) ./ 2
bird.vel = bird.vel ./ norm(bird.vel)
# Move bird according to new velocity and speed
move_agent!(bird, model, bird.speed)
end
neighbor_ids
には、各birdの視界の半径の中に居る鳥をnearby_ids
メソッドで取得しています。そして、for分の中で近隣鳥から受ける影響について計算しています。それぞれの影響は、cohere
, separate
, match
3つに落とし込まれています。
・cohere:近隣鳥の平均位置へ向かう方向
・separate:最小距離以内にいる近隣鳥から離れる方向の平均
・match:近隣鳥の軌道(速度)の平均へ向かう方向
それぞれのの影響は、和の計算→正規化→重みづけ→velの更新という順に計算され、更新されたvelを用いてmove_agent!
メソッドによりagentの位置が移動します。
可視化
using InteractiveDynamics
using CairoMakie
abm_video(
"flocking.gif", model, agent_step!;
framerate = 20, frames = 100,
title = "Flocking"
)
# jupyter notebook上で見るためにgif形式
display("image/gif", read("flocking.gif"))
ライブラリを利用し、シミュレーションを実行して可視化を行います。
※tutorialには、plotの形状を任意に指定する方法が書かれていますが、可視化のためのバックエンドライブラリのupdateに伴う影響からかうまく動作できなかったので、デフォルトのものを使用しました。
agent同士が群れをなす様子が見て取れると思います。鳥の行動ルールとそこで指定のパラメータを用いてシミュレーションしました。しかし、群れを成した後、その位置に留まってしまうのでやや現実から乖離があるような気がします。そこで、パラメータにおいて、平均位置に留まる影響を小さくし、軌道に合わせる影響を大きくして試してみたいと思います。具体的には、
・cohere_factor: 0.25→0.1
・match: 0.01→0.1
・visual_distance: 5.0→10.0
にしてシミュレーションしてみます。
今度は位置に留まることなく、近隣鳥と同じ方向に動き続ける様子を再現することができました。こちらの方が、なんとなくより現実に沿っているような気がします。
おわりに
鳥の群れができる様子を、ABMで実装しました。ルールやパラメータを色々と変えてみると、動きも変わっているのでとても興味深い内容だったと思います。ぜひ、オリジナルのルールを追加したり、パラメータを変えてみたりして遊んでみてください。
Discussion