--- title: "Force Plate and Inverse Dynamics Workflow" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Force Plate and Inverse Dynamics Workflow} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r, include = FALSE} knitr::opts_chunk$set(collapse = TRUE, comment = "#>") ``` This vignette demonstrates a standard lower-limb kinetics workflow in `PhysioMoCap`: 1. Filter force-plate GRF signals. 2. Compute center of pressure (COP). 3. Extract loading rate and impulse. 4. Estimate joint moments and power with planar inverse dynamics. 5. Run multi-plate selection and 3D inverse dynamics. ## 1. Simulate force-plate inputs ```{r} library(PhysioMoCap) set.seed(1) n <- 1000 sr <- 1000 forces <- cbind( Fx = rnorm(n, 0, 8), Fy = rnorm(n, 0, 8), Fz = c(rep(0, 200), abs(sin(seq(0, pi, length.out = 600))) * 900, rep(0, 200)) ) moments <- cbind( Mx = rnorm(n, 0, 25), My = rnorm(n, 0, 25), Mz = rnorm(n, 0, 10) ) ``` ## 2. Run force-plate analysis ```{r} out <- analyzeForcePlate( forces = forces, moments = moments, sampling_rate = sr, cutoff = 20, threshold = 20, filter_method = "moving_average" ) out$summary head(out$loading_rate) head(out$impulse) ``` ## 3. Build inverse-dynamics inputs ```{r} t <- seq(0, (n - 1) / sr, length.out = n) joints <- data.frame( ankle_x = rep(0.00, n), ankle_y = rep(0.05, n), knee_x = rep(0.00, n), knee_y = rep(0.45, n), hip_x = rep(0.00, n), hip_y = rep(0.85, n) ) grf_2d <- data.frame( fx = out$filtered_forces$force_x, fy = out$filtered_forces$force_z, cop_x = if (!is.null(out$cop)) out$cop$cop_x else rep(0, n), cop_y = if (!is.null(out$cop)) out$cop$cop_y else rep(0, n) ) angles <- data.frame( ankle = 0.20 * sin(2 * pi * 1 * t), knee = 0.45 * sin(2 * pi * 1 * t + 0.2), hip = 0.35 * sin(2 * pi * 1 * t + 0.4) ) ``` ## 4. Estimate inertial parameters and compute moments/power ```{r} inertial <- estimateSegmentInertia( body_mass = 70, segment_lengths = c(foot = 0.25, shank = 0.43, thigh = 0.45) ) id_out <- inverseDynamics2D( joints = joints, grf = grf_2d, sampling_rate = sr, angles = angles, inertial = inertial ) head(id_out) ``` ## 5. Multi-plate selection and 3D inverse dynamics ```{r} force_z <- cbind( fp1 = out$filtered_forces$force_z * 0.6, fp2 = out$filtered_forces$force_z ) pe_fp <- PhysioCore::PhysioExperiment( assays = S4Vectors::SimpleList( force_x = cbind(fp1 = out$filtered_forces$force_x * 0.6, fp2 = out$filtered_forces$force_x), force_y = cbind(fp1 = out$filtered_forces$force_y * 0.6, fp2 = out$filtered_forces$force_y), force_z = force_z ), colData = S4Vectors::DataFrame( label = c("fp1", "fp2"), type = c("forceplate", "forceplate") ), samplingRate = sr ) fp_auto <- analyzeForcePlatePE(pe_fp, plate_index = "auto", threshold = 20, cutoff = 20, filter_method = "moving_average") fp_auto$selected_plate ``` ```{r} joints_3d <- transform( joints, ankle_z = ankle_y, knee_z = knee_y, hip_z = hip_y, ankle_y = 0, knee_y = 0, hip_y = 0 ) grf_3d <- data.frame( fx = out$filtered_forces$force_x, fy = out$filtered_forces$force_y, fz = out$filtered_forces$force_z, cop_x = rep(0, n), cop_y = rep(0, n), cop_z = rep(0, n) ) angles_3d <- data.frame( ankle_x = angles$ankle, ankle_y = 0 * angles$ankle, ankle_z = 0 * angles$ankle, knee_x = angles$knee, knee_y = 0 * angles$knee, knee_z = 0 * angles$knee, hip_x = angles$hip, hip_y = 0 * angles$hip, hip_z = 0 * angles$hip ) id3 <- inverseDynamics3D( joints = joints_3d, grf = grf_3d, sampling_rate = sr, angles = angles_3d ) head(id3) ``` ## Notes - For publication-grade kinetics, verify sign conventions and coordinate frames from your lab's force-plate and marker setup. - Use subject-specific segment lengths and MVC/body-mass normalization where appropriate.