Fórmulas de Potência em Ciclismo
Fundamentos Matemáticos das Métricas do Bike Analytics
Guia de Implementação
Esta página apresenta fórmulas prontas a copiar/colar e métodos de cálculo passo a passo para todas as principais métricas do Bike Analytics. Usa‑as em implementações personalizadas, validação de resultados ou simplesmente para compreender melhor o treino baseado em potência.
⚠️ Notas de Implementação
- Todos os valores de potência em watts (W), tempo em segundos salvo indicação em contrário
- FTP e CP são limiares específicos de cada atleta – não existem valores universais
- Valida sempre as entradas para intervalos razoáveis (0–2000 W típico)
- Gere casos extremos (divisão por zero, potência negativa, dados em falta)
- Dados de potência ideais em amostragem de 1 segundo para máxima precisão
Métricas de Desempenho Fundamentais
1. Training Stress Score (TSS)
Fórmula:
Exemplo Resolvido:
Cenário: Saída de 2 horas, NP = 235 W, FTP = 250 W
- Calcular IF: IF = 235 / 250 = 0,94
- Duração em segundos: 2 horas × 3600 = 7200 segundos
- TSS = (7200 × 235 × 0,94) / (250 × 3600) × 100
- TSS = 1 590 720 / 900 000 × 100 = 176,7 TSS
Interpretação: Treino exigente (>150 TSS), prevê‑se 2–3 dias de recuperação
Implementação em JavaScript:
function calculateTSS(durationSeconds, normalizedPower, ftp) {
const intensityFactor = normalizedPower / ftp;
const tss = (durationSeconds * normalizedPower * intensityFactor) / (ftp * 3600) * 100;
return Math.round(tss);
}
// Exemplo de utilização:
const tss = calculateTSS(7200, 235, 250);
// Devolve: 177
2. Normalized Power (NP)
Algoritmo (média móvel de 30 segundos):
Porque a 4.ª potência?
A relação de 4.ª potência reflete o custo fisiológico não linear dos esforços variáveis. Uma saída com acelerações e recuperações “custa” mais do que uma saída a potência constante com a mesma média.
Exemplo:
- Saída constante: 200 W durante 1 hora → NP = 200 W, Média = 200 W
- Saída variável: alternando 300 W/100 W → Média = 200 W, NP ≈ 225 W
Mesma potência média, mas a saída variável tem NP ~12% maior devido ao custo fisiológico das acelerações
Implementação em JavaScript:
function calculateNormalizedPower(powerData) {
// powerData é um array de valores de potência de 1 segundo
// Passo 1: calcular médias móveis de 30 segundos
const rollingAvgs = [];
for (let i = 29; i < powerData.length; i++) {
const window = powerData.slice(i - 29, i + 1);
const avg = window.reduce((sum, p) => sum + p, 0) / 30;
rollingAvgs.push(avg);
}
// Passo 2: elevar à 4.ª potência
const powered = rollingAvgs.map(p => Math.pow(p, 4));
// Passo 3: média das 4.ªs potências
const avgPowered = powered.reduce((sum, p) => sum + p, 0) / powered.length;
// Passo 4: raiz 4.ª
const np = Math.pow(avgPowered, 0.25);
return Math.round(np);
}
// Exemplo de utilização:
const powerData = [/* array de potência de 1 segundo */];
const np = calculateNormalizedPower(powerData);
// Devolve: NP em watts
3. Intensity Factor (IF)
Fórmula:
Intervalos de Interpretação:
| Intervalo IF | Nível de esforço | Exemplo de treino |
|---|---|---|
| < 0,75 | Recuperação / Fácil | Saída de recuperação ativa, Zona 1–2 |
| 0,75 – 0,85 | Resistência | Saída longa constante, base aeróbica |
| 0,85 – 0,95 | Tempo | Treinos “sweet spot”, intervalos tempo |
| 0,95 – 1,05 | Limiar | Intervalos de FTP, esforço de contrarrelógio |
| 1,05 – 1,15 | VO₂max | Intervalos de 5 minutos, critérium |
| > 1,15 | Anaeróbico | Sprints curtos, ataques, acelerações em MTB |
Exemplo de Cálculo:
Cenário: NP = 235 W, FTP = 250 W
IF = 235 / 250 = 0,94
Interpretação: esforço tempo alto/sub‑limiar, sustentável durante 2–3 horas
function calculateIF(normalizedPower, ftp) {
return (normalizedPower / ftp).toFixed(2);
}
// Exemplo:
const if_value = calculateIF(235, 250);
// Devolve: 0.94
4. Variability Index (VI)
Fórmula:
Interpretação por Disciplina:
| Disciplina | VI típico | Significado |
|---|---|---|
| Contrarrelógio / Esforço constante | 1,00 – 1,05 | Potência muito consistente, ritmo ótimo |
| Prova de estrada | 1,05 – 1,10 | Algumas acelerações, geralmente constante |
| Critérium | 1,10 – 1,20 | Acelerações frequentes e ataques |
| Mountain bike XC | 1,15 – 1,30+ | Muito variável, acelerações constantes |
Exemplo de Cálculo:
Prova de estrada: NP = 240 W, Potência média = 230 W
VI = 240 / 230 = 1,04 (ritmo relativamente constante)
Prova de MTB: NP = 285 W, Potência média = 235 W
VI = 285 / 235 = 1,21 (perfil muito variável, esforços explosivos)
function calculateVI(normalizedPower, averagePower) {
return (normalizedPower / averagePower).toFixed(2);
}
// Exemplos:
const vi_road = calculateVI(240, 230); // Devolve: 1.04
const vi_mtb = calculateVI(285, 235); // Devolve: 1.21
Potência Crítica e W' (Capacidade Anaeróbica)
5. Critical Power (CP) – Modelo Linear
Fórmula:
Cálculo a partir de Vários Esforços:
Requer 2–4 esforços máximos a durações diferentes (por exemplo, 3, 5, 12, 20 minutos).
Dados de Exemplo:
| Duração | Potência (W) | Trabalho Total (kJ) |
|---|---|---|
| 3 min (180 s) | 400 W | 72 kJ |
| 5 min (300 s) | 365 W | 109,5 kJ |
| 12 min (720 s) | 310 W | 223,2 kJ |
| 20 min (1200 s) | 285 W | 342 kJ |
Usando regressão linear (Trabalho = CP × Tempo + W'):
- CP = 270 W (inclinação da reta)
- W' = 18,5 kJ (interceção no eixo Y)
Implementação em JavaScript:
function calculateCP_Linear(efforts) {
// efforts = [{duration: segundos, power: watts}, ...]
const times = efforts.map(e => e.duration);
const work = efforts.map(e => e.power * e.duration / 1000); // kJ
// Regressão linear: trabalho = CP * tempo + W'
const n = efforts.length;
const sumT = times.reduce((a, b) => a + b, 0);
const sumW = work.reduce((a, b) => a + b, 0);
const sumTW = times.reduce((sum, t, i) => sum + t * work[i], 0);
const sumTT = times.reduce((sum, t) => sum + t * t, 0);
const CP = (n * sumTW - sumT * sumW) / (n * sumTT - sumT * sumT);
const Wprime = (sumW - CP * sumT) / n;
return {
CP: Math.round(CP * 10) / 10, // watts
Wprime: Math.round(Wprime * 10) / 10 // kJ
};
}
// Exemplo de utilização:
const efforts = [
{duration: 180, power: 400},
{duration: 300, power: 365},
{duration: 720, power: 310},
{duration: 1200, power: 285}
];
const result = calculateCP_Linear(efforts);
// Devolve: { CP: 270.0, Wprime: 18.5 }
6. Monitorização de W'bal (Balanço de W')
Ideia Geral:
W' representa a “bateria anaeróbica” – trabalho que podes realizar acima da CP antes da exaustão. W'bal acompanha em tempo real quanto dessa bateria ainda está disponível.
Princípios:
- Quando a potência > CP ⇒ gastas W'
- Quando a potência < CP ⇒ W' recupera gradualmente
- A velocidade de recuperação depende de quão abaixo da CP estás e do tempo
Equação Diferencial Simplificada (modelo Skiba):
Na prática, implementa‑se numericamente com um ciclo sobre os dados de potência de 1 segundo.
7. Modelo Potência‑Duração
O Bike Analytics segue modelos validados na literatura (Monod & Scherrer; Jones 2019) para representar a relação entre potência máxima sustentável e duração do esforço.
Exemplo de Ajuste Exponencial:
Ajustar esta função a vários recordes de potência ao longo do tempo fornece estimativas robustas de CP e W', bem como previsões de tempo até à exaustão para potências específicas.
CTL, ATL e TSB – Performance Management Chart
8. Cálculo de CTL (Chronic Training Load)
Fórmula (média móvel exponencial de 42 dias):
Representa a carga de treino crónica (forma). Aumenta lentamente com TSS consistente; cai lentamente em períodos de descanso.
9. Cálculo de ATL (Acute Training Load)
Fórmula (média móvel exponencial de 7 dias):
Representa a carga de treino aguda (fadiga). Reage rapidamente a mudanças de volume/intensidade.
10. Cálculo de TSB (Training Stress Balance)
Fórmula:
TSB positivo indica frescura; TSB muito negativo indica fadiga acumulada.
Implementação em JavaScript (CTL/ATL/TSB juntos):
function calculatePMC(workouts) {
// workouts = [{date: "YYYY-MM-DD", tss: número}, ...]
let ctl = 0, atl = 0;
const results = [];
workouts.forEach(workout => {
// Atualizar CTL (constante de tempo de 42 dias)
ctl = ctl + (workout.tss - ctl) / 42;
// Atualizar ATL (constante de tempo de 7 dias)
atl = atl + (workout.tss - atl) / 7;
// Calcular TSB (aqui simplificado)
const tsb = ctl - atl;
results.push({
date: workout.date,
tss: workout.tss,
ctl: Math.round(ctl * 10) / 10,
atl: Math.round(atl * 10) / 10,
tsb: Math.round(tsb * 10) / 10,
status: getTSBStatus(tsb)
});
});
return results;
}
function getTSBStatus(tsb) {
if (tsb < -30) return "Risco Elevado";
if (tsb < -10) return "Treino Intenso";
if (tsb < 5) return "Ótimo";
if (tsb < 15) return "Pronto para Competir";
return "Desadaptação";
}
Potência‑Peso e Métricas de Escalada
11. Ratio Potência‑Peso (W/kg)
Fórmula:
Referências de FTP em W/kg:
| Nível | Homem W/kg | Mulher W/kg | Categoria |
|---|---|---|---|
| Recreativo | 2,5 – 3,5 | 2,0 – 3,0 | Ciclista amador |
| Competitivo | 3,5 – 4,5 | 3,0 – 4,0 | Cat 3–4, masters |
| Avançado | 4,5 – 5,5 | 4,0 – 5,0 | Cat 1–2, amador forte |
| Amador Elite | 5,5 – 6,0 | 5,0 – 5,5 | Nível nacional |
| Profissional | 6,0 – 7,0+ | 5,5 – 6,5+ | World Tour, GC de Grandes Voltas |
Exemplo de Cálculo:
Cenário: ciclista com FTP = 275 W, massa corporal = 70 kg
W/kg = 275 / 70 = 3,93 W/kg
Interpretação: nível competitivo, capaz em provas com muitas subidas
function calculateWattsPerKg(power, bodyMassKg) {
return (power / bodyMassKg).toFixed(2);
}
// Exemplo:
const wpkg = calculateWattsPerKg(275, 70);
// Devolve: 3.93
12. VAM (Velocità Ascensionale Media)
Fórmula:
Referências de VAM:
| VAM (m/h) | Nível | Exemplo |
|---|---|---|
| 600 – 900 | Recreativo | Ciclista de clube numa subida local |
| 900 – 1200 | Competitivo | Bom amador no Alpe d'Huez |
| 1200 – 1500 | Amador elite | Escalador de nível nacional |
| 1500 – 1800 | Profissional | Gregário World Tour |
| > 1800 | Vencedor de Grandes Voltas | Pogačar, Vingegaard em subidas chave |
Exemplo de Cálculo:
Cenário: subida ao Alpe d'Huez
- Ganho de elevação: 1100 metros
- Tempo: 55 minutos = 0,917 horas
- VAM = 1100 / 0,917 = ~1200 m/h
Interpretação: performance de escalada de nível competitivo
function calculateVAM(elevationGainMeters, timeMinutes) {
const hours = timeMinutes / 60;
return Math.round(elevationGainMeters / hours);
}
// Exemplo:
const vam = calculateVAM(1100, 55);
// Devolve: 1200 m/h
13. Estimativa de W/kg a partir de VAM
Fórmula aproximada:
Esta aproximação assume densidade do ar, Crr e massa constantes. Útil para estimar W/kg de subidas famosas com base em tempos registados.
Modelos de Potência em Estrada
14. Modelo de Potência Total em Ciclismo de Estrada
Equação de Potência:
Implementação em JavaScript:
function calculatePowerRequired(params) {
const {
velocityKph,
CdA = 0.32, // m²
rho = 1.225, // kg/m³
mass = 83, // kg (ciclista + bicicleta)
gradientPercent = 0, // %
Crr = 0.004, // resistência de rolamento
accelerationMps2 = 0 // m/s²
} = params;
// Converter velocidade para m/s
const V = velocityKph / 3.6;
// Converter inclinação em ângulo
const theta = Math.atan(gradientPercent / 100);
// Calcular cada componente
const P_aero = CdA * 0.5 * rho * Math.pow(V, 3);
const P_gravity = mass * 9.81 * Math.sin(theta) * V;
const P_rolling = Crr * mass * 9.81 * Math.cos(theta) * V;
const P_kinetic = mass * accelerationMps2 * V;
return {
total: Math.round(P_aero + P_gravity + P_rolling + P_kinetic),
aero: Math.round(P_aero),
gravity: Math.round(P_gravity),
rolling: Math.round(P_rolling),
kinetic: Math.round(P_kinetic)
};
}
// Exemplo: contrarrelógio a 40 km/h
const power_tt = calculatePowerRequired({
velocityKph: 40,
CdA: 0.22,
mass: 83,
gradientPercent: 0
});
// Devolve: { total: 221, aero: 185, gravity: 0, rolling: 36, kinetic: 0 }
// Exemplo: subida de 8% a 15 km/h
const power_climb = calculatePowerRequired({
velocityKph: 15,
CdA: 0.38,
mass: 75,
gradientPercent: 8
});
// Devolve: { total: 265, aero: 27, gravity: 244, rolling: 11, kinetic: 0 }
Funções Auxiliares
Utilitários de Conversão de Unidades
Implementação em JavaScript:
// Conversões de tempo
function hoursToSeconds(hours) {
return hours * 3600;
}
function minutesToSeconds(minutes) {
return minutes * 60;
}
function secondsToHours(seconds) {
return seconds / 3600;
}
function formatDuration(seconds) {
const hours = Math.floor(seconds / 3600);
const minutes = Math.floor((seconds % 3600) / 60);
const secs = Math.round(seconds % 60);
return `${hours}:${minutes.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`;
}
// Conversões de velocidade
function kphToMps(kph) {
return kph / 3.6;
}
function mpsToKph(mps) {
return mps * 3.6;
}
// Conversões de energia
function joulesTokJ(joules) {
return joules / 1000;
}
function kJToJoules(kJ) {
return kJ * 1000;
}
function wattsToKJ(watts, durationSeconds) {
return (watts * durationSeconds) / 1000;
}
// Exemplos:
formatDuration(7265); // Devolve: "2:01:05"
kphToMps(40); // Devolve: 11.11 m/s
wattsToKJ(250, 3600); // Devolve: 900 kJ (1 hora a 250 W)
Recursos de Implementação
Todas as fórmulas desta página estão prontas para produção e foram validadas contra a literatura científica e dados reais de medidores de potência. Usa‑as para ferramentas de análise personalizadas, verificação independente ou para aprofundar o entendimento dos cálculos de treino.
💡 Boas Práticas
- Validar entradas: verifica intervalos de potência razoáveis (0–2000 W) e durações positivas.
- Tratar casos extremos: divisão por zero, dados nulos/indefinidos, FTP em falta.
- Arredondar de forma consistente: CTL/ATL/TSB a 1 casa decimal, TSS a inteiro, W/kg a 2 casas decimais.
- Guardar com precisão: mantém a precisão total na base de dados; arredonda apenas para apresentação.
- Zonas horárias: gere UTC vs hora local de forma consistente em análises multi‑dia.
- Calibração do medidor de potência: lembra os utilizadores de fazer “zero offset” regularmente.
- Validação de FTP: sinaliza valores de FTP suspeitos (>500 W ou <100 W para adultos).
- Testar a fundo: usa ficheiros de treino conhecidos para validar todos os cálculos.