OpenSim Tool Pipeline: IK to ID to SO
Source:vignettes/opensim-tool-pipeline.Rmd
opensim-tool-pipeline.RmdOverview
A common OpenSim workflow chains multiple tools in sequence:
- Inverse Kinematics (IK) – Compute joint angles from marker data
- Inverse Dynamics (ID) – Compute joint moments from kinematics + forces
- Static Optimization (SO) – Estimate muscle forces from joint moments
PhysioOpenSim’s template system and tool runners make it straightforward to script this pipeline entirely in R.
Prerequisites
This vignette assumes you have:
- An OpenSim model file (
.osim) - Marker trajectory data (
.trc) - External forces data (
.motor.xml) - Template XML files for each tool
Step 1: Inverse Kinematics
Generate the IK setup from a template, then run it.
library(PhysioOpenSim)
# Create dummy files for demonstration
model_file <- tempfile(fileext = ".osim")
marker_file <- tempfile(fileext = ".trc")
writeLines("dummy", model_file)
writeLines("dummy", marker_file)
# Create an IK template
ik_tpl <- tempfile(fileext = ".xml")
writeLines(c(
'<?xml version="1.0" encoding="UTF-8" ?>',
'<OpenSimDocument Version="40000">',
' <InverseKinematicsTool name="ik">',
' <model_file>Unassigned</model_file>',
' <marker_file>Unassigned</marker_file>',
' <output_motion_file>Unassigned</output_motion_file>',
' <time_range>0 1</time_range>',
' <results_directory>./</results_directory>',
' </InverseKinematicsTool>',
'</OpenSimDocument>'
), ik_tpl)
ik_output <- tempfile(fileext = ".mot")
ik_setup <- tempfile(fileext = ".xml")
ik_result <- opensimWriteIKSetupFromTemplate(
template_file = ik_tpl,
output_file = ik_setup,
model_file = model_file,
marker_file = marker_file,
output_motion_file = ik_output,
time_range = c(0.5, 2.0),
results_directory = tempdir()
)
cat("Applied tags:", paste(ik_result$applied_tags, collapse = ", "), "\n")
#> Applied tags: model_file, marker_file, output_motion_file, time_range, results_directory
# Execute IK (requires OpenSim)
ik_run <- opensimRunIK(ik_setup)
cat("IK exit status:", ik_run$status, "\n")
cat("IK elapsed:", round(ik_run$elapsed_sec, 2), "sec\n")Step 2: Inverse Dynamics
ID uses the IK output as input coordinates.
# Create an ID template
id_tpl <- tempfile(fileext = ".xml")
writeLines(c(
'<?xml version="1.0" encoding="UTF-8" ?>',
'<OpenSimDocument Version="40000">',
' <InverseDynamicsTool name="id">',
' <model_file>Unassigned</model_file>',
' <coordinates_file>Unassigned</coordinates_file>',
' <output_gen_force_file>Unassigned</output_gen_force_file>',
' <time_range>0 1</time_range>',
' <lowpass_cutoff_frequency_for_coordinates>-1</lowpass_cutoff_frequency_for_coordinates>',
' <results_directory>./</results_directory>',
' </InverseDynamicsTool>',
'</OpenSimDocument>'
), id_tpl)
# IK output becomes ID input -- create a dummy for the demo
ik_output_file <- tempfile(fileext = ".mot")
writeLines("dummy", ik_output_file)
id_output <- tempfile(fileext = ".sto")
id_setup <- tempfile(fileext = ".xml")
id_result <- opensimWriteIDSetupFromTemplate(
template_file = id_tpl,
output_file = id_setup,
model_file = model_file,
coordinates_file = ik_output_file,
output_gen_force_file = id_output,
time_range = c(0.5, 2.0),
lowpass_cutoff_frequency_for_coordinates = 6
)
cat("Applied tags:", paste(id_result$applied_tags, collapse = ", "), "\n")
#> Applied tags: model_file, coordinates_file, output_gen_force_file, lowpass_cutoff_frequency_for_coordinates, time_range
# Execute ID
id_run <- opensimRunID(id_setup)
cat("ID exit status:", id_run$status, "\n")Step 3: Static Optimization
SO uses the same coordinates as ID.
# Create an SO template
so_tpl <- tempfile(fileext = ".xml")
writeLines(c(
'<?xml version="1.0" encoding="UTF-8" ?>',
'<OpenSimDocument Version="40000">',
' <StaticOptimization name="so">',
' <model_file>Unassigned</model_file>',
' <coordinates_file>Unassigned</coordinates_file>',
' <time_range>0 1</time_range>',
' <results_directory>./</results_directory>',
' </StaticOptimization>',
'</OpenSimDocument>'
), so_tpl)
so_setup <- tempfile(fileext = ".xml")
so_result <- opensimWriteSOSetupFromTemplate(
template_file = so_tpl,
output_file = so_setup,
model_file = model_file,
coordinates_file = ik_output_file,
time_range = c(0.5, 2.0),
results_directory = tempdir()
)
cat("Applied tags:", paste(so_result$applied_tags, collapse = ", "), "\n")
#> Applied tags: model_file, coordinates_file, time_range, results_directory
# Execute SO
so_run <- opensimRunSO(so_setup)
cat("SO exit status:", so_run$status, "\n")Running the Full Pipeline
In practice you would run all three steps sequentially.
# Full pipeline
ik <- opensimRunIK(ik_setup)
stopifnot(ik$status == 0)
id <- opensimRunID(id_setup)
stopifnot(id$status == 0)
so <- opensimRunSO(so_setup)
stopifnot(so$status == 0)
cat("Pipeline complete.\n")Inspecting Results
Tool execution returns a list with useful metadata:
str(ik)
# List of 9
# $ execution : chr "cli"
# $ command : chr "/usr/local/bin/opensim-cmd"
# $ args : chr [1:2] "run-tool" "/path/to/ik_setup.xml"
# $ setup_file : chr "/path/to/ik_setup.xml"
# $ workdir : chr "/path/to/workdir"
# $ status : int 0
# $ stdout : chr [1:..] "..."
# $ stderr : chr [1:..] "..."
# $ elapsed_sec: num 3.45Backend Selection
The execution parameter controls which backend is
used:
| Value | Behavior |
|---|---|
"auto" (default) |
Native if available and tool_type is specific; CLI otherwise |
"native" |
Force native; errors if not compiled with OpenSim |
"cli" |
Force CLI; errors if opensim-cmd is not found |
Native execution is faster but does not support
extra_args or the generic tool_type = "tool"
token.