Showing posts with label Biomechanics. Show all posts
Showing posts with label Biomechanics. Show all posts

Friday, October 25, 2013

Random thought - Differences between locomotor profile in running and biking, or Power vs Velocity profiling



I am playing with Metabolic Power data estimates and I come to one interesting and confusing "finding" and I want to ask for an opinion and/or direction on this.

Locomotor profile (see slides from Martin Buchheit) is basically MAS and MSS, or Maximal Aerobic Speed and Maximal Sprinting Speed. Based on these two parameters we can predict performance on a lot of distances.  Add there a little bit of CV (Critical Velocity), vLT and we have a full profile.

The same could be done with cycling, but here we use power (see Power Profiling). Certain authors use average power levels over certain durations (CP5s, CP1min, CP5min, MAP, CP20min, FTP, etc) while others use ("real") Critical Power estimation. 

Anyway, the rationale behind them are the same. We have a profile and we make training recommendations based on that profile and activity demands, we judge performance improvements, etc.

But, that's not the point of this blog post. The point is their difference. In numbers.

For example, in running for an average athlete MAS is around 16 km/h and MSS is around 30 km/h. The ANR (Anaerobic Reserve) is MSS-MAS or 30 - 16 = 14 km/h. I have calculated various ratios

MAS = 16
MSS = 30
ANR = 14

MAS/MSS = 0,53
MAS/ANR = 1,14
ANR/MSS =  0,46

In cycling the measurement unit is power. CP5min (or average power one can produce for 5min) or MAP (Maximal Aerobic Power) which is equivalent to MAS is around 7 W/kg for very good cyclists. Their CP5sec could be representative of MSS (let's call it Maximal Sprinting Power) and it is around 23 W/kg for very good cyclists (don't get too hang up on numbers, they are just an example). Let's calculate the ratios

MAP = 7
MSP = 23
ANR = 16

MAP/MSP = 0,30
MAP/ANR = 0,43
ANR/MSP = 0,69

We can immediately see that the profiles are totally different when we look at ratios. 

Ok - this might be related to differences between cycling and running in general, or using power vs. velocity for profiling. 

But here is the thing - I mentioned that I have been looking at Metabolic Power estimates. MAS or in this case MAP is around 20 W/kg approximately 57ml/kg/min O2  for an average player. When accelerating for 5 to 10m, power output might go over 80 to 100 W/kg. That's the ratio MAP/MSP around 0,2 to 0,25. Almost twice lower then when we use velocity (MAS/MSS = 0,53)

The missing piece of the puzzle is the power output in 40-60m zone when we have high speed and zero acceleration. I would be interested to see the difference between Metabolic Power (MP) estimates in 5-10m sprint from standing still, and MP when someone is running at top speed with no acceleration (e.g. zone 40-60m). Another interesting comparison might be straight-line Vam-Eval test results in both velocity and MP (estimated using GPS), and also Vam-Eval done in shuttle mode. 

To cut the long story short, here are some of my thoughts:

- The "Metabolic Profile" (to blend Locomotor- in running and Power profile in cycling) might have the tendency to be different depending on the way we estimate it (velocity vs. power). Or depending on the locomotion.

- If we show that MP is lower in top speed than in top acceleration, then the top speed might not be limited by metabolic, but rather mechanical factors (musculoskeletal force applications). Bundle and Weyand wrote interesting article on this.  In other words - supply is there, but the demand is lower than maximal. [squiggle sense alert: supply~demand]

I am not sure if using MP estimates in running (short- medium- long distance) will change much, but they might change a lot in short burst, start-stop, change of direction type sports such as most team sports and tennis. This might mean that metabolic profiling based on power instead of speed might give us more information. 

Looking for your opinions and suggestions dear readers....



Thursday, October 24, 2013

Playing with R, ggplot2 and knitr

Playing with R, ggplot2 and knitr

Playing with R, ggplot2 and knitr

Over the last month I have been learning R programming language that is completely FREE and used extensively in statistics and data analysis. I have been also playing with ggplot2 which is a great graphics package for R, along with knitr which was used to produce this blog post/report.

In this short demo, besides bragging with my newly acquired skillz, I wanted to give an example for power calculation in uniform (same speed) circular motion for my friend Michael Clarkson from UK. This was a part of our discussion on difference between running at the same velocity on a straight line, and running on that same velocity in the circle (air resistanc disregarded) in terms of Power production. In the simple dot mass model, in which we are going to represent human body with a dot with a mass of m we are going to disregard air resistance, body oscilations and limb movements. According to this simple model, because the velocity is same all the time, the Power output will be zero. Of course, this is not the case in real life.

