Kin/pot energy & joint reactions from MocoTrajectory in Matlab

OpenSim Moco is a software toolkit to solve optimal control problems with musculoskeletal models defined in OpenSim using the direct collocation method.
User avatar
Nicholas Bianco
Posts: 1082
Joined: Thu Oct 04, 2012 8:09 pm

Re: Kin/pot energy & joint reactions from MocoTrajectory in Matlab

Post by Nicholas Bianco » Fri Mar 19, 2021 1:32 pm

This works! At the start of my Matlab script, I have the following statement: "import org.opensim.modeling.*;". I now suspect that this does not load all of the Moco-specific scripts.
Great! That should import all OpenSim/Moco libraries for whatever the currently installed version of OpenSim/Moco you're using. It's just that the version of Moco you're using needs the opensimMoco library prefix to access that free function.

For TimeSeriesTableSpatialVec, you can call the 'flatten()' method, which should repackage the table into scalar columns (or Vec3, not sure for SpatialVec), which should be usable in scripting.

-Nick

User avatar
Pasha van Bijlert
Posts: 235
Joined: Sun May 10, 2020 3:15 am

Re: Kin/pot energy & joint reactions from MocoTrajectory in Matlab

Post by Pasha van Bijlert » Sat Mar 20, 2021 2:47 pm

Hi Nick,

Thank you so much, this works! 'flatten()' provides you with a TimeSeriesTable with 6 + 1 columns for each column of TimeSeriesTableSpatialVec. Essentially, this means that each element of a SpatialVec (3 Torque and 3 Force elements), gets unpacked to its "own column', and the extra column is the time column.

As a final question: the default behaviour of "analyzeSpatialVec" is to express the SpatialVecs in the "Ground" (i.e. an inertial) frame of reference, is this correct?

If so, to transform each spatialVec to the desired frame, I must implement something like the following (pseudo) code:

Code: Select all


 state = model.realizeDynamics(statesTraj.get(0)) ; %statesTraj is the StatesTrajectory from a prediction solution

    Torque_ground = Vec3(JR(1,1), JR(1,2), JR(1,3)); 
    Force_ground  = Vec3(JR(1,4), JR(1,5), JR(1,6));
    
    %JR is an array obtained after flattening a TimeSeriesTableSpatialVec and converting 
    % it to a struct using osimTableToStruct
    
    ground = model.getGround();
    child_frame = model.getJointSet().get(0).getChildFrame; %change this to the desired frame for each joint
        
    Torque_local =  ground.expressVectorInAnotherFrame(state, Torque_ground, child_frame);
    Force_local  =  ground.expressVectorInAnotherFrame(state,  Force_ground, child_frame);
        
    joint_reactions_local(1,1) = Torque_local.get(0);
    joint_reactions_local(1,2) = Torque_local.get(1);
    joint_reactions_local(1,3) = Torque_local.get(2);
    
    joint_reactions_local(1,4) = Force_local.get(0);
    joint_reactions_local(1,5) = Force_local.get(1);
    joint_reactions_local(1,6) = Force_local.get(2);
Thank you so much, I think I'm all set to implement my project now!
Pasha

EDIT: different implementation to transform the vectors

User avatar
Pasha van Bijlert
Posts: 235
Joined: Sun May 10, 2020 3:15 am

Re: Kin/pot energy & joint reactions from MocoTrajectory in Matlab

Post by Pasha van Bijlert » Tue Mar 23, 2021 7:12 am

EDIT: this code snippet only works for MOCO 0.4, if you've updated to 1.0 you need to modify according to the posts by Nick & Ross later on in this thread.

Here is a snippet of Matlab code that extracts all the jointreactions from a GaitPredictionSolution, expresses them in the child body frame, and places them in a structured array, with tidied up column headers as a seperate cell array (for easier plotting/processing). You should be able to place this directly inline with the script that runs a simulation, or you could place this in a function (with inputs: model, study, and a predictionSolution). If you'd like the reactions on parent instead, change every instance of child (or Child) to parent (or Parent).

Hope this helps someone!

Code: Select all

JR_paths=StdVectorString();
 
 JR_paths.add('.*reaction_on_child'); %regexp selecting any output that ends with this expression, so this selects all the joints

JR_outputs_table = opensimMoco.analyzeSpatialVec(model, gaitPredictionSolution, JR_paths).flatten(); %Flatten turns a TimeSeriesTableSpatialVec into a TimeSeriesTable, with 6 columns for each spatialvec
% A SpatialVec in this context provides the following outputs: Mx My Mz Fx Fy Fz


