Skip to content

Commit fe5c456

Browse files
committed
Merge branch 'realiseKinematics' - improved realiseKinematics and MFD matlab function
2 parents 64bc07f + 65dd664 commit fe5c456

11 files changed

Lines changed: 11083 additions & 210 deletions

File tree

_test_data/Arm26/elbow_flexion.mot

Lines changed: 128 additions & 128 deletions
Large diffs are not rendered by default.

_test_data/gait2392/subject01.osim

Lines changed: 10652 additions & 0 deletions
Large diffs are not rendered by default.

_test_data/gait2392/subject01_walk1_ik.mot

Lines changed: 132 additions & 0 deletions
Large diffs are not rendered by default.

matlab_tool/.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
*.log
2+
*.asv

matlab_tool/TEST_plugin.m

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,29 @@
1414
effective_attachm = 'true';
1515
test_input = [];
1616

17-
N_frame_test = 5
17+
N_frame_test = 5;
1818
muscleLinesOfActionStruct = muscleForceDirection_plugin(osimModel_name,...
1919
IK_mot_file,...
2020
bodyOfInterest_name,...
2121
bodyExpressResultsIn_name,...
2222
effective_attachm,...
23-
[]);
23+
'true',...
24+
[]);
25+
26+
27+
28+
% osimModel_name = '../_test_data/gait2392/subject01.osim';
29+
% IK_mot_file = '../_test_data/gait2392/subject01_walk1_ik.mot';
30+
% bodyOfInterest_name = 'femur_r';
31+
% bodyExpressResultsIn_name = [];
32+
% effective_attachm = 'true';
33+
% test_input = [];
34+
%
35+
% N_frame_test = 5;
36+
% muscleLinesOfActionStruct = muscleForceDirection_plugin(osimModel_name,...
37+
% IK_mot_file,...
38+
% bodyOfInterest_name,...
39+
% bodyExpressResultsIn_name,...
40+
% effective_attachm,...
41+
% 'true',...
42+
% []);

matlab_tool/getCurrentMusclePathAsMat.m

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@
5050

5151
% point under examination
5252
curr_point = curr_PathPointSet.get(n_p);
53-
attachBody = char(curr_point.getBodyName());
53+
attachBody = curr_point.getBodyName();
5454

5555
% if pathpoint is conditional, then check if it is active
5656
if strcmp(char(curr_point.getConcreteClassName), 'ConditionalPathPoint')
@@ -59,15 +59,15 @@
5959
if cond_viapoint.isActive(s)
6060
% if yes add it to point set
6161
mus_pointset_mat(n_pp, 1:3) = [curr_point.getLocation.get(0), curr_point.getLocation.get(1), curr_point.getLocation.get(2)]; %#ok<*AGROW>
62-
mus_bodyset_list(n_pp) = {attachBody};
62+
mus_bodyset_list(n_pp) = {char(attachBody)};
6363
n_pp = n_pp+1;
6464
else
6565
continue
6666
end
6767
else
6868
% if not conditional add it
6969
mus_pointset_mat(n_pp, 1:3) = [curr_point.getLocation.get(0), curr_point.getLocation.get(1), curr_point.getLocation.get(2)];
70-
mus_bodyset_list(n_pp) = {attachBody};
70+
mus_bodyset_list(n_pp) = {char(attachBody)};
7171
n_pp = n_pp+1;
7272
end
7373
end
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
function [out, missing_coords_list] = isKinMatchingModelCoords(osimModel, headers_in_struct)
2+
3+
% OpenSim suggested settings
4+
import org.opensim.modeling.*
5+
OpenSimObject.setDebugLevel(3);
6+
7+
% getting model coordinates and their number
8+
coordsModel = osimModel.updCoordinateSet();
9+
N_coordsModel = coordsModel.getSize();
10+
11+
% check if coordinates are the same (sometimes time is also here)
12+
if any(strcmp('time', headers_in_struct))
13+
coordsNames_in_struct = headers_in_struct(~strcmp('time', headers_in_struct));
14+
else
15+
coordsNames_in_struct = headers_in_struct;
16+
end
17+
n_miss = 1;
18+
n_matched = 1;
19+
coordNames = {};
20+
missing_coords_list = {};
21+
22+
for n = 0:N_coordsModel-1
23+
%extracting the column for the state variable of interest
24+
cur_coordName = char(coordsModel.get(n).getName());
25+
if find(strcmp(cur_coordName, coordsNames_in_struct)) == n+1
26+
coordNames{n_matched} = cur_coordName;
27+
n_matched = n_matched+1;
28+
else
29+
missing_coords_list{n_miss} = cur_coordName;
30+
n_miss = n_miss+1;
31+
end
32+
end
33+
34+
if length(coordNames) == length(coordsNames_in_struct)
35+
disp('Coordinate structure and model are properly matched (coordinates and their order).');
36+
out = 1;
37+
return
38+
else
39+
out = 0;
40+
41+
end
42+
43+
end

