====== Normalize EMG to MVC ======
**Normalize EMG Signal to Maximum Within Gait Cycle**
For this example, the goal is as follows:
* Process the raw EMG signal
* In this example, a simple linear envelope was done and saved in ANALOG::ENVELOPE
* Compute the maximum of the EMG signal during each gait cycle
* Normalize the EMG signal to the maximum during each gait cycle
===== Sample Files =====
[[https://www.has-motion.com/download/examples/EMG/Maximum_Per_Cycle.zip|You can download the sample files here.]]
The download contains:
* Sample_Workspace.cmz with -
* 2 Dynamic Trials
* Processed EMG signals (ANALOG::ENVELOPE), you should use your own processing, not the example processing used to create this envelope signal
* EMG_Norm_Gait_Cycle.v3s (Example Pipeline Script)
* EMG_Norm_Gait_Cycle.v3m (Example Meta-Command)
* Call_EMG_Norm_Gait_Cycle_Meta.v3s (Example Pipeline Script calling Meta-Command)
===== Script Explanation =====
|The image on the right corresponds to four steps in the pipeline below. As a general outline, the procedure has the following checkpoints:\\ \\ * First graph: raw EMG signal with gait events RHS (pink) and RTO (purple). These correspond to the signal within ANALOG::ORIGINAL in the Sample_Workspace.cmz download.\\ * Second graph: the linear envelope (ANALOG::ENVELOPE) showing the rectified and smoothed signal with maximums A (~0.76) and B (~0.69).\\ * Third graph: all frames within each gait cycle set to the maximum value, and all other frames are set to zero.\\ * Fourth graph: the linear envelope signal is divided by the maximum of the corresponding gait cycle. Notice how the peaks of both cycles are now 1, and all values outside the defined event sequence are set to "data not found". | {{:EMG_Normalized_Gait_Cycle.png}}|
===== Example Pipeline Script =====
Set_Pipeline_Parameter
/PARAMETER_NAME=R_EMG_SIGNAL_LIST
/PARAMETER_VALUE=EMG_A+EMG_B+EMG_C
! /PARAMETER_VALUE_SEARCH_FOR=
! /PARAMETER_VALUE_REPLACE_WITH=
! /PARAMETER_VALUE_PREFIX=
! /PARAMETER_VALUE_APPEND=
! /MULTI_PASS=FALSE
;
Set_Pipeline_Parameter
/PARAMETER_NAME=L_EMG_SIGNAL_LIST
/PARAMETER_VALUE=EMG_A+EMG_B+EMG_C
! /PARAMETER_VALUE_SEARCH_FOR=
! /PARAMETER_VALUE_REPLACE_WITH=
! /PARAMETER_VALUE_PREFIX=
! /PARAMETER_VALUE_APPEND=
! /MULTI_PASS=FALSE
;
Select_Active_File
/FILE_NAME=ALL_FILES
! /QUERY=
;
Event_Define_Event_Sequence
/EVENT_SEQUENCE_NAME=RCYCLE_SEQUENCE
/EVENT_SEQUENCE=RHS+RTO
! /EXCLUDE_EVENTS=
! /INSIDE_OF_SEQUENCE=
! /OFFSET_FROM_START=
! /OFFSET_FROM_END=
! /OFFSET_BY=PERCENT
/EVENT_SEQUENCE_INSTANCE=0
;
Event_Define_Event_Sequence
/EVENT_SEQUENCE_NAME=LCYCLE_SEQUENCE
/EVENT_SEQUENCE=LHS+LTO
! /EXCLUDE_EVENTS=
! /INSIDE_OF_SEQUENCE=
! /OFFSET_FROM_START=
! /OFFSET_FROM_END=
! /OFFSET_BY=PERCENT
/EVENT_SEQUENCE_INSTANCE=0
;
For_Each
/ITERATION_PARAMETER_NAME=SIDE
! /ITERATION_PARAMETER_COUNT_NAME=
/ITEMS=R+L
;
Set_Pipeline_Parameter
/PARAMETER_NAME=EMG_SIGNAL_LIST
/PARAMETER_VALUE=:&:&::SIDE&_EMG_SIGNAL_LIST
! /PARAMETER_VALUE_SEARCH_FOR=
! /PARAMETER_VALUE_REPLACE_WITH=
! /PARAMETER_VALUE_PREFIX=
! /PARAMETER_VALUE_APPEND=
/MULTI_PASS=TRUE
;
! ---------------------------------------------------------------------
! Get the maximum number of cycles in a trial the entire CMZ file
! ---------------------------------------------------------------------
! We're going to loop over every sequence in the trial,
! so first calculate the maximum number of sequences in the longest trial
! Count the number of cycles in each trial
Metric_Event_Sequence_Count
/RESULT_METRIC_FOLDER=EMG_NORM
/RESULT_METRIC_NAME=&::SIDE&Cycle_Count
! /TIME_INTERVAL=
/EVENT_SEQUENCE=&::SIDE&CYCLE_SEQUENCE
/EXCLUDE_EVENTS=
/GENERATE_COUNT_TOTAL_IN_GLOBAL=FALSE
;
! Calculate the maximum number of cycles across all trials
Metric_Maximum
/RESULT_METRIC_FOLDER=EMG_NORM
/RESULT_METRIC_NAME=&::SIDE&Cycle_Count_Global
! /APPLY_AS_SUFFIX_TO_SIGNAL_NAME=FALSE
/SIGNAL_TYPES=METRIC
/SIGNAL_FOLDER=EMG_NORM
/SIGNAL_NAMES=&::SIDE&Cycle_Count
/COMPONENT_SEQUENCE=ALL
/EVENT_SEQUENCE=
/EXCLUDE_EVENTS=
/SEQUENCE_PERCENT_START=
/SEQUENCE_PERCENT_END=
/GENERATE_MEAN_AND_STDDEV=FALSE
! /APPEND_TO_EXISTING_VALUES=FALSE
/CREATE_GLOBAL_MAXIMUM=TRUE
;
! Create a list from 1 to the maximum event sequence in steps of 1
Set_Pipeline_Parameter_From_For_Loop
/PARAMETER_NAME=SEQUENCE_COUNT
/PARAMETER_INDEX_START=1
/PARAMETER_INDEX_END=GLOBAL::METRIC::EMG_NORM::&::SIDE&Cycle_Count_Global_Max
/PARAMETER_INDEX_STEP=1
/PARAMETER_INDEX_TYPE=INTEGER
;
! ---------------------------------------------------------------------
! Create Normalize Signal
! ---------------------------------------------------------------------
! Create a temporary signal which contains zeros at all frames
Evaluate_Expression
/EXPRESSION=CURRENT_SIGNAL * 0
/SIGNAL_TYPES=ANALOG
/SIGNAL_FOLDER=ENVELOPE
/SIGNAL_NAMES=::EMG_SIGNAL_LIST
/RESULT_TYPES=DERIVED
/RESULT_FOLDERS=EMG_NORM_TEMP
/RESULT_NAME=
/APPLY_AS_SUFFIX_TO_SIGNAL_NAME=TRUE
;
! Loop over every sequence
For_Each
/ITERATION_PARAMETER_NAME=SEQUENCE_CURRENT
! /ITERATION_PARAMETER_COUNT_NAME=
/ITEMS=::SEQUENCE_COUNT
;
! Define the "current" sequence as TEMP_CYCLE
Event_Define_Event_Sequence
/EVENT_SEQUENCE_NAME=TEMP_CYCLE
/EVENT_SEQUENCE=&::SIDE&CYCLE_SEQUENCE
! /EXCLUDE_EVENTS=
! /INSIDE_OF_SEQUENCE=
! /OFFSET_FROM_START=
! /OFFSET_FROM_END=
! /OFFSET_BY=PERCENT
/EVENT_SEQUENCE_INSTANCE=::SEQUENCE_CURRENT
;
! Loop over every EMG Signal
For_Each
/ITERATION_PARAMETER_NAME=EMG_SIGNAL_CURRENT
! /ITERATION_PARAMETER_COUNT_NAME=
/ITEMS=::EMG_SIGNAL_LIST
;
! For all frames during the current cycle,
! set the values to the maximum of the EMG signal
Set_Data_To_New_Values
/SIGNAL_TYPES=DERIVED
/SIGNAL_FOLDER=EMG_NORM_TEMP
/SIGNAL_NAMES=::EMG_SIGNAL_CURRENT
! /SIGNAL_COMPONENTS=ALL_COMPONENTS
/RESULT_TYPES=DERIVED
/RESULT_FOLDERS=EMG_NORM_TEMP
/RESULT_SUFFIX=
/EVENT_SEQUENCE=TEMP_CYCLE
! /EXCLUDE_EVENTS=
! /START_FRAME=
! /END_FRAME=
! /USE_POINT_RATE=TRUE
/REPLACEMENT_VALUES=METRIC_MAXIMUM(ANALOG::ENVELOPE::&::EMG_SIGNAL_CURRENT&,EVENT_LABEL::SEQUENCE::TEMP_CYCLE)
! /THRESHOLD_HIGH=NO_DATA
! /THRESHOLD_LOW=NO_DATA
! /SET_DATA_AT_SINGLE_EVENT=FALSE
;
End_For_Each
/ITERATION_PARAMETER_NAME=EMG_SIGNAL_CURRENT
;
End_For_Each
/ITERATION_PARAMETER_NAME=SEQUENCE_CURRENT
;
! ---------------------------------------------------------------------
! Normalize EMG Signal
! ---------------------------------------------------------------------
! Divide the EMG signals by the temporary signal above
For_Each
/ITERATION_PARAMETER_NAME=EMG_SIGNAL_CURRENT
! /ITERATION_PARAMETER_COUNT_NAME=
/ITEMS=::EMG_SIGNAL_LIST
;
Evaluate_Expression
/EXPRESSION=ANALOG::ENVELOPE::&::EMG_SIGNAL_CURRENT& / DERIVED::EMG_NORM_TEMP::&::EMG_SIGNAL_CURRENT&
! /SIGNAL_TYPES=
! /SIGNAL_FOLDER=
! /SIGNAL_NAMES=
/RESULT_TYPES=DERIVED
/RESULT_FOLDERS=EMG_NORM
/RESULT_NAME=&::EMG_SIGNAL_CURRENT&
! /APPLY_AS_SUFFIX_TO_SIGNAL_NAME=FALSE
;
End_For_Each
/ITERATION_PARAMETER_NAME=EMG_SIGNAL_CURRENT
;
End_For_Each
/ITERATION_PARAMETER_NAME=SIDE
;
===== Calling Example Meta-Command =====
Once the example Meta-Command from the download is placed in your [[Visual3D:Documentation:Pipeline:Meta_Commands:Meta_Commands_Overview|Meta-Command folder]], you can call the command using the following syntax:
EMG_Norm_Gait_Cycle
/EMG_INPUT_FOLDER=ENVELOPE
/EMG_OUTPUT_FOLDER=EMG_NORM
/R_EMG_SIGNAL_LIST=EMG_A+EMG_B+EMG_C
/R_EVENT_SEQUENCE=RHS+RTO
/L_EMG_SIGNAL_LIST=EMG_A_L+EMG_B_L+EMG_C_L
/L_EVENT_SEQUENCE=LHS+LTO
;
* **EMG_INPUT_FOLDER** should be the EMG signals you want to normalized
* **EMG_OUTPUT_FOLDER** should be the resulting EMG folder for the normalized signals
* **R_EMG_SIGNAL_LIST** the list of right side EMG signals to normalize to the right gait cycle
* **R_EVENT_SEQUENCE** events which indicate the right gait cycle (this example normalizes to stance, but you probably want to normalize to the entire gait cycle)
* **L_EMG_SIGNAL_LIST** the list of left side EMG signals to normalize to the left gait cycle
* **R_EVENT_SEQUENCE** events which indicate the left gait cycle (this example normalizes to stance, but you probably want to normalize to the entire gait cycle)