--- title: "EMG Analysis: Preprocessing, Amplitude, and Onset Detection" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{EMG Analysis: Preprocessing, Amplitude, and Onset Detection} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r setup, include=FALSE} knitr::opts_chunk$set(eval = FALSE) ``` ## Introduction PhysioEMG provides a suite of functions for surface electromyography (EMG) analysis built on the PhysioExperiment data model. This vignette demonstrates the core workflow for EMG signal processing: creating EMG data objects, extracting amplitude envelopes, normalizing amplitudes, and detecting muscle activation onsets. ## Creating EMG Data PhysioEMG includes synthetic data generators for testing and demonstration. The `make_emg()` function creates a multi-channel EMG PhysioExperiment with simulated muscle activity bursts. ```{r create-data} library(PhysioEMG) # Create a 4-channel EMG recording at 1000 Hz pe <- make_emg(n_time = 2000, n_channels = 4, sr = 1000) pe # View the channel metadata SummarizedExperiment::colData(pe) ``` For testing onset detection, `make_emg_contraction()` generates a single-channel signal with a known contraction window: ```{r create-contraction} pe_contraction <- make_emg_contraction( n_time = 5000, sr = 1000, contraction_start = 0.3, contraction_end = 0.7 ) ``` ## Amplitude Envelope Extraction The `emgEnvelope()` function extracts the amplitude envelope using one of three methods: - **RMS** (root mean square): Computes RMS over a sliding window. Best for general-purpose amplitude tracking. - **Hilbert**: Uses the analytic signal via the Hilbert transform. Provides instantaneous amplitude without windowing artifacts. - **Lowpass**: Rectifies the signal and applies a moving-average lowpass filter. A simple and computationally efficient approach. ```{r envelope-rms} # RMS envelope with 50 ms window pe_env <- emgEnvelope(pe, method = "rms", window_ms = 50) # The envelope is stored as a new assay SummarizedExperiment::assayNames(pe_env) # [1] "raw" "envelope" ``` ```{r envelope-hilbert} # Hilbert envelope for instantaneous amplitude pe_hilbert <- emgEnvelope(pe, method = "hilbert", output_assay = "hilbert_env") ``` ```{r envelope-lowpass} # Lowpass envelope with 6 Hz cutoff pe_lp <- emgEnvelope(pe, method = "lowpass", cutoff = 6, output_assay = "lp_env") ``` ## Amplitude Normalization EMG amplitudes vary across subjects and recording sessions. Normalization enables meaningful comparisons. PhysioEMG supports two normalization methods via `emgAmplitudeNormalize()`: - **MVC** (maximum voluntary contraction): Divides by the peak of a separate MVC trial, yielding percentage-of-MVC units. - **Peak**: Divides by the within-trial peak so each channel ranges from 0 to 1. ```{r normalize-peak} # Peak normalization (no external reference needed) pe_norm <- emgAmplitudeNormalize(pe_env, method = "peak", assay_name = "envelope") # Check the normalized values range from 0 to 1 range(SummarizedExperiment::assay(pe_norm, "normalized")) ``` ```{r normalize-mvc} # MVC normalization requires a separate MVC trial mvc_trial <- make_emg(n_time = 1000, n_channels = 4, sr = 1000) pe_mvc <- emgAmplitudeNormalize( pe_env, method = "mvc", mvc_data = mvc_trial, assay_name = "envelope" ) ``` ## Muscle Activation Onset Detection Detecting when a muscle becomes active is fundamental to many biomechanics analyses. PhysioEMG implements two onset detection methods: ### Hodges-Bui Method The Hodges-Bui method (1996) thresholds the rectified EMG signal at a multiple of baseline standard deviations. It is widely used due to its simplicity and robustness. ```{r onset-hodges} pe_contraction <- make_emg_contraction(n_time = 5000, sr = 1000) # Detect onset using Hodges-Bui method result_hb <- emgOnsetDetect( pe_contraction, method = "hodges_bui", threshold_sd = 3, baseline_sec = 0.2, min_duration_ms = 50 ) # View detected onsets result_hb$onsets # Expected: onset near 30% of signal (1.5 seconds at 1000 Hz) ``` ### Teager-Kaiser Energy Operator The Teager-Kaiser method applies the Teager-Kaiser energy operator before thresholding. This can be more sensitive to sudden onsets because the TKEO responds to both amplitude and frequency changes. ```{r onset-teager} result_tk <- emgOnsetDetect( pe_contraction, method = "teager_kaiser", threshold_sd = 3, baseline_sec = 0.2 ) result_tk$onsets ``` ### Comparing Methods Both methods return onset and offset data.frames with channel, sample, and time information: ```{r onset-compare} # Multi-channel onset detection pe_multi <- make_emg(n_time = 5000, n_channels = 4, sr = 1000) onsets_hb <- emgOnsetDetect(pe_multi, method = "hodges_bui") onsets_tk <- emgOnsetDetect(pe_multi, method = "teager_kaiser") # Compare onset times across methods merge( onsets_hb$onsets, onsets_tk$onsets, by = "channel", suffixes = c("_hb", "_tk") ) ``` ## Complete Workflow Example A typical EMG analysis pipeline chains these steps together: ```{r workflow} library(PhysioEMG) # 1. Load or create EMG data pe <- make_emg(n_time = 5000, n_channels = 4, sr = 1000) # 2. Extract amplitude envelope pe <- emgEnvelope(pe, method = "rms", window_ms = 50) # 3. Normalize amplitudes pe <- emgAmplitudeNormalize(pe, method = "peak", assay_name = "envelope") # 4. Detect muscle activation onsets onsets <- emgOnsetDetect(pe, method = "hodges_bui", threshold_sd = 3) # 5. Review results print(onsets$onsets) print(onsets$offsets) ``` ## References - De Luca, C.J. (1997). "The use of surface electromyography in biomechanics." Journal of Applied Biomechanics, 13(2), 135-163. - Hodges, P.W. & Bui, B.H. (1996). "A comparison of computer-based methods for the determination of onset of muscle contraction using electromyography." Electroencephalography and Clinical Neurophysiology, 101(6), 511-519. - Merletti, R. & Parker, P.A. (2004). "Electromyography: Physiology, Engineering, and Non-Invasive Applications." Wiley-IEEE Press.