A digital twin couples a mechanistic model (whose parameters are physical or clinical quantities) with realistic sensor emulation and data assimilation, so a subject-specific model can be personalised from real measurements and then used for in-silico experiments. This vignette runs the four layers end-to-end on the movement modality — the reference vertical slice — using only synthetic data, so it is fully reproducible offline.
Layer 1 is a mechanistic single-joint limb whose parameters map to
clinical quantities (muscle strength, passive
damping, joint stiffness). Layer 2 turns the
clean simulated motion into a realistic inertial-measurement-unit stream
with bias and noise.
subject <- limbTwin(strength = 5.5, damping = 0.7, stiffness = 3.2)
truth <- simulateTwin(subject, duration = 4, dt = 0.02, theta0 = 0.1)
rec <- imuMeasure(truth,
imuSensor(gyro_bias = 0.015, gyro_noise = 0.035,
accel_noise = 0.12))
head(rec, 3)
#> time gyro ax ay
#> 1 0.00 -0.006925883 0.9452639 1.193912
#> 2 0.02 -0.595207653 0.4400077 3.050682
#> 3 0.04 -1.101135445 -0.3177440 4.734341Layer 3 assimilates the IMU stream into a twin, starting from a
wrong prior and recovering the subject’s parameters.
n_starts is reduced here to keep the vignette fast; the
default (25) is more robust.
With a personalised twin, perturb parameters and read off the predicted change: here a rehabilitation that adds 40% strength and removes 30% damping.
effect <- insilicoIntervention(twin$twin,
scale = list(strength = 1.4, damping = 0.7),
duration = 5, dt = 0.01)
effect$outcomes
#> outcome before after pct_change
#> 1 rom 2.4761 4.7339 91.2
#> 2 peak_velocity 8.2776 14.9181 80.2
#> 3 mean_speed 4.3151 7.4452 72.5
#> 4 peak_accel 56.2426 108.1979 92.4The same generators produce domain-randomised, labelled data for machine learning — each row is one simulated subject with its true parameters and signal features.
Simulation validity is not the same as matching one dataset.
validateTwin() separates verification (are
the equations solved correctly?) from validation (are
they the right equations?) and reports the operating envelope. The cheap
checks are shown here; recovery, sensitivity
and prediction (which refit the twin many times) are
available via the checks argument.
validateTwin(limbTwin(strength = 6, damping = 0.5, stiffness = 6),
checks = c("verification", "identifiability", "domain"))
#> Twin validity report
#> L0 verification : energy drift 9.63e-05, RK4 order 3.97
#> L1 identifiab. : rank 2/2, condition 7.5
#> L5 domain : amplitude 3.46 rad (large-amplitude (strongly nonlinear))The physiological generators (jansenRit() for EEG,
ecgsyn() / windkessel() for the cardiovascular
system, respiration(), kuramoto()) and the
wider estimator family (unscentedKalmanFilter(),
particleFilter(), abcSMC(),
gpCalibrate()) are the building blocks for coupling the
other modalities into this same loop. See ?PhysioTwin for
the full map.