Python: Analysing EMG signals – Part 4

We have seen how Python can be used to process and analyse EMG signals in lessons 1, 2 and 3. When EMG signals are filtered, how does changing filter settings change the appearance of the filtered EMG signal?

A low pass filter allows frequencies below the cut-off frequency to pass through (ie. higher frequencies are removed). The filtered EMG signal can be used to interpret different physiological properties. For example, scientists investigating muscle force and muscle activity often use a low pass filter to capture the shape or “envelope” of the EMG signal as this is thought to better reflect force generated by a muscle. Let’s see how changing the low pass cut-off frequency changes the rectified signal.

We want to write a bit of code to filter and rectify an EMG signal and plot graphs comparing the unfiltered and filtered signal, for different low pass cut-off frequencies. Since this involves doing things repeatedly (eg. EMG processing, plotting) for different values (low pass cut-offs), we will write a function to do the EMG processing etc. and send the function different values in a for loop. For quick refreshers, see this series on functions and this post on loops in Python.

Here is our function and the for loop at the end. Key bits of code are discussed below:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
def filteremg(time, emg, low_pass=10, sfreq=1000, high_band=20, low_band=450):
    """
    time: Time data
    emg: EMG data
    high: high-pass cut off frequency
    low: low-pass cut off frequency
    sfreq: sampling frequency
    """
    
    # normalise cut-off frequencies to sampling frequency
    high_band = high_band/(sfreq/2)
    low_band = low_band/(sfreq/2)
    
    # create bandpass filter for EMG
    b1, a1 = sp.signal.butter(4, [high_band,low_band], btype='bandpass')
    
    # process EMG signal: filter EMG
    emg_filtered = sp.signal.filtfilt(b1, a1, emg)    
    
    # process EMG signal: rectify
    emg_rectified = abs(emg_filtered)
    
    # create lowpass filter and apply to rectified signal to get EMG envelope
    low_pass = low_pass/sfreq
    b2, a2 = sp.signal.butter(4, low_pass, btype='lowpass')
    emg_envelope = sp.signal.filtfilt(b2, a2, emg_rectified)
    
    # plot graphs
    fig = plt.figure()
    plt.subplot(1, 4, 1)
    plt.subplot(1, 4, 1).set_title('Unfiltered,' + '\n' + 'unrectified EMG')
    plt.plot(time, emg)
    plt.locator_params(axis='x', nbins=4)
    plt.locator_params(axis='y', nbins=4)
    plt.ylim(-1.5, 1.5)
    plt.xlabel('Time (sec)')
    plt.ylabel('EMG (a.u.)')
    
    plt.subplot(1, 4, 2)
    plt.subplot(1, 4, 2).set_title('Filtered,' + '\n' + 'rectified EMG: ' + str(int(high_band*sfreq)) + '-' + str(int(low_band*sfreq)) + 'Hz')
    plt.plot(time, emg_rectified)
    plt.locator_params(axis='x', nbins=4)
    plt.locator_params(axis='y', nbins=4)
    plt.ylim(-1.5, 1.5)
    plt.plot([0.9, 1.0], [1.0, 1.0], 'r-', lw=5)
    plt.xlabel('Time (sec)')

    plt.subplot(1, 4, 3)
    plt.subplot(1, 4, 3).set_title('Filtered, rectified ' + '\n' + 'EMG envelope: ' + str(int(low_pass*sfreq)) + ' Hz')
    plt.plot(time, emg_envelope)
    plt.locator_params(axis='x', nbins=4)
    plt.locator_params(axis='y', nbins=4)
    plt.ylim(-1.5, 1.5)
    plt.plot([0.9, 1.0], [1.0, 1.0], 'r-', lw=5)
    plt.xlabel('Time (sec)')
    
    plt.subplot(1, 4, 4)
    plt.subplot(1, 4, 4).set_title('Focussed region')
    plt.plot(time[int(0.9*1000):int(1.0*1000)], emg_envelope[int(0.9*1000):int(1.0*1000)])
    plt.locator_params(axis='x', nbins=4)
    plt.locator_params(axis='y', nbins=4)
    plt.xlim(0.9, 1.0)
    plt.ylim(-1.5, 1.5)
    plt.xlabel('Time (sec)')

    fig_name = 'fig_' + str(int(low_pass*sfreq)) + '.png'
    fig.set_size_inches(w=11,h=7)
    fig.savefig(fig_name)

# show what different low pass filter cut-offs do
for i in [3, 10, 40]:
    filteremg(time, emg_correctmean, low_pass=i)

Line 1. Define a function called filteremg to accept time and emg values (for plotting on the x- and y-axes) with default values for a low pass filter (for the EMG envelope), sampling frequency, and high and low pass filter frequencies. The default values mean that when we call the function, if we don’t specify different values, the function uses the defaults during code execution.

Line 28 onwards. Plot 3 subplots to see (1) the unfiltered, unrectified EMG signal, (2) the filtered, rectified signal, (3) the rectified signal with a low pass filter to get the EMG envelope and (4) a zoomed-in section of the signal from (3) over the time period indicated by the red line to see the underlying shape of the final signal. This line says to index values from 0.9 to 1.0 seconds, convert these numerical float values to integers, and plot time on the x- and EMG signal on the y-axis.

Line 70. Write a for loop that says: for each low pass cut-off value in the list [3, 10, 40], when i is 3, call the function filteremg and assign the value of i to the local variable low. When i is 10, call the function and do the same. Etc.

For each of the cut-off frequencies supplied, we see in Figure 1 below that low cut-off frequencies produce a “smoothed” EMG signal which has a more noticable shape (envelope) compared to when higher cut-off frequencies are used.

There is still discussion around which lowpass cut-off frequencies are appropriate to get the EMG envelope under different conditions (eg. sustained contractions, gait). The biomechanist David Winter argues that choosing a lowpass cut-off value should be based on how the biomechanics of production of muscle force is modelled; see here for a discussion on the linear EMG envelope.


Figure 1: Unfiltered vs filtered EMG signals with different low pass cut-off frequencies

Summary

Changing the cut-off frequencies changes the shape and appearance of the filtered EMG signal. We now have a function that can be used to see how changing these frequencies changes the appearance of the signal. As a learning activity, stick the function into an IPython notebook (or similar) and play around with the filter settings to see just how much the shape can change.

 

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s