Processing Mocap Data

This documentation is WIP:

Nimble is focused on human motion and biomechanics analysis. We include many utilities that will be familiar to biomechanics practitioners: loading C3D files, loading multiple OpenSim formats, and the algorithms that power AddBiomechanics.

To load C3D files, check out:

class nimblephysics.biomechanics.C3DLoader
static debugToGUI(file: nimblephysics_libs._nimblephysics.biomechanics.C3D, server: nimblephysics_libs._nimblephysics.server.GUIWebsocketServer) None
static fixupMarkerFlips(c3d: nimblephysics_libs._nimblephysics.biomechanics.C3D) List[List[Tuple[str, str]]]
static loadC3D(uri: str) nimblephysics_libs._nimblephysics.biomechanics.C3D

To run some heuristics to clean up the C3D data, you can use:

class nimblephysics.biomechanics.MarkerFixer
static generateDataErrorsReport(immutableMarkerObservations: List[Dict[str, numpy.ndarray[numpy.float64[3, 1]]]], dt: float, dropProlongedStillness: bool = False, rippleReduce: bool = True, rippleReduceUseSparse: bool = True, rippleReduceUseIterativeSolver: bool = True, rippleReduceSolverIterations: int = 100000.0) nimblephysics_libs._nimblephysics.biomechanics.MarkersErrorReport

This will return you an object with a field markerObservationsAttemptedFixed

class nimblephysics.biomechanics.MarkersErrorReport
property droppedMarkerWarnings
getMarkerMapOnTimestep(self: nimblephysics_libs._nimblephysics.biomechanics.MarkersErrorReport, t: int) Dict[str, numpy.ndarray[numpy.float64[3, 1]]]
getMarkerNamesOnTimestep(self: nimblephysics_libs._nimblephysics.biomechanics.MarkersErrorReport, t: int) List[str]
getMarkerPositionOnTimestep(self: nimblephysics_libs._nimblephysics.biomechanics.MarkersErrorReport, t: int, marker: str) numpy.ndarray[numpy.float64[3, 1]]
getNumTimesteps(self: nimblephysics_libs._nimblephysics.biomechanics.MarkersErrorReport) int
property info
property markerObservationsAttemptedFixed
property markersRenamedFromTo
property warnings

For OpenSim files, check out:

nimblephysics.biomechanics.OpenSimParser

alias of <module ‘nimblephysics_libs._nimblephysics.biomechanics.OpenSimParser’>

This class is the basis of the kinematic fit (just bone scaling, marker offsets, and IK, no dynamics) for AddBiomechanics.

