Tutorial EMG

From Software Product Documentation
Jump to navigation Jump to search
Language:  English  • français • italiano • português • español 

Introduction

EMG stands for electromyography, which is the study of electrical signals from active muscles that are receiving input from the central nervous system. More information on EMG can be found in most good biomechanics and motor control textbooks, and on Wikipedia. Additional information on EMG processing requirements for the International Society of Electrophysiology and Kinesiology. You will also find other useful information on EMG signal processing posted by [Noraxon]

This tutorial will show the basic steps for processing EMG data into Visual3D. Files used in this tutorial can be found here: [EMG Tutorial]

The tutorial will go through a step by step process using Visual 3D pipeline commands to process the example EMG data. It will do the following:

  1. Open Files and Tags
  2. Create Pipeline Parameter "EMG Signals"
  3. Apply a Band Pass Filter
  4. Compute an Envelope
  5. Normalize based off of 4 different methods

Visual 3D Implementation

Visual3D expects the EMG signals to be stored in the C3D file as ANALOG data, not unlike force platform data. One point of importance is that EMG typically has very high frequency content, which means that it must be sampled at high data sampling rates.

As of Visual3D Version 5.0 EMG signals are stored as integer multiples of the Motion Capture sampling rate (e.g. consistent with the common use of the C3D file format). One of the nuisances caused by this requirement is that force platform data must be sampled at a very high rate in order to match the EMG sampling rate.

All signal processing commands in the pipeline can be used with EMG data.

EMG Processing using Visual 3D Pipeline commands

The tutorial will go through a step by step process using Visual 3D pipeline commands to process the example EMG data.

Open Files and Tag

This section will walk you through pipeline commands that will open files and automatically assign tags to motion files for Movement and MVIC trials.

!--------------------------------------------------------------------------------------
! Open files and automatically assign tag to motion files for Movement and MVIC trials
! --------------------------------------------------------------------------------------
 
! Fresh Workspace
File_New
;

!Open files
File_Open
! /FILE_NAME=
;

!Assign Tags to MVC_A file
Assign_Tags_To_File
/MOTION_FILE_NAMES=*MVC_A.C3D
! /QUERY=
/TAGS=MVC_A
;

!Assign Tags to MVC_B file
Assign_Tags_To_File
/MOTION_FILE_NAMES=*MVC_B.C3D
! /QUERY=
/TAGS=MVC_B
;

!Assign Tags to MVC_C file
Assign_Tags_To_File
/MOTION_FILE_NAMES=*MVC_C.C3D
! /QUERY=
/TAGS=MVC_C
';

!Assign Tags to walking files
Assign_Tags_To_File
/MOTION_FILE_NAMES=*trial*.C3D
!/QUERY=
/TAGS=MOVEMENT
;

Create Pipeline Parameter "EMG Signals"

Use the command Set_Pipeline_Parameter, to set up a pipeline parameter to contain the EMG signal names.

Set_Pipeline_Parameter
/PARAMETER_NAME=EMG_SIGNALS
/PARAMETER_VALUE=EMG_A+EMG_B+EMG_C
;

Apply a Band Pass Filter

Apply a band pass filter to the EMG data with the commands Highpass_Filter and Lowpass_Filter. Surface EMG signals have a frequency content between 20 and 500 Hz.

Note: that the highpass filter often has a cutoff frequency anywhere between 20 and 50 Hz. The purpose of this high pass filter is to remove movement artifact (low frequency content).

!-------------------------------------------------------------
! Apply a band pass filter - highpass and lowpass filter
!------------------------------------------------------------- 

! Select active file ALL_FILES
Select_Active_File 
/FILE_NAME=ALL_FILES 
! /QUERY= 
;

! Apply highpass filter with 50 Hz cutoff
Highpass_Filter 
/Signal_Types=ANALOG 
/SIgnal_Names=::EMG_SIGNALS 
/Signal_Folder=ORIGINAL 
! /Result_Suffix= 
! /Result_Folder=PROCESSED 
! /Filter_Class=BUTTERWORTH 
/Frequency_Cutoff=50 
/Num_Reflected= 0 
/Total_Buffer_Size=100 
/Num_Bidirectional_Passes=1 
; 

! Apply lowpass filter with 500 Hz cutoff
Lowpass_Filter 
/Signal_Types=ANALOG 
/SIgnal_Names=::EMG_SIGNALS 
/Signal_Folder=PROCESSED 
! /Result_Suffix= 
! /Result_Folder=PROCESSED 
! /Filter_Class=BUTTERWORTH 
/Frequency_Cutoff=500 
/Num_Reflected= 0 
/Total_Buffer_Size=100 
/Num_Bidirectional_Passes=1 
;

Compute an Envelope

Compute the linear envelope of the EMG signal by calculating the RMS value using the command Moving_RMS for short successive time periods (the time period is described as a moving window). More information on calculating the RMS can be found at the Moving RMS page.

The command below will apply a moving RMS with a 100ms window

