Flocking 3D
using EasyABM
Step 1: Create Agents and Model
Lets create 200 agents with properties shape
, pos
, vel
and orientation
(The orientation
property is used internally by EasyABM to draw the direction agent is facing). The position pos
is only accepted as a Vect which is an inbuilt vector type in EasyABM. It is also recommended for both convenience as well as performance to use Vect type for any vectorial properties in the model such as velocity and forces. The keeps_record_of
argument is set of properties that an agent will record during time evolution. The model is defined with properties:
min_dis
: The distance between boids below which they start repelling each other.coh_fac
: The proportionality constant for the cohere force.sep_fac
: The proportionality constant for the separation force.aln_fac
: The proportionality constant for the alignment force.vis_range
: The visual range of boids.dt
: The proportionality constant between change in position and velocity.
The argument agents_type
is Static which means that the agents number will remain fixed during simulation.
boids = con_3d_agents(200, shape = :cone, pos = Vect(0.0,0.0,0.0),
vel=Vect(0.0,0.0,0.0), orientation = Vect(0.0,0.0,0.0), keeps_record_of = Set([:pos, :orientation]))
model = create_3d_model(boids, agents_type=Static,
space_type = Periodic, min_dis = 0.3, coh_fac = 0.05,
sep_fac = 0.5, dt= 0.1, vis_range = 2.0, aln_fac = 0.35)
Step 2: Initialise the model
In this step we set the positions, velocities and orientations of boids and initialise the model.
function initialiser!(model)
xdim, ydim, zdim = model.size
for boid in model.agents
boid.pos = Vect(rand()*xdim, rand()*ydim, rand()*zdim)
boid.vel = Vect(1-2*rand(), 1-2*rand(), 1-2*rand())
boid.vel /= veclength(boid.vel)
boid.orientation = boid.vel
end
end
init_model!(model, initialiser = initialiser!)
Step 3: Defining the step_rule! and running the model
In this step we implement the step logic of the flocking model in the step_rule!
function and run the model for 500 steps.
const ep = 0.00001 # to avoid division by zero
function step_rule!(model)
dt = model.properties.dt
for boid in model.agents
nbrs = neighbors(boid, model, model.properties.vis_range)
coh_force = Vect(0.0,0.0,0.0)
sep_force = Vect(0.0,0.0,0.0)
aln_force = Vect(0.0,0.0,0.0)
num = 0
for nbr in nbrs
num+=1
vec = nbr.pos - boid.pos
coh_force += vec
if veclength(vec)< model.properties.min_dis
sep_force -= vec
end
aln_force += nbr.vel
end
aln_force = num>0 ? (aln_force / num - boid.vel) * model.properties.aln_fac : aln_force
num = max(1, num)
coh_force *= (model.properties.coh_fac/num)
sep_force *= model.properties.sep_fac
boid.vel += (coh_force + sep_force) + aln_force
boid.vel /= (veclength(boid.vel) + ep)
boid.orientation = boid.vel
boid.pos += boid.vel*dt
end
end
run_model!(model, steps=500, step_rule = step_rule!)
Step 4: Visualisation
The following code will draw the state of the model at frame number 4.
draw_frame(model, frame=4)
If one wants to see the animation of the model run, it can be done as
animate_sim(model)
After defining the step_rule!
function we can also choose to create an interactive application (which currently works in Jupyter with WebIO installation) as shown below. It is recommended to define a fresh model and not initialise it with init_model!
or run with run_model!
before creating interactive app.
boids = con_3d_agents(200, shape = :cone, pos = Vect(0.0,0.0,0.0),
vel=Vect(0.0,0.0,0.0), orientation = Vect(0.0,0.0,0.0), keeps_record_of = Set([:pos, :orientation]))
model = create_3d_model(boids, agents_type=Static,
space_type = Periodic, min_dis = 0.3, coh_fac = 0.05,
sep_fac = 0.5, dt= 0.1, vis_range = 2.0, aln_fac = 0.35)
create_interactive_app(model, initialiser= initialiser!,
step_rule= step_rule!,
model_controls=[(:min_dis, "slider", 0.01:0.1:1.0),
(:coh_fac, "slider", 0.01:0.01:1.0),
(:sep_fac, "slider", 0.01:0.01:1.0),
(:aln_fac, "slider", 0.01:0.01:1.0),
(:vis_range, "slider", 0.5:0.5:4.0)], frames=400)
Step 5: Fetch Data
It is easy to fetch any data recorded during simulation. For example, the data of average velocity of agents at each time step can be obtained as -
df = get_agents_avg_props(model, agent -> agent.vel, labels = ["average velocity"])
Individual agent data recorded during model run can be obtained as
df = get_agent_data(model.agents[1], model).record
References
1.) https://en.wikipedia.org/wiki/Flocking