IMU Gait Analysis [Old] (skdh.gait_old)#
Pipeline gait processing#
|
Process IMU data to extract endpoints of gait. |
Event Level Gait Endpoints#
The time to complete 1 full gait cycle for 1 foot. |
|
The time during a stride in which the foot is on the ground. |
|
The time during which the foot is off the ground. |
|
|
The duration from heel-strike (initial contact) to heel-strike of the opposite foot. |
The time immediately following heel strike during which the opposite foot is still on the ground. |
|
The time immediately before toe-off (final contact) in which the opposite foot has contacted the ground. |
|
The combined initial and terminal double support times. |
|
The time during a stride that only the current foot is in contact with the ground. |
|
The distance traveled during a step (heel-strike to opposite foot heel-strike). |
|
The distance traveled during a stride (heel-strike to current foot heel-strike). |
|
How fast distance is being traveled. |
|
|
The number of steps taken in 1 minute. |
The autocovariance of vertical acceleration of 1 step with lag equal to the step duration. |
|
The autocovariance of vertical acceleration of 1 stride with lag equal to the stride duration. |
|
Symmetry measure of the 2 steps that occur during each stride. |
Bout Level Gait Endpoints#
Assessment of the symmetry between steps during straight overground gait. |
|
Assessment of the symmetry between steps during straight overground gait. |
|
The autocovariance at a lag time of 1 step for the vertical acceleration. |
|
Autocovariance at a lag time of 1 stride for the vertical acceleration. |
|
The absolute difference between stride and step regularity for the vertical axis. |
|
The combination of both step and stride regularity into one endpoint. |
Background Information#
| IC FC IC FC IC FC
| i-1 i-1 i+1 i+1 i+3 i+3
| L |--------------| |--------------| |--------------|
| R |--------------| |--------------|
| i i i+2 i+2
| IC FC IC FC
| time --->
General terminology:
Initial Contact (IC): the first contact of a foot with the ground, also “Heel Strike”
Final Contact (FC): the last contact of a foot with the ground, also “Toe Off”
Stride: between ICs of the same foot, eg IC(i) to IC(i+2), IC(i+1) to IC(i+3)
Per stride terminology:
Step: between ICs of opposite feet, eg IC(i) to IC(i+1), IC(i+1) to IC(i+2). There are 2 steps to every stride
Stance: when the foot is in contact with the ground, eg IC(i) to FC(i)
Swing: when the foot is not in contact with the ground, eg FC(i) to IC(i+2)
Double Support: when both feet are in contact with the ground simultaneously
Single Support: when only 1 foot is in contact with the ground
Adding Custom Gait Endpoints#
A modular system for computing gait endpoints is employed to aid in the addition of custom gait endpoints. Two base classes exist depending on what type of endpoint is being added:
gait_endpoints.GaitEventEndpoint for per-step/-stride endpoints
gait_endpoints.GaitBoutEndpoint for per-bout endpoints
New endpoints should be subclasses of either of these base classes, which provide some basic functionality behind the scenes. The definition and initialization is very straight-forward
from skdh.gait_old.gait_endpoints import GaitEventEndpoint, basic_symmetry
class NewEndpoint(GaitEventEndpoint):
def __init__(self):
super().__init__('new endpoint', depends=[gait_endpoints.StrideTime])
__init__ should take no arguments, and its call to the super method has 1 required and 1 optional argument: the name for the endpoint (this will appear in the output as “PARAM:{name}” or “BOUTPARAM:{name}” for bout endpoints, and references to any other endpoints it depends upon. In this case, the calculation of the new endpoint would reference the computed stride time.
To implement the feature computation, the _predict method should be defined
from skdh.gait_old.gait_endpoints import GaitEventEndpoint
class NewEndpoint(GaitEventEndpoint):
...
def _predict(self, dt, leg_length, gait, gait_aux):
mask, mask_ofst= self._predict_init(gait, True, 1)
key = 'PARAM:stride time'
gait[self.k_][mask] = gait[key][mask_ofst] - gait[key][mask]
The arguments to the _predict method are as follows:
dt: sampling period in seconds
leg_length: leg length in meters, if provided to the Gait process. Otherwise None
gait: dictionary containing all the end result gait endpoints. Where the newly endpoint will be stored as well. Not returned, just modified in place. Has several keys that are defined before any endpoints are calculated - ‘IC’, ‘FC’, ‘FC opp foot’ and ‘delta h’
gait_aux: dictionary containing the acceleration (3D, key: ‘accel’), vertical acceleration axis (‘vert axis’), vertical velocity (‘vert velocity’) and vertical position (‘vert position’) for each bout of gait. Additionally contains a mapping from individual steps to bouts (‘inertial data i’), or from bouts to a non-unique value per step (see bout endpoint example)
There are a few convenience functionalities, namely the _predict_init function, and the self.k_ attribute. The _predict_init function optionally does up to two things:
Initialize the results (self.k_) to an array of nan values, if the second argument is True
Create mask and mask_ofst, which can be used to index into the per event values. Combined, they work together to provide similar functionality to something like x[ofst:] - x[:-ofst] where ofst (third argument) is either 1 or 2. They additionally account for steps that are not valid/at the end of bouts where values would be nonsensical.
The self.k_ attribute simply stores the full name of the endpoint for easy/shorthand access. Finally, if the custom endpoint has a basic symmetry value computed by subtracting sequential values, this can be quickly implemented by adding the decorator gait_endpoints.basic_symmetry above the _predict definition:
from skdh.gait_old.gait_endpoints import GaitEventEndpoint, basic_symmetry
class NewEndpoint(GaitEventEndpoint):
...
@basic_symmetry
def _predict(self, dt, leg_length, gait, gait_aux):
...
Below is a full example of a bout endpoint, with broadcasting from individual per-bout values to having repeating values for each step in the bout (since the gait dictionary is fundamentally defined on a per-event level):
from skdh.gait_old.gait_endpoints import GaitBoutEndpoint
class StepRegularityV(GaitBoutEndpoint):
def __init__(self):
super().__init__('step regularity - V', depends=[StepTime])
def _predict(self, dt, leg_length, gait, gait_aux):
# initialize 1 value per bout
stepreg = zeros(len(gait_aux['accel']), dtype=float64)
for i, acc in enumerate(gait_aux['accel']):
lag = int(
round(nanmean(gait['PARAM:step time'][gait_aux['inertial data i'] == i]) / dt)
)
acf = _autocovariancefunction(acc[:, gait_aux['vert axis']], int(4.5 * dt))
pks, _ = find_peaks(acf)
idx = argmin(abs(pks - lag))
stepreg[i] = acf[idx]
# broadcast step regularity into gait for each step
gait[self.k_] = stepreg[gait_aux['inertial data i']]