Skip to content

Commit

Permalink
Merge pull request #25 from stanfordnmbl/dev-squat-analysis
Browse files Browse the repository at this point in the history
update squat analysis metrics and squat type detection
  • Loading branch information
carmichaelong authored Jun 10, 2024
2 parents 7e2472b + d5c77a2 commit 01c81f8
Show file tree
Hide file tree
Showing 2 changed files with 241 additions and 102 deletions.
116 changes: 40 additions & 76 deletions squat_analysis/function/handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,82 +70,45 @@ def handler(event, context):
lowpass_cutoff_frequency_for_coordinate_values=filter_frequency,
n_repetitions=n_repetitions)
squat_events = squat.get_squat_events()

max_knee_flexion_angle_r_mean, max_knee_flexion_angle_r_std, _ = squat.compute_peak_angle('knee_angle_r')
max_knee_flexion_angle_l_mean, max_knee_flexion_angle_l_std, _ = squat.compute_peak_angle('knee_angle_l')
max_knee_flexion_angle_mean_mean = np.round(np.mean(np.array([max_knee_flexion_angle_r_mean, max_knee_flexion_angle_l_mean])))
max_knee_flexion_angle_mean_std = np.round(np.mean(np.array([max_knee_flexion_angle_r_std, max_knee_flexion_angle_l_std])))

max_hip_flexion_angle_r_mean, max_hip_flexion_angle_r_std, _ = squat.compute_peak_angle('hip_flexion_r')
max_hip_flexion_angle_l_mean, max_hip_flexion_angle_l_std, _ = squat.compute_peak_angle('hip_flexion_l')
max_hip_flexion_angle_mean_mean = np.round(np.mean(np.array([max_hip_flexion_angle_r_mean, max_hip_flexion_angle_l_mean])))
max_hip_flexion_angle_mean_std = np.round(np.mean(np.array([max_hip_flexion_angle_r_std, max_hip_flexion_angle_l_std])))

max_hip_adduction_angle_r_mean, max_hip_adduction_angle_r_std, _ = squat.compute_peak_angle('hip_adduction_r')
max_hip_adduction_angle_l_mean, max_hip_adduction_angle_l_std, _ = squat.compute_peak_angle('hip_adduction_l')
max_hip_adduction_angle_mean_mean = np.round(np.mean(np.array([max_hip_adduction_angle_r_mean, max_hip_adduction_angle_l_mean])))
max_hip_adduction_angle_mean_std = np.round(np.mean(np.array([max_hip_adduction_angle_r_std, max_hip_adduction_angle_l_std])))

rom_knee_flexion_angle_r_mean, rom_knee_flexion_angle_r_std, _ = squat.compute_range_of_motion('knee_angle_r')
rom_knee_flexion_angle_l_mean, rom_knee_flexion_angle_l_std, _ = squat.compute_range_of_motion('knee_angle_l')
rom_knee_flexion_angle_mean_mean = np.round(np.mean(np.array([rom_knee_flexion_angle_r_mean, rom_knee_flexion_angle_l_mean])))
rom_knee_flexion_angle_mean_std = np.round(np.mean(np.array([rom_knee_flexion_angle_r_std, rom_knee_flexion_angle_l_std])))

# Detect squat type
eventTypes = squat.squatEvents['eventTypes']

# Return squat type (none detected, more than one type, or all same type).
squat_type = ''
unique_types = set(eventTypes)
if len(unique_types) < 1:
squat_type = 'No squats detected'

elif len(unique_types) > 1:
squat_type = 'Mixed squat types detected'

else:
if eventTypes[0] == 'double_leg':
squat_type = 'Double leg squats'
elif eventTypes[0] == 'single_leg_l':
squat_type = 'Single leg squats (left)'
elif eventTypes[0] == 'single_leg_r':
squat_type = 'Single leg squats (right)'

# Compute metrics.
max_trunk_lean_ground_mean, max_trunk_lean_ground_std, max_trunk_lean_ground_units = squat.compute_trunk_lean_relative_to_ground()
max_trunk_flexion_mean, max_trunk_flexion_std, max_trunk_flexion_units = squat.compute_trunk_flexion_relative_to_ground()
squat_depth_mean, squat_depth_std, squat_depth_units = squat.compute_squat_depth()

# Store metrics dictionary.
squat_scalars = {}
squat_scalars['peak_knee_flexion_angle_mean'] = {'value': max_knee_flexion_angle_mean_mean}
squat_scalars['peak_knee_flexion_angle_mean']['label'] = 'Mean peak knee flexion angle (deg)'
squat_scalars['peak_knee_flexion_angle_mean']['colors'] = ["red", "yellow", "green"]
peak_knee_flexion_angle_threshold = 100
squat_scalars['peak_knee_flexion_angle_mean']['min_limit'] = float(np.round(0.90*peak_knee_flexion_angle_threshold))
squat_scalars['peak_knee_flexion_angle_mean']['max_limit'] = float(peak_knee_flexion_angle_threshold)

