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:

TSS = (varighet_sekunder × NP × IF) / (FTP × 3600) × 100
hvor IF = NP / FTP

Eksempel:

Scenario: 2-timers tur, NP = 235 W, FTP = 250 W

  1. Beregn IF: IF = 235 / 250 = 0,94
  2. Varighet i sekunder: 2 timer × 3600 = 7200 sekunder
  3. TSS = (7200 × 235 × 0,94) / (250 × 3600) × 100
  4. 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):

1. Beregn 30-sekunders glidende gjennomsnitt for hele turen
2. Opphøy hver 30-sekunders verdi i fjerde potens
3. Beregn gjennomsnittet av alle disse verdiene
4. Ta fjerderoten av dette gjennomsnittet
NP = ⁴√(gjennomsnitt av [30s_avg^4])

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:

IF = NP / FTP

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:

VI = NP / Gjennomsnittskraft

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:

Tid = W' / (Kraft - CP)
Omorganisert: Kraft × Tid = CP × Tid + W'

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:

Forbruk (når P > CP):
W'exp(t) = ∫(P(t) - CP) dt
Restitusjon (når P < CP):
W'rec(t) = W' × (1 - e^(-t/τ))
hvor τ = 546 × e^(-0,01 × ΔCP) + 316
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):

CTL_i dag = CTL_i går + (TSS_i dag - CTL_i går) / 42
ATL_i dag = ATL_i går + (TSS_i dag - ATL_i går) / 7
TSB_i dag = CTL_i går - ATL_i går

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:

W/kg = Kraft (watt) / Kroppsvekt (kg)

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 (m/t) = Høydemeter (m) / Tid (timer)

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:

W/kg ≈ VAM (m/t) / 100 / (Stigningsprosent + 3)

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:

P_total = P_aero + P_gravity + P_rolling + P_kinetic

Formler for hver komponent:

Luftmotstand (Aero):
P_aero = CdA × 0,5 × ρ × V³
Tyngdekraft (Klatring):
P_gravity = m × g × sin(θ) × V
Rullemotstand:
P_rolling = Crr × m × g × cos(θ) × V
Kinetisk (Akselerasjon):
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:

  1. P_aero = 0,22 × 0,5 × 1,225 × 11,11³ = 185 W
  2. P_gravity = 0 W (flat vei)
  3. P_rolling = 0,004 × 83 × 9,81 × 11,11 = 36 W
  4. P_kinetic = 0 W (konstant fart)
  5. 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