[592feb]: / MATLAB_tool / MuscleParOptTool / optimMuscleParams.m

Download this file

203 lines (170 with data), 8.6 kB

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
%-------------------------------------------------------------------------%
% Copyright (c) 2019 Modenese L., Ceseracciu, E., Reggiani M., Lloyd, D.G.%
% %
% Licensed under the Apache License, Version 2.0 (the "License"); %
% you may not use this file except in compliance with the License. %
% You may obtain a copy of the License at %
% http://www.apache.org/licenses/LICENSE-2.0. %
% %
% Unless required by applicable law or agreed to in writing, software %
% distributed under the License is distributed on an "AS IS" BASIS, %
% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or %
% implied. See the License for the specific language governing %
% permissions and limitations under the License. %
% %
% Author: Luca Modenese, January 2015 %
% email: l.modenese@imperial.ac.uk %
% ----------------------------------------------------------------------- %
%
% This function optimizes the muscle parameters as described in Modenese L,
% Ceseracciu E, Reggiani M, Lloyd DG (2015). Estimation of
% musculotendon parameters for scaled and subject specific musculoskeletal
% models using an optimization technique. Journal of Biomechanics (submitted)
% and prints the results to command window.
% Also it stores information about the optimization in the structure SimInfo
function [osimModel_opt, SimInfo] = optimMuscleParams(osimModel_ref_filepath, osimModel_targ_filepath, N_eval, log_folder)
% import opensim libraries
import org.opensim.modeling.*
% results file identifier
res_file_id_exp = ['_N',num2str(N_eval)];
% import models
osimModel_ref = Model(osimModel_ref_filepath);
osimModel_targ = Model(osimModel_targ_filepath);
% models details
[~, name, ext] = fileparts(osimModel_targ_filepath);
% assigning new name to the model
osimModel_opt_name = [name,'_opt',res_file_id_exp,ext];
osimModel_targ.setName(osimModel_opt_name);
% initializing log file
if ~isdir(log_folder)
warning([log_folder,' does not exist. It will be created']);
mkdir(log_folder);
end
log_filepath = fullfile(log_folder,[name,'_opt',res_file_id_exp,'.log']);
% cleaning file (otherwise it appends)
fopen(log_filepath,'w+'); fclose all;
% set up diary, without writing at the moment
diary(log_filepath); diary off
% get muscles
muscles = osimModel_ref.getMuscles;
muscles_scaled = osimModel_targ.getMuscles;
% init model
% si = osimModel_ref.initSystem;
% si_scaled = osimModel_targ.initSystem;
% initialize with recognizable values
LmOptLts_opt = ones(muscles.getSize,2)*(-1000);
for n_mus = 0:muscles.getSize-1
tic
% current muscle name (here so that it is possible to choose a single
% muscle when developing.
curr_mus_name = char(muscles.get(n_mus).getName);
display(['processing mus ',num2str(n_mus+1),': ',char(curr_mus_name)]);
% import muscles
curr_mus = muscles.get(n_mus);
curr_mus_scaled = muscles_scaled.get(curr_mus_name);
% extracting the muscle parameters from reference model
LmOptLts = [curr_mus.getOptimalFiberLength, curr_mus.getTendonSlackLength];
PenAngleOpt = curr_mus.getPennationAngleAtOptimalFiberLength();
Mus_ref = sampleMuscleQuantities(osimModel_ref,curr_mus,'all',N_eval);
% calculating minimum fiber length before having pennation 90 deg
% acos(0.1) = 1.47 red = 84 degrees, chosen as in OpenSim
limitPenAngle = acos(0.1);
% this is the minimum length the fiber can be for geometrical reasons.
LfibNorm_min = sin(PenAngleOpt)/sin(limitPenAngle);
% LfibNorm as calculated above can be shorter than the minimum length
% at which the fiber can generate force (taken to be 0.5 Zajac 1989)
if (LfibNorm_min<0.5)==1
LfibNorm_min = 0.5;
end
% checking the muscle configuration that do not respect the condition.
LfibNorm_ref = Mus_ref(:,2);
okList = (LfibNorm_ref>LfibNorm_min);
% keeping only acceptable values
LfibNorm_ref = LfibNorm_ref(okList);
LtenNorm_ref = Mus_ref(okList,3)/LmOptLts(2);
%Ffib = Mus_ref(okList,4)/curr_mus.getMaxIsometricForce;
MTL_ref = Mus_ref(okList,1);
penAngle_ref = Mus_ref(okList,5);
LfibNormOnTen_ref = LfibNorm_ref.*cos(penAngle_ref);
% in the target only MTL is needed for all muscles
MTL_targ = sampleMuscleQuantities(osimModel_targ,curr_mus_scaled,'MTL',N_eval);
evalTotPoints = length(MTL_targ);
MTL_targ = MTL_targ(okList)';
evalOkPoints = length(MTL_targ);
% The problem to be solved is:
% [LmNorm*cos(penAngle) LtNorm]*[Lmopt Lts]' = MTL;
% written as Ax = b
A = [LfibNormOnTen_ref LtenNorm_ref];
b = MTL_targ;
% ===== LINSOL =======
% solving the problem to calculate the muscle param
x = A\b;
LmOptLts_opt(n_mus+1,:) = x;
% checking the results
if min(x)<=0
diary on
% informing the user
display(['Negative value estimated for muscle parameter of muscle ',curr_mus_name]);
display( ' Lm Opt Lts' );
display(['Template model : ',num2str(LmOptLts)]);
display(['Optimized param : ',num2str(LmOptLts_opt(n_mus+1,:))]);
% ===== IMPLEMENTING CORRECTIONS IF ESTIMATION IS NOT CORRECT =======
% first try lsqnonlin
x = lsqnonneg(A,b);
LmOptLts_opt(n_mus+1,:) = x;
display(['Opt params (lsqnonneg): ',num2str(LmOptLts_opt(n_mus+1,:))]);
% In our tests, if something goes wrong is generally tendon slack
% length becoming negative or zero because tendon length doesn't change
% throughout the range of motion, so lowering the rank of A.
% if x(2)==0
if min(x)<=0
if (max(Mus_ref(okList,3))-min(Mus_ref(okList,3)))<0.0001
display('Tendon length not changing throughout range of motion')
end
% calculating proportion of tendon and fiber
% Lfib_fraction = LfibNormOnTen*LmOptLts(1)./MTL;
Lten_fraction = Mus_ref(okList,3)./MTL_ref;
Lten_targ = (Lten_fraction.*MTL_targ);
% first round: optimizing Lopt maintaing the proportion of
% tendon as in the reference model
A_1 = LfibNormOnTen_ref;
b_1 = (MTL_targ-Lten_targ);
x(1) = A_1\b_1;
% second round: using the optimized Lopt to recalculate Lts
b_2 = MTL_targ-A_1*x(1);
A_2 = LtenNorm_ref;
x(2) = A_2\b_2;
LmOptLts_opt(n_mus+1,:) = x;
end
diary off
end
% Here tests about '\' against optimizers were implemented.
% calculating the error (sum of squared errors)
fval = norm(A*x-b).^2.0;
% update muscles from scaled model
curr_mus_scaled.setOptimalFiberLength(LmOptLts_opt(n_mus+1,1));
curr_mus_scaled.setTendonSlackLength(LmOptLts_opt(n_mus+1,2));
% PRINT LOGS
diary on
display(' ');
display(['Calculated optimized muscle parameters for ', char(curr_mus),' in ',num2str(toc),' seconds.'])
display( ' Lm Opt Lts' );
display(['Template model : ',num2str(LmOptLts)]);
display(['Optimized param : ',num2str(LmOptLts_opt(n_mus+1,:))]);
display(['Nr of eval points : ',num2str(evalOkPoints), '/',num2str(evalTotPoints),' used'])
display(['fval : ',num2str(fval)]);
display(['var from template [%]: ',num2str(100*(abs(LmOptLts-LmOptLts_opt(n_mus+1,:)))./LmOptLts),'%'])
display(' ');
diary off
% SIMULATION INFO AND RESULTS
SimInfo.colheader(n_mus+1) = {char(curr_mus)};
SimInfo.LmOptLts_ref(1:2,n_mus+1) = LmOptLts;
SimInfo.LmOptLts_opt(1:2,n_mus+1) = LmOptLts_opt(n_mus+1,:);
SimInfo.varPercLmOptLts(1:2,n_mus+1) = 100*(abs(LmOptLts-LmOptLts_opt(n_mus+1,:)))./LmOptLts;
SimInfo.sampledEvalPoints(n_mus+1) = evalOkPoints;
SimInfo.usedEvalPoints(n_mus+1) = evalTotPoints;
SimInfo.fval(n_mus+1) = fval;
end
% assigning optimized model as output
osimModel_opt = osimModel_targ;
end