Estrategia y patrones para combate robótico de sumo (mini-sumo y full-sumo) con LEGO Spike Prime y Pybricks. Usar SIEMPRE que se trabaje en sumo robotico, mini-sumo, robo-sumo, lego sumo, edge detection en el dohyo, opponent tracking con sensores ultrasónicos o IR, push strategies, bullying patterns, dohyo, sumo arena, evitar caer del ring, o se mencione 'sumo', 'mini-sumo', 'dohyo', 'opponent', 'edge sensor', 'push', 'bull rush'. NO usar para line following (eso va en pybricks-line-following) ni soccer.
Sumo robótico es un combate 1v1 entre dos robots autónomos sobre un dohyo (ring circular) donde el objetivo es empujar al oponente fuera del ring sin caer uno mismo. Las dos categorías principales son:
| Categoría | Tamaño máx | Peso máx | Dohyo |
|---|---|---|---|
| Mini-sumo | 10×10 cm base | 500 g | 77 cm de diámetro |
| Full-sumo | 20×20 cm base | 3 kg | 154 cm de diámetro |
| Lego sumo (educativo) | varía | varía | varía |
Para LEGO Spike Prime, lo más común es mini-sumo o lego-sumo educativo.
El robot va al centro del dohyo y embiste al primer contacto detectado. Apuesta por la potencia bruta.
Pros: 70% de los matches se ganan en los primeros 5 segundos por bull rush exitoso. Simple de programar.
Contras: si el oponente es más rápido o tiene mejor pala, perdés.
El robot espera en el centro, gira buscando al oponente, ataca solo cuando el oponente está identificado y bien encarado.
Pros: aprovecha errores del oponente, mejor contra robots agresivos pero mal calibrados.
Contras: contra otro defensivo es un standoff aburrido que termina en empate.
Empieza agresivo, si pierde una ronda cambia a defensivo, si pierde otra vuelve a agresivo con un patrón distinto.
Antes de pensar en empujar, el robot tiene que NO caer del dohyo. Los dos sensores de color al frente leen el piso constantemente:
color_left = ColorSensor(Port.E)
color_right = ColorSensor(Port.F)
# El dohyo es negro, el borde es blanco
EDGE_THRESHOLD = 50 # >50 = blanco = borde
def at_edge():
"""Devuelve qué borde detectado: 'left', 'right', 'both', None."""
l = color_left.reflection() > EDGE_THRESHOLD
r = color_right.reflection() > EDGE_THRESHOLD
if l and r:
return 'both'
elif l:
return 'left'
elif r:
return 'right'
return None
Y la rutina de evasión cuando se detecta el borde interrumpe TODO:
def avoid_edge(edge):
"""Reacción inmediata al detectar borde. PRIORIDAD ABSOLUTA."""
drive.stop()
if edge == 'both':
# Borde frontal directo → retroceder y girar
drive.straight(-200)
drive.turn(180)
elif edge == 'left':
# Borde a la izquierda → retroceder y girar a la derecha
drive.straight(-100)
drive.turn(45)
elif edge == 'right':
drive.straight(-100)
drive.turn(-45)
# Después de evadir, volver a buscar al oponente
El sensor frontal ultrasónico te dice si hay algo enfrente. Los sensores laterales te dicen si está a un costado.
us_front = UltrasonicSensor(Port.D)
def opponent_distance():
"""Distancia al oponente enfrente, en mm. None si no detectado."""
d = us_front.distance()
if d > 1500:
return None # nadie a la vista
return d
def opponent_direction():
"""Devuelve 'front', 'left', 'right', None."""
front = us_front.distance() < 800
# Suponiendo que tenemos sensores IR a los lados
left = ir_left.value() > IR_THRESHOLD
right = ir_right.value() > IR_THRESHOLD
if front:
return 'front'
elif left and not right:
return 'left'
elif right and not left:
return 'right'
return None
SEARCH_SPEED = 200
ATTACK_SPEED = 800 # MÁXIMA velocidad cuando hay contacto inminente
DELAY_START = 5000 # ms
def main():
# Delay inicial obligatorio
sw = StopWatch()
sw.reset()
while sw.time() < DELAY_START:
wait(50)
# Combate
while True:
# PRIORIDAD 1: edge detection
edge = at_edge()
if edge:
avoid_edge(edge)
continue
# PRIORIDAD 2: si hay oponente detectado, atacar
direction = opponent_direction()
if direction == 'front':
# Bull rush
drive.drive(ATTACK_SPEED, 0)
elif direction == 'left':
drive.drive(SEARCH_SPEED, -200) # girar hacia él
elif direction == 'right':
drive.drive(SEARCH_SPEED, 200)
else:
# PRIORIDAD 3: buscar al oponente girando en el lugar
drive.drive(0, 150) # rotar
wait(20)
def main_defensive():
sw = StopWatch()
sw.reset()
while sw.time() < DELAY_START:
wait(50)
while True:
edge = at_edge()
if edge:
avoid_edge(edge)
continue
direction = opponent_direction()
distance = opponent_distance()
if direction == 'front' and distance and distance < 200:
# Solo atacar cuando está MUY cerca y enfrente
drive.drive(ATTACK_SPEED, 0)
elif direction in ['left', 'right']:
# Encararlo pero sin avanzar
turn_rate = 200 if direction == 'right' else -200
drive.drive(0, turn_rate)
else:
# Quedarse en el centro buscando
drive.drive(0, 100)
wait(20)
Cuando detectás al oponente embistiendo de frente, moverte lateralmente en el último momento. Si el oponente va a 800 mm/s y vos te corrés 30 cm en 0.3 seg, le esquivás y queda con la inercia mirando hacia el borde.
def dodge_left():
"""Esquive lateral izquierdo de un ataque frontal."""
drive.use_gyro(False)
left_motor.run(-800)
right_motor.run(800) # rotar 90° rápido a la izquierda
wait(150)
drive.straight(150) # avanzar lateralmente
drive.turn(90) # reorientar para contraatacar al oponente que pasó de largo
Engañar al oponente acercándose y desviando en el último momento para tomarlo por el costado, donde su pala no protege.
Una vez que tu pala está debajo del oponente, NO PARES de empujar hasta que cruce el borde del dohyo. Mantener drive.drive(MAX_SPEED, 0) hasta detectar que el oponente desapareció (cayó) o que vos estás llegando al borde.
def push_until_win_or_edge():
while True:
edge = at_edge()
if edge:
# CUIDADO: sos vos el que está por caer
avoid_edge(edge)
return
if us_front.distance() > 500:
# El oponente desapareció = lo empujamos fuera
drive.stop()
hub.light.on(Color.GREEN)
return
# Seguir empujando
drive.drive(MAX_SPEED, 0)
wait(20)
Lo que más importa en sumo después del código es la mecánica:
| Síntoma | Causa | Solución |
|---|---|---|
| Cae del dohyo en los primeros segundos | No hay edge detection prioritaria | avoid_edge() antes que cualquier otra lógica |
| Detecta al oponente pero no lo alcanza | Velocidad de búsqueda baja | Subir SEARCH_SPEED |
| Empuja pero el oponente lo levanta | Pala no llega al ras | Rebajar pala mecánicamente |
| Va al centro y se queda esperando | No tiene patrón de búsqueda | Agregar rotación continua cuando no hay oponente detectado |
| Pierde contra robots laterales | Sin sensores laterales | Agregar IR proximity a 30-45° del frente |
| Se atasca empujando contra una pala enemiga | Sin lógica de retroceso | Detectar stall: si el robot no avanza por >500ms, retroceder y reintentar otro ángulo |