--- title: "EMG Fatigue Analysis and Muscle Synergy Decomposition" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{EMG Fatigue Analysis and Muscle Synergy Decomposition} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r setup, include=FALSE} knitr::opts_chunk$set(eval = FALSE) ``` ## Introduction This vignette covers two advanced EMG analysis capabilities in PhysioEMG: 1. **Fatigue analysis**: Tracking spectral changes over time during sustained contractions to quantify muscle fatigue. 2. **Muscle synergy decomposition**: Extracting coordinated muscle activation patterns from multi-channel EMG using matrix factorization methods. ## Fatigue Analysis Muscle fatigue during sustained contractions causes a well-documented shift in the EMG power spectrum toward lower frequencies. This occurs because fatigued motor units have reduced conduction velocity, which compresses the spectral content. PhysioEMG provides tools to track this shift and quantify fatigue. ### Creating Fatigue Test Data The `make_emg_fatigue()` generator creates synthetic EMG with progressively decreasing median frequency, simulating a fatiguing isometric contraction: ```{r fatigue-data} library(PhysioEMG) pe_fatigue <- make_emg_fatigue(n_time = 10000, sr = 1000) pe_fatigue # 10 seconds of data at 1000 Hz, single channel ``` ### Windowed Fatigue Tracking The `emgFatigue()` function divides the signal into overlapping windows and computes the median frequency and mean frequency for each window. A declining trend in median frequency over time indicates fatigue. ```{r fatigue-analysis} fatigue_results <- emgFatigue( pe_fatigue, window_sec = 1.0, overlap = 0.5 ) head(fatigue_results) # Columns: channel, window, time_sec, median_freq, mean_freq, rms_amplitude ``` The `window_sec` parameter controls the analysis window length. Longer windows provide more stable frequency estimates but lower temporal resolution. The `overlap` parameter (0 to 1) controls how much adjacent windows overlap: ```{r fatigue-window} # Higher temporal resolution with shorter windows and more overlap fatigue_fine <- emgFatigue(pe_fatigue, window_sec = 0.5, overlap = 0.75) # Lower resolution with longer windows fatigue_coarse <- emgFatigue(pe_fatigue, window_sec = 2.0, overlap = 0.5) ``` ### Fatigue Index The `emgFatigueIndex()` function provides a single summary metric by comparing the median frequency at the beginning and end of the recording. A fatigue index less than 1 indicates frequency decrease (fatigue), while a value near 1 suggests no fatigue. ```{r fatigue-index} fi <- emgFatigueIndex(pe_fatigue, initial_pct = 0.2, final_pct = 0.2) fi # Columns: channel, fatigue_index, initial_mdf, final_mdf # fatigue_index < 1 indicates fatigue # initial_mdf > final_mdf confirms spectral compression ``` The `initial_pct` and `final_pct` parameters control what proportion of the signal is used for the initial and final frequency estimates. Larger values provide more stable estimates but may blur transient changes. ### Spectral Moments For more detailed spectral characterization, `emgSpectralMoments()` computes the zeroth (M0, total power), first (M1), and second (M2) spectral moments over sliding windows: ```{r spectral-moments} moments <- emgSpectralMoments(pe_fatigue, window_sec = 1.0, overlap = 0.5) head(moments) # Columns: channel, window, m0, m1, m2 # Derive mean frequency from moments moments$mean_freq <- moments$m1 / moments$m0 ``` The mean frequency derived from spectral moments (M1/M0) is mathematically equivalent to the mean frequency reported by `emgFatigue()`. ## Muscle Synergy Decomposition Muscle synergy analysis decomposes multi-channel EMG into a small number of coordinated activation patterns (synergies). Each synergy consists of a weight vector (which muscles participate) and an activation profile (when the synergy is active). This approach is based on the motor control hypothesis that the nervous system simplifies movement by activating muscles in fixed groups. ### Decomposition Methods PhysioEMG supports three decomposition methods via `muscleSynergy()`: - **NMF** (non-negative matrix factorization): Constrains both weights and activations to be non-negative, which is physiologically appropriate for rectified EMG. This is the most commonly used method for synergy extraction. - **PCA** (principal component analysis): Finds orthogonal components that explain maximum variance. Fast but allows negative values. - **ICA** (independent component analysis): Finds statistically independent components. Useful when synergies may overlap temporally. ### NMF Synergy Extraction ```{r synergy-nmf} # Create multi-channel EMG (8 muscles) pe_multi <- make_emg(n_time = 2000, n_channels = 8, sr = 1000) # Extract 3 synergies using NMF syn_nmf <- muscleSynergy( pe_multi, n_synergies = 3, method = "nmf", max_iter = 200, tol = 1e-4 ) # Examine results str(syn_nmf) # W: 3 x 8 weight matrix (synergies x muscles) # H: 2000 x 3 activation matrix (time x synergies) # vaf: variance accounted for (0-1) # method: "nmf" cat("Variance accounted for:", round(syn_nmf$vaf, 3), "\n") ``` ### PCA and ICA Methods ```{r synergy-pca-ica} # PCA decomposition syn_pca <- muscleSynergy(pe_multi, n_synergies = 3, method = "pca") # ICA decomposition syn_ica <- muscleSynergy(pe_multi, n_synergies = 3, method = "ica") # Compare VAF across methods cat("NMF VAF:", round(syn_nmf$vaf, 3), "\n") cat("PCA VAF:", round(syn_pca$vaf, 3), "\n") cat("ICA VAF:", round(syn_ica$vaf, 3), "\n") ``` ### Determining the Number of Synergies A common approach is to extract synergies for increasing numbers and look for the "elbow" in the VAF curve, or use a VAF threshold (e.g., 0.90): ```{r synergy-selection} vaf_values <- numeric(6) for (n in 1:6) { result <- muscleSynergy(pe_multi, n_synergies = n, method = "nmf") vaf_values[n] <- result$vaf } # Find minimum synergies for 90% VAF n_selected <- which(vaf_values >= 0.90)[1] cat("Selected", n_selected, "synergies (VAF =", round(vaf_values[n_selected], 3), ")\n") ``` ### Synergy Reconstruction Use `synergyReconstruct()` to reconstruct the EMG signal from a subset of synergies. This helps verify the quality of the decomposition and test how many synergies are needed to adequately represent the data: ```{r synergy-reconstruct} # Full decomposition with 4 synergies syn_full <- muscleSynergy(pe_multi, n_synergies = 4, method = "nmf") # Reconstruct using only 2 synergies recon_2 <- synergyReconstruct(syn_full, n_synergies = 2) cat("VAF with 2 synergies:", round(recon_2$vaf, 3), "\n") # Reconstruct using 3 synergies recon_3 <- synergyReconstruct(syn_full, n_synergies = 3) cat("VAF with 3 synergies:", round(recon_3$vaf, 3), "\n") # Reconstruct using all 4 recon_4 <- synergyReconstruct(syn_full, n_synergies = 4) cat("VAF with 4 synergies:", round(recon_4$vaf, 3), "\n") ``` ### Comparing Synergy Solutions When comparing synergies across conditions, subjects, or decomposition methods, use `synergyCompare()`. It computes pairwise correlations between synergy weight vectors and finds the best-match pairing: ```{r synergy-compare} # Extract synergies from two conditions syn_a <- muscleSynergy(pe_multi, n_synergies = 3, method = "nmf") syn_b <- muscleSynergy(pe_multi, n_synergies = 3, method = "nmf") # Compare the weight vectors comparison <- synergyCompare(syn_a, syn_b) comparison # Columns: synergy1, synergy2, correlation # High correlations (> 0.8) indicate similar synergies ``` ## Complete Fatigue and Synergy Workflow ```{r workflow} library(PhysioEMG) # --- Fatigue Analysis --- pe_fatigue <- make_emg_fatigue(n_time = 10000, sr = 1000) # Track frequency shift fatigue <- emgFatigue(pe_fatigue, window_sec = 1.0, overlap = 0.5) # Summary fatigue metric fi <- emgFatigueIndex(pe_fatigue) cat("Fatigue index:", round(fi$fatigue_index, 3), "\n") # Spectral moments for additional characterization moments <- emgSpectralMoments(pe_fatigue, window_sec = 1.0, overlap = 0.5) # --- Synergy Decomposition --- pe_multi <- make_emg(n_time = 2000, n_channels = 8, sr = 1000) # Extract synergies syn <- muscleSynergy(pe_multi, n_synergies = 3, method = "nmf") cat("Synergy VAF:", round(syn$vaf, 3), "\n") # Validate with reconstruction recon <- synergyReconstruct(syn, n_synergies = 2) cat("Reconstruction VAF (2 synergies):", round(recon$vaf, 3), "\n") ``` ## References - De Luca, C.J. (1984). "Myoelectrical manifestations of localized muscular fatigue in humans." Critical Reviews in Biomedical Engineering, 11(4), 251-279. - De Luca, C.J. (1997). "The use of surface electromyography in biomechanics." Journal of Applied Biomechanics, 13(2), 135-163. - Merletti, R. & Parker, P.A. (2004). "Electromyography: Physiology, Engineering, and Non-Invasive Applications." Wiley-IEEE Press.