SlZijlstra#

class mobgap.stride_length.SlZijlstra(
*,
orientation_method: BaseOrientationEstimation | None = None,
acc_smoothing: BaseFilter = cf(ButterworthFilter(cutoff_freq_hz=0.1, filter_type='highpass', order=4, zero_phase=True)),
speed_smoothing: BaseFilter = cf(ButterworthFilter(cutoff_freq_hz=1, filter_type='highpass', order=4, zero_phase=True)),
step_length_smoothing: BaseFilter = cf(HampelFilter(half_window_size=2, n_sigmas=3.0)),
max_interpolation_gap_s: float = 3,
step_length_scaling_factor: float = 1.14675,
)[source]#

Implementation of the stride length algorithm by Zijlstra (2003) [1] modified by Soltani (2021) [2].

This algorithms uses an inverted pendulum model to estimate the step length. The step length is then “smoothed” using a robust outlier removal approach to deal with missing initial contacts. The output stride length is reported as the average for each 1 second bin within the data.

For more details see the Notes section.

This is based on the implementation published as part of the mobilised project [3]. However, this implementation deviates from the original implementation in some places. For details, see the notes section and the examples.

Parameters:
step_length_scaling_factor

The scaling factor for the Zijlstra biomechanical model. The final step length per step, will be scaled by this factor. Possible attributes are step_length_scaling_factor_ms_ms and step_length_scaling_factor_ms_all.

acc_smoothing

Lowpass filter applied to the vertical acceleration before integration.

speed_smoothing

Lowpass filter applied to the calculated vertical velocity for integration.

step_length_smoothing

The filter used to smooth the step length. This is used to remove outliers in the step length (e.g. when initial contacts are not detected). The filter is applied twice, once to the raw step length and a second time on the interpolated step length values per second. We recommend to use a Hampel filter for this.

max_interpolation_gap_s

The maximum gap in seconds that is interpolated. If the gap is larger than this value, the second is filled with NaNs. We don’t fill “gaps” at the start and end of the recording, as we assume that gait sequences are cut anyway to start and end with a valid initial contact.

orientation_method

The orientation method for aligning the sensor axes with the global reference system. If this argument is not passed, orientation is not performed (default value: None).

Other Parameters:
data

The raw IMU data of the gait sequence passed to the calculate method.

initial_contacts

The initial contacts passed to the calculate method.

sampling_rate_hz

The sampling rate of the IMU data in Hz passed to the calculate method.

sensor_height_m

Height of the sensor mounted on the lower-back in meters.

Attributes:
stride_length_per_sec_

The main output of the stride length calculation. It provides a DataFrame with the column stride_length_m that contains the stride length values with one value per full second of the provided data. The unit is m. The index of this dataframe is named sec_center_samples and contains the sample number of the center of the each second.

raw_step_length_per_step_

Secondary output. It provides a copy of the passed initial contact df with an additional column step_length_m that contains the estimated step length for each step. Note, that the df is one row shorter thant the passed list of ICs, as there is one less actual step then ICs.

step_length_per_sec_

Secondary output. A pandas dataframe of the same shape as stride_length_per_sec_, but containing the intermediate step length values in a column called step_length_m

Notes

The core algorithm performs the following steps:

  1. Sensor alignment (optional): Madgwick complementary filter

  2. High-pass filtering –> lower cut-off: 0.1 Hz, filter design: Butterworth IIR, filter order: 4

  3. Integration of vertical acceleration –> vertical speed

  4. Drift removal (high-pass filtering) –> lower cut-off: 1 Hz, filter design: Butterworth IIR, filter order: 4

  5. Integration of vertical speed –> vertical displacement d(t)

  6. Compute total vertical displacement during the step (d_step):

    \[d_step = |max(d(t)) - min(d(t))|\]
  7. Biomechanical model:

    \[\text{StepLength} = A * 2 * \sqrt{2 * LBh * d_{step} - d_{step}^2}\]
    A

    tuning coefficient, optimized by grid search.

    LBh

    sensor height in meters, representative of the height of the center of mass.

This output is then further post-processed and interpolated to second bins:

  1. We calculate the step length from the initial contacts. This results in one less step time than initial contacts. We replicate the last step length to get the same number of step lengths as initial contacts, to also provide a step length value for the last initial contact for the last part of a gait sequence.

  2. We smooth the step length to remove outliers (using a Hampel filter by default).

  3. We calculate the step length per second by averaging the step length over the second. This is a little tricky, as some seconds might not contain any initial contacts. For all seconds that contain at least one initial contact, we calculate the average step length over the second. The step length for seconds without initial contacts is interpolated linearly based on the surrounding values. If the gap is larger than the specified maximum interpolation gap, the second is filled with NaNs. We don’t fill “gaps” at the start and end of the recording, as we assume that gait sequences are cut anyway to start and end with a valid initial contact.

  4. We smooth the step length per second again to remove outliers on a per second level.

  5. We multiply by 2 to convert from step length per second to stride length per second. This is an approximation and will not reflect the exact stride length of any individual stride.

