STOFileAdapter doesn't indicate if a file is in degrees
cvhammond opened this issue · 11 comments
When parsing and chopping the IK file for preprocessing it is in degrees from OpenSim, but when we save it after our processing, it is in ambiguous units, assumed radians by OpenSim. This causes the position data to be incorrect.
Our options to resolve this:
Detect if the data is in degrees somehow and convert to radians
As part of import process, check if in degrees somehow (maybe built-in to OpenSim) and convert right away on import
IK results files are imported through storageToDoubleMatrix and exported through writeToSto, right? I don't see a way to check for angular units through the OpenSim API. I think the easiest way to handle this would be to check in storageToDoubleMatrix if any value is greater than pi or less than negative pi and convert to radians if so. To prevent it from converting non-angles or other files, it would only run this check for columns with names containing "adduction", "abduction", "flexion", "extension", "rotation", or "angle". Are there any examples I can test this with?
I believe the correct thing to do here is to add a check to the file loading stage such that if it is a .mot
file use the Storage
object function isInDegrees()
. Maybe this should not be automated (hidden in storageToDoubleMatrix()
), but an auxiliary function that is called when we know we are importing .mot files. It's very frustrating that the GUI expects degrees, but the underlying MultibodySystem
requires radians.
The short term solution would be to add a function that checks Storage.isInDegrees()
right after loading ik.mot
in MTP Preprocessing and converts it right then to radians, I think.
Reference:
Issue in opensim-core
FYI MATLAB can split a file into it's parts for you so you just check strcmp(ext, ".mot")
to evaluate if you are loading a .mot
file.
@SpencerTWilliams I will add the Preprocessing
data to the Google Drive folder I shared with you if you want to explore that space as well. It's the zip file no-preprocessed-yet.zip
Will the inDegrees property always be specified at this step? I was using gait_1.mot from the MTP test files you sent me as an example for what IK results files would look like, and its header doesn't have that.
gait_1.mot
isn't actually a .mot
file, it's a .sto
. See previous linked issue, there is no specification for exporting to .mot
automatically. Also see the specification for .mot
which is exported from the IK Tool in the GUI. We could change the gait_1.mot
to export to .sto
just to prevent confusion, but gait_1.sto
would need to be able to be loaded into the GUI by the user (need to verify capacity to do that)
Thanks, that makes more sense now. I'll check for .mot
and then use a separate function to change the units if needed. Is there any case where we'd have to make sure not to change columns containing translations, or can we assume that only angles are used?
You're lucky I've spent so much time messing around with OpenSim. The function you are looking for is Coordinate.getMotionType()
. Loop through the CoordinateSet()
and change the ones that return the string Rotational
. See documentation, we may need to test/think about Coupled
return values as well, but I suspect we shouldn't change those ones.
@SpencerTWilliams can you make a function with signature type (Model, Storage) -> (Array of string, Array of double, 2D Array of double)
to turn a Storage object loaded from a .mot
into a radians based 2D Array of doubles? Can you return the time column and column names as well?
This will allow us to call [names, time, data] = fn(Storage(fileName))
.
Sure, that would make it easier to get coordinates from the model in the same step too.
Just committed b93a9eb with modelMotToDoubleMatrix.m, that should work. I tested it with the CPRIT Patient 3 model file and IK you sent me.
01c212e fixed formatting in the function, now called parseMotToComponents, and made this change in processMotionLabData:
% copyfile(inputs.ikResultsFileName, fullfile(inputs.resultsDir, ...
% ikResultsDir, inputs.prefix + ".mot"))
% Loads IK data and converts to .mot to .sto
[ikColumnLabels, ikTime, ikData] = parseMotToComponents( ...
Model(inputs.model), Storage(inputs.ikResultsFileName));
writeToSto(ikColumnLabels, ikTime, ikData, fullfile(inputs.resultsDir, ...
ikResultsDir, inputs.prefix + ".sto"))