!-------------------------------------------
! Apply a moving RMS with a 100ms window
!-------------------------------------------
Moving_RMS 
/SIGNAL_TYPES=ANALOG 
/SIGNAL_NAMES=::EMG_SIGNALS 
/SIGNAL_FOLDER=PROCESSED 
! /RESULT_SUFFIX= 
/RESULT_FOLDER=RMS 
/NUM_WINDOW_FRAMES=1+0.1*PARAMETER::ANALOG::RATE 
;

Create Normalization Factor and Scale

There are four alternative normalization methods describes in this tutorial.

  1. Normalize EMG to MVC
  2. Normalize EMG to Generic Global Variable
  3. Normalize to Maximum 30 second interval
  4. Normalize to the Maximum within each Gait Cycle

The beginning of the pipeline for all methods is as follows.

!--------------------------------------------------------------------------------------------------
! There are often artifacts in the EMG signal related to filtering, so we want to ignore the first
! and last 50 frames of data (at POINT rate) when we create the normalization (scale) factors
!--------------------------------------------------------------------------------------------------

Event_Explicit
/EVENT_NAME=START
/FRAME=50
! /TIME=
;

Event_Explicit
/EVENT_NAME=END
/FRAME=250
! /TIME=
;

!---------------------------------------------------
! Set Pipeline parameters for START and END events
!---------------------------------------------------

Set_Pipeline_Parameter 
/PARAMETER_NAME=START_AT_EVENT 
/PARAMETER_VALUE=START 
;

Set_Pipeline_Parameter 
/PARAMETER_NAME=END_AT_EVENT  
/PARAMETER_VALUE=END 
;

The following for loop will loop through each of the signals and first create a global event for the maximum and mean.

!---------------------------------------------------
! Loop to calculate scale to normalize EMG signals
!---------------------------------------------------
For_Each 
/ITERATION_PARAMETER_NAME=INDEX 
/ITEMS=::EMG_SIGNALS 
;

! Create an event at the maximum value
Event_Global_Maximum 
/SIGNAL_TYPES=ANALOG 
/SIGNAL_NAMES=::INDEX 
/SIGNAL_FOLDER=RMS 
/EVENT_NAME=EMG_MAX 
/SELECT_X=TRUE 
! /SELECT_Y=FALSE 
! /SELECT_Z=FALSE 
/START_AT_EVENT=::START_AT_EVENT 
/END_AT_EVENT=::END_AT_EVENT 
;

! Create an event MEAN_START 15 frames prior to the max event
Event_Copy 
/EVENT_NAME=EMG_MAX 
! /EVENT_INSTANCE=0 
! /START_AT_EVENT= 
! /END_AT_EVENT= 
/NEW_EVENT_NAME=MEAN_START 
/FRAME_OFFSET=-15 
;

! Create an event MEAN_END 15 frames after the max event
Event_Copy 
/EVENT_NAME=EMG_MAX 
! /EVENT_INSTANCE=0 
! /START_AT_EVENT= 
! /END_AT_EVENT= 
/NEW_EVENT_NAME=MEAN_END 
/FRAME_OFFSET=15 
; 

The mean of the signal and the maximum of the signal will then be computed.

! Calculate mean of EMG signal between MEAN_START and MEAN_END 
Metric_Mean 
/RESULT_METRIC_NAME=::INDEX&_SCALE 
! /APPLY_AS_SUFFIX_TO_SIGNAL_NAME=FALSE 
/RESULT_METRIC_FOLDER=EMG_SCALE 
/SIGNAL_TYPES=ANALOG 
/SIGNAL_NAMES=::INDEX 
/SIGNAL_FOLDER=RMS 
/SIGNAL_COMPONENTS=0 
/EVENT_SEQUENCE=MEAN_START+MEAN_END 
/EXCLUDE_EVENTS= 
/GENERATE_MEAN_AND_STDDEV=FALSE 
! /APPEND_TO_EXISTING_VALUES=FALSE 
;

! calculate the global maximum of the mean signal
Metric_Maximum 
/RESULT_METRIC_NAME=_MAX 
/APPLY_AS_SUFFIX_TO_SIGNAL_NAME=TRUE 
/RESULT_METRIC_FOLDER=PROCESSED 
/SIGNAL_TYPES=METRIC 
/SIGNAL_NAMES=::INDEX&_SCALE 
/SIGNAL_FOLDER=EMG_SCALE 
! /SIGNAL_COMPONENTS=ALL_COMPONENTS 
/EVENT_SEQUENCE= 
/EXCLUDE_EVENTS= 
! /GENERATE_MEAN_AND_STDDEV=TRUE 
! /APPEND_TO_EXISTING_VALUES=FALSE 
/CREATE_GLOBAL_MAXIMUM=FALSE 
;

! Clean up some temporary signals 
Event_Delete 
/EVENT_NAME=EMG_MAX+MEAN_START+MEAN_END 
! /EVENT_SEQUENCE= 
! /EXCLUDE_EVENTS= 
! /TIME= 
;

Remove_Signals 
/SIGNAL_TYPES=METRIC 
/SIGNAL_NAMES=::INDEX&_SCALE_MAX 
/SIGNAL_FOLDER=PROCESSED 
;