The goal of this exercise is to play with vector multiplication and to check if the power is different than zero in uniform circular motion.

Let's generate some data in a data.frame called sample

# create time vector that contains our samplings time.
sample <- data.frame(time = seq(from = 0, to = 1, by = 1/1e+05))

# Calculate time difference or dt used for velocity and acceleration
# differentiation
sample$dt <- with(sample, c(NA, diff(time)))

# Create angular velocity of the object in rad/s
sample$angular.velocity <- 10

# Create radius of the circle
sample$radius <- 2

# Create mass of the object
sample$mass <- 10

This how the sample data.frame looks like

str(sample)
## 'data.frame':    100001 obs. of  5 variables:
##  $ time            : num  0e+00 1e-05 2e-05 3e-05 4e-05 5e-05 6e-05 7e-05 8e-05 9e-05 ...
##  $ dt              : num  NA 1e-05 1e-05 1e-05 1e-05 1e-05 1e-05 1e-05 1e-05 1e-05 ...
##  $ angular.velocity: num  10 10 10 10 10 10 10 10 10 10 ...
##  $ radius          : num  2 2 2 2 2 2 2 2 2 2 ...
##  $ mass            : num  10 10 10 10 10 10 10 10 10 10 ...

From these parameters that are already in sample data.frame we will generate x and y cordinates of the object.

# Calculate X position
sample$x <- with(sample, sin(time * angular.velocity) * radius)

# Calculate Y position
sample$y <- with(sample, cos(time * angular.velocity) * radius)

On the following graph we can se the path of the object

# Load the ggplot2 package
library(ggplot2)

ggplot(sample, aes(x = x, y = y)) + geom_point(color = "steelblue", size = 2, 
    alpha = 1/2) + labs(title = "Motion path") + geom_hline() + geom_vline()
plot of chunk unnamed-chunk-4

Now we have positional data and dt or time difference between sampling. We will use this to get velocity for x component and y component (velocity is a vector and we will represent it with x and y components). I beleive this is called reverse dynamics (where you estimate velocity, acceleration and force from positional and time data )

velocity_x = dx / dt

Where dx is chnage in x position between two samples (x2 - x1) and dt is change in time (we have already calculated this when designing the data.frame)

# Calculate velocity for x component
sample$vx <- with(sample, c(NA, diff(x))/dt)

# Calculate velocity for y component
sample$vy <- with(sample, c(NA, diff(y))/dt)

We can now plot velocity (x component)

ggplot(sample, aes(x = time, y = vx)) + geom_path(color = "steelblue", size = 2, 
    alpha = 1/2, na.rm = TRUE) + labs(y = "Velocity x") + geom_hline()
plot of chunk unnamed-chunk-6

Having velocity data we can calculate acceleration and force, where

acceleration_x = dvelocity_x / dt

Force_x = acceleration_x X mass

# Calculate acceleration for x component
sample$ax <- with(sample, c(NA, diff(vx))/dt)

# Calculate acceleration for y component
sample$ay <- with(sample, c(NA, diff(vy))/dt)

# Calculate force for x and y directions
sample$fx <- with(sample, ax * mass)
sample$fy <- with(sample, ay * mass)

One the following picture is the graph of acceleration (x component)

ggplot(sample, aes(x = time, y = ax)) + geom_path(color = "steelblue", size = 2, 
    alpha = 1/2, na.rm = TRUE) + labs(y = "Acceleration x") + geom_hline()
plot of chunk unnamed-chunk-8

Now it is time to calculate power on x and on y (scalar product)

# Calculate power for x component and y component
sample$px <- with(sample, vx * fx)
sample$py <- with(sample, vy * fy)

Here is the graph of Power (x component)

ggplot(sample, aes(x = time, y = px)) + geom_path(color = "steelblue", size = 2, 
    alpha = 1/2, na.rm = TRUE) + labs(y = "Power x") + geom_hline()
plot of chunk unnamed-chunk-10

It seems that the power is fluctuating, but what happens when we calculate total power (sum of x and y component power; power is scalar so we can just add them) and visualize it?

sample$total.power <- with(sample, px + py)
ggplot(sample, aes(x = time, y = total.power)) + geom_path(color = "steelblue", 
    size = 2, alpha = 1/2, na.rm = TRUE) + labs(y = "Total Power") + geom_hline()
plot of chunk unnamed-chunk-11

Because centripetal force is acting on the object, and that force is perpendicular to the velocity vector, there is no work done by this force and hence power is zero. On the graph above you can see that Total Power has some value - this is constant error due sampling frequency. Higher the sample frequency, lower the error (will see the relationship later)

Another way to calculate total power would be to use scalar velocity