matlab_tool/isMuscleConnectedToBody.m

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,11 @@
4242
% initialise answ
4343
flag = 0;
4444

45+
% check name
46+
if isjava(osimBodyName)
47+
osimBodyName = char(osimBodyName);
48+
end
49+
4550
% extract muscle pathpointset
4651
muspathpointset = osimMuscle.getGeometryPath.getPathPointSet();
4752

@@ -51,9 +56,9 @@
5156
% current attachment body of the muscle point
5257
curr_attach_body = char(muspathpointset.get(n_p).getBodyName);
5358
% check if this is the specified body
54-
if strcmp(curr_attach_body, char(osimBodyName))
59+
if strcmp(curr_attach_body, osimBodyName)
5560
flag = 1;
56-
display(['Muscle ', char(osimMuscle.getName),' is attached to body ', char(osimBodyName)]);
61+
display(['Muscle ', char(osimMuscle.getName),' is attached to body ', osimBodyName]);
5762
return
5863
end
5964
end

matlab_tool/muscleForceDirection_plugin.m

Lines changed: 62 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
bodyOfInterest_name,...
3838
bodyExpressResultsIn_name,...
3939
effective_attachm,...
40+
visualise,...
4041
test_input)
4142

4243
% Load Library
@@ -67,78 +68,83 @@
6768
end
6869
end
6970

71+
% define muscles to include in the analysis
7072
% get muscles
7173
muscleSet = osimModel.getMuscles();
72-
N_muscles = muscleSet.getSize();
74+
n_keep = 1;
75+
for n_m = 0:muscleSet.getSize()-1
76+
77+
% extract muscle
78+
curr_mus = muscleSet.get(n_m);
79+
curr_mus_name = char(muscleSet.get(n_m).getName());
80+
81+
% check if muscle is attached (1) or not (0) to bone of interest
82+
% left in internal loop to handle conditional viapoints, if
83+
% necessary. They might be active only at certain states, therefore
84+
% it is risky to exclude muscles a priori.
85+
% NO! CANNOT CHANGE ORIGIN OR INSERTION
86+
musc_attach = isMuscleConnectedToBody(curr_mus, bodyOfInterest_name);
87+
88+
if musc_attach == 1
89+
target_mus_names{n_keep} = curr_mus_name;
90+
91+
% defining headers as well
92+
rowheaders{n_keep,1} = curr_mus_name;
93+
store_range = 1+3*(n_keep-1):3+3*(n_keep-1);
94+
colheaders_MFD_vec(store_range) = { [curr_mus_name,'_vx_in_',bodyExpressResultsIn_name],...
95+
[curr_mus_name,'_vy_in_',bodyExpressResultsIn_name],...
96+
[curr_mus_name,'_vz_in_',bodyExpressResultsIn_name]};
97+
colheaders_MFD_attach(store_range) = { [curr_mus_name,'_px_in_',bodyExpressResultsIn_name],...
98+
[curr_mus_name,'_py_in_',bodyExpressResultsIn_name],...
99+
[curr_mus_name,'_pz_in_',bodyExpressResultsIn_name]};
100+
n_keep = n_keep +1;
101+
end
102+
end
73103

74-
% TODO: get subset of muscles if needed
104+
N_target_muscles = length(target_mus_names);
75105

76106
% current state. It will change according to kinematics
107+
if strcmp(visualise, 'true')
108+
osimModel.setUseVisualizer(true);
109+
end
77110
curr_state = osimModel.initSystem();
78111

112+
% number of frames
79113
N_frames = size(IKStruct.data,1);
80-
81-
% initialize matrix to use as output. Same output as plugin.
82-
% The idea is to have a matrix with a row for each muscle and three groups
83-
% of columns: attachment on bone, line of action and transport moment.
84-
% This matrix will make easy both checking the equilibrium and setting the
85-
% loads in the FE model
86-
colheaders = {'bone_attach_X', 'bone_attach_Y', 'bone_attach_Z', 'effect_attach_X', 'effect_attach_Y', 'effect_attach_Z', ...
87-
'act_line_X', 'act_line_Y', 'act_line_Z', 'trans_mom_X', 'trans_mom_Y', 'trans_mom_Z'};
88-
89-
90-
% Variables could be preallocated for speed, but I didn't like to have all
91-
% zeros, which might be confused with actual values from the analysis.
92-
% mus_info_mat = zeros(muscleSet.getSize(),12, N_frames);
93-
94114
% this is a testing option to reduce processed kinematics frames when
95115
% developing related functions
96116
if ~isempty(test_input)
97117
N_frames = test_input;
98118
end
99119

