Estrategia y patrones para WRO Football (RoboSport Football) — competición de fútbol robótico 2v2 con bola IR pulsada. Usar SIEMPRE que se trabaje en WRO Football, fútbol robótico, soccer robot, IR ball tracking, sensor de bola infrarroja, posicionamiento en cancha de fútbol robótico, roles goalkeeper/striker, omnidirectional drive, kicker mechanism, o se mencione 'WRO Football', 'soccer', 'IR ball', 'pulsed infrared', 'goalkeeper', 'striker', 'kicker', 'goal post', 'penalty area', 'RCJ Soccer'. La bola es siempre IR pulsada según estándar RoboCup. NO usar para WRO RoboMission (eso va en wro-robomission-strategy) ni Future Engineers.
WRO Football (también conocido como RoboSport Football) y RoboCup Junior Soccer son la categoría de fútbol robótico para equipos juveniles. Dos robots autónomos por equipo (2v2) compiten sobre una cancha verde rectangular con una pelota infrarroja pulsada, dos arcos coloreados, y los robots deben marcar goles. Esta skill cubre la estrategia, la detección de pelota, y los patrones tácticos.
RoboCup Junior Soccer es la versión históricamente más establecida y muchos de sus principios se aplican directamente a WRO Football.
Las competiciones de soccer robotico estandarizan la pelota como una bola infrarroja pulsada que emite IR a una frecuencia específica (típicamente 833 Hz para RCJ, ver el rulebook del año). Los robots usan sensores IR direccionales que detectan la pelota a 360° alrededor del robot.
Las dos pelotas estándar son:
Las competiciones internacionales han pasado a usar pelotas pasivas para hacerlo más realista, pero a nivel escolar/regional la pelota activa sigue siendo común.
| Componente | Función |
|---|---|
| Spike Prime hub o controller equivalente | Cerebro |
| Anillo de sensores IR (8-16 sensores alrededor del robot) | Detección direccional de la pelota |
| Brújula digital o IMU con yaw confiable | Orientación absoluta |
| 2-4 motores de tracción | Diferencial o omnidireccional (mejor para soccer) |
| Sensor de color apuntando al piso | Detectar las líneas de la cancha (white border, penalty area) |
| Kicker mechanism (opcional) | Solenoide o motor para patear con fuerza |
| Sensor ultrasónico o ToF lateral/frontal | Detectar paredes y oponentes |
Spike Prime no tiene anillo de sensores IR oficial. Para WRO Football con Spike, hay que usar:
Para soccer competitivo, el setup ganador es omnidireccional. Para escuela / iniciación, diferencial está bien.
El anillo de sensores IR devuelve un array donde cada sensor reporta la intensidad recibida. Por ejemplo con 12 sensores:
def read_ir_ring():
"""Lee los 12 sensores y devuelve [intensidades_0_a_11]."""
return [ir_sensors[i].value() for i in range(12)]
Para encontrar la dirección de la pelota se calcula el centroide angular:
from umath import sin, cos, atan2, pi
def ball_direction(ir_values):
"""Devuelve (angle_rad, magnitude). Angle 0 = enfrente, +CCW."""
n = len(ir_values)
sum_x = 0.0
sum_y = 0.0
for i, v in enumerate(ir_values):
angle = (i / n) * 2 * pi
sum_x += v * cos(angle)
sum_y += v * sin(angle)
magnitude = (sum_x*sum_x + sum_y*sum_y) ** 0.5
if magnitude < 5: # threshold de ruido
return None # no se detecta pelota
angle = atan2(sum_y, sum_x)
return (angle, magnitude)
magnitude te da una idea de qué tan cerca está la pelota — más alto = más cerca.
def striker_loop():
while True:
result = ball_direction(read_ir_ring())
if result is None:
# Sin pelota visible — buscar
search_for_ball()
continue
angle_rad, distance = result
angle_deg = angle_rad * 180.0 / pi
# 1. Si la pelota está enfrente y cerca → patear
if abs(angle_deg) < 15 and distance > 50:
kick_and_advance()
continue
# 2. Si la pelota está enfrente lejos → avanzar derecho
if abs(angle_deg) < 30:
move_toward_goal_with_ball()
continue
# 3. Pelota a un lado → rotar para encararla
rotate_toward_ball(angle_deg)
wait(20)
Error clásico de equipos principiantes: hacer que el robot siga la pelota directamente.
# ❌ MAL — robot va directo a la pelota
def naive_follow():
angle, _ = ball_direction(read_ir_ring())
move_in_direction(angle)
Por qué no funciona: la pelota está sobre el piso. Si el robot va directo a ella, se topa con ella por el costado equivocado y patea la pelota lejos del arco rival, no hacia él. O peor, hacia el propio arco (autogol).
El robot debe posicionarse DETRÁS de la pelota (relativo al arco rival) y EMPUJARLA hacia adelante. Esto se llama "approach from behind" o "ball capture path".
def approach_ball_from_behind():
"""Calcula un punto detrás de la pelota (entre el propio arco y la pelota)
y se dirige a ese punto. Después, una vez detrás, empuja la pelota."""
ball_x, ball_y = estimate_ball_position() # de odometría + IR
own_goal_x, own_goal_y = OWN_GOAL_CENTER
# Vector del propio arco a la pelota
dx = ball_x - own_goal_x
dy = ball_y - own_goal_y
norm = (dx*dx + dy*dy) ** 0.5
# Punto a 200 mm detrás de la pelota (en dirección del propio arco)
behind_x = ball_x - 200 * dx / norm
behind_y = ball_y - 200 * dy / norm
if not at_position(behind_x, behind_y, tolerance=50):
navigate_to(behind_x, behind_y)
else:
# Estamos detrás de la pelota, empujar hacia el arco rival
push_ball_toward_enemy_goal()
El portero tiene una lógica más simple pero muy distinta:
def goalkeeper_loop():
while True:
result = ball_direction(read_ir_ring())
# Mantenerse en el penalty area
my_x, my_y, _ = odo.pose()
if my_y < GOAL_DEFENSE_LINE:
move_to(GOAL_X, GOAL_DEFENSE_LINE)
continue
if result is None:
# Sin pelota — quedarse en el centro del arco
move_to(GOAL_X, GOAL_DEFENSE_LINE)
continue
angle_rad, magnitude = result
ball_x_estimated = estimate_ball_x_from_angle(angle_rad)
# Moverse lateralmente para tapar el ángulo de tiro
target_x = clamp(ball_x_estimated, GOAL_LEFT, GOAL_RIGHT)
move_to(target_x, GOAL_DEFENSE_LINE)
# Si la pelota está MUY cerca → salir a despejar
if magnitude > 100 and abs(angle_rad) < pi/4:
sortie_and_clear()
wait(20)
La cancha de soccer tiene un borde blanco alrededor del campo verde. Si el robot detecta blanco bajo sus ruedas, está saliendo de la cancha y debe retroceder inmediatamente.
color_ground = ColorSensor(Port.E)
def check_out_of_bounds():
if color_ground.reflection() > 70: # blanco
drive.straight(-100) # retroceder
return True
return False
# En el loop principal
if check_out_of_bounds():
continue # skipear comportamiento normal este tick
Hay 3 enfoques:
kicker_motor = Motor(Port.D)
def kick():
"""Liberar el resorte cargado."""
kicker_motor.run_angle(speed=1000, angle=90) # liberar
wait(100)
# Recargar
kicker_motor.run_angle(speed=300, angle=-90)
Con dos robots, uno es striker y otro es goalkeeper (o ambos atacan en formación 2-0 con uno más adelantado). NO ambos atacan la pelota al mismo tiempo: chocan entre sí y dejan el arco descubierto.
Comunicación entre robots: WRO no permite comunicación directa entre robots durante el partido, así que los roles tienen que ser estáticos (uno SIEMPRE es portero, otro SIEMPRE es delantero) o basados en posición (el más cercano a la pelota ataca, el otro defiende — esto se determina por la posición percibida con sensores).
| Síntoma | Causa | Solución |
|---|---|---|
| Robot autogol | Empuja la pelota desde el lado equivocado | Implementar approach-from-behind |
| Pierde la pelota constantemente | Sensor IR de baja calidad | Cambiar a TSSP4038 o módulo Pololu |
| Robots del mismo equipo chocan | Sin diferenciación de roles | Asignar roles fijos |
| Sale de la cancha | Sin detección de borde blanco | ColorSensor mirando al piso, retroceder si > 70 |
| Pelota lateral nunca detectada | Anillo IR con cobertura insuficiente | Mínimo 8 sensores, idealmente 12-16 |