# Calculate scalar velocity from angular velocity and radius
sample$v = with(sample, angular.velocity * radius)

# Calculate acceleration from change in scalar velocity divided by dt
sample$a = with(sample, c(NA, diff(v))/dt)

# Calculate force acting on the object
sample$f = with(sample, a * mass)

# Calculate power
sample$p = with(sample, f * v)

# Plot power calculated
ggplot(sample, aes(x = time, y = p)) + geom_path(color = "steelblue", size = 2, 
    alpha = 1/2, na.rm = TRUE) + labs(y = "Total Power") + geom_hline()
plot of chunk unnamed-chunk-12

Now we can see that the total power is zero (in this simple model, NOT the real life). Let's code a function that calculates the mean total power for sampling frequency. This will involve copying the whole code above, but changing sampling frequency

mean.total.power <- function(frequency = 100) {

    sample <- data.frame(time = seq(from = 0, to = 1, by = 1/frequency))
    sample$dt <- with(sample, c(NA, diff(time)))
    sample$angular.velocity <- 10
    sample$radius <- 2
    sample$mass <- 10
    sample$x <- with(sample, sin(time * angular.velocity) * radius)
    sample$y <- with(sample, cos(time * angular.velocity) * radius)
    sample$vx <- with(sample, c(NA, diff(x))/dt)
    sample$vy <- with(sample, c(NA, diff(y))/dt)
    sample$ax <- with(sample, c(NA, diff(vx))/dt)
    sample$ay <- with(sample, c(NA, diff(vy))/dt)
    sample$fx <- with(sample, ax * mass)
    sample$fy <- with(sample, ay * mass)
    sample$px <- with(sample, vx * fx)
    sample$py <- with(sample, vy * fy)
    sample$total.power <- with(sample, px + py)
    return(mean(sample$total.power, na.rm = TRUE))
}

Now we can see how does the error in Total Power behaves with different sampling frequencies. We are going to create a vector (series of data) that contains sampling frequencies - 10, 100, 1000, etc and pass that to the function. We are going to graph the error against sampling frequency. If you run this on your computer it is going to take some time.

probing.data <- data.frame(frequency = seq(from = 500, to = 1e+05, by = 1000))  # Different sampling frequencies
probing.data$power = with(probing.data, numeric(length(frequency)))  # This contains the results

for (i in seq_along(probing.data$frequency)) {
    probing.data$power[i] <- mean.total.power(probing.data$frequency[i])
}

ggplot(probing.data, aes(x = frequency, y = power)) + geom_path(color = "steelblue", 
    size = 2, alpha = 1/2, na.rm = TRUE) + labs(y = "Total Power") + geom_hline()
plot of chunk unnamed-chunk-14
If we do this on other indices, like velocity and acceleration we won't see such a change based on sampling frequency. Let's try - we just need to modify the written function to return the maximum of acceleration on x.
max.x.acceleration <- function(frequency = 100) {

    sample <- data.frame(time = seq(from = 0, to = 1, by = 1/frequency))
    sample$dt <- with(sample, c(NA, diff(time)))
    sample$angular.velocity <- 10
    sample$radius <- 2
    sample$mass <- 10
    sample$x <- with(sample, sin(time * angular.velocity) * radius)
    sample$y <- with(sample, cos(time * angular.velocity) * radius)
    sample$vx <- with(sample, c(NA, diff(x))/dt)
    sample$vy <- with(sample, c(NA, diff(y))/dt)
    sample$ax <- with(sample, c(NA, diff(vx))/dt)
    sample$ay <- with(sample, c(NA, diff(vy))/dt)
    return(max(sample$ax, na.rm = TRUE))
}

And code the same probing procedure as we did for error in total power calculus

probing.data <- data.frame(frequency = seq(from = 500, to = 1e+05, by = 1000))  # Different sampling frequencies
probing.data$acceleration = with(probing.data, numeric(length(frequency)))  # This contains the results

for (i in seq_along(probing.data$frequency)) {
    probing.data$acceleration[i] <- max.x.acceleration(probing.data$frequency[i])
}

ggplot(probing.data, aes(x = frequency, y = acceleration)) + geom_path(color = "steelblue", 
    size = 2, alpha = 1/2, na.rm = TRUE) + labs(y = "Max calculated acceleration") + 
    geom_hline()
plot of chunk unnamed-chunk-16

As can be seen from the plot, maximal acceleretaion (x comp) is always the same regardles of the sampling frequency.

I hope that this demonstration of R, ggplot and knitr and my simulation knowledge didn't cause major headache.

I will be using R more and more and my next project is something that will be of interest for sport scientists interested in metabolic power measurement using GPS with the goal of real-time tracking of fatigue.

Stay tuned….