class nimblephysics.biomechanics.MarkerFitter
addZeroConstraint(self: nimblephysics_libs._nimblephysics.biomechanics.MarkerFitter, name: str, loss: Callable[[nimblephysics_libs._nimblephysics.biomechanics.MarkerFitterState], float]) None
autorotateC3D(self: nimblephysics_libs._nimblephysics.biomechanics.MarkerFitter, c3d: nimblephysics_libs._nimblephysics.biomechanics.C3D) None
checkForEnoughMarkers(self: nimblephysics_libs._nimblephysics.biomechanics.MarkerFitter, markerObservations: List[Dict[str, numpy.ndarray[numpy.float64[3, 1]]]]) bool
checkForFlippedMarkers(self: nimblephysics_libs._nimblephysics.biomechanics.MarkerFitter, markerObservations: List[Dict[str, numpy.ndarray[numpy.float64[3, 1]]]], init: nimblephysics_libs._nimblephysics.biomechanics.MarkerInitialization, report: nimblephysics_libs._nimblephysics.biomechanics.MarkersErrorReport) bool
debugTrajectoryAndMarkersToGUI(self: nimblephysics_libs._nimblephysics.biomechanics.MarkerFitter, server: nimblephysics_libs._nimblephysics.server.GUIWebsocketServer, init: nimblephysics_libs._nimblephysics.biomechanics.MarkerInitialization, markerObservations: List[Dict[str, numpy.ndarray[numpy.float64[3, 1]]]], forcePlates: List[nimblephysics_libs._nimblephysics.biomechanics.ForcePlate] = None, goldOsim: nimblephysics_libs._nimblephysics.biomechanics.OpenSimFile = None, goldPoses: numpy.ndarray[numpy.float64[m, n]] = array([], shape=(0, 0), dtype=float64)) None
findJointCenters(self: nimblephysics_libs._nimblephysics.biomechanics.MarkerFitter, initializations: nimblephysics_libs._nimblephysics.biomechanics.MarkerInitialization, newClip: List[bool], markerObservations: List[Dict[str, numpy.ndarray[numpy.float64[3, 1]]]]) None
fineTuneWithIMU(self: nimblephysics_libs._nimblephysics.biomechanics.MarkerFitter, accObservations: List[Dict[str, numpy.ndarray[numpy.float64[3, 1]]]], gyroObservations: List[Dict[str, numpy.ndarray[numpy.float64[3, 1]]]], markerObservations: List[Dict[str, numpy.ndarray[numpy.float64[3, 1]]]], newClip: List[bool], init: nimblephysics_libs._nimblephysics.biomechanics.MarkerInitialization, dt: float, weightAccs: float = 1.0, weightGyros: float = 1.0, weightMarkers: float = 100.0, regularizePoses: float = 1.0, useIPOPT: bool = True, iterations: int = 300, lbfgsMemory: int = 100) nimblephysics_libs._nimblephysics.biomechanics.MarkerInitialization
generateDataErrorsReport(self: nimblephysics_libs._nimblephysics.biomechanics.MarkerFitter, markerObservations: List[Dict[str, numpy.ndarray[numpy.float64[3, 1]]]], dt: float, rippleReduce: bool = True, rippleReduceUseSparse: bool = True, rippleReduceUseIterativeSolver: bool = True, rippleReduceSolverIterations: int = 100000.0) nimblephysics_libs._nimblephysics.biomechanics.MarkersErrorReport
getIMUFineTuneProblem(self: nimblephysics_libs._nimblephysics.biomechanics.MarkerFitter, accObservations: List[Dict[str, numpy.ndarray[numpy.float64[3, 1]]]], gyroObservations: List[Dict[str, numpy.ndarray[numpy.float64[3, 1]]]], markerObservations: List[Dict[str, numpy.ndarray[numpy.float64[3, 1]]]], init: nimblephysics_libs._nimblephysics.biomechanics.MarkerInitialization, dt: float, start: int, end: int) nimblephysics_libs._nimblephysics.biomechanics.IMUFineTuneProblem
getImuList(self: nimblephysics_libs._nimblephysics.biomechanics.MarkerFitter) List[Tuple[nimblephysics_libs._nimblephysics.dynamics.BodyNode, nimblephysics_libs._nimblephysics.math.Isometry3]]
getImuMap(self: nimblephysics_libs._nimblephysics.biomechanics.MarkerFitter) Dict[str, Tuple[nimblephysics_libs._nimblephysics.dynamics.BodyNode, nimblephysics_libs._nimblephysics.math.Isometry3]]
getImuNames(self: nimblephysics_libs._nimblephysics.biomechanics.MarkerFitter) List[str]
getInitialization(self: nimblephysics_libs._nimblephysics.biomechanics.MarkerFitter, markerObservations: List[Dict[str, numpy.ndarray[numpy.float64[3, 1]]]], newClip: List[bool], params: nimblephysics_libs._nimblephysics.biomechanics.InitialMarkerFitParams = InitialMarkerFitParams(numBlocks=12)) nimblephysics_libs._nimblephysics.biomechanics.MarkerInitialization
getMarkerIsTracking(self: nimblephysics_libs._nimblephysics.biomechanics.MarkerFitter, marker: str) bool
static getMarkerLossGradientWrtJoints(skeleton: nimblephysics_libs._nimblephysics.dynamics.Skeleton, markers: List[Tuple[nimblephysics_libs._nimblephysics.dynamics.BodyNode, numpy.ndarray[numpy.float64[3, 1]]]], lossGradWrtMarkerError: numpy.ndarray[numpy.float64[m, 1]]) numpy.ndarray[numpy.float64[m, 1]]
getNumMarkers(self: nimblephysics_libs._nimblephysics.biomechanics.MarkerFitter) int
measureAccelerometerRMS(self: nimblephysics_libs._nimblephysics.biomechanics.MarkerFitter, accObservations: List[Dict[str, numpy.ndarray[numpy.float64[3, 1]]]], newClip: List[bool], init: nimblephysics_libs._nimblephysics.biomechanics.MarkerInitialization, dt: float) float
measureGyroRMS(self: nimblephysics_libs._nimblephysics.biomechanics.MarkerFitter, gyroObservations: List[Dict[str, numpy.ndarray[numpy.float64[3, 1]]]], newClip: List[bool], init: nimblephysics_libs._nimblephysics.biomechanics.MarkerInitialization, dt: float) float
optimizeBilevel(self: nimblephysics_libs._nimblephysics.biomechanics.MarkerFitter, markerObservations: List[Dict[str, numpy.ndarray[numpy.float64[3, 1]]]], initialization: nimblephysics_libs._nimblephysics.biomechanics.MarkerInitialization, numSamples: int, applyInnerProblemGradientConstraints: bool = True) nimblephysics_libs._nimblephysics.biomechanics.BilevelFitResult
static pickSubset(markerObservations: List[Dict[str, numpy.ndarray[numpy.float64[3, 1]]]], subsetSize: int) List[Dict[str, numpy.ndarray[numpy.float64[3, 1]]]]
removeZeroConstraint(self: nimblephysics_libs._nimblephysics.biomechanics.MarkerFitter, name: str) None
rotateIMUs(self: nimblephysics_libs._nimblephysics.biomechanics.MarkerFitter, accObservations: List[Dict[str, numpy.ndarray[numpy.float64[3, 1]]]], gyroObservations: List[Dict[str, numpy.ndarray[numpy.float64[3, 1]]]], newClip: List[bool], init: nimblephysics_libs._nimblephysics.biomechanics.MarkerInitialization, dt: float) None
runKinematicsPipeline(self: nimblephysics_libs._nimblephysics.biomechanics.MarkerFitter, markerObservations: List[Dict[str, numpy.ndarray[numpy.float64[3, 1]]]], newClip: List[bool], params: nimblephysics_libs._nimblephysics.biomechanics.InitialMarkerFitParams, numSamples: int = 20, skipFinalIK: bool = False) nimblephysics_libs._nimblephysics.biomechanics.MarkerInitialization
runMultiTrialKinematicsPipeline(self: nimblephysics_libs._nimblephysics.biomechanics.MarkerFitter, markerTrials: List[List[Dict[str, numpy.ndarray[numpy.float64[3, 1]]]]], params: nimblephysics_libs._nimblephysics.biomechanics.InitialMarkerFitParams, numSamples: int = 50) List[nimblephysics_libs._nimblephysics.biomechanics.MarkerInitialization]
runPrescaledPipeline(self: nimblephysics_libs._nimblephysics.biomechanics.MarkerFitter, markerObservations: List[Dict[str, numpy.ndarray[numpy.float64[3, 1]]]], params: nimblephysics_libs._nimblephysics.biomechanics.InitialMarkerFitParams) nimblephysics_libs._nimblephysics.biomechanics.MarkerInitialization
saveTrajectoryAndMarkersToGUI(self: nimblephysics_libs._nimblephysics.biomechanics.MarkerFitter, path: str, init: nimblephysics_libs._nimblephysics.biomechanics.MarkerInitialization, markerObservations: List[Dict[str, numpy.ndarray[numpy.float64[3, 1]]]], accObservations: List[Dict[str, numpy.ndarray[numpy.float64[3, 1]]]], gyroObservations: List[Dict[str, numpy.ndarray[numpy.float64[3, 1]]]], frameRate: int, forcePlates: List[nimblephysics_libs._nimblephysics.biomechanics.ForcePlate] = None, goldOsim: nimblephysics_libs._nimblephysics.biomechanics.OpenSimFile = None, goldPoses: numpy.ndarray[numpy.float64[m, n]] = array([], shape=(0, 0), dtype=float64)) None
setAnatomicalMarkerDefaultWeight(self: nimblephysics_libs._nimblephysics.biomechanics.MarkerFitter, weight: float) None
setAnthropometricPrior(self: nimblephysics_libs._nimblephysics.biomechanics.MarkerFitter, prior: nimblephysics_libs._nimblephysics.biomechanics.Anthropometrics, weight: float = 0.001) None
setCustomLossAndGrad(self: nimblephysics_libs._nimblephysics.biomechanics.MarkerFitter, loss: Callable[[nimblephysics_libs._nimblephysics.biomechanics.MarkerFitterState], float]) None
setDebugJointVariability(self: nimblephysics_libs._nimblephysics.biomechanics.MarkerFitter, debug: bool) None
setDebugLoss(self: nimblephysics_libs._nimblephysics.biomechanics.MarkerFitter, debug: bool) None
setExplicitHeightPrior(self: nimblephysics_libs._nimblephysics.biomechanics.MarkerFitter, prior: float, weight: float = 1000.0) None
setIgnoreJointLimits(self: nimblephysics_libs._nimblephysics.biomechanics.MarkerFitter, ignore: bool) None
setImuMap(self: nimblephysics_libs._nimblephysics.biomechanics.MarkerFitter, imuMap: Dict[str, Tuple[nimblephysics_libs._nimblephysics.dynamics.BodyNode, nimblephysics_libs._nimblephysics.math.Isometry3]]) None
setInitialIKMaxRestarts(self: nimblephysics_libs._nimblephysics.biomechanics.MarkerFitter, starts: int) None
setInitialIKSatisfactoryLoss(self: nimblephysics_libs._nimblephysics.biomechanics.MarkerFitter, loss: float) None
setIterationLimit(self: nimblephysics_libs._nimblephysics.biomechanics.MarkerFitter, iters: int) None
setJointAxisFitSGDIterations(self: nimblephysics_libs._nimblephysics.biomechanics.MarkerFitter, iters: int) None
setJointForceFieldSoftness(self: nimblephysics_libs._nimblephysics.biomechanics.MarkerFitter, softness: float) None