End_For_Each 
/ITERATION_PARAMETER_NAME=INDEX 
;

Normalize EMG to MVC

This pipeline normalizes the EMG signal by dividing the EMG signal by the maximum value of the EMG signal from a separate trial (e.g. a trial that elicits a maximum voluntary contraction (MVC) from the muscle).

! ------------------------------------------------------------- 
!   Normalize
! -------------------------------------------------------------

! For each EMG signal, divide by it's maximum value

For_Each
/Iteration_Parameter_Name=CURRENT_SIGNAL
/Items=EMG_A+EMG_B+EMG_C
;

 ! Divide the EMG signal by it's maximum value, which was stored automatically in the GLOBAL workspace 

 Evaluate_Expression
 /EXPRESSION= ANALOG::ORIGINAL::&::CURRENT_SIGNAL& / GLOBAL::METRIC::MVC_MAX::&::CURRENT_SIGNAL&_MAX
 ! /SIGNAL_TYPES=
 ! /SIGNAL_FOLDER=ORIGINAL
 ! /SIGNAL_NAMES=
 /RESULT_TYPES=ANALOG
 /RESULT_FOLDERS=NORMALIZED
 /RESULT_NAME=::CURRENT_SIGNAL
 ! /APPLY_AS_SUFFIX_TO_SIGNAL_NAME=FALSE
 ;

End_For_Each 
/Iteration_Parameter_Name= CURRENT_SIGNAL
;

Normalize EMG to Generic Global Variable

Typically the scaling factors for EMG signals are the result of other data trials (such as MVIC trials). Individual files in Visual3D cannot access the data from other files, so the only way signals can be shared is through the Global Workspace. This pipeline normalizes the EMG signal to a global variable.

! ------------------------------------------------------------- 
!   Normalize
! -------------------------------------------------------------

! For each EMG signal, divide by it's maximum value

For_Each
/Iteration_Parameter_Name=CURRENT_SIGNAL
/Items=EMG_A+EMG_B+EMG_C
;

 ! Divide the EMG signal by it's maximum value, which was stored in the GLOBAL workspace 

 Evaluate_Expression 
 /EXPRESSION= ANALOG::ORIGINAL::&::CURRENT_SIGNAL& / GLOBAL::METRIC::MVC_MAX::&::CURRENT_SIGNAL&_MAX
 ! /SIGNAL_TYPES=
 ! /SIGNAL_FOLDER=ORIGINAL
 ! /SIGNAL_NAMES=
 /RESULT_TYPES=ANALOG
 /RESULT_FOLDERS=NORMALIZED
 /RESULT_NAME=::CURRENT_SIGNAL
 ! /APPLY_AS_SUFFIX_TO_SIGNAL_NAME=FALSE
 ;

End_For_Each 
/Iteration_Parameter_Name= CURRENT_SIGNAL
;

Normalize to Maximum 30 second interval

This pipeline normalizes to the greatest ½ second activity during a 5 second trial. A window of twenty-five .02 second intervals of integrated EMG is moved one interval at a time across the 5 seconds of data to find the greatest EMG. The average integrated EMG during the ½ second is used to compute the normalization factor. It does the following:

  • Compute a normalization factor
  • Normalize to greatest ½ second during 5 second trial. This is accomplished by using an average filter (see the bottom of the page)
  • A window of twenty-five .02 second intervals of integrated EMG is moved one interval at a time across the 5 seconds of data to find the greatest EMG.
  • The average integrated EMG during the ½ second is used to compute the normalization factor.
! --------------------------------------------------------------- 
!   Normalize
! --------------------------------------------------------------- 
For_Each 
/ITERATION_PARAMETER_NAME=INDEX 
/ITEMS=::EMG_SIGNALS 
;

Evaluate_Expression 
/EXPRESSION=ANALOG::RMS&:&:&::INDEX&/&GLOBAL::METRIC::PROCESSED&:&:&::INDEX&_SCALE_MAX_MEAN 
/RESULT_NAME=::INDEX 
/RESULT_TYPE=ANALOG 
/RESULT_FOLDER=RESULT 
;

End_For_Each 
/ITERATION_PARAMETER_NAME=INDEX 
;

Normalize to the Maximum within Each Gait Cycle

For this example, the goal is as follows:

  • Compute the maximum of the EMG signal during each gait cycle
  • Normalize the EMG signal to the maximum during each gait cycle

The beginning of this pipeline are separating the left and right legs and identifying specific gait events depending on the leg.

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
; 

A for loop will be used to loop through both the right and left leg.

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
;

We're going to then loop over every sequence in the trial, so we first calculate the maximum number of sequences in the longest trial.

! ---------------------------------------------------------------------
! Get the maximum number of cycles in a trial the entire CMZ file
! ---------------------------------------------------------------------


! 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
;

We will then create a temporary signal to normalize the EMG signal with, which will contain the maximum of the EMG Signal of the current cycle.

! ---------------------------------------------------------------------
! 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
;

We can then normalize the EMG signal by dividing it by the temporary signal above.

! ---------------------------------------------------------------------
! 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
;
Retrieved from ""