squat_scalars['peak_knee_flexion_angle_std'] = {'value': max_knee_flexion_angle_mean_std}
squat_scalars['peak_knee_flexion_angle_std']['label'] = 'Std peak knee flexion angle (deg)'
squat_scalars['peak_knee_flexion_angle_std']['colors'] = ["green", "yellow", "red"]
std_threshold_min = 2
std_threshold_max = 4
squat_scalars['peak_knee_flexion_angle_std']['min_limit'] = float(std_threshold_min)
squat_scalars['peak_knee_flexion_angle_std']['max_limit'] = float(std_threshold_max)

squat_scalars['peak_hip_flexion_angle_mean'] = {'value': max_hip_flexion_angle_mean_mean}
squat_scalars['peak_hip_flexion_angle_mean']['label'] = 'Mean peak hip flexion angle (deg)'
squat_scalars['peak_hip_flexion_angle_mean']['colors'] = ["red", "yellow", "green"]
peak_hip_flexion_angle_threshold = 100
squat_scalars['peak_hip_flexion_angle_mean']['min_limit'] = float(np.round(0.90*peak_hip_flexion_angle_threshold))
squat_scalars['peak_hip_flexion_angle_mean']['max_limit'] = float(peak_hip_flexion_angle_threshold)

squat_scalars['peak_hip_flexion_angle_std'] = {'value': max_hip_flexion_angle_mean_std}
squat_scalars['peak_hip_flexion_angle_std']['label'] = 'Std peak hip flexion angle (deg)'
squat_scalars['peak_hip_flexion_angle_std']['colors'] = ["green", "yellow", "red"]
squat_scalars['peak_hip_flexion_angle_std']['min_limit'] = float(std_threshold_min)
squat_scalars['peak_hip_flexion_angle_std']['max_limit'] = float(std_threshold_max)

squat_scalars['peak_knee_adduction_angle_mean'] = {'value': max_hip_adduction_angle_mean_mean}
squat_scalars['peak_knee_adduction_angle_mean']['label'] = 'Mean peak knee adduction angle (deg)'
squat_scalars['peak_knee_adduction_angle_mean']['colors'] = ["red", "green", "red"]
knee_adduction_angle_threshold = 5
squat_scalars['peak_knee_adduction_angle_mean']['min_limit'] = float(-knee_adduction_angle_threshold)
squat_scalars['peak_knee_adduction_angle_mean']['max_limit'] = float(knee_adduction_angle_threshold)

squat_scalars['peak_knee_adduction_angle_std'] = {'value': max_hip_adduction_angle_mean_std}
squat_scalars['peak_knee_adduction_angle_std']['label'] = 'Std peak knee adduction angle (deg)'
squat_scalars['peak_knee_adduction_angle_std']['colors'] = ["green", "yellow", "red"]
squat_scalars['peak_knee_adduction_angle_std']['min_limit'] = float(std_threshold_min)
squat_scalars['peak_knee_adduction_angle_std']['max_limit'] = float(std_threshold_max)

squat_scalars['rom_knee_flexion_angle_mean'] = {'value': rom_knee_flexion_angle_mean_mean}
squat_scalars['rom_knee_flexion_angle_mean']['label'] = 'Mean range of motion knee flexion angle (deg)'
squat_scalars['rom_knee_flexion_angle_mean']['colors'] = ["red", "yellow", "green"]
rom_knee_flexion_angle_threshold_min = 85
rom_knee_flexion_angle_threshold_max = 115
squat_scalars['rom_knee_flexion_angle_mean']['min_limit'] = float(rom_knee_flexion_angle_threshold_min)
squat_scalars['rom_knee_flexion_angle_mean']['max_limit'] = float(rom_knee_flexion_angle_threshold_max)

squat_scalars['rom_knee_flexion_angle_std'] = {'value': rom_knee_flexion_angle_mean_std}
squat_scalars['rom_knee_flexion_angle_std']['label'] = 'Std range of motion knee flexion angle (deg)'
squat_scalars['rom_knee_flexion_angle_std']['colors'] = ["green", "yellow", "red"]
squat_scalars['rom_knee_flexion_angle_std']['min_limit'] = float(std_threshold_min)
squat_scalars['rom_knee_flexion_angle_std']['max_limit'] = float(std_threshold_max)
squat_scalars['max_trunk_lean_ground'] = {'value': np.round(max_trunk_lean_ground_mean, 2),
'std': np.round(max_trunk_lean_ground_std, 2),
'label': 'Mean max trunk lean (deg)'}

squat_scalars['max_trunk_flexion'] = {'value': np.round(max_trunk_flexion_mean, 2),
'std': np.round(max_trunk_flexion_std, 2),
'label': 'Mean max trunk flexion (deg)'}

squat_scalars['squat_depth'] = {'value': np.round(squat_depth_mean, 2),
'std': np.round(squat_depth_std, 2),
'label': 'Mean squat depth (m)'}

# %% Return indices for visualizer and line curve plot.
# %% Create json for deployement.
Expand Down Expand Up @@ -178,13 +141,14 @@ def handler(event, context):
y_axes.remove('mtp_angle_r')
y_axes.remove('mtp_angle_l')

# Create results dictionnary.
# Create results dictionary.
results = {
'indices': times,
'metrics': squat_scalars,
'datasets': datasets,
'x_axis': 'time',
'y_axis': y_axes}
'y_axis': y_axes,
'squat_type': squat_type}

return {
'statusCode': 200,
Expand Down
Loading

0 comments on commit 01c81f8

Please sign in to comment.