% Get a Matlab struct
JR_outputs = osimTableToStruct (JR_outputs_table);
JRHeaders= fieldnames(JR_outputs);


%%% Place in an array
for i = 1:length(JRHeaders) -1
Joint_reactions (:,i) = JR_outputs.(JRHeaders{i,1}); %These are still expressed in the ground frame
end




statesTraj= gaitPredictionSolution.exportToStatesTrajectory(model); %get a StatesTrajectory, so we can loop through it
[~,n] = size(Joint_reactions);

for i = 1:statesTraj.getSize  %loop through each timepoint in the trajectory
    state=statesTraj.get(i-1);
    model.realizeDynamics(state);      
    for j = 1:(n/6) %Loop through each joint (joint reactions provide a spatialvec as output, so 6 columns per joint)
    
    Torque_ground = Vec3(Joint_reactions(i,j*6 -5), Joint_reactions(i,j*6 -4), Joint_reactions(i,j*6 -3));
    Force_ground  = Vec3(Joint_reactions(i,j*6 -2), Joint_reactions(i,j*6 -1), Joint_reactions(i,j*6   ));
    
    ground = model.getGround();
    child_frame = model.getJointSet().get(j-1).getChildFrame; %it is worth double checking if these frames
    %correspond to the right joints as in JR_outputs_table
                                                             
    % Express the torques and forces in the child frame    
    Torque_local =  ground.expressVectorInAnotherFrame(state, Torque_ground, child_frame);
    Force_local  =  ground.expressVectorInAnotherFrame(state,  Force_ground, child_frame);
    
    
    %place back in an array.
    joint_reactions_local(i,j*6 -5) = Torque_local.get(0);
    joint_reactions_local(i,j*6 -4) = Torque_local.get(1);
    joint_reactions_local(i,j*6 -3) = Torque_local.get(2);
    
    joint_reactions_local(i,j*6 -2) = Force_local.get(0);
    joint_reactions_local(i,j*6 -1) = Force_local.get(1);
    joint_reactions_local(i,j*6   ) = Force_local.get(2);
    end

end

%Improve formatting of the headers
JRHeaders = strrep(JRHeaders,'unlabeled_jointset_','');
JRHeaders = strrep(JRHeaders,'_',' ');
JRHeaders = strrep(JRHeaders,'reaction on child 1','Mx on child');
JRHeaders = strrep(JRHeaders,'reaction on child 2','My on child');
JRHeaders = strrep(JRHeaders,'reaction on child 3','Mz on child');
JRHeaders = strrep(JRHeaders,'reaction on child 4','Fx on child');
JRHeaders = strrep(JRHeaders,'reaction on child 5','Fy on child');
JRHeaders = strrep(JRHeaders,'reaction on child 6','Fz on child');

%place in a struct
data.JRHeaders = JRHeaders(1:end-1,:); %remove the last entry which corresponded to the time vector
data.JR = joint_reactions_local; %joint reactions on the child body, expressed in the child body frame
Last edited by Pasha van Bijlert on Fri Apr 23, 2021 6:00 am, edited 1 time in total.

User avatar
Ross Miller
Posts: 375
Joined: Tue Sep 22, 2009 2:02 pm

Re: Kin/pot energy & joint reactions from MocoTrajectory in Matlab

Post by Ross Miller » Wed Apr 21, 2021 4:46 am

Hi all,

I'm trying to run the code Pasha posted in the previous post to compute joint reactions from a MocoSolution and it's not working for me. It seems my machine either doesn't have analyzeSpatialVec or doesn't know where to find it. For each of the three following code snippets:

Code: Select all

import org.opensim.modeling.*;
JR_paths = StdVectorString();
JR_paths.add('.*reaction_on_child');
JR_outputs_table = opensimMoco.analyzeSpatialVec(model,gaitTrackingSolution,JR_paths).flatten();

Code: Select all

import org.opensim.modeling.*;
JR_paths = StdVectorString();
JR_paths.add('.*reaction_on_child');
JR_outputs_table = study.analyzeSpatialVec(model,gaitTrackingSolution,JR_paths).flatten();

Code: Select all