Larger values will increase the softness of the threshold penalty. Smaller values, as they approach zero, will have an almost perfectly vertical penality for going below the threshold distance. That would be hard to optimize, so don’t make it too small.

setJointForceFieldThresholdDistance(self: nimblephysics_libs._nimblephysics.biomechanics.MarkerFitter, minDistance: float) None

This sets the minimum distance joints have to be apart in order to get zero “force field” loss. Any joints closer than this (in world space) will incur a penalty.

setJointSphereFitSGDIterations(self: nimblephysics_libs._nimblephysics.biomechanics.MarkerFitter, iters: int) None
setMarkerIsTracking(self: nimblephysics_libs._nimblephysics.biomechanics.MarkerFitter, marker: str, isTracking: bool = True) None
setMaxAxisWeight(self: nimblephysics_libs._nimblephysics.biomechanics.MarkerFitter, weight: float) None
setMaxJointWeight(self: nimblephysics_libs._nimblephysics.biomechanics.MarkerFitter, weight: float) None
setMaxMarkerOffset(self: nimblephysics_libs._nimblephysics.biomechanics.MarkerFitter, offset: float) None
setMinAxisFitScore(self: nimblephysics_libs._nimblephysics.biomechanics.MarkerFitter, score: float) None
setMinJointVarianceCutoff(self: nimblephysics_libs._nimblephysics.biomechanics.MarkerFitter, cutoff: float) None
setMinSphereFitScore(self: nimblephysics_libs._nimblephysics.biomechanics.MarkerFitter, score: float) None
setParallelIKWarps(self: nimblephysics_libs._nimblephysics.biomechanics.MarkerFitter, parallelWarps: bool) None
If True, this processes “single threaded” IK tasks 32 timesteps at a time

