我对一份声称的出版物感到困惑:可以在Bikeradar的一分钟内测量您的FTP 。进一步的谷歌搜索显示,Baronbiosys.com开发了一个Xert-App ,声称这一点:
该方法使用复杂的技术和模式识别来确定您的FTP。过去,您要么需要使用20分钟的FTP协议进行测试,要么检查数月的数据以获取现实的FTP值,因此此方法使您可以在当天甚至当时确定您的FTP。 CF男爵
在我的Garmin(830)上安装Xert应用程序后,我已经经历过,直到花费免费试用时间为止,它的估计值非常好。 Dcrainmaker对其进行了审查,并对其性能和准确性非常有肯定,并得出结论:
而这是我实时获得FTP反馈的事实是如此独特。我可以出去兜风,开始看到这些价值观,因为我正在努力努力。没有等待几个小时,甚至几分钟后。我不知道这样做的任何其他平台或应用程序。 CF DCRAINMAKER
我决定为室内培训应用程序开发C ++代码,该应用程序正在Arduino NRF52840 Express板上运行,并且与XERT应用程序的功能(对于Garmin Connect)尽可能近。它成为较大项目的组成部分:气流。以下是对其基本面背后的科学和数学的解释。
对我来说, XERT应用程序很明显是基于所谓的厌氧工作能力( AWC )或功能储备能力( FRC )的耗尽。为了保持简单,假设骑自行车的人有一个给定的数量有限的工作能力(一个能源储备)在乘车开始时内部存储。这种能量在数学术语中被称为w' (发音w prime ),它是能量,因此在焦耳中进行了测量。当您以低强度骑行时, W'由于没有花费而保持完整水平,并且您可以长时间以这种强度继续骑行。但是,如果您更加努力,您将开始使用这种能量。您开始使用此能源储备的极限称为关键功率( CP )。如果您比CP更难推动踏板,则W'将减少。一旦您产生的功率(瓦特)低于CP , W'将“再生”,能源储备将再次增加。当您在CP以下骑行足够长的时间时, W'将再次接近100%的水平。但是,当您努力工作且在CP上方足够长的时间时, W'将完全耗尽,您将在那一刻筋疲力尽(又称lim )! W'的变化表示为Skiba算法(2)中的“ w'平衡”。该算法的另一个参数是tau ,该参数定义了当功率低于CP时, W'重新生成的速度。
CP被定义为可以长时间维持的最高运动强度,通常为45至60分钟。功能性阈值功率( FTP )在休闲循环中更为知名,已被定义为可以维持60分钟(1)的最高平均功率输出。鉴于定义上的相似性很大,我们假设非精英循环是CP和FTP值之间没有显着差异的情况!
必须实现的算法是Dave Waterworth(3)的原始Skiba算法(2)和整体Skiba算法的优化(近似)。 AART GOOSSENS发布了(在Python中)和有关Github(4)的解释信息,这些信息有助于在Arduino环境中理解和实施不同的算法。以下信息从他的原始作品中得到了解释,以在算法的数学背景中给读者一些见解,请参阅他的作品:aart goosens @ github
积分SKIBA算法是计算平衡并经过科学验证的最著名的算法(5)。算法的方程是: 
Where W' bal(t) is equal to W' bal at time t , W' is the amount of available energy above CP (Critical Power), t the time for which W' bal is calculated, u the iterator of the summation, W' exp(u) amount of energy above CP that is used at time u (expended), e the Euler number and Ʈ W' (pronounced Tau ) a time constant that describes the recovery speed.数字546,-0.01和316在Skiba的原始文章中实验确定,并且在个人之间不会改变。 D CP是CP和功率低于CP的间隔的平均功率之间的差异。 D CP可以动态计算(直到时间t的平均值),也可以在整个锻炼中计算一次,并用作静态值。 Skiba建议使用D CP使用静态值。 p(t)是时间t产生的功率。
数学家Dave Waterworth(3)帮助Mark Liversedge的Golden Cheetah的核心开发商开发了SKIBA算法的优化(6)。该重新印象近似于SKIBA算法,因此仅在极端情况下可能会有所不同,尤其是当( Tau )与样品时间非常小时。沃特沃思(Waterworth 
其中s(t)是启动后时间t的运行总和,其他符号符合先前的方程式。 tau ( ʈw' )和w'exp (t)是用Skiba提出的原始方程计算的。即使在快速计算机上,整数SKIBA算法的计算也非常昂贵,因为必须再次重复求和。 Waterworth优化的最大优势是,现在可以实时计算余额:在骑行期间,不仅是之后!此外,当人们想在HIIT锻炼过程中飞行或艰苦的锻炼时,当w'平衡变得负面并耗尽时,这将非常有帮助!

从数学上讲,功率 - 持续关系被描述为双曲线函数。当达到相应的最大可持续功率并发生疲惫时,曲线上的4个不同点表示时间点( T LIM )。当考虑运动耐受性时,功率拼合被称为CP (瓦特)。曲率常数称为w' (即, w prime ),以完成的工作单位(焦耳)进行测量。请注意,代表W'的4个灰色区域的形式不同,但大小相等。如果将完成的工作绘制在时间上,则可以将这种双曲功率 - 持续关系变为线性关系,以使线的斜率等于CP和截距等于W' 。应该强调的是,力量持续关系描述了运动的宽容,但没有解释。然而,在CP下和更高的锻炼中对运动的生理反应可能会为疲劳过程提供重要的见解。 CP最初被定义为可以“无限期”或很长时间内无疲劳的外部功率输出。但是,该定义应被视为理论上,因为永远无法无限期进行任何锻炼。现在可以理解, CP可以将锻炼公差的功率输出分开,而锻炼公差是可以预见的(运动功率> CP )。在上述CP上进行的锻炼的实际不宽容时间( T LIM )是通过方程式定义的,因此可以仔细地预测:
t lim = w'/(p-cp)
该方程强调,不耐受性在CP上方的时间是功率输出( P )持续到CP和W'的大小的邻近的函数。当P高于CP时,将迅速使用由W'参数代表的恒定工作量,而T LIM将很短。如果P更靠近CP ,那么W'将被更慢地“使用”,而T LIM将更长。这里的关键考虑是,对于CP上方的所有P,都认为W'是恒定的。因此,这个“两个参数”的功率时间或功率持续模型意味着绝对练习性能仅取决于CP的值(瓦特)和W'的值(在Joules中)。 CP和W'参数在个体中都可以差异,这是健康/疾病,年龄,健身和培训的函数。
// ------------ W' Balance calculation -------------------
// Global variables related to Cycling Power and W-Prime
uint16_t TAWC_Mode = 1 ; // Track Anaerobic Capacity Depletion Mode == TRUE -> APPLY and SHOW
uint16_t CP60 = 160 ; // Your (estimate of) Critical Power, more or less the same as FTP
uint16_t eCP = CP60; // Algorithmic estimate of Critical Power during intense workout
uint16_t w_prime_usr = 7500 ; // Your (estimate of) W-prime or a base value
uint16_t ew_prime_mod = w_prime_usr; // First order estimate of W-prime modified during intense workout
uint16_t ew_prime_test = w_prime_usr; // 20-min-test algorithmic estimate (20 minute @ 5% above eCP) of W-prime for a given eCP!
long int w_prime_balance = 0 ; // Can be negative !!!
bool IsShowWprimeValuesDominant = false ; // Boolean that determines to show W Prime data on Oled or not
// ------------------------------------------------------- // ------------------------ W'Balance Functions -----------------------------------
uint16_t CalculateAveragePowerBelowCP ( uint16_t iPower, uint16_t iCP);
void CalculateAveragePowerAboveCP ( uint16_t iPower, uint16_t &iavPwr, unsigned long &iCpACp);
double tau_w_prime_balance ( uint16_t iPower, uint16_t iCP);
void w_prime_balance_waterworth ( uint16_t iPower, uint16_t iCP, uint16_t iw_prime);
void ConstrainW_PrimeValue ( uint16_t &iCP, uint16_t &iw_prime);
uint16_t GetCPfromTwoParameterAlgorithm ( uint16_t iav_Power, unsigned long iT_lim, uint16_t iw_prime);
uint16_t GetWPrimefromTwoParameterAlgorithm ( uint16_t iav_Power, double iT_lim, uint16_t iCP);
// ------------------------ W'Balance Functions ------------------------------------ uint16_t CalculateAveragePowerBelowCP ( uint16_t iPower, uint16_t iCP){
// calculate avg_power_below_cp real time using a running sum and counter
static unsigned long int CountPowerBelowCP = 0 ;
static unsigned long int SumPowerBelowCP = 0 ;
if (iPower < iCP) {
SumPowerBelowCP += ( unsigned long int )iPower;
CountPowerBelowCP++;
}
return uint16_t (SumPowerBelowCP/CountPowerBelowCP); // average power below CP
} // end calculate avg_power_below_cp
void CalculateAveragePowerAboveCP ( uint16_t iPower, uint16_t &iavPwr, unsigned long int &iCpACp){
// calculate avg_power_above_cp real time using a running sum and counter
// returning the values by C++ reference!
static unsigned long int SumPowerAboveCP = 0 ;
SumPowerAboveCP += ( unsigned long int )iPower;
iCpACp++;
iavPwr = uint16_t (SumPowerAboveCP/iCpACp); // average power above CP
} // end calculate avg_power_above_cp
double tau_w_prime_balance ( uint16_t iPower, uint16_t iCP){
uint16_t avg_power_below_cp = CalculateAveragePowerBelowCP (iPower, iCP);
double delta_cp = double (iCP - avg_power_below_cp);
return ( double ( 546.00 ) * exp (- 0.01 * delta_cp) + double ( 316.00 ));
} // end Tau W Prime Balance
void w_prime_balance_waterworth ( uint16_t iPower, uint16_t iCP, uint16_t iw_prime) {
// Most power meters measure power, torque a.o. in a high frequency (20-60 Hz) but
// transmit (BLE) datasets to a monitoring device in much lower frequency: 1-4 times per second.
int power_above_cp = 0 ; // Power > CP
static double T_lim = 0 ; // Time (duration) while Power is above CP, the summed value of every sample time value P > CP
double w_prime_expended = 0.0 ; // Expended energy in Joules
double ExpTerm1 = 0.0 , ExpTerm2 = 0.0 ;
static double TimeSpent = 0.0 ; // Total Time spent in the workout, the summed value of every sample time value
static double running_sum = 0.0 ;
static unsigned long int CountPowerAboveCP = 0 ; // Count the Power readings above CP
static uint16_t avPower = 0 ; // Average power above CP
const long int NextLevelStep = 1000 ; // Stepsize of the next level of w-prime modification --> 1000 Joules step
static long int NextUpdateLevel = 0 ; // The next level at which to update eCP, e_w_prime_mod and ew_prime_test
// Quarq Dfour Zero Spider power meter sends between 2 and 1.2 power readings per second, dependent of POWER level !!!
// We assume that the sample frequency (number of samples per second) is VARIABLE !!!
// Determine the individual sample time in seconds, it may/will vary during the workout !!!
static unsigned long PrevReadingTime = 0 ;
double SampleTime = double ( millis ()-PrevReadingTime)/ 1000 ; // Time or duration since the previous sample, convert from millis to seconds
PrevReadingTime = millis (); // Update for the next sample
double tau = tau_w_prime_balance (iPower, iCP); // Determine the value for tau
TimeSpent += SampleTime ; // The summed value of all sample time values during the workout
power_above_cp = (iPower - iCP);
# ifdef DEBUGAIR
Serial. printf ( " Time:%6.1f ST: %4.2f tau: %f " , TimeSpent, SampleTime , tau);
# endif
// w_prime is energy and measured in Joules = Watt*second
// Determine the expended energy above CP since the previous measurement (--> i.e. during sample time)
w_prime_expended = double ( max ( 0 , power_above_cp))*SampleTime; // Determine (Watts_above_CP) * (its duration in seconds) = expended energy in Joules!
// Calculate some terms of the equation
ExpTerm1 = exp (TimeSpent/tau); // Exponential term1
ExpTerm2 = exp (-TimeSpent/tau); // Exponential term2
# ifdef DEBUGAIR
Serial. printf ( " W prime expended: %3.0f exp-term1: %f exp-term2: %f " , w_prime_expended , ExpTerm1, ExpTerm2);
# endif
running_sum = running_sum + (w_prime_expended*ExpTerm1); // Determine the running sum
# ifdef DEBUGAIR
Serial. printf ( " Running Sum: %f " , running_sum);
# endif
w_prime_balance = ( long int )( ( double )iw_prime - (running_sum*ExpTerm2) ) ; // Determine w prime balance and cast from double to int
# ifdef DEBUGAIR
Serial. printf ( " w_prime_balance: %d " , w_prime_balance);
# endif
// --------------- extra --------------------------------------------------------------------------------------
// Workout starts at a certain W'= ##,### Joules and CP = ### watts, set by the user; the algorithm increases CP and W' stepwise
// to more realistic values every time when W'balance is depleted to a certain level; -> 2-Parameter Algorithm updates CP and W'
if (power_above_cp > 0 ) {
CalculateAveragePowerAboveCP (iPower, avPower, CountPowerAboveCP); // Average power above CP is to be calculated for future use
T_lim += SampleTime ; // Time to exhaustion: the accurate sum of every second spent above CP, calculated for future use
}
# ifdef DEBUGAIR
Serial. printf ( " [%d] n " , CountPowerAboveCP);
# endif
// When working above CP, the moment comes that we need to update eCP and ew_prime !!
if ( (w_prime_balance < NextUpdateLevel) && (w_prime_expended > 0 ) ) { // W' balance is further depleted --> test for an update moment
NextUpdateLevel -= NextLevelStep; // Move down another level of depletion, update eCP, ew_prime_mod and ew_prime_test
eCP = GetCPfromTwoParameterAlgorithm (avPower, T_lim, iw_prime); // Estimate a new eCP value
ew_prime_mod = w_prime_usr - NextUpdateLevel; // Adjust ew_prime_modified to the next level of depletion to be checked
ew_prime_test = GetWPrimefromTwoParameterAlgorithm ( uint16_t (eCP* 1.045 ), double ( 1200 ), eCP); // 20-Min-test estimate for W-Prime
# ifdef DEBUGAIR
Serial. printf ( " Update of eCP - ew_prime %5d - avPower: %3d - T-lim:%6.1f --> eCP: %3d " , ew_prime_mod, avPower, T_lim, eCP);
Serial. printf ( " --> Test estimate of W-Prime: %d n " , ew_prime_test );
# endif
}
// -----------------extra -------------------------------------------------------------------------------
} // end
// Check and Set starting value of w_prime to realistic numbers!!
void ConstrainW_PrimeValue ( uint16_t &iCP, uint16_t &iw_prime) {
if (iCP < 100 ) { iCP = 100 ; } // Update to lowest level that we allow for
// First determine the "minimal" value for W_Prime according to a 20-min-test estimate, given the iCP value!
uint16_t w_prime_estimate = GetWPrimefromTwoParameterAlgorithm ( uint16_t (iCP* 1.045 ), double ( 1200 ), iCP);
if (iw_prime < w_prime_estimate) { iw_prime = w_prime_estimate; } // Update iw_prime to a realistic level
return ;
} // end
uint16_t GetCPfromTwoParameterAlgorithm ( uint16_t iav_Power, double iT_lim, uint16_t iw_prime) {
uint16_t WprimeDivTlim = uint16_t ( double (iw_prime)/iT_lim ); // type cast for correct calculations
if (iav_Power > WprimeDivTlim){ // test for out of scope
return (iav_Power - WprimeDivTlim); // Solve 2-parameter algorithm to estimate CP
} else {
return eCP; // Something went wrong do'nt allow an update of CP
}
} // end
uint16_t GetWPrimefromTwoParameterAlgorithm ( uint16_t iav_Power, double iT_lim, uint16_t iCP) {
if (iav_Power > iCP){ // test for out of scope
return (iav_Power-iCP)*(( uint16_t )iT_lim); // Solve 2-parameter algorithm to estimate new W-Prime
} else {
return w_prime_usr; // Something went wrong don't allow an update of w_prime
}
} // end
该代码已集成到一个名为Airffore的较大项目中。设置或更改基础CP和W Prime是气流伴侣应用程序不可或缺的一部分!气流智能设备在室内骑自行车锻炼的所有阶段,热身,热身,强度间隔,间歇性恢复和冷却后,连续调谐冷却风扇所需的气流速度以达到稳定的热量平衡。这位骑自行车的人没有在线干扰,可以完全专注于现场培训师锻炼的需求,始终面对理想的气流,使他/她适当地冷却。此外,骑自行车的人(作为奖励)洞察力在训练强度很强烈且足够长时间时,对他/她的关键力量的发展得到了见解!应用的Arduino NRF52480 Express CPU非常强大,以至于可以将CP和W Prime的实时计算完成所有计算,以确定热量术语,并将风扇置于适当的吹吹动能力。
骑自行车的人(属性: CP = 140瓦和W Prime = 7.2 kJ )正在进行强烈的锻炼,如图所示,持续时间和强度。在第一个间隔块中, W Prime耗尽了几次。 CP和W Prime的新值只有在适当的时候才能在锻炼时间内估算算法。运行视频以看到W Prime的100%耗竭和实时计算的估计值并在OLED屏幕上显示...请注意,当功率高于175和195瓦的CP时,水平条如何收缩。该条与w'余额成正比,此外,“油箱中剩下的东西”还显示为百分比!读数以加速序列显示,不符合锻炼的持续时间! 