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ådeInnsatsnivåEksempel på økt
< 0,75Restitusjon / RoligAktiv restitusjonstur, Sone 1–2
0,75 – 0,85UtholdenhetLang rolig tur, aerob base
0,85 – 0,95TempoSweet spot-trening, tempointervaller
0,95 – 1,05TerskelFTP-intervaller, tempoinnsats (TT)
1,05 – 1,15VO₂max5-minutters intervaller, ritt (criterium)
> 1,15AnaerobKorte 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:

DisiplinTypisk VIBetydning
Landevei TT / Jevn innsats1,00 – 1,05Svært jevn kraft, optimal pacing
Landeveisritt1,05 – 1,10Noen rykk, generelt jevnt
Criterium (Gateritt)1,10 – 1,20Hyppige akselerasjoner og støt
Terrengsykkel XC1,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:

VarighetKraft (W)Totalt arbeid (kJ)
3 min (180 s)400 W72 kJ
5 min (300 s)365 W109,5 kJ
12 min (720 s)310 W223,2 kJ
20 min (1200 s)285 W342 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):

DagTSSCTLATLTSBStatus
Man10075,080,0-5,0Trening
Tir5074,475,7-1,3Restitusjon
Ons12075,582,0-6,5Hard trening
Tor073,770,3+3,4Hviledag
Fre8073,871,7+2,1Moderat
Lør15075,682,9-7,3Langtur
Søn4074,876,8-2,0Restitusjon

Tolkning av TSB:

TSB-områdeStatusHandling
< -30Høy risikoFare for overtrening – reduser belastningen
-30 til -10Uke med hard treningBygger form, overvåk restitusjonen
-10 til +5OptimaltNormal treningssone
+5 til +15Klar for rittToppform – klar for konkurranse
> +25FormfallTap 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/kgKvinner W/kgKategori
Mosjonist2,5 – 3,52,0 – 3,0Aktiv hobbysyklist
Konkurranserytter3,5 – 4,53,0 – 4,0Turritt, aktiv klasse
Avansert4,5 – 5,54,0 – 5,0Nasjonal elite, sterk amatør
Elite5,5 – 6,05,0 – 5,5Topp nasjonalt nivå
Profesjonell6,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 – 900MosjonistKlubbrytter i lokal bakke
900 – 1200KonkurranserytterGod amatør på Alpe d'Huez
1200 – 1500EliteamatørNasjonal toppklatrer
1500 – 1800ProfesjonellWorld Tour hjelperytter
> 1800Grand Tour-vinderPogač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