This is a python pygame implementation of the boids algorithm which simulates flocking behavior and emergent group dynamics. With three simple rules and only the ability to see local neighbors, the boids create realistic, flock-like movement. Most of the code is based off of this paper by Craig Reynolds: Flocks, Herds, and Schools: A Distributed Behavioral Model.
There are three main rules that govern the behavior of each boid in the simulation. Every boid interacts with only local neighbors within a certain radius.
Principle: Avoid crowding local flockmates.
For each boid
-
= member of the set of neighbors -
= the position of boid -
= the position of boid
Derivation:
-
We want to move away from
. The vector pointing away from is . We only want the direction so we can normalize it to get . -
We want to move away from
with a strength that drops off with distance. A common way to do this is to use the inverse square law.
We know that due to the inverse square law, $$ \text{strength} \propto \frac{1}{\text{distance}^2} $$ or, better for our purposes, $$ \text{strength} = \omega_{\text{sep}} \frac{1}{\text{distance}^2} $$ where $ whereis a constant that controls the strength of the force.
We can substitute the distance with the norm of the vector $\mathbf{p}i-\mathbf{p}j$ to get:
$$
\text{strength} = \omegaga{\text{sep}} \frac{1}{\lVert\mathbf{p}i-\mathbf{p}j\rVert^{2}}
$$
3. We can combine direction and magnitude (strength) to get the force vector from $j$ to $i$:
$$
\begin{align*}
\mathbf Fhbf F{i\leftarrow j} &= \underbrace{\widehat{\mathbf p_i-\mathbf p_j}}_j}}{\text{direction}} ; \underbrace{\left(\omega{\text{sep}},\frac{1}{\lVert\mathbf p_i-\mathbf p_j\rVert^{2}}\right)}{\text{magnitude}} \
&= \omegamegaa{\text{sep}} \frac{\widehat{\mathbf p_i-\mathbf p_j}} {\lVert\mathbf p_i-\mathbf p_j\rVert^{2}}
\end{align*}
$$
4. To aggregate the forces from all neighbors, we sum over all ll
The total separation force on boid
\end{align*}
$$
Factoring out the constant nt
Principle: Steer toward the average heading of local flock-mates.
For each boid
Where
-
= velocity of neighbour -
= maximum speed (scalar) -
= alignment weight (tunable gain) -
= unit vector in the direction of the mean velocity.
Derivation
- We want to align ourselves with our neighbors. The first step is to compute the average velocity of all visible neighbours
:
And then get only the direction from it.
- The desired velocity is the maximum speed in the direction of the average velocity of the neighbours:
So we want to steer towards this desired velocity. 3. The difference between the desired velocity and the current velocity gives us the steering force $\mathbf{v}}{\text{des}} - \mathbf v_i$. We multiply by a tuneable gain the form of $\omegaa{\text{align}}$ to control the strength of the alignment force to get:
Substituting, we get $$ \boxed {\mathbf F_{\text{align}} = \omega_{\text{align}}\Bigl(v_{\max},\widehat{\bar{\mathbf v}} - \mathbf v_i\Bigr)},,; \bar{\mathbf v} = \frac1{|S|}\sum_{j\in S}\mathbf v_j $$ $$
This
Principle: Steer towards the average position of local flockmates.
For each boid
Where:
-
= position of neighbor -
= center of mass of neighbors -
= maximum speed (scalar) -
= cohesion weight (tunable gain)
Derivation
- We want to move toward the center of mass of our neighbors. The formula for the center of mass for discrete points is
Where
Where
- Next, we find the direction from our current position to the center of mass:
And normalize it to get the unit direction vector:
- The desired velocity is the maximum speed in the direction toward the center of mass:
So we want to steer towards this desired velocity.
- The difference between the desired velocity and the current velocity gives us the steering force $\mathbf{v}_{\text{des}} - \mathbf{v}i$. We multiply by a tunable gain $\omega{\text{coh}}$ to control the strength of the cohesion force:
Substituting, we get: