Este programa prueba el comportamiento de carga y empuje de tu robot. Tú posicionas el robot, presionas A para armarlo, y espera a que un balón entre en el rango — luego carga a potencia máxima, hace contacto y avanza durante una duración fija antes de retroceder.
Calcula las estadísticas, luego espera un balón dentro de 70 cm. Cuando se detecta, pausa brevemente y ejecuta la secuencia de carga.
Avanza por 0 → 60 → 120 → 180 segundos de tiempo de partido simulado. Valores más altos aplican un decaimiento de endurance más fuerte a cada carga.
let SPEED = 0
let ENDURANCE = 0
let TURNING = 0
let POWER = 0
TURNING — cuenta hacia el total — pero la prueba de power no lo usa. Solo SPEED, ENDURANCE y POWER afectan lo que sucede en el campo.
function computeStats() {}
function waitForBall() {}
function chargeAndFollowThrough() {}
input.onButtonPressed(Button.A, function () {
computeStats()
waitForBall()
basic.pause(500)
chargeAndFollowThrough()
})
computeStats() convierte tus estadísticas en valores de motor funcionales. Luego waitForBall() mantiene el robot en su lugar hasta que algo entra en el rango. Después de una pausa de 500 ms (para que puedas alejarte), chargeAndFollowThrough() ejecuta la secuencia de carga completa.
let simulatedMatchTime = 0
input.onButtonPressed(Button.B, function () {
if (simulatedMatchTime == 0) {
simulatedMatchTime = 60
basic.showString("60s")
} else if (simulatedMatchTime == 60) {
simulatedMatchTime = 120
basic.showString("120s")
} else if (simulatedMatchTime == 120) {
simulatedMatchTime = 180
basic.showString("180s")
} else {
simulatedMatchTime = 0
basic.showString("0s")
}
basic.pause(500)
basic.clearScreen()
})
currentPower como effectiveCharge. Presiona antes de armar — no tiene efecto en medio de la carga.
let SPEED = 0
let ENDURANCE = 0
let POWER = 0
let simulatedMatchTime = 0
let currentPower = 0
let chargePower = 0
let FOLLOW_THROUGH_MS = 0
let DECAY_RATE = 0
let endurancePercent = 0
let enduranceFactor = 0
function computeStats () {
currentPower = SPEED * 100 / 20
chargePower = Math.min(POWER * POWER, 100)
FOLLOW_THROUGH_MS = 120 + POWER * 35
DECAY_RATE = 20 - ENDURANCE
endurancePercent = 100 - DECAY_RATE * simulatedMatchTime / 40
endurancePercent = Math.max(endurancePercent, 20)
enduranceFactor = endurancePercent / 100
currentPower = Math.round(currentPower * enduranceFactor)
currentPower = Math.max(currentPower, 15)
}
currentPower es tu velocidad crucero — usada durante el seguimiento. chargePower es POWER al cuadrado, limitado en 100. FOLLOW_THROUGH_MS es 120 + (POWER × 35) ms. Luego el decaimiento de endurance reduce todos los valores de potencia según qué tan avanzado esté el partido. Ambos tienen un piso mínimo de 15%.
let dist = 0
let BALL_DETECT_CM = 0
function waitForBall () {
basic.showLeds(`
. . . . .
. . . . .
. . # . .
. . . . .
. . . . .
`)
while (true) {
dist = CutebotPro.ultrasonic(SonarUnit.Centimeters)
if (dist > 0 && dist < BALL_DETECT_CM) {
basic.showLeds(`
. . . . .
. # # # .
# . . . #
# . . . #
. # # # .
`)
basic.pause(300)
basic.clearScreen()
return
}
basic.pause(50)
}
}
dist < BALL_DETECT_CM, por defecto 70 cm), muestra el círculo y regresa — devolviendo el control al Botón A, que luego espera 500 ms y ejecuta la carga. La verificación dist > 0 filtra las lecturas de ruido de 0 cm que obtienes cuando el sensor está demasiado cerca para obtener un ping limpio.
let chargeHitBorder = false
let chargePower = 0
let enduranceFactor = 0
let effectiveCharge = 0
let borderSide = 0
let dist = 0
let contactCount = 0
let contactMade = false
let currentPower = 0
let followStart = 0
let FOLLOW_THROUGH_MS = 0
let CHARGE_CHECK_MS = 0
function checkBorder() { return 0 }
function chargeAndFollowThrough () {
chargeHitBorder = false
effectiveCharge = Math.round(chargePower * enduranceFactor)
effectiveCharge = Math.max(effectiveCharge, 15)
basic.showString("C")
CutebotPro.pwmCruiseControl(effectiveCharge, effectiveCharge)
while (!(chargeHitBorder) && !(contactMade)) {
borderSide = checkBorder()
if (borderSide > 0) {
CutebotPro.stopImmediately(CutebotProMotors.ALL)
chargeHitBorder = true
basic.showString("B")
break
}
dist = CutebotPro.ultrasonic(SonarUnit.Centimeters)
if (dist <= 5) {
contactCount += 1
if (contactCount >= 2) {
contactMade = true
}
} else {
contactCount = 0
}
basic.pause(CHARGE_CHECK_MS)
}
CutebotPro.stopImmediately(CutebotProMotors.ALL)
if (!(chargeHitBorder)) {
basic.showString("F")
CutebotPro.pwmCruiseControl(currentPower, currentPower)
followStart = input.runningTime()
while (input.runningTime() - followStart < FOLLOW_THROUGH_MS) {
borderSide = checkBorder()
if (borderSide > 0) {
CutebotPro.stopImmediately(CutebotProMotors.ALL)
basic.showString("B")
break
}
basic.pause(CHARGE_CHECK_MS)
}
CutebotPro.stopImmediately(CutebotProMotors.ALL)
basic.showString("R")
CutebotPro.pwmCruiseControl(-1 * currentPower, -1 * currentPower)
basic.pause(300)
CutebotPro.stopImmediately(CutebotProMotors.ALL)
}
basic.showString("OK")
basic.pause(500)
basic.clearScreen()
}
effectiveCharge (chargePower reducido por endurance) y verifica los sensores cada 20 ms. Sale al alcanzar el borde O con dos lecturas de sonar consecutivas ≤ 5 cm. Si se hizo contacto y no se alcanzó el borde, se ejecuta la fase de seguimiento — empujando a currentPower durante FOLLOW_THROUGH_MS. Luego retrocede 300 ms y muestra OK.
let sensor1State = 0
let sensor2State = 0
let sensor3State = 0
let sensor4State = 0
let BORDER_THRESHOLD = 0
function checkBorder () {
CutebotPro.trackbitStateValue()
sensor1State = CutebotPro.trackbitgetGray(TrackbitChannel.One)
sensor2State = CutebotPro.trackbitgetGray(TrackbitChannel.Two)
sensor3State = CutebotPro.trackbitgetGray(TrackbitChannel.Three)
sensor4State = CutebotPro.trackbitgetGray(TrackbitChannel.Four)
if (sensor1State > BORDER_THRESHOLD) {
return 1
} else if (sensor4State > BORDER_THRESHOLD) {
return 2
} else if (sensor2State > BORDER_THRESHOLD || sensor3State > BORDER_THRESHOLD) {
return 3
}
return 0
}
El sensor ultrasónico puede dar una lectura de 0 cuando está demasiado cerca para obtener un ping limpio — pero un solo 0 puede ser ruido. El programa confirma el contacto con dos lecturas bajas consecutivas.
⚡ POWER es cuadrático. Pasar de POWER=5 a POWER=6 agrega 11 puntos porcentuales de potencia de carga (36−25=11). Pasar de POWER=9 a POWER=10 agrega 19 puntos (100−81=19). Cada punto de POWER que agregas vale más que el anterior. Invertir fuertemente en POWER tiene retornos crecientes marcados — pero consume presupuesto de estadísticas que no puede ir a SPEED o ENDURANCE.
Una pregunta abierta: la fórmula anterior de follow-through hacía que el robot rebasara el balón con POWER alto, fijando el punto óptimo práctico en P9. La nueva fórmula, 120 + (POWER × 35), produce follow-throughs mucho más cortos en todo el rango (P9 → 435 ms, P10 → 470 ms). Si el rebase sigue siendo un problema en la parte alta — o si el punto óptimo se movió — es una pregunta para la Temporada 1. Compara tus distancias de tiro en cada valor de POWER y deja que los datos del partido decidan.
| Tiempo del Partido | Endurance % | Carga Efectiva (mín 15%) |
|---|