import org.opensim.modeling.*;
JR_paths = StdVectorString();
JR_paths.add('.*reaction_on_child');
JR_outputs_table = analyzeSpatialVec(model,gaitTrackingSolution,JR_paths).flatten();
...Matlab reports the error that "Unrecognized function or variable 'analyzeSpatialVec'."

Any guesses on the problem? I'm running the latest release that came with the 4.2 version of OpenSim:

Code: Select all

>> org.opensim.modeling.opensimCommon.GetVersionAndDate()

ans =

version 4.2-2021-03-12-fcedec9, build date 00:59:17 Mar 18 2021
Ross

User avatar
Nicholas Bianco
Posts: 1082
Joined: Thu Oct 04, 2012 8:09 pm

Re: Kin/pot energy & joint reactions from MocoTrajectory in Matlab

Post by Nicholas Bianco » Wed Apr 21, 2021 10:12 am

Hi Ross,

Since you're using OpenSim 4.2, you need to call that function using the 'opensimSimulation' prefix in Matlab, since the analyze() functions were added under the "osimSimulation" OpenSim library:

Code: Select all

import org.opensim.modeling.*;
JR_paths = StdVectorString();
JR_paths.add('.*reaction_on_child');
JR_outputs_table = opensimSimulation.analyzeSpatialVec(model,gaitTrackingSolution,JR_paths).flatten();
This needs to be documented clearly somewhere so it's easier to find.

-Nick

User avatar
Ross Miller
Posts: 375
Joined: Tue Sep 22, 2009 2:02 pm

Re: Kin/pot energy & joint reactions from MocoTrajectory in Matlab

Post by Ross Miller » Thu Apr 22, 2021 3:58 am

Thanks Nick. Now I'm getting a new error unfortunately:

Code: Select all

JR_paths = StdVectorString();
JR_paths.add('.*reaction_on_child');
JR_outputs_table = opensimSimulation.analyzeSpatialVec(model,gaitTrackingSolution,JR_paths).flatten();
Matlab says: "No method 'org.opensim.modeling.opensimSimulation.analyzeSpatialVec' with matching signature found."

Ross

User avatar
Nicholas Bianco
Posts: 1082
Joined: Thu Oct 04, 2012 8:09 pm

Re: Kin/pot energy & joint reactions from MocoTrajectory in Matlab

Post by Nicholas Bianco » Thu Apr 22, 2021 9:24 am

Hi Ross,

Ah sorry, you have to call that function with these arguments:

Code: Select all

JR_paths = StdVectorString();
JR_paths.add('.*reaction_on_child');
statesTable = gaitTrackingSolution.exportToStatesTable();
controlsTable = gaitTrackingSolution.exportToControlsTable();
JR_outputs_table = opensimSimulation.analyzeSpatialVec(model, statesTable, controlsTable, JR_paths).flatten();
Again, the documentation should be improved for these functions. However, you can use "methodsview" like with other OpenSim classes:

Code: Select all

methodsview opensimSimulation

User avatar
Pasha van Bijlert
Posts: 235
Joined: Sun May 10, 2020 3:15 am

Re: Kin/pot energy & joint reactions from MocoTrajectory in Matlab

Post by Pasha van Bijlert » Fri Apr 23, 2021 5:58 am

Hi Ross, Nick,

Good to know that the implementation has changed, I'll update my post so that it explicitly states that it's for version 0.4.

Cheers,
Pasha

User avatar
Pasha van Bijlert
Posts: 235
Joined: Sun May 10, 2020 3:15 am

Re: Kin/pot energy & joint reactions from MocoTrajectory in Matlab

Post by Pasha van Bijlert » Fri Apr 15, 2022 2:45 am

