1 /* -------------------------------------------------------------------------- *
2 * OpenSim: Kinematics.cpp *
3 * -------------------------------------------------------------------------- *
4 * The OpenSim API is a toolkit for musculoskeletal modeling and simulation. *
5 * See http://opensim.stanford.edu and the NOTICE file for more information. *
6 * OpenSim is developed at Stanford University and supported by the US *
7 * National Institutes of Health (U54 GM072970, R24 HD065690) and by DARPA *
8 * through the Warrior Web program. *
9 * *
10 * Copyright (c) 2005-2017 Stanford University and the Authors *
11 * Author(s): Frank C. Anderson *
12 * *
13 * Licensed under the Apache License, Version 2.0 (the "License"); you may *
14 * not use this file except in compliance with the License. You may obtain a *
15 * copy of the License at http://www.apache.org/licenses/LICENSE-2.0. *
16 * *
17 * Unless required by applicable law or agreed to in writing, software *
18 * distributed under the License is distributed on an "AS IS" BASIS, *
19 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
20 * See the License for the specific language governing permissions and *
21 * limitations under the License. *
22 * -------------------------------------------------------------------------- */
23
24
25 //=============================================================================
26 // INCLUDES
27 //=============================================================================
28 #include <OpenSim/Simulation/Model/Model.h>
29 #include "Kinematics.h"
30
31
32 using namespace OpenSim;
33 using namespace std;
34
35
36 //=============================================================================
37 // CONSTANTS
38 //=============================================================================
39
40
41
42 //=============================================================================
43 // CONSTRUCTOR(S) AND DESTRUCTOR
44 //=============================================================================
45 //_____________________________________________________________________________
46 /**
47 * Destructor.
48 */
~Kinematics()49 Kinematics::~Kinematics()
50 {
51 }
52 //_____________________________________________________________________________
53 /**
54 * Construct an Kinematics object for recording the kinematics of
55 * a model's generalized coordinates during a simulation.
56 *
57 * @param aModel Model for which the kinematics are to be recorded.
58 */
Kinematics(Model * aModel)59 Kinematics::Kinematics(Model *aModel) :
60 Analysis(aModel)
61 {
62 // NULL
63 setNull();
64
65 // DESCRIPTION
66 constructDescription();
67
68 // CHECK MODEL
69 if(aModel==NULL) return;
70 setModel(*aModel);
71
72 }
73 //=============================================================================
74 // Object Overrides
75 //=============================================================================
76 //_____________________________________________________________________________
77 /**
78 * Construct an object from file.
79 *
80 * The object is constructed from the root element of the XML document.
81 * The type of object is the tag name of the XML root element.
82 *
83 * @param aFileName File name of the document.
84 */
Kinematics(const std::string & aFileName)85 Kinematics::Kinematics(const std::string &aFileName):
86 Analysis(aFileName, false)
87 {
88 setNull();
89
90 // Serialize from XML
91 updateFromXMLDocument();
92
93 // CONSTRUCT DESCRIPTION AND LABELS
94 constructDescription();
95
96 }
97
98 //_____________________________________________________________________________
99 /**
100 * Set all member variables to their null or default values.
101 */
102 void Kinematics::
setNull()103 setNull()
104 {
105 constructProperties();
106
107 setName("Kinematics");
108 _pStore=_vStore=_aStore=NULL;
109
110 // Let the list own the storages so we don't have to manually delete them
111 _storageList.setMemoryOwner(true);
112
113 _recordAccelerations = true;
114 }
115 //_____________________________________________________________________________
116 /**
117 * Connect properties to local pointers.
118 */
119 void Kinematics::
constructProperties()120 constructProperties()
121 {
122 Array<std::string> coordArray;
123 coordArray.setSize(1);
124 coordArray[0] = "all";
125 constructProperty_coordinates(coordArray);
126 }
127
128 //=============================================================================
129 // CONSTRUCTION METHODS
130 //=============================================================================
131 //_____________________________________________________________________________
132 /**
133 * Allocate storage for the kinematics.
134 */
allocateStorage()135 void Kinematics::allocateStorage()
136 {
137 //Free-up any past storages by resizing which will delete pointers
138 //if memory owner is set to true, as set in setNull() above
139 _storageList.setSize(0);
140
141 // ACCELERATIONS
142 if(_recordAccelerations) {
143 _aStore = new Storage(1000,"Accelerations");
144 _aStore->setDescription(getDescription());
145 _storageList.append(_aStore);
146 }
147
148 // VELOCITIES
149 _vStore = new Storage(1000,"Speeds");
150 _vStore->setDescription(getDescription());
151 _storageList.append(_vStore);
152
153 // POSITIONS
154 _pStore = new Storage(1000,"Coordinates");
155 _pStore->setDescription(getDescription());
156 _storageList.append(_pStore);
157 }
158
159
160 //=============================================================================
161 // DESTRUCTION METHODS
162 //=============================================================================
163 //_____________________________________________________________________________
164 /**
165 * Delete storage objects.
166 */
167 void Kinematics::
deleteStorage()168 deleteStorage()
169 {
170 if(_aStore!=NULL) { delete _aStore; _aStore=NULL; }
171 if(_vStore!=NULL) { delete _vStore; _vStore=NULL; }
172 if(_pStore!=NULL) { delete _pStore; _pStore=NULL; }
173 }
174
175
176 //_____________________________________________________________________________
177 /**
178 * Update coordinates to record
179 */
180 void Kinematics::
updateCoordinatesToRecord()181 updateCoordinatesToRecord()
182 {
183 if(!_model) {
184 _coordinateIndices.setSize(0);
185 _values.setSize(0);
186 return;
187 }
188
189 const CoordinateSet& coordSet = _model->getCoordinateSet();
190 _coordinateIndices.setSize(getProperty_coordinates().size());
191 for(int i=0; i<getProperty_coordinates().size(); i++) {
192 if(get_coordinates(i) == "all") {
193 _coordinateIndices.setSize(coordSet.getSize());
194 for(int j=0;j<coordSet.getSize();j++) _coordinateIndices[j]=j;
195 break;
196 }
197
198 int index = coordSet.getIndex(get_coordinates(i));
199 if(index<0)
200 throw Exception("Kinematics: ERR- Could not find coordinate named '"+get_coordinates(i)+"'",__FILE__,__LINE__);
201 _coordinateIndices[i] = index;
202 }
203 _values.setSize(_coordinateIndices.getSize());
204
205 if(_values.getSize()==0) {
206 cout << "WARNING: Kinematics analysis has no coordinates to record values for" << endl;
207 }
208 }
209
210 //=============================================================================
211 // GET AND SET
212 //=============================================================================
213 //-----------------------------------------------------------------------------
214 // DESCRIPTION
215 //-----------------------------------------------------------------------------
216 //_____________________________________________________________________________
217 /**
218 * Construct the description for the kinematics files.
219 */
220 void Kinematics::
constructDescription()221 constructDescription()
222 {
223 char descrip[1024];
224
225 strcpy(descrip,"\nUnits are S.I. units (second, meters, Newtons, ...)");
226 if(getInDegrees()) {
227 strcat(descrip,"\nAngles are in degrees.");
228 } else {
229 strcat(descrip,"\nAngles are in radians.");
230 }
231 strcat(descrip,"\n\n");
232
233 setDescription(descrip);
234 }
235
236 //-----------------------------------------------------------------------------
237 // COLUMN LABELS
238 //-----------------------------------------------------------------------------
239 //_____________________________________________________________________________
240 /**
241 * Construct the column labels for the kinematics files.
242 */
243 void Kinematics::
constructColumnLabels()244 constructColumnLabels()
245 {
246 // CHECK FOR NULL
247 if (!_model || _model->getNumSpeeds() == 0)
248 {
249 setColumnLabels(Array<string>());
250 return;
251 }
252
253 Array<string> labels;
254 labels.append("time");
255 const CoordinateSet& cs = _model->getCoordinateSet();
256 for(int i=0; i<_coordinateIndices.getSize(); i++) {
257 labels.append(cs.get(_coordinateIndices[i]).getName());
258 }
259
260 setColumnLabels(labels);
261 if (_pStore){
262 _pStore->setColumnLabels(getColumnLabels());
263 if (getInDegrees()) _pStore->setInDegrees(true);
264 }
265 if (_vStore) {
266 _vStore->setColumnLabels(getColumnLabels());
267 if (getInDegrees()) _vStore->setInDegrees(true);
268 }
269 if (_aStore) {
270 _aStore->setColumnLabels(getColumnLabels());
271 if (getInDegrees()) _aStore->setInDegrees(true);
272 }
273 }
274
275
276 //-----------------------------------------------------------------------------
277 // STORAGE
278 //-----------------------------------------------------------------------------
279 //_____________________________________________________________________________
280 /**
281 * Get the acceleration storage.
282 *
283 * @return Acceleration storage.
284 */
285 Storage* Kinematics::
getAccelerationStorage()286 getAccelerationStorage()
287 {
288 return(_aStore);
289 }
290 //_____________________________________________________________________________
291 /**
292 * Get the velocity storage.
293 *
294 * @return Velocity storage.
295 */
296 Storage* Kinematics::
getVelocityStorage()297 getVelocityStorage()
298 {
299 return(_vStore);
300 }
301 //_____________________________________________________________________________
302 /**
303 * Get the position storage.
304 *
305 * @return Position storage.
306 */
307 Storage* Kinematics::
getPositionStorage()308 getPositionStorage()
309 {
310 return(_pStore);
311 }
312 //_____________________________________________________________________________
313 /**
314 * Set the model pointer for analyzing kinematics.
315 */
setModel(Model & aModel)316 void Kinematics::setModel(Model& aModel)
317 {
318 // BASE CLASS
319 Analysis::setModel(aModel);
320
321 // Allocate storages to contain the results of the analysis
322 allocateStorage();
323 }
324
325 //-----------------------------------------------------------------------------
326 // STORAGE CAPACITY
327 //-----------------------------------------------------------------------------
328 //_____________________________________________________________________________
329 /**
330 * Set the capacity increments of all storage instances.
331 *
332 * @param aIncrement Increment by which storage capacities will be increased
333 * when storage capacities run out.
334 */
335 void Kinematics::
setStorageCapacityIncrements(int aIncrement)336 setStorageCapacityIncrements(int aIncrement)
337 {
338 if (_aStore) _aStore->setCapacityIncrement(aIncrement);
339 _vStore->setCapacityIncrement(aIncrement);
340 _pStore->setCapacityIncrement(aIncrement);
341 }
342
343
344 //=============================================================================
345 // ANALYSIS
346 //=============================================================================
347 //_____________________________________________________________________________
348 /**
349 * Record the kinematics.
350 *
351 * @return 0 of success, -1 on error.
352 */
record(const SimTK::State & s)353 int Kinematics::record(const SimTK::State& s)
354 {
355 if(_recordAccelerations){
356 _model->getMultibodySystem().realize(s, SimTK::Stage::Acceleration);
357 }
358 else{
359 _model->getMultibodySystem().realize(s, SimTK::Stage::Velocity);
360 }
361 // RECORD RESULTS
362 const CoordinateSet& cs = _model->getCoordinateSet();
363 int nvalues = _coordinateIndices.getSize();
364 for(int i=0;i<nvalues;i++){
365 _values[i] = cs[_coordinateIndices[i]].getValue(s);
366 if(getInDegrees() && (cs[_coordinateIndices[i]].getMotionType() == Coordinate::Rotational))
367 _values[i] *= SimTK_RADIAN_TO_DEGREE;
368 }
369 _pStore->append(s.getTime(),nvalues,&_values[0]);
370
371 for(int i=0;i<nvalues;i++){
372 _values[i] = cs[_coordinateIndices[i]].getSpeedValue(s);
373 if(getInDegrees() && (cs[_coordinateIndices[i]].getMotionType() == Coordinate::Rotational))
374 _values[i] *= SimTK_RADIAN_TO_DEGREE;
375 }
376
377 _vStore->append(s.getTime(),nvalues,&_values[0]);
378
379 if(_recordAccelerations) {
380 for(int i=0;i<nvalues;i++){
381 _values[i] = cs[_coordinateIndices[i]].getAccelerationValue(s);
382 if(getInDegrees() && (cs[_coordinateIndices[i]].getMotionType() == Coordinate::Rotational))
383 _values[i] *= SimTK_RADIAN_TO_DEGREE;
384 }
385 _aStore->append(s.getTime(),nvalues,&_values[0]);
386 }
387
388
389 return(0);
390 }
391 //_____________________________________________________________________________
392 /**
393 * This method is called at the beginning of an analysis so that any
394 * necessary initializations may be performed.
395 *
396 * This method is meant to be called at the beginning of an integration
397 *
398 * @param s current state of System
399 *
400 * @return -1 on error, 0 otherwise.
401 */
402 int Kinematics::
begin(const SimTK::State & s)403 begin( const SimTK::State& s )
404 {
405 if(!proceed()) return(0);
406
407 double time = s.getTime();
408
409 // UPDATE LABELS
410 updateCoordinatesToRecord();
411 constructColumnLabels();
412
413 // RESET STORAGE
414 _pStore->reset(time);
415 _vStore->reset(time);
416 if (_aStore) _aStore->reset(time);
417
418 // RECORD
419 int status = 0;
420 if(_pStore->getSize()<=0) {
421 if(_recordAccelerations && s.getSystemStage() < SimTK::Stage::Acceleration )
422 _model->getMultibodySystem().realize(s, SimTK::Stage::Acceleration);
423 else
424 _model->getMultibodySystem().realize(s, SimTK::Stage::Velocity);
425
426 status = record(s);
427 }
428
429 return(status);
430 }
431 //_____________________________________________________________________________
432 /**
433 * This method is called to perform the analysis. It can be called during
434 * the execution of a forward integrations or after the integration by
435 * feeding it the necessary data.
436 *
437 * When called during an integration, this method is meant to be called
438 *
439 * This method should be overridden in derived classes. It is
440 * included here so that the derived class will not have to implement it if
441 * it is not necessary.
442 *
443 * @param s current state of system
444 *
445 * @return -1 on error, 0 otherwise.
446 */
447 int Kinematics::
step(const SimTK::State & s,int stepNumber)448 step(const SimTK::State& s, int stepNumber )
449 {
450 if(proceed(stepNumber) && getOn()) record(s);
451
452 return(0);
453 }
454 //_____________________________________________________________________________
455 /**
456 * This method is called at the end of an analysis so that any
457 * necessary finalizations may be performed.
458 *
459 * This method is meant to be called at the end of an integration
460 *
461 * This method should be overridden in the child class. It is
462 * included here so that the child class will not have to implement it if it
463 * is not necessary.
464 *
465 * @param s current state of System
466 *
467 * @return -1 on error, 0 otherwise.
468 */
469 int Kinematics::
end(const SimTK::State & s)470 end( const SimTK::State& s )
471 {
472 if (!proceed()) return 0;
473
474 record(s);
475
476 return(0);
477 }
478
479
480
481
482 //=============================================================================
483 // IO
484 //=============================================================================
485 //_____________________________________________________________________________
486 /**
487 * Print results.
488 *
489 * The file names are constructed as
490 * aDir + "/" + aBaseName + "_" + ComponentName + aExtension
491 *
492 * @param aDir Directory in which the results reside.
493 * @param aBaseName Base file name.
494 * @param aDT Desired time interval between adjacent storage vectors. Linear
495 * interpolation is used to print the data out at the desired interval.
496 * @param aExtension File extension.
497 *
498 * @return 0 on success, -1 on error.
499 */
500 int Kinematics::
printResults(const string & aBaseName,const string & aDir,double aDT,const string & aExtension)501 printResults(const string &aBaseName,const string &aDir,double aDT,
502 const string &aExtension)
503 {
504 if(!getOn()) {
505 printf("Kinematics.printResults: Off- not printing.\n");
506 return(0);
507 }
508
509 // ACCELERATIONS
510 if(_recordAccelerations) {
511 Storage::printResult(_aStore,aBaseName+"_"+getName()+"_dudt",aDir,aDT,aExtension);
512 }
513
514 // VELOCITIES
515 Storage::printResult(_vStore,aBaseName+"_"+getName()+"_u",aDir,aDT,aExtension);
516
517 // POSITIONS
518 Storage::printResult(_pStore,aBaseName+"_"+getName()+"_q",aDir,aDT,aExtension);
519
520 /*
521 // POSITIONS (.mot file)
522 // For now (until we resolve the .sto vs .mot issue) also write
523 // an .mot file so the motion can be viewed in SIMM -- Eran.
524 bool writeSIMMHeader=_pStore->getWriteSIMMHeader();
525 _pStore->setWriteSIMMHeader(true);
526 Storage::printResult(_pStore,aBaseName,aDir,aDT,".mot");
527 _pStore->setWriteSIMMHeader(writeSIMMHeader);
528 */
529 return(0);
530 }
531