Formler for sykkelkraft
Det matematiske grunnlaget for Bike Analytics-måleverdier
Implementeringsguide
Denne siden inneholder formler og trinnvise beregningsmetoder for alle Bike Analytics-måleverdier. Bruk disse for egne implementeringer, verifisering eller dypere forståelse av kraftbasert trening.
⚠️ Implementeringsnotater
- Alle kraftverdier i watt (W), tid i sekunder med mindre annet er oppgitt
- FTP og CP er individspesifikke terskler – det finnes ingen universelle verdier
- Valider alltid inndata for rimelige områder (typisk 0–2000 W)
- Håndter kanttilfeller (divisjon med null, negativ kraft)
- Kraftdata krever 1-sekunds registreringsintervaller for nøyaktighet
Sentrale prestasjonsmål
1. Training Stress Score (TSS)
Formel:
Eksempel:
Scenario: 2-timers tur, NP = 235 W, FTP = 250 W
- Beregn IF: IF = 235 / 250 = 0,94
- Varighet i sekunder: 2 timer × 3600 = 7200 sekunder
- TSS = (7200 × 235 × 0,94) / (250 × 3600) × 100
- TSS = 1 590 720 / 900 000 × 100 = 176,7 TSS
Tolkning: Hard treningsøkt (>150 TSS), forvent 2–3 dagers restitusjon
JavaScript-implementering:
function calculateTSS(durationSeconds, normalizedPower, ftp) {
const intensityFactor = normalizedPower / ftp;
const tss = (durationSeconds * normalizedPower * intensityFactor) / (ftp * 3600) * 100;
return Math.round(tss);
}
// Eksempel:
const tss = calculateTSS(7200, 235, 250);
// Returnerer: 177
2. Normalized Power (NP)
Algoritme (30-sekunders glidende gjennomsnitt):
Hvorfor fjerde potens?
Forholdet i fjerde potens gjenspeiler den ikke-lineære fysiologiske belastningen av varierende innsats. En tur med rykk og restitusjon koster mer energi enn jevn kraft ved samme gjennomsnitt.
Eksempel:
- Jevn tur: 200 W i 1 time → NP = 200 W, Gjennomsnitt = 200 W
- Varierende tur: Veksler mellom 300 W / 100 W → Gjennomsnitt = 200 W, NP = 225 W
Samme gjennomsnittskraft, men den varierende turen har 12 % høyere NP på grunn av den fysiologiske kostnaden ved rykkene
JavaScript-implementering:
function calculateNormalizedPower(powerData) {
// powerData er en array med 1-sekunds kraftverdier
// Trinn 1: Beregn 30-sekunders glidende gjennomsnitt
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);
}
// Trinn 2: Opphøy i 4. potens
const powered = rollingAvgs.map(p => Math.pow(p, 4));
// Trinn 3: Gjennomsnitt av 4. potenser
const avgPowered = powered.reduce((sum, p) => sum + p, 0) / powered.length;
// Trinn 4: Ta fjerderoten
const np = Math.pow(avgPowered, 0.25);
return Math.round(np);
}
// Eksempel:
const powerData = [/* 1-sekunds kraft-array */];
const np = calculateNormalizedPower(powerData);
// Returnerer: NP i watt
3. Intensity Factor (IF)
Formel:
Tolkningsområder:
| IF-område | Innsatsnivå | Eksempel på økt |
|---|---|---|
| < 0,75 | Restitusjon / Rolig | Aktiv restitusjonstur, Sone 1–2 |
| 0,75 – 0,85 | Utholdenhet | Lang rolig tur, aerob base |
| 0,85 – 0,95 | Tempo | Sweet spot-trening, tempointervaller |
| 0,95 – 1,05 | Terskel | FTP-intervaller, tempoinnsats (TT) |
| 1,05 – 1,15 | VO₂max | 5-minutters intervaller, ritt (criterium) |
| > 1,15 | Anaerob | Korte sprinter, rykk, terrengsykkel-eksplosjoner |
Eksempel:
Scenario: NP = 235 W, FTP = 250 W
IF = 235 / 250 = 0,94
Tolkning: Høy tempo / sub-terskel innsats, holdbar i 2–3 timer
function calculateIF(normalizedPower, ftp) {
return (normalizedPower / ftp).toFixed(2);
}
// Eksempel:
const if_value = calculateIF(235, 250);
// Returnerer: 0.94
4. Variabilitetsindeks (VI)
Formel:
Tolkning etter disiplin:
| Disiplin | Typisk VI | Betydning |
|---|---|---|
| Landevei TT / Jevn innsats | 1,00 – 1,05 | Svært jevn kraft, optimal pacing |
| Landeveisritt | 1,05 – 1,10 | Noen rykk, generelt jevnt |
| Criterium (Gateritt) | 1,10 – 1,20 | Hyppige akselerasjoner og støt |
| Terrengsykkel XC | 1,15 – 1,30+ | Svært variert, konstante rykk |
Eksempel:
Landeveisritt: NP = 240 W, Gjennomsnitt = 230 W
VI = 240 / 230 = 1,04 (jevn pacing)
Terrengritt: NP = 285 W, Gjennomsnitt = 235 W
VI = 285 / 235 = 1,21 (veldig variert, eksplosiv innsats)
function calculateVI(normalizedPower, averagePower) {
return (normalizedPower / averagePower).toFixed(2);
}
// Eksempel:
const vi_road = calculateVI(240, 230); // Returnerer: 1.04
const vi_mtb = calculateVI(285, 235); // Returnerer: 1.21
Critical Power og W' (Anaerob kapasitet)
5. Critical Power (CP) – Lineær modell
Formel:
Beregning fra flere forsøk:
Krever 2–4 maksimale innsatser med ulik varighet (f.eks. 3, 5, 12, 20 minutter)
Eksempeldata:
| Varighet | Kraft (W) | Totalt arbeid (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 |
Bruk av lineær regresjon (Arbeid = CP × Tid + W'):
- CP = 270 W (stigningstall for regresjonslinjen)
- W' = 18,5 kJ (skjæringspunkt med y-aksen)
JavaScript-implementering:
function calculateCP_Linear(efforts) {
// efforts = [{duration: seconds, power: watts}, ...]
const times = efforts.map(e => e.duration);
const work = efforts.map(e => e.power * e.duration / 1000); // kJ
// Lineær regresjon: arbeid = CP * tid + 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, // watt
Wprime: Math.round(Wprime * 10) / 10 // kJ
};
}
// Eksempel:
const efforts = [
{duration: 180, power: 400},
{duration: 300, power: 365},
{duration: 720, power: 310},
{duration: 1200, power: 285}
];
const result = calculateCP_Linear(efforts);
// Returnerer: { CP: 270.0, Wprime: 18.5 }
6. W' Balance (W'bal) – Differensialligningsmodell
Formler:
W'exp(t) = ∫(P(t) - CP) dt
W'rec(t) = W' × (1 - e^(-t/τ))
og ΔCP = (CP - P(t))
Praktisk eksempel:
Syklists spesifikasjoner: CP = 270 W, W' = 18,5 kJ
Scenario 1 – Hardt rykk:
- Syklisten rykker til 400 W i 30 sekunder
- W'-forbruk: (400 - 270) × 30 = 3 900 J = 3,9 kJ
- Gjenværende W'bal: 18,5 - 3,9 = 14,6 kJ
Scenario 2 – Restitusjon:
- Etter rykket faller kraften til 200 W (70 W under CP) i 2 minutter
- ΔCP = 270 - 200 = 70 W
- τ = 546 × e^(-0,01 × 70) + 316 = 588 sekunder
- Restitusjon på 120 s: 18,5 × (1 - e^(-120/588)) = 3,5 kJ restituert
- Ny W'bal: 14,6 + 3,5 = 18,1 kJ
JavaScript-implementering:
function calculateWbalance(powerData, CP, Wprime) {
// powerData = array med {time: seconds, power: watts}
let wbal = Wprime * 1000; // Konverter til joule
const wbalHistory = [];
for (let i = 1; i < powerData.length; i++) {
const dt = powerData[i].time - powerData[i-1].time;
const power = powerData[i].power;
if (power > CP) {
// Forbruk over CP
const expenditure = (power - CP) * dt;
wbal -= expenditure;
} else {
// Restitusjon under CP
const deltaCP = CP - power;
const tau = 546 * Math.exp(-0.01 * deltaCP) + 316;
const recovery = (Wprime * 1000 - wbal) * (1 - Math.exp(-dt / tau));
wbal += recovery;
}
// Sikre at W'bal ikke overstiger maks eller blir negativ
wbal = Math.max(0, Math.min(wbal, Wprime * 1000));
wbalHistory.push({
time: powerData[i].time,
wbal: wbal / 1000, // kJ
percent: (wbal / (Wprime * 1000)) * 100
});
}
return wbalHistory;
}
// Eksempel:
const powerData = [
{time: 0, power: 200},
{time: 1, power: 210},
// ... resten av turen
];
const wbalHistory = calculateWbalance(powerData, 270, 18.5);
// Returnerer array med W'bal-verdier over tid
Performance Management Chart (PMC)
7. Beregning av CTL, ATL og TSB
Formler (Eksponensielt veide glidende gjennomsnitt):
Definisjoner:
- CTL (Chronic Training Load): 42-dagers eksponensielt vektet gjennomsnitt – representerer form/fitness
- ATL (Acute Training Load): 7-dagers eksponensielt vektet gjennomsnitt – representerer tretthet
- TSB (Training Stress Balance): Overskudd = Form - Tretthet
Eksempel (7-dagers treningsblokk):
| Dag | TSS | CTL | ATL | TSB | Status |
|---|---|---|---|---|---|
| Man | 100 | 75,0 | 80,0 | -5,0 | Trening |
| Tir | 50 | 74,4 | 75,7 | -1,3 | Restitusjon |
| Ons | 120 | 75,5 | 82,0 | -6,5 | Hard trening |
| Tor | 0 | 73,7 | 70,3 | +3,4 | Hviledag |
| Fre | 80 | 73,8 | 71,7 | +2,1 | Moderat |
| Lør | 150 | 75,6 | 82,9 | -7,3 | Langtur |
| Søn | 40 | 74,8 | 76,8 | -2,0 | Restitusjon |
Tolkning av TSB:
| TSB-område | Status | Handling |
|---|---|---|
| < -30 | Høy risiko | Fare for overtrening – reduser belastningen |
| -30 til -10 | Uke med hard trening | Bygger form, overvåk restitusjonen |
| -10 til +5 | Optimalt | Normal treningssone |
| +5 til +15 | Klar for ritt | Toppform – klar for konkurranse |
| > +25 | Formfall | Tap av kondisjon – øk belastningen |
JavaScript-implementering:
function calculatePMC(workouts) {
// workouts = [{date: "YYYY-MM-DD", tss: number}, ...]
let ctl = 0, atl = 0;
const results = [];
workouts.forEach(workout => {
// Oppdater CTL (42 dagers tidskonstant)
ctl = ctl + (workout.tss - ctl) / 42;
// Oppdater ATL (7 dagers tidskonstant)
atl = atl + (workout.tss - atl) / 7;
// Beregn TSB (gårdsdagens CTL - dagens ATL for tradisjonell beregning)
// For enkelhets skyld bruker vi gjeldende verdier her
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 "Høy risiko";
if (tsb < -10) return "Hard trening";
if (tsb < 5) return "Optimalt";
if (tsb < 15) return "Klar for ritt";
return "Formfall";
}
// Eksempel:
const workouts = [
{date: "2025-01-01", tss: 100},
{date: "2025-01-02", tss: 50},
{date: "2025-01-03", tss: 120},
// ... flere økter
];
const pmc = calculatePMC(workouts);
// Returnerer array med CTL, ATL, TSB for hver dag
Kraft-til-vekt og klatremåleverdier
8. Kraft-til-vekt-forhold (W/kg)
Formel:
Benchmarks for FTP W/kg:
| Nivå | Menn W/kg | Kvinner W/kg | Kategori |
|---|---|---|---|
| Mosjonist | 2,5 – 3,5 | 2,0 – 3,0 | Aktiv hobbysyklist |
| Konkurranserytter | 3,5 – 4,5 | 3,0 – 4,0 | Turritt, aktiv klasse |
| Avansert | 4,5 – 5,5 | 4,0 – 5,0 | Nasjonal elite, sterk amatør |
| Elite | 5,5 – 6,0 | 5,0 – 5,5 | Topp nasjonalt nivå |
| Profesjonell | 6,0 – 7,0+ | 5,5 – 6,5+ | World Tour, Grand Tour GC |
Eksempel:
Scenario: Syklist med FTP = 275 W, kroppsvekt = 70 kg
W/kg = 275 / 70 = 3,93 W/kg
Tolkning: Godt konkurransenivå, takler kuperte ritt bra
function calculateWattsPerKg(power, bodyMassKg) {
return (power / bodyMassKg).toFixed(2);
}
// Eksempel:
const wpkg = calculateWattsPerKg(275, 70);
// Returnerer: 3.93
9. VAM (Velocità Ascensionale Media)
Formel:
VAM-benchmarks:
| VAM (m/t) | Nivå | Eksempel |
|---|---|---|
| 600 – 900 | Mosjonist | Klubbrytter i lokal bakke |
| 900 – 1200 | Konkurranserytter | God amatør på Alpe d'Huez |
| 1200 – 1500 | Eliteamatør | Nasjonal toppklatrer |
| 1500 – 1800 | Profesjonell | World Tour hjelperytter |
| > 1800 | Grand Tour-vinder | Pogačar, Vingegaard i avgjørende bakker |
Eksempel:
Scenario: Alpe d'Huez-klatring
- Høydemeter: 1100 meter
- Tid: 55 minutter = 0,917 timer
- VAM = 1100 / 0,917 = 1200 m/t
Tolkning: Konkurransedyktig klatreprestasjon
function calculateVAM(elevationGainMeters, timeMinutes) {
const hours = timeMinutes / 60;
return Math.round(elevationGainMeters / hours);
}
// Eksempel:
const vam = calculateVAM(1100, 55);
// Returnerer: 1200 m/t
10. Estimering fra VAM til W/kg
Formel:
Eksempel:
Scenario: Bakke med 8 % gjennomsnittlig stigning, VAM = 1200 m/t
W/kg = 1200 / 100 / (8 + 3)
W/kg = 12 / 11 = 4,36 W/kg
Kontroll: For en rytter på 70 kg → 305 W kontinuerlig kraft i bakken
function estimateWkgFromVAM(vam, gradientPercent) {
return (vam / 100 / (gradientPercent + 3)).toFixed(2);
}
// Eksempel:
const wkg = estimateWkgFromVAM(1200, 8);
// Returnerer: 4.36
Formel for aerodynamisk kraft
11. Totale kraftkrav
Fullstendig formel:
Formler for hver komponent:
P_aero = CdA × 0,5 × ρ × V³
P_gravity = m × g × sin(θ) × V
P_rolling = Crr × m × g × cos(θ) × V
P_kinetic = m × a × V
Konstanter og variabler:
- CdA = Luftmotstandskoeffisient × frontareal (m²)
- Vanlig landeveissykkel (hendlene): 0,35–0,40 m²
- I bukken: 0,32–0,37 m²
- Tempoposisjon: 0,20–0,25 m²
- ρ = Lufttetthet (1,225 kg/m³ ved havnivå, 15 °C)
- V = Hastighet (m/s)
- m = Total masse (rytter + sykkel, kg)
- g = Tyngdeakselerasjon (9,81 m/s²)
- θ = Stigningsvinkel (radianer eller grader konvertert)
- Crr = Rullemotstandskoeffisient (~0,004 for gode landeveisdekk)
- a = Akselerasjon (m/s²)
Eksempel (Tempo på flat vei):
Scenario:
- Hastighet: 40 km/t = 11,11 m/s
- CdA: 0,22 m² (god tempoposisjon)
- Total masse: 75 kg (rytter) + 8 kg (sykkel) = 83 kg
- Flat vei (stigning = 0°)
- Konstant fart (akselerasjon = 0)
Beregning:
- P_aero = 0,22 × 0,5 × 1,225 × 11,11³ = 185 W
- P_gravity = 0 W (flat vei)
- P_rolling = 0,004 × 83 × 9,81 × 11,11 = 36 W
- P_kinetic = 0 W (konstant fart)
- P_total = 185 + 0 + 36 + 0 = 221 W
Tolkning: Trenger 221 W for å opprettholde 40 km/t i tempoposisjon på flat vei
JavaScript-implementering:
function calculatePowerRequired(params) {
const {
velocityKph,
CdA = 0.32, // m²
rho = 1.225, // kg/m³
mass = 83, // kg (rytter + sykkel)
gradientPercent = 0, // %
Crr = 0.004, // rullemotstand
accelerationMps2 = 0 // m/s²
} = params;
// Konverter hastighet til m/s
const V = velocityKph / 3.6;
// Konverter stigning til vinkel
const theta = Math.atan(gradientPercent / 100);
// Beregn hver komponent
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)
};
}
// Eksempel: Tempo i 40 km/t
const power_tt = calculatePowerRequired({
velocityKph: 40,
CdA: 0.22,
mass: 83,
gradientPercent: 0
});
// Returnerer: { total: 221, aero: 185, gravity: 0, rolling: 36, kinetic: 0 }
// Eksempel: 8 % klatring i 15 km/t
const power_climb = calculatePowerRequired({
velocityKph: 15,
CdA: 0.38,
mass: 75,
gradientPercent: 8
});
// Returnerer: { total: 265, aero: 27, gravity: 244, rolling: 11, kinetic: 0 }
Hjelpefunksjoner
Verktøy for enhetskonvertering
JavaScript-implementering:
// Tidskonverteringer
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')}`;
}
// Hastighetskonverteringer
function kphToMps(kph) {
return kph / 3.6;
}
function mpsToKph(mps) {
return mps * 3.6;
}
// Energikonverteringer
function joulesTokJ(joules) {
return joules / 1000;
}
function kJToJoules(kJ) {
return kJ * 1000;
}
function wattsToKJ(watts, durationSeconds) {
return (watts * durationSeconds) / 1000;
}
// Eksempler:
formatDuration(7265); // Returnerer: "2:01:05"
kphToMps(40); // Returnerer: 11.11 m/s
wattsToKJ(250, 3600); // Returnerer: 900 kJ (1 time ved 250 W)
Ressurser for implementering
Alle formler på denne siden er klare for produksjon og validert mot vitenskapelig litteratur og reelle kraftmålerdata. Bruk dem til egne analyseverktøy, verifisering eller dypere forståelse av beregninger for kraftbasert trening.
💡 Beste praksis
- Valider inndata: Sjekk for rimelige kraftområder (0–2000 W) og positive varigheter
- Håndter kanttilfeller: Divisjon med null, manglende data, manglende FTP
- Avrund hensiktsmessig: CTL/ATL/TSB til 1 desimal, TSS til heltall, W/kg til 2 desimaler
- Lagre nøyaktighet: Behold full nøyaktighet i databasen, avrund kun for visning
- Tidssoner: Håndter UTC mot lokal tid konsekvent for analyser over flere dager
- Kalibrering av kraftmåler: Minn brukere på å nullstille (zero-offset) før turer
- FTP-validering: Flagg mistenkelige FTP-verdier (>500 W eller <100 W for voksne)
- Test grundig: Bruk kjente filer med turer for å verifisere beregningene