Here's an updated Matlab code snippet that works with the current version of Moco, which assumes a mocoTrajectory (Named Traj) which is compatible with the model. It extracts the Joint reactions (on child, expressed in the ground frame). Then it transforms them all to the child body frame, and then saves it as a Matlab struct. This implementation works for my code, but I've slightly modified it (because it's part of a bigger script in my analyses), so if this doesn't run as a standalone script please let me know and I'll modify it.


Code: Select all

%% assumes: model and Traj (a mocoTrajectory)
JR_paths=StdVectorString();
 
JR_paths.add('.*reaction_on_child'); %regexp selecting any output that ends with this expression, so this selects all the joints

ST_table = Traj.exportToStatesTable(); %states table
controls_table = Traj.exportToControlsTable(); %controls table

JR_outputs_table = opensimSimulation.analyzeSpatialVec(model, ST_table, controls_table, JR_paths).flatten(); %Flatten turns a TimeSeriesTableSpatialVec into a TimeSeriesTable, with 6 columns for each spatialvec
% A SpatialVec in this context provides the following outputs: Mx My Mz Fx Fy Fz


% Get a Matlab struct
JR_outputs = osimTableToStruct (JR_outputs_table);
JRHeaders= fieldnames(JR_outputs);


%%% Place in an array
for i = 1:length(JRHeaders) -1
Joint_reactions (:,i) = JR_outputs.(JRHeaders{i,1}); %These are still expressed in the ground frame
end




statesTraj= gaitPredictionSolution.exportToStatesTrajectory(model); %get a StatesTrajectory, so we can loop through it
[~,n] = size(Joint_reactions);

for i = 1:statesTraj.getSize  %loop through each timepoint in the trajectory
    state=statesTraj.get(i-1);
    model.realizeDynamics(state);      
    for j = 1:(n/6) %Loop through each joint (joint reactions provide a spatialvec as output, so 6 columns per joint)
    
    Torque_ground = Vec3(Joint_reactions(i,j*6 -5), Joint_reactions(i,j*6 -4), Joint_reactions(i,j*6 -3));
    Force_ground  = Vec3(Joint_reactions(i,j*6 -2), Joint_reactions(i,j*6 -1), Joint_reactions(i,j*6   ));
    
    ground = model.getGround();
    child_frame = model.getJointSet().get(j-1).getChildFrame; %it is worth double checking if these frames
    %correspond to the right joints as in JR_outputs_table
                                                             
    % Express the torques and forces in the child frame    
    Torque_local =  ground.expressVectorInAnotherFrame(state, Torque_ground, child_frame);
    Force_local  =  ground.expressVectorInAnotherFrame(state,  Force_ground, child_frame);
    
    
    %place back in an array.
    joint_reactions_local(i,j*6 -5) = Torque_local.get(0);
    joint_reactions_local(i,j*6 -4) = Torque_local.get(1);
    joint_reactions_local(i,j*6 -3) = Torque_local.get(2);
    
    joint_reactions_local(i,j*6 -2) = Force_local.get(0);
    joint_reactions_local(i,j*6 -1) = Force_local.get(1);
    joint_reactions_local(i,j*6   ) = Force_local.get(2);
    end

end

%Improve formatting of the headers
JRHeaders = strrep(JRHeaders,'unlabeled_jointset_','');
JRHeaders = strrep(JRHeaders,'_',' ');
JRHeaders = strrep(JRHeaders,'reaction on child 1','Mx on child');
JRHeaders = strrep(JRHeaders,'reaction on child 2','My on child');
JRHeaders = strrep(JRHeaders,'reaction on child 3','Mz on child');
JRHeaders = strrep(JRHeaders,'reaction on child 4','Fx on child');
JRHeaders = strrep(JRHeaders,'reaction on child 5','Fy on child');
JRHeaders = strrep(JRHeaders,'reaction on child 6','Fz on child');

%place in a struct
data.JRHeaders = JRHeaders(1:end-1,:); %remove the last entry which corresponded to the time vector
data.JR = joint_reactions_local; %joint reactions on the child body, expressed in the child body frame

User avatar
Molly Shepherd
Posts: 17
Joined: Thu Nov 14, 2019 9:36 am

Re: Kin/pot energy & joint reactions from MocoTrajectory in Matlab

Post by Molly Shepherd » Mon Jul 25, 2022 10:03 am

Hi all,

I am trying to run the code that Pasha posted to calculate Joint reactions. I set the mocoTrajectory as the output .sto file that I got from running MocoInverse. However, when I get to the line:

JR_outputs_table = opensimSimulation.analyzeSpatialVec(model, ST_table, controls_table, JR_paths).flatten();

I receive the following error:

java.lang.RuntimeException: The following 58 states from Model 'model_SS' are missing from the data:
/forceset/addlong_r/fiber_length
/forceset/addmagIsch_r/fiber_length
... (same for all muscles in the model)
Thrown at StatesTrajectory.cpp:227 in createFromStatesTable().

All of the optimal fiber lengths are defined in the model file, however I did notice that no fiber_lengths exist in the .sto file. Is there a different type of mocoTrajectory file I need to be using to get the joint reactions?

Thanks!
Molly

POST REPLY