In case there are less initial contacts than the window size of the smoothing filter, we return NaNs for all seconds.

This approach deviates from the original Matlab implementations in a couple of ways:

  1. The original Matlab implementation has no concept of “maximum interpolation gap”. Values are interpolated by taking the average of the surrounding values, independent of the length of the gap. We decided to introduce a maximum interpolation gap to not mask “issues” in the ic-detection.

  2. When values are interpolated, the original Matlab implementation uses the average of the surrounding values. We decided to use linear interpolation instead, as this is more robust to outliers. These approaches are identical, if we only need to interpolate a single value, but for larger gaps, linear interpolation will start favoring the “closer” neighboring values, which we think is more appropriate.

[1]

W. Zijlstra, & A. L. Hof, “Assessment of spatio-temporal gait parameters from trunk accelerations during human walking” Gait & posture, vol. 18, no. 2, pp. 1-10, 2003.

[2]

A. Soltani, et al. “Algorithms for walking speed estimation using a lower-back-worn inertial sensor: A cross-validation on speed ranges.” IEEE TNSRE, vol 29, pp. 1955-1964, 2021.

Methods

PredefinedParameters()

Predefined factors for scaling the step length model.

calculate(data, initial_contacts, *, ...)

Calculate per-sec stride length values in the passed data..

clone()

Create a new instance of the class with all parameters copied over.

get_params([deep])

Get parameters for this algorithm.

set_params(**params)

Set the parameters of this Algorithm.

__init__(
*,
orientation_method: BaseOrientationEstimation | None = None,
acc_smoothing: BaseFilter = cf(ButterworthFilter(cutoff_freq_hz=0.1, filter_type='highpass', order=4, zero_phase=True)),
speed_smoothing: BaseFilter = cf(ButterworthFilter(cutoff_freq_hz=1, filter_type='highpass', order=4, zero_phase=True)),
step_length_smoothing: BaseFilter = cf(HampelFilter(half_window_size=2, n_sigmas=3.0)),
max_interpolation_gap_s: float = 3,
step_length_scaling_factor: float = 1.14675,
) None[source]#
class PredefinedParameters[source]#

Predefined factors for scaling the step length model.

The step length scaling factor to be used in the biomechanical model. The provided values are optimized based on the MsProject dataset as part of the Mobilise-D project.

Attributes:
step_length_scaling_factor_ms_ms

Optimized factor based on all MS patients within the MsProject dataset. Default value for the SlZijlstra algorithm.

step_length_scaling_factor_ms_all

Optimized factor based on ALL participants within the MsProject dataset.

Examples

>>> SlZijlstra(
...     max_interpolation_gap_s=2,
...     **SlZijlstra.PredefinedParameters.step_length_scaling_factor_ms_ms,
... )
calculate(
data: DataFrame,
initial_contacts: DataFrame,
*,
sampling_rate_hz: float,
sensor_height_m: float,
**_: Unpack[dict[str, Any]],
) Self[source]#

Calculate per-sec stride length values in the passed data..

Parameters:
data

The raw IMU data of a single sensor. We usually assume that this is one gait sequence (i.e. that there are no non-walking periods in the data).

initial_contacts

The initial contacts of the gait sequence. This should be passed as a DataFrame with the colum ic that contains the sample number of the initial contacts. We usually assume that the first IC marks the start of the passed gait sequence and the last IC marks the end.

sampling_rate_hz

The sampling rate of the IMU data in Hz.

Returns:
self

The instance of the class with the stride_length_per_sec_list_ attribute set to the estimated stride length per second values.

clone() Self[source]#

Create a new instance of the class with all parameters copied over.

This will create a new instance of the class itself and all nested objects

get_params(deep: bool = True) dict[str, Any][source]#

Get parameters for this algorithm.

Parameters:
deep

Only relevant if object contains nested algorithm objects. If this is the case and deep is True, the params of these nested objects are included in the output using a prefix like nested_object_name__ (Note the two “_” at the end)

Returns:
params

Parameter names mapped to their values.

set_params(**params: Any) Self[source]#

Set the parameters of this Algorithm.

To set parameters of nested objects use nested_object_name__para_name=.

Examples using mobgap.stride_length.SlZijlstra#

Custom Data and Datasets

Custom Data and Datasets

The Mobilise-D pipeline: Step-by-Step Breakdown

The Mobilise-D pipeline: Step-by-Step Breakdown

SL Zijlstra

SL Zijlstra

Stride Length Evaluation

Stride Length Evaluation