Launch control sem wheelie protection é incompleto. Aqui está como o Pistonix faz.
Em moto V-twin de torque alto, soltar o 2-step direto na 1ª em WOT levanta a roda da frente. Wheelie protection é parte do launch control, não follow-up. Landamos o controlador no firmware fechado e tem demo interativo no /playground.
Tem uma diferença essencial entre carro e moto quando se fala de launch control. No carro, o pior que acontece é o motor cortar e o ET piorar. Na moto V-twin de torque alto — Twin Cam 96+, Milwaukee-Eight, qualquer custom S&S 124+ — soltar o 2-step direto na 1ª marcha em WOT levanta a roda da frente. Sem proteção, o launch control que deveria melhorar o ET vira liability.
Por isso, no Pistonix, launch control e wheelie protection são a mesma feature. Não tem launch control “puro” no roadmap esperando uma fase posterior pra ganhar proteção. Já saem juntos no firmware.
O que ficou pronto
Esse round landou três coisas relacionadas, todas no firmware fechado:
1. WheelieProtectionConfig no LaunchController. Configuração
declarativa: pitch threshold (onde a intervenção começa), pitch max
(onde está saturada), front/rear wheel-speed ratio threshold (proxy
quando IMU não dá pra confiar sozinho), e modo de resposta — Off,
SparkRetard, FuelCut ou Progressive (rampa retard até 80% de
severidade, depois corta). Constantes prontas: STREET (intervenção
cedo, suave), RACE (drag aceita mais frente no ar antes de cortar),
OFF (validação / drag dedicado com frente reforçada).
2. Severidade vem de duas fontes, OR’d. Pitch IMU é o sinal direto
(“frente está subindo”). Razão front/rear wheel-speed é o sinal
indireto (“frente parou de girar enquanto trás continua” = frente no
ar OU patinando). O controlador usa max(severity_pitch, severity_ratio)
— qualquer um dos dois aciona. Em standstill / hookup inicial (abaixo
de min_speed_for_ratio_kph), o ratio check é suprimido pra evitar
falso positivo enquanto a moto ainda nem ganhou velocidade.
3. Backwards-compat preservado. LaunchController.update() legado
virou wrapper de update_with_dynamics(.., RideDynamics::SAFE). Tudo
que está no firmware hoje sem sensores de IMU/wheel-speed (Phase 0-3
hardware) vê severidade zero por construção — proteção dorme até ter
dado pra agir.
let mut lc = LaunchController::new(LaunchControlConfig::TWIN_CAM_STREET);
let dynamics = RideDynamics {
pitch_deg: 8.0, // 43% do range street (5..12°)
front_wheel_speed_kph: 30.0,
rear_wheel_speed_kph: 30.0,
};
let out = lc.update_with_dynamics(95.0, 4500.0, 1, false, false, dynamics);
match out.wheelie {
WheelieIntervention::SparkRetardDeg(deg) => /* ~8° retard */,
WheelieIntervention::FuelCut => /* severidade saturada */,
WheelieIntervention::None => /* dormente */,
}
15 testes cobrem a controladora — incluindo “legado nunca dispara”, “abaixo do threshold inerte”, “ratio suprimido em standstill”, “Off nunca age”, “safety override silencia” e os caminhos de ramping + escalada pra fuel cut.
Por que essa lógica e não “spark cut e pronto”
Spark cut binário é o jeito mais comum no aftermarket porque é fácil — quando pitch passa de N°, mata a faísca. Mas tem dois problemas:
- Drivetrain shock. Cortar tudo de uma vez sob WOT engatado faz o trem de força balançar. Em V-twin com correia primária, isso aparece em vibração na transmissão e na corrente final.
- Wheelie controlado existe. Drag racer que sabe o que faz quer a moto subindo num ângulo controlado, não na rua de cabeça pra baixo. Cortar a 5° estraga o launch.
A solução é progressivo: spark retard rampa de 0° até spark_max_deg
linearmente com a severidade até 80%. Aí, na faixa final 80..100%, vai
pra fuel cut. Spark retard reduz potência sem cortar abruptamente — a
moto desacelera o ângulo, piloto sente o pushback no chassis, ajusta
o body weight. Fuel cut só entra quando spark retard sozinho não
estabiliza.
Esses números (5°, 12°, 80%) não são chute: vieram de leitura de papers + comparação com sistemas que existem (Ducati DTC, BMW DBC). São ponto de partida; vão ser refinados em pista quando hardware Fase 4 estiver instalado e a gente tiver dados reais.
Cuidado que falta — IMU + wheel-speed
A controladora está pronta. Mas não vai dispor sozinha sem os sensores físicos. Hardware Fase 0 (Proteus F7 standalone) não tem IMU embarcado nem entrada pra wheel-speed sensors. Phase 4 é onde entram:
- IMU 6-axis sobre o chassi — leitura de pitch real-time.
- Sensores de roda dianteira/traseira — proxy independente do IMU (defesa em profundidade: se IMU mente, ratio acusa).
Até lá, launch control sai com disclaimer track-only em todos
os builds race. Não é um produto de rua até a proteção estar
completa. O /compatibilidade page reflete isso, e dentro do
/playground?build=race tem um banner laranja explicando o boundary.
Demo interativo
E ja que landamos a controladora, ela está exposta no /playground.
Escolhe build = “race”, clica em Trigger wheelie test — uma
animação de 3 segundos faz o pitch IMU subir de 0° até 18° e voltar.
A barra de severidade enche em verde → laranja → vermelho. O texto
de intervenção muda de — pra Spark retard 7.3° rampando, e
quando satura, Fuel cut.
Quem quiser exportar o trace via “Download CSV” leva pra casa três
colunas novas: wheelie_severity, wheelie_spark_retard_deg,
wheelie_fuel_cut. É a mesma estrutura de dados que a ECU vai
mandar pro datalogger, só que computada no navegador.
Por que isso importa pra o produto
A constraint que motivou esse round veio do owner em conversa:
“Como é moto, o launch control não pode deixar empinar.” É o tipo
de detalhe que separa “produto que diz que é safety-conscious” de
“produto que prova”. Dentro do código, deixei um SAFETY block no
header de ecu-firmware/core/src/launch/mod.rs apontando pra memória
do projeto, pra qualquer pessoa que mexer no módulo no futuro entender
que wheelie protection não é opcional.
Confiança em moto é construída em milhares de pequenas decisões assim. Essa é uma delas, registrada no código onde precisa estar.