100-
120+
% Variables could be preallocated for speed, but I didn't like to have all
121+
% zeros, which might be confused with actual values from the analysis.
122+
% mus_info_mat = zeros(muscleSet.getSize(),12, N_frames);
101123
for n_frame = 1:N_frames
102124

103125
% realize kinematics
104126
state_new = realizeMatStructKinematics(osimModel, curr_state, IKStruct, n_frame);
105-
127+
if strcmp(visualise, 'true')
128+
osimModel.updVisualizer().show(state_new);
129+
end
106130
% counter for muscles to be kept at each frame
107131
n_keep = 1;
108132

109-
for n_m = 0:N_muscles-1
133+
% display time progression
134+
disp('--------------------');
135+
disp(['FRAME: ',num2str(n_frame),'/',num2str(N_frames),'.']);
136+
disp('--------------------');
137+
138+
for n_m = 0:N_target_muscles-1
110139

111140
% extract muscle
112-
curr_mus = muscleSet.get(n_m);
113-
curr_mus_name = muscleSet.get(n_m).getName();
141+
curr_mus_name = target_mus_names{n_m+1};
142+
curr_mus = muscleSet.get(curr_mus_name);
114143

115144
% loop through the points
116-
disp('----------------------')
117-
disp(['FRAME: ',num2str(n_frame),'/',num2str(N_frames),'. Processing muscle (',num2str(n_m+1),'/',num2str(N_muscles),'): ',char(curr_mus_name)])
118145

119-
% check if muscle is attached (1) or not (0) to bone of interest
120-
% left in internal loop to handle conditional viapoints, if
121-
% necessary. They might be active only at certain states, therefore
122-
% it is risky to exclude muscles a priori.
123-
musc_attach = isMuscleConnectedToBody(curr_mus, bodyOfInterest_name);
146+
disp(['Processing muscle (',num2str(n_m+1),'/',num2str(N_target_muscles),'): ', curr_mus_name]);
124147

125-
if musc_attach == 0
126-
% if not attached then nan for the sake of this investigation
127-
continue
128-
else
129-
% if attached
130-
131-
% for final data structure
132-
if n_frame == 1
133-
rowheaders{n_keep,1} = char(curr_mus_name);
134-
store_range = 1+3*(n_keep-1):3+3*(n_keep-1);
135-
colheaders_MFD_vec(store_range) = {[char(curr_mus_name),'_vx_in_',bodyExpressResultsIn_name],...
136-
[char(curr_mus_name),'_vy_in_',bodyExpressResultsIn_name],...
137-
[char(curr_mus_name),'_vz_in_',bodyExpressResultsIn_name]};
138-
colheaders_MFD_attach(store_range) = {[char(curr_mus_name),'_px_in_',bodyExpressResultsIn_name],...
139-
[char(curr_mus_name),'_py_in_',bodyExpressResultsIn_name],...
140-
[char(curr_mus_name),'_pz_in_',bodyExpressResultsIn_name]};
141-
end
142148
% make available info about the muscle pointSet (body names
143149
% and points coordinates)
144150
[mus_bodyset_list, mus_pointset_mat] = getCurrentMusclePathAsMat(curr_mus, state_new);
@@ -167,7 +173,7 @@
167173
if attach_vec(1)==0 && attach_vec(end)==0
168174
% This case is for multi-articular muscles that jump the
169175
% body and can be attached with a viapoint.
170-
display(['Muscle has no bone attachments on ',bodyOfInterest_name]);
176+
display(['Muscle has only viapoints (no bone attachments) on ',bodyOfInterest_name,'. Skipping...']);
171177
continue
172178
end
173179

@@ -230,7 +236,7 @@
230236
% clear variables to avoid surprises
231237
clear attachment force_dir_norm transp_mom_norm lastAttach otherBodyAttach_vec force_dir_res
232238

233-
end
239+
% end
234240
end
235241
end
236242