(a “warp”), in parallel, using the first timestep of the warp as the initialization for the whole warp. Defaults to False.

setPostprocessAnatomicalMarkerOffsets(self: nimblephysics_libs._nimblephysics.biomechanics.MarkerFitter, postprocess: bool) None

If we set this to true, then after the main optimization completes we will do a final step to “center” the error of the anatomical markers. This minimizes marker RMSE, but does NOT respect the weights about how far markers should be allowed to move.

setPostprocessTrackingMarkerOffsets(self: nimblephysics_libs._nimblephysics.biomechanics.MarkerFitter, postprocess: bool) None

If we set this to true, then after the main optimization completes we will do a final step to “center” the error of the tracking markers. This minimizes marker RMSE, but does NOT respect the weights about how far markers should be allowed to move.

setRegularizeAllBodyScales(self: nimblephysics_libs._nimblephysics.biomechanics.MarkerFitter, weight: float) None
setRegularizeAnatomicalMarkerOffsets(self: nimblephysics_libs._nimblephysics.biomechanics.MarkerFitter, weight: float) None
setRegularizeIndividualBodyScales(self: nimblephysics_libs._nimblephysics.biomechanics.MarkerFitter, weight: float) None
setRegularizeJointBounds(self: nimblephysics_libs._nimblephysics.biomechanics.MarkerFitter, weight: float) None
setRegularizeJointWithVirtualSpring(self: nimblephysics_libs._nimblephysics.biomechanics.MarkerFitter, jointName: str, weight: float) None
setRegularizePelvisJointsWithVirtualSpring(self: nimblephysics_libs._nimblephysics.biomechanics.MarkerFitter, weight: float) None
setRegularizeTrackingMarkerOffsets(self: nimblephysics_libs._nimblephysics.biomechanics.MarkerFitter, weight: float) None
setStaticTrial(self: nimblephysics_libs._nimblephysics.biomechanics.MarkerFitter, markerObservationsMapAtStaticPose: Dict[str, numpy.ndarray[numpy.float64[3, 1]]], staticPose: numpy.ndarray[numpy.float64[m, 1]]) None
setStaticTrialWeight(self: nimblephysics_libs._nimblephysics.biomechanics.MarkerFitter, weight: float) None
setTrackingMarkerDefaultWeight(self: nimblephysics_libs._nimblephysics.biomechanics.MarkerFitter, weight: float) None
setTrackingMarkers(self: nimblephysics_libs._nimblephysics.biomechanics.MarkerFitter, trackingMarkerNames: List[str]) None
setTriadsToTracking(self: nimblephysics_libs._nimblephysics.biomechanics.MarkerFitter) None
writeCSVData(self: nimblephysics_libs._nimblephysics.biomechanics.MarkerFitter, path: str, init: nimblephysics_libs._nimblephysics.biomechanics.MarkerInitialization, rmsMarkerErrors: List[float], maxMarkerErrors: List[float], timestamps: List[float]) None