Miles de estorninos giran a la vez en el cielo del atardecer como si fueran un solo animal, y sin embargo ninguno manda. En 1986, el informático Craig Reynolds demostró que para imitar ese vuelo basta con que cada pájaro siga tres reglas sencillas mirando solo a sus vecinos. Llamó a sus pájaros de mentira boids, y su idea acabó en el cine, en los videojuegos y en los enjambres de drones. Aquí puedes ver la bandada, cambiar las reglas y asustarla con el ratón.
Tres reglas y ningún jefe
Reynolds trabajaba en animación por ordenador y quería que una bandada se moviera de forma natural sin tener que dibujar la trayectoria de cada pájaro a mano. Su solución fue no programar la bandada, sino a cada pájaro: un agente que solo ve lo que tiene alrededor, dentro de un radio, y decide hacia dónde girar. Presentó el modelo en 1987 en la conferencia de gráficos SIGGRAPH, con el artículo «Flocks, Herds, and Schools: A Distributed Behavioral Model» (bandadas, manadas y bancos de peces). El nombre boid viene de bird-oid object, «objeto parecido a un pájaro», y de paso imita cómo se pronuncia bird con acento de Nueva York. Cada boid sigue estas reglas:
- Separación: se aparta de los vecinos que tiene demasiado cerca, para no chocar.
- Alineación: gira hacia el rumbo medio de los vecinos que ve.
- Cohesión: se dirige hacia el centro de los vecinos que ve, para no quedarse solo.
Ninguna regla habla de bandadas, de formas ni de líderes. Todo lo que se ve en la animación sale de que cada boid aplique las tres, a la vez, en cada instante.
Pruébalo
La animación tiene entre 50 y 170 boids, según el tamaño de tu pantalla, y el lienzo se cierra sobre sí mismo: lo que sale por un borde entra por el contrario. Los botones vuelven a empezar con los boids al azar y una de las reglas quitada, para ver qué papel juega cada una, y los deslizadores cambian su peso sobre la marcha, es decir, cuánto cuenta en la decisión de cada boid. Marca «Ver lo que ve un boid» para seguir a uno: el círculo grande es su radio de visión, el discontinuo es la distancia a la que empieza a apartarse, y los vecinos que tiene en cuenta salen en azul. Pasa el ratón por encima o toca el lienzo y los boids huirán de ti, como de un halcón. El orden mide cuánto se parecen sus rumbos: 0 si cada uno va a su aire y 1 si todos vuelan en la misma dirección.
Qué pasa si quitas una regla
Las tres reglas se necesitan unas a otras, y la mejor forma de entenderlo es quitarlas de una en una:
- Sin separación, nada impide que se amontonen. Cada grupo se aprieta hasta que todos sus boids ocupan el mismo punto y vuelan uno encima de otro: parece que la bandada se ha quedado en unos pocos pájaros.
- Sin alineación, los boids se buscan pero no copian el rumbo de nadie. Se forman corrillos que bullen y se deshacen, como una nube de mosquitos, y el orden se queda cerca de 0.
- Sin cohesión, copian el rumbo de sus vecinos, pero nada los atrae hacia el grupo. Acaban repartidos por todo el cielo, a distancias casi iguales, como en una rejilla, sin formar grupos compactos. Eso sí, como siguen copiando el rumbo, a menudo terminan volando todos en la misma dirección.
Con las tres a la vez, la separación mantiene las distancias, la cohesión impide que el grupo se disperse y la alineación hace que viaje en una dirección. El equilibrio entre ellas decide la forma: sube la cohesión a 3 y saldrán unas pocas bolas apretadas; sube la separación y los boids se espaciarán como en una rejilla, en bandadas más anchas y ralas; sube la alineación y todos acabarán volando casi en paralelo.
Cómo se programa
En cada paso de la animación, cada boid recorre a todos los demás y se queda con los que están dentro de su radio de visión, aquí 50 píxeles. Con ellos calcula tres direcciones: la de huida de los que están a menos de 20 píxeles (cuanto más cerca, más empujan), el rumbo medio y el punto medio. Para cada una calcula una fuerza de dirección, la diferencia entre la velocidad que lleva y la que llevaría si fuera a toda velocidad hacia ese objetivo, y la limita para que no pueda girar de golpe. Las tres fuerzas, multiplicadas por sus pesos, se suman a su velocidad. Así funcionan las «conductas de dirección» que Reynolds desarrolló después, y por eso la bandada gira con suavidad, sin tirones.
Hay dos detalles importantes. El primero: todos los boids deciden mirando la misma foto de la bandada, la del paso anterior, y solo después se mueven todos a la vez; si cada uno se moviera nada más decidir, el orden en que se recorren cambiaría el resultado. El segundo es el coste: si cada boid mira a todos los demás, con 170 boids son casi 29,000 comparaciones por paso, y el número crece con el cuadrado de la bandada. Para miles de boids, los programas reparten el espacio en una rejilla y cada boid solo mira las casillas que tiene al lado.
Boids en Python
Este programa es exactamente el mismo cálculo que la animación de arriba: lo hemos comprobado pasando el mismo estado inicial a los dos y, tras 300 pasos, las posiciones coinciden hasta el último decimal. Simula 80 boids sin dibujarlos y mide el orden cada 60 pasos, primero con las tres reglas y después sin la alineación:
import math, random
# Mundo de 400 × 400 que se cierra sobre sí mismo: lo que sale por un borde entra por el contrario.
ANCHO = ALTO = 400.0
VISION, ESPACIO = 50.0, 20.0 # radio en el que un boid ve a los demás y radio en el que los aparta
VMAX, VMIN, FMAX = 3.0, 1.5, 0.05 # velocidad máxima y mínima, giro máximo por paso
def envolver(d, lado):
"""Diferencia más corta en un mundo que se cierra: de 390 a 10 hay 20, no 380."""
if d > lado / 2: return d - lado
if d < -lado / 2: return d + lado
return d
def dirigir(dx, dy, vx, vy):
"""Fuerza para pasar de la velocidad (vx, vy) a ir a toda velocidad hacia (dx, dy), con un tope."""
m = math.sqrt(dx * dx + dy * dy)
if m == 0: return 0.0, 0.0
fx, fy = dx / m * VMAX - vx, dy / m * VMAX - vy
n = math.sqrt(fx * fx + fy * fy)
if n > FMAX: fx, fy = fx / n * FMAX, fy / n * FMAX
return fx, fy
def paso(bandada, separacion=1.5, alineacion=1.0, cohesion=1.0):
"""Un paso de la simulación. Cada boid es [x, y, vx, vy]. Todos deciden con la foto del paso anterior."""
giros = []
for i, (x, y, vx, vy) in enumerate(bandada):
n = 0; mvx = mvy = cx = cy = sx = sy = 0.0; aparta = False
for j, (x2, y2, vx2, vy2) in enumerate(bandada):
if j == i: continue
dx, dy = envolver(x2 - x, ANCHO), envolver(y2 - y, ALTO)
d2 = dx * dx + dy * dy
if d2 < VISION * VISION:
n += 1; mvx += vx2; mvy += vy2; cx += dx; cy += dy
if 0 < d2 < ESPACIO * ESPACIO:
sx -= dx / d2; sy -= dy / d2; aparta = True # cuanto más cerca, más empuja
ax = ay = 0.0
if aparta:
fx, fy = dirigir(sx, sy, vx, vy); ax += separacion * fx; ay += separacion * fy
if n:
fx, fy = dirigir(mvx, mvy, vx, vy); ax += alineacion * fx; ay += alineacion * fy # rumbo medio
fx, fy = dirigir(cx, cy, vx, vy); ax += cohesion * fx; ay += cohesion * fy # centro del grupo
giros.append((ax, ay))
for b, (ax, ay) in zip(bandada, giros):
b[2] += ax; b[3] += ay
v = math.sqrt(b[2] * b[2] + b[3] * b[3])
if v > VMAX: b[2] = b[2] / v * VMAX; b[3] = b[3] / v * VMAX
elif 0 < v < VMIN: b[2] = b[2] / v * VMIN; b[3] = b[3] / v * VMIN
b[0] += b[2]; b[1] += b[3]
if b[0] < 0: b[0] += ANCHO
elif b[0] >= ANCHO: b[0] -= ANCHO
if b[1] < 0: b[1] += ALTO
elif b[1] >= ALTO: b[1] -= ALTO
def orden(bandada):
"""De 0 (cada uno a su aire) a 1 (todos en la misma dirección): media de las direcciones."""
ux = uy = 0.0
for _, _, vx, vy in bandada:
v = math.sqrt(vx * vx + vy * vy); ux += vx / v; uy += vy / v
return math.sqrt(ux * ux + uy * uy) / len(bandada)
def nueva(n, semilla):
r = random.Random(semilla)
bandada = []
for _ in range(n):
a = r.uniform(0, 2 * math.pi)
bandada.append([r.uniform(0, ANCHO), r.uniform(0, ALTO), 2 * math.cos(a), 2 * math.sin(a)])
return bandada
if __name__ == "__main__":
for titulo, pesos in [("Las tres reglas", (1.5, 1.0, 1.0)), ("Sin alineación", (1.5, 0.0, 1.0))]:
bandada = nueva(80, semilla=7)
print(titulo)
for t in range(301):
if t % 60 == 0:
print(f" paso {t:3d}: orden {orden(bandada):.2f} " + "#" * round(orden(bandada) * 40))
paso(bandada, *pesos)salidaLas tres reglas paso 0: orden 0.14 ###### paso 60: orden 0.34 ############# paso 120: orden 0.47 ################### paso 180: orden 0.71 ############################# paso 240: orden 0.94 ##################################### paso 300: orden 0.97 ####################################### Sin alineación paso 0: orden 0.14 ###### paso 60: orden 0.17 ####### paso 120: orden 0.16 ###### paso 180: orden 0.26 ########## paso 240: orden 0.22 ######### paso 300: orden 0.26 ##########
Con las tres reglas, el orden empieza en 0.14, porque cada boid sale en una dirección al azar, y pasa de 0.9 en unos 240 pasos: la bandada ya vuela en un solo sentido. Sin alineación, en cambio, se queda por debajo de 0.3, por mucho que los boids se busquen unos a otros. El generador de números aleatorios lleva una semilla fija, así que la salida es idéntica en cada ejecución.
Del cine a los estorninos
La primera película con boids fue el corto Stanley and Stella in: Breaking the Ice, que se estrenó en el mismo SIGGRAPH de 1987. El gran salto llegó en 1992 con Batman Returns, de Tim Burton, que usó una versión modificada para los enjambres de murciélagos y los ejércitos de pingüinos. En 1998 la Academia de Hollywood premió a Reynolds con un Oscar técnico por su trabajo pionero en animación por ordenador. Los boids también están en videojuegos, como los pájaros de Half-Life (1998), que en los archivos del juego se llaman precisamente «boid», y en la robótica, donde las mismas reglas coordinan grupos de drones.
Y la naturaleza resultó parecerse al modelo, con un matiz. En 2008, un equipo italiano reconstruyó en tres dimensiones bandadas de miles de estorninos sobre Roma y descubrió que cada pájaro no sigue a todos los que tiene a cierta distancia, sino a un número fijo de vecinos: de seis a siete, estén cerca o lejos. Según sus simulaciones, esa regla mantiene la bandada unida aunque se estire o se comprima al esquivar a un halcón. Es una pequeña corrección a los boids de Reynolds, que miden por distancia, y una prueba de lo bien que funcionaba la idea de partida: comportamientos complejos que surgen de reglas locales, la misma emergencia que se ve en el juego de la vida o en la hormiga de Langton.
Fuentes: C. W. Reynolds, «Flocks, Herds, and Schools: A Distributed Behavioral Model», Computer Graphics 21(4), SIGGRAPH ’87, y su página sobre los boids; M. Ballerini y otros, «Interaction ruling animal collective behavior depends on topological rather than metric distance», PNAS 105(4), 2008; las páginas de Wikipedia sobre los boids y sobre Craig Reynolds. El programa se ejecutó con Python 3.13. La animación y las imágenes, incluida la portada, las genera el propio algoritmo: decodigo.com.