@@ -257,12 +263,20 @@
257263
% attachments in the local reference system, the file will contain the
258264
% first and last muscle points specified for that muscle in the original model file.
259265
MFD_attachments.colheaders = colheaders_MFD_attach;
260-
if (effective_attachm==1) || strcmp(effective_attachm, 'true')
266+
if strcmp(effective_attachm, 'true')
261267
MFD_attachments.data = mus_info_mat(:, :, 1);
262268
else
263269
MFD_attachments.data = mus_info_mat(:, :, 2);
264270
end
265271

272+
%--------------------------
273+
% Advanced MATLAB summary |
274+
%--------------------------
275+
% The idea is to have a matrix with frames as rows and in column bone
276+
% attachments, effective attachments, lines of action and transport
277+
% moments.
278+
colheaders = {'bone_attach_X', 'bone_attach_Y', 'bone_attach_Z', 'effect_attach_X', 'effect_attach_Y', 'effect_attach_Z', ...
279+
'act_line_X', 'act_line_Y', 'act_line_Z', 'trans_mom_X', 'trans_mom_Y', 'trans_mom_Z'};
266280
% this is an advanced summary for Matlab use
267281
MFDStruct.colheaders = colheaders;
268282
MFDStruct.rowheaders = rowheaders;

matlab_tool/realizeMatStructKinematics.m

Lines changed: 31 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -39,41 +39,47 @@
3939
coordsModel = osimModel.updCoordinateSet();
4040
N_coordsModel = coordsModel.getSize();
4141

42+
% checking is kinematics matches coordinates order
43+
[out, missing_coords_list] = isKinMatchingModelCoords(osimModel, kinStruct.colheaders);
44+
45+
% correct if there are generalised coordinates not computed in IK (e.g.
46+
% models modified after running IK)
47+
% NB: not tested!!
48+
if ~out
49+
for n_m = 1:length(missing_coords_list)
50+
missing_coords_vals(n_m) = coordsModel.get(coordName).getDefaultValue;
51+
end
52+
else
53+
missing_coords_vals = [];
54+
end
55+
% if needed
56+
% kinStruct.colheader = [kinStruct.colheaders, missing_coords_list];
57+
58+
% account for time column in data structure
59+
if any(strcmp('time', kinStruct.colheaders))
60+
kin_state_angles = [kinStruct.data(n_frame, 2:end), missing_coords_vals];
61+
else
62+
kin_state_angles = [kinStruct.data(n_frame, :), missing_coords_vals];
63+
end
64+
4265
% looping thought coordinates to assign them a value
43-
for n_StateCoord = 0:N_coordsModel-1
44-
45-
%%%%%% updating pose of the model %%%%%
46-
%extracting the column for the state variable of interest
47-
coordName = char(coordsModel.get(n_StateCoord).getName());
66+
for n_modelCoords = 0:N_coordsModel-1
4867

49-
% check if the coordinates as an equivalent in storage.
50-
CoordInd_in_kinStorage = getMatStructColumn(kinStruct, coordName);
51-
52-
if isempty(CoordInd_in_kinStorage)
53-
% this is the case when a coordinate is not in the storage file
54-
if n_frame==0
55-
warndlg([coordName,' not found in storage. Default value will be assigned.'])
56-
end
57-
% assign default value
58-
currentCoordValue = coordsModel.get(coordName).getDefaultValue;
59-
else
60-
coordColumValues = getMatStructColumn(kinStruct, coordName);
61-
% Value of the state variable at that frame
62-
currentCoordValue = coordColumValues(n_frame);
63-
end
68+
% Value of the state variable at that frame
69+
cur_coordValue = kin_state_angles(n_modelCoords+1);
6470

6571
% assigning the coordinates depending on their motion type
66-
switch char(coordsModel.get(n_StateCoord).get_motion_type())
72+
switch char(coordsModel.get(n_modelCoords).get_motion_type())
6773
case 'rotational'
6874
% transform to radiant for angles
69-
coordsModel.get(n_StateCoord).setValue(state,currentCoordValue*pi/180);
75+
coordsModel.get(n_modelCoords).setValue(state,cur_coordValue*pi/180);
7076
case 'translational'
7177
% not changing linear distances
72-
coordsModel.get(n_StateCoord).setValue(state,currentCoordValue);
78+
coordsModel.get(n_modelCoords).setValue(state,cur_coordValue);
7379
case 'coupled'
74-
error('motiontype ''coupled'' is not managed by this function');
80+
error('motiontype ''coupled'' is not managed by realizeMatStructKinematics.m');
7581
otherwise
76-
error('motion type not recognized. Error due to OpenSim update?')
82+
error('motion type not recognized. Please check OpenSim model (maybe update error?')
7783
end
7884
end
7985

0 commit comments

Comments
 (0)