EMG Analysis - Time and Frequency Parameters
Difficulty Level:
Tags extract☁emg☁muscular-activations

Muscles perform an essential role on movement, postural control and in the vital cardiorespiratory functions. These three examples have something in common, which is, the origin of the muscular contraction is in the nervous impulse that reaches the muscle, triggering a sequence of physiological mechanisms that ultimately cause the muscle contraction.

In the last example (muscle in vital cardiorespiratory processes), myocardium (cardiac muscle) functions in an involuntary way, under the coordination of the autonomic nervous system.

The previously mentioned nervous impulse and the respective changes in electric potential can be monitored by ECG acquisition and analysis.

However, for movement and postural control, another type of muscle goes into action, the skeletal muscle. In contrast to cardiac muscle, the action of skeletal muscles is voluntary, causing drastic differences in EMG signal when comparing to ECG, namely the inexistence of natural periodicity.

In this Jupyter Notebook it will be explained how some parameters can be extracted from EMG, both from time and frequency domain.

List of EMG analysis parameters:

  • Number of Muscular Activations;
  • Maximum, Minimum and Average duration of muscular activations;
  • Minimum, Maximum, Average and Standard Deviation values of EMG samples;
  • Root Mean Square (RMS) and Area under curve;
  • Total Power, Maximum Frequency and Median Frequency;

1 - Importation of the needed packages

In [1]:
# biosignalsnotebooks python package
import biosignalsnotebooks as bsnb

# Scientific packages
from numpy import linspace, max, min, average, std, sum, sqrt, where, argmax
from scipy.integrate import cumtrapz
from scipy.signal import welch

2 - Load of acquired EMG data

In [2]:
# Load of data
data, header = bsnb.load_signal("emg_bursts", get_header=True)

3 - Identification of mac address of the device and the channel used during acquisition

In [3]:
channel = list(data.keys())[0]
device = header["device"]
resolution = int(header["resolution"][0])
In [4]:
from sty import fg, rs
print (fg(98,195,238) + "\033[1mDevice: \033[0m" + fg.rs + device + fg(98,195,238) + "\033[1m Channel: \033[0m" + fg.rs + str(channel) + fg(98,195,238) + "\033[1m Resolution: \033[0m" + fg.rs + str(resolution) + " bits")
Device: channeller Channel: CH3 Resolution: 16 bits

4 - Storage of sampling frequency and acquired data inside variables

In [5]:
# Sampling frequency and acquired data
fs = header["sampling rate"]

# Signal Samples
signal = bsnb.raw_to_phy("EMG", device, data[channel], resolution, option="mV") # Conversion to mV
time = linspace(0, len(signal) / fs, len(signal))

5 -EMG parameter extraction
5.1 -Detection and accounting of muscular activations

In [6]:
burst_begin, burst_end = bsnb.detect_emg_activations(signal, fs, smooth_level=20, threshold_level=10, 
                                                     time_units=True, plot_result=True)[:2]
In [7]:
# Number of activation periods
print (fg(98,195,238) + "\033[1mNumber of Muscular Activations: \033[0m" + fg.rs + str(len(burst_begin)))
Number of Muscular Activations: 9

5.2 -Maximum, Minimum and Average duration of muscular activation periods

In [8]:
# Bursts Duration
bursts_time = burst_end - burst_begin

# Parameter extraction
max_time = max(bursts_time)
min_time = min(bursts_time)
avg_time = average(bursts_time)
std_time = std(bursts_time)
In [9]:
print (fg(98,195,238) + "\033[1m[Maximum, Minimum, Average] duration of Muscular Activations \033[0m" + fg.rs + " = [" + str(max_time) + ", " + str(min_time) + ", " + str(avg_time) + "] s")
print (fg(98,195,238) + "\033[1mStandard Deviation \033[0m" + fg.rs + "= " + str(std_time) + " s")
[Maximum, Minimum, Average] duration of Muscular Activations  = [1.3460471982607487, 0.6010210744091449, 0.9090318746055124] s
Standard Deviation = 0.22846855960280285 s
In [10]:
bsnb.plot_emg_graphical_durations(max_time, min_time, avg_time, std_time)

5.3 - Maximum, Minimum, Average and Standard Deviation of EMG sample values

In [11]:
# Maximum
max_sample_value = max(signal)

# Minimum
min_sample_value = min(signal)

# Average and Standard Deviation
avg_sample_value = average(signal)
std_sample_value = std(signal)

time_param_dict = {"Maximum EMG": max_sample_value, "Minimum EMG": min_sample_value, 
                   "Average EMG": avg_sample_value, "Standard Deviation EMG": std_sample_value}
In [12]:
print (fg(98,195,238) + "\033[1m[Maximum, Minimum, Average, Standard Deviation] mV \033[0m" + fg.rs + " = [" + str(max_sample_value) + ", " + str(min_sample_value) + ", " + str(avg_sample_value) + ", " + str(std_sample_value) + "] mV")
[Maximum, Minimum, Average, Standard Deviation] mV  = [0.478729248046875, -0.910400390625, 0.0016734834397682248, 0.06294409866652229] mV
In [13]:
bsnb.plot_emg_graphical_statistical(time, signal, max_sample_value, min_sample_value, avg_sample_value, std_sample_value)

5.4 - Root Mean Square and Area under the curve (Signal Intensity Estimators)

In [14]:
# Root Mean Square
rms = sqrt(sum(signal * signal) / len(signal))

# Area under the curve
area = cumtrapz(signal)
In [15]:
print (fg(98,195,238) + "\033[1mRoot Mean Square \033[0m" + fg.rs + " = " + str(rms) + " mV")
print (fg(98,195,238) + "\033[1mArea \033[0m" + fg.rs + " = " + str(area[-1]) + " mV.s")
Root Mean Square  = 0.06296634103839979 mV
Area  = 47.72003173828125 mV.s
In [16]:
bsnb.plot_emg_rms_area(time, signal, rms, area)

5.5 - Total power and some reference points on the frequency domain

In [17]:
# Signal Power Spectrum
f, P = welch(signal, fs=fs, window='hanning', noverlap=0, nfft=int(256.))

# Total Power and Median Frequency (Frequency that divides the spectrum into two regions with equal power)
area_freq = cumtrapz(P, f, initial=0)
total_power = area_freq[-1]
median_freq = f[where(area_freq >= total_power / 2)[0][0]]
f_max = f[argmax(P)]
In [18]:
print (fg(98,195,238) + "\033[1mTotal Power \033[0m" + fg.rs + " = " + str(total_power))
print (fg(98,195,238) + "\033[1m[Median Frequency, Maximum Power Frequency] \033[0m" + fg.rs + " = [" + str(median_freq) + ", " + str(f_max) + "] Hz")
Total Power  = 0.0038750259945056083
[Median Frequency, Maximum Power Frequency]  = [82.03125, 70.3125] Hz
In [19]:
bsnb.plot_emg_spect_freq(f, P, f_max, median_freq)

This procedure can be automatically done by emg_parameters function in extract module of biosignalsnotebooks package

In [20]:
bsnb.emg_parameters(signal, fs, raw_to_mv=False)
Out[20]:
{'Number of Muscular Activations': 9,
 'Maximum Muscular Activation Duration': 1.3460471982607487,
 'Minimum Muscular Activation Duration': 0.6010210744091449,
 'Average Muscular Activation Duration': 0.9090318746055124,
 'Standard Deviation of Muscular Activation Duration': 0.22846855960280285,
 'Maximum Sample Value': 0.478729248046875,
 'Minimum Sample Value': -0.910400390625,
 'Average Sample Value': 0.0016734834397682248,
 'Standard Deviation Sample Value': 0.06294409866652229,
 'RMS': 0.06296634103839979,
 'Area': 47.72003173828125,
 'Total Power Spect': 0.0038750259945056083,
 'Median Frequency': 82.03125,
 'Maximum Power Frequency': 70.3125}

This set of parameters reveals interesting information about EMG signal, however you can extract much more features during your signal processing journey !

We hope that you have enjoyed this guide. biosignalsnotebooks is an environment in continuous expansion, so don"t stop your journey and learn more with the remaining Notebooks !

In [21]:
from biosignalsnotebooks.__notebook_support__ import css_style_apply
css_style_apply()
.................... CSS Style Applied to Jupyter Notebook .........................
Out[21]: