1 /* -------------------------------------------------------------------------- *
2 * OpenSim: SimbodyEngine.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, Ajay Seth *
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 // INCLUDES
26 //=============================================================================
27 #include <OpenSim/Common/Exception.h>
28 #include <OpenSim/Common/GCVSplineSet.h>
29 #include <OpenSim/Common/Storage.h>
30 #include <OpenSim/Common/LoadOpenSimLibrary.h>
31 #include <OpenSim/Simulation/Model/Model.h>
32 #include <OpenSim/Simulation/Model/MarkerSet.h>
33 #include <OpenSim/Simulation/Model/BodySet.h>
34 #include <OpenSim/Simulation/Model/PhysicalOffsetFrame.h>
35
36 #include "SimbodyEngine.h"
37 #include "Coordinate.h"
38
39 //=============================================================================
40 // STATICS
41 //=============================================================================
42 using namespace std;
43 using namespace OpenSim;
44 using namespace SimTK;
45
46 //=============================================================================
47 // EXCEPTIONS
48 //=============================================================================
49 class PhysicalOffsetFrameIsInvalidArgument : public OpenSim::Exception {
50 public:
PhysicalOffsetFrameIsInvalidArgument(const std::string & file,size_t line,const std::string & func,const Object & obj)51 PhysicalOffsetFrameIsInvalidArgument(const std::string& file,
52 size_t line,
53 const std::string& func,
54 const Object& obj) :
55 Exception(file, line, func, obj) {
56 std::string msg = "Cannot use PhysicalOffsetFrame with ";
57 msg += "SimbodyEngine. Use methods from the Frame class instead.";
58 addMessage(msg);
59 }
60 };
61
62 //=============================================================================
63 // CONSTRUCTOR(S) AND DESTRUCTOR
64 //=============================================================================
65 //_____________________________________________________________________________
66 /**
67 * Destructor.
68 */
~SimbodyEngine()69 SimbodyEngine::~SimbodyEngine()
70 {
71 }
72 //_____________________________________________________________________________
73 /**
74 * Default constructor.
75 */
SimbodyEngine()76 SimbodyEngine::SimbodyEngine() :
77 Object()
78 {
79 setNull();
80 }
81
82 //_____________________________________________________________________________
83 /**
84 * Constructor from an XML Document
85 */
SimbodyEngine(const string & aFileName)86 SimbodyEngine::SimbodyEngine(const string &aFileName) :
87 Object(aFileName, false)
88 {
89 setNull();
90 connectSimbodyEngineToModel(*_model);
91 }
92
93
94 //_____________________________________________________________________________
95 /**
96 * Copy constructor.
97 */
SimbodyEngine(const SimbodyEngine & aEngine)98 SimbodyEngine::SimbodyEngine(const SimbodyEngine& aEngine) :
99 Object(aEngine)
100 {
101 setNull();
102 copyData(aEngine);
103 connectSimbodyEngineToModel(*_model);
104 }
105
106
107 //=============================================================================
108 // CONSTRUCTION METHODS
109 //=============================================================================
110
111
112 //_____________________________________________________________________________
113 /**
114 * Copy data members from one SimbodyEngine to another.
115 *
116 * @param aEngine SimbodyEngine to be copied.
117 */
copyData(const SimbodyEngine & aEngine)118 void SimbodyEngine::copyData(const SimbodyEngine &aEngine)
119 {
120 _model = aEngine._model;
121 }
122
123 //_____________________________________________________________________________
124 /**
125 * Set NULL values for all the variable members of this class.
126 */
setNull()127 void SimbodyEngine::setNull()
128 {
129 setAuthors("Frank C. Anderson, Ajay Seth");
130 _model = NULL;
131 }
132
133
134 //_____________________________________________________________________________
135 /**
136 * Perform some set up functions that happen after the
137 * object has been deserialized or copied.
138 *
139 * @param aModel model containing this SimbodyEngine.
140 */
connectSimbodyEngineToModel(Model & aModel)141 void SimbodyEngine::connectSimbodyEngineToModel(Model& aModel)
142 {
143 _model = &aModel;
144 }
145
146
147 //=============================================================================
148 // OPERATORS
149 //=============================================================================
150 //_____________________________________________________________________________
151 /**
152 * Assignment operator.
153 *
154 * @return Reference to this object.
155 */
operator =(const SimbodyEngine & aEngine)156 SimbodyEngine& SimbodyEngine::operator=(const SimbodyEngine &aEngine)
157 {
158 // Base class
159 Object::operator=(aEngine);
160 copyData(aEngine);
161 connectSimbodyEngineToModel(*aEngine._model);
162 return(*this);
163 }
164
165
166
167 //--------------------------------------------------------------------------
168 // COORDINATES
169 //--------------------------------------------------------------------------
170
171 //_____________________________________________________________________________
172 /**
173 * Get the set of coordinates that are not locked
174 *
175 * @param rUnlockedCoordinates set of unlocked coordinates is returned here
176 */
getUnlockedCoordinates(const SimTK::State & s,CoordinateSet & rUnlockedCoordinates) const177 void SimbodyEngine::getUnlockedCoordinates(const SimTK::State &s, CoordinateSet& rUnlockedCoordinates) const
178 {
179 rUnlockedCoordinates.setSize(0);
180 rUnlockedCoordinates.setMemoryOwner(false);
181
182 for (int i = 0; i < _model->getCoordinateSet().getSize(); i++)
183 if (!_model->getCoordinateSet().get(i).getLocked(s))
184 rUnlockedCoordinates.adoptAndAppend(&_model->getCoordinateSet().get(i));
185 }
186
187
188
189 //--------------------------------------------------------------------------
190 // KINEMATICS
191 //--------------------------------------------------------------------------
192 //_____________________________________________________________________________
193 /**
194 * Get the inertial position of a point on a body.
195 *
196 * Note that the configuration of the model must be set before calling this
197 * method.
198 *
199 * @param aBody Pointer to body.
200 * @param aPoint Point on the body expressed in the body-local frame.
201 * @param rPos Position of the point in the inertial frame.
202 */
getPosition(const SimTK::State & s,const PhysicalFrame & aBody,const Vec3 & aPoint,Vec3 & rPos) const203 void SimbodyEngine::getPosition(const SimTK::State& s,
204 const PhysicalFrame& aBody, const Vec3& aPoint, Vec3& rPos) const
205 {
206 OPENSIM_THROW_IF_FRMOBJ(
207 dynamic_cast<const PhysicalOffsetFrame*>(&aBody),
208 PhysicalOffsetFrameIsInvalidArgument);
209
210 rPos = aBody.getMobilizedBody().findStationLocationInGround(s, aPoint);
211 }
212
213 //_____________________________________________________________________________
214 /**
215 * Get the inertial velocity of a point on a body.
216 *
217 * Note that the configuration of the model must be set before calling this
218 * method.
219 *
220 * @param aBody Pointer to body.
221 * @param aPoint Point on the body expressed in the body-local frame.
222 * @param rVel Velocity of the point in the inertial frame.
223 */
getVelocity(const SimTK::State & s,const PhysicalFrame & aBody,const Vec3 & aPoint,Vec3 & rVel) const224 void SimbodyEngine::getVelocity(const SimTK::State& s,
225 const PhysicalFrame& aBody, const Vec3& aPoint, Vec3& rVel) const
226 {
227 OPENSIM_THROW_IF_FRMOBJ(
228 dynamic_cast<const PhysicalOffsetFrame*>(&aBody),
229 PhysicalOffsetFrameIsInvalidArgument);
230
231 rVel = aBody.getMobilizedBody().findStationVelocityInGround(s, aPoint);
232 }
233
234 //_____________________________________________________________________________
235 /**
236 * Get the inertial acceleration of a point on a body.
237 *
238 * Note that the configuration of the model must be set and accelerations of
239 * the generalized coordinates must be computed before calling this method.
240 *
241 * @param aBody Pointer to body.
242 * @param aPoint Point on the body expressed in the body-local frame.
243 * @param rAcc Acceleration of the point in the inertial frame.
244 *
245 * @see set()
246 * @see computeAccelerations()
247 */
getAcceleration(const SimTK::State & s,const PhysicalFrame & aBody,const Vec3 & aPoint,Vec3 & rAcc) const248 void SimbodyEngine::getAcceleration(const SimTK::State& s,
249 const PhysicalFrame& aBody, const Vec3& aPoint, Vec3& rAcc) const
250 {
251 OPENSIM_THROW_IF_FRMOBJ(
252 dynamic_cast<const PhysicalOffsetFrame*>(&aBody),
253 PhysicalOffsetFrameIsInvalidArgument);
254
255 rAcc = aBody.getMobilizedBody().findStationAccelerationInGround(s, aPoint);
256 }
257
258 //_____________________________________________________________________________
259 /**
260 * Get the body orientation with respect to the ground.
261 *
262 * @param aBody Pointer to body.
263 * @param rDirCos Orientation of the body with respect to the ground frame.
264 */
getDirectionCosines(const SimTK::State & s,const PhysicalFrame & aBody,double rDirCos[3][3]) const265 void SimbodyEngine::getDirectionCosines(const SimTK::State& s,
266 const PhysicalFrame& aBody, double rDirCos[3][3]) const
267 {
268 OPENSIM_THROW_IF_FRMOBJ(
269 dynamic_cast<const PhysicalOffsetFrame*>(&aBody),
270 PhysicalOffsetFrameIsInvalidArgument);
271
272 Mat33::updAs(&rDirCos[0][0]) =
273 aBody.getMobilizedBody().getBodyRotation(s).asMat33();
274 }
275
276 //_____________________________________________________________________________
277 /**
278 * Get the body orientation with respect to the ground.
279 *
280 * @param aBody Pointer to body.
281 * @param rDirCos Orientation of the body with respect to the ground frame.
282 */
getDirectionCosines(const SimTK::State & s,const PhysicalFrame & aBody,double * rDirCos) const283 void SimbodyEngine::getDirectionCosines(const SimTK::State& s,
284 const PhysicalFrame& aBody, double *rDirCos) const
285 {
286 OPENSIM_THROW_IF_FRMOBJ(
287 dynamic_cast<const PhysicalOffsetFrame*>(&aBody),
288 PhysicalOffsetFrameIsInvalidArgument);
289
290 Mat33::updAs(rDirCos) =
291 aBody.getMobilizedBody().getBodyRotation(s).asMat33();
292 }
293
294 //_____________________________________________________________________________
295 /**
296 * Get the inertial angular velocity of a body in the ground reference frame.
297 *
298 * @param aBody Pointer to body.
299 * @param rAngVel Angular velocity of the body.
300 */
getAngularVelocity(const SimTK::State & s,const PhysicalFrame & aBody,Vec3 & rAngVel) const301 void SimbodyEngine::getAngularVelocity(const SimTK::State& s,
302 const PhysicalFrame& aBody, Vec3& rAngVel) const
303 {
304 OPENSIM_THROW_IF_FRMOBJ(
305 dynamic_cast<const PhysicalOffsetFrame*>(&aBody),
306 PhysicalOffsetFrameIsInvalidArgument);
307
308 rAngVel = aBody.getMobilizedBody().getBodyAngularVelocity(s);
309 }
310
311 //_____________________________________________________________________________
312 /**
313 * Get the inertial angular velocity in the local body reference frame.
314 *
315 * @param aBody Pointer to body.
316 * @param rAngVel Angular velocity of the body.
317 */
getAngularVelocityBodyLocal(const SimTK::State & s,const PhysicalFrame & aBody,Vec3 & rAngVel) const318 void SimbodyEngine::getAngularVelocityBodyLocal(const SimTK::State& s,
319 const PhysicalFrame& aBody, Vec3& rAngVel) const
320 {
321 OPENSIM_THROW_IF_FRMOBJ(
322 dynamic_cast<const PhysicalOffsetFrame*>(&aBody),
323 PhysicalOffsetFrameIsInvalidArgument);
324
325 rAngVel = aBody.getMobilizedBody().getBodyAngularVelocity(s);
326 }
327
328 //_____________________________________________________________________________
329 /**
330 * Get the inertial angular acceleration of a body in the ground reference
331 * frame.
332 *
333 * @param aBody Pointer to body.
334 * @param rAngAcc Angular acceleration of the body.
335 */
getAngularAcceleration(const SimTK::State & s,const PhysicalFrame & aBody,Vec3 & rAngAcc) const336 void SimbodyEngine::getAngularAcceleration(const SimTK::State& s,
337 const PhysicalFrame& aBody, Vec3& rAngAcc) const
338 {
339 OPENSIM_THROW_IF_FRMOBJ(
340 dynamic_cast<const PhysicalOffsetFrame*>(&aBody),
341 PhysicalOffsetFrameIsInvalidArgument);
342
343 rAngAcc = aBody.getMobilizedBody().getBodyAngularAcceleration(s);
344 }
345
346 //_____________________________________________________________________________
347 /**
348 * Get the inertial angular acceleration in the local body reference frame.
349 *
350 * @param aBody Pointer to body.
351 * @param rAngAcc Angular acceleration of the body.
352 */
getAngularAccelerationBodyLocal(const SimTK::State & s,const PhysicalFrame & aBody,Vec3 & rAngAcc) const353 void SimbodyEngine::getAngularAccelerationBodyLocal(const SimTK::State& s,
354 const PhysicalFrame &aBody, Vec3& rAngAcc) const
355 {
356 OPENSIM_THROW_IF_FRMOBJ(
357 dynamic_cast<const PhysicalOffsetFrame*>(&aBody),
358 PhysicalOffsetFrameIsInvalidArgument);
359
360 rAngAcc = aBody.getMobilizedBody().getBodyAngularAcceleration(s);
361 }
362
363 //_____________________________________________________________________________
364 /**
365 * get a copy of the transform from the inertial frame to a body
366 *
367 * @param aBody
368 * @return Transform from inertial frame to body
369 */
getTransform(const SimTK::State & s,const PhysicalFrame & aBody) const370 SimTK::Transform SimbodyEngine::getTransform(const SimTK::State& s,
371 const PhysicalFrame& aBody) const
372 {
373 OPENSIM_THROW_IF_FRMOBJ(
374 dynamic_cast<const PhysicalOffsetFrame*>(&aBody),
375 PhysicalOffsetFrameIsInvalidArgument);
376
377 return aBody.getMobilizedBody().getBodyTransform(s);
378 }
379
380 //--------------------------------------------------------------------------
381 // LOAD ACCESS AND COMPUTATION
382 //--------------------------------------------------------------------------
383 //_____________________________________________________________________________
384 /**
385 * Compute the reaction forces and torques at all the joints in the model.
386 * For each joint, what's reported is the force and torque the joint structure
387 * applies on the child body at the joint frame attached to the child body.
388 * Both vectors are expressed in the ground reference frame.
389 *
390 * It is necessary to call computeAccelerations() before this method
391 * to get valid results. The cost to calculate joint forces and moments is 114
392 * flops/body.
393 *
394 * @param rForces Matrix of reaction forces. The size should be
395 * at least NumberOfJoints x 3.
396 * @param rTorques Matrix of reaction torques. The size should be
397 * at least NumberOfJoints x 3.
398 */
computeReactions(const SimTK::State & s,Vector_<Vec3> & rForces,Vector_<Vec3> & rTorques) const399 void SimbodyEngine::computeReactions(const SimTK::State& s, Vector_<Vec3>& rForces, Vector_<Vec3>& rTorques) const
400 {
401 // get the number of mobilized bodies in the underlying SimbodyMatterSubsystem
402 //int nmb = _model->getMatterSubsystem().getNumBodies();
403
404 // get the number of bodies in the OpenSim model
405 int nj = _model->getNumJoints();
406
407 //int nf = rForces.size();
408 //int ntorq = rTorques.size();
409
410 // there may be more mobilized bodies than joint exposed in the OpenSim model
411 // since joints and other components may use (massless) bodies internally
412 assert(_model->getMatterSubsystem().getNumBodies() >= nj);
413 assert(nj == rForces.size());
414 assert(rForces.size() == rTorques.size());
415
416 SimTK::Vector_<SpatialVec> reactionForces(nj);
417
418 // Systems must be realized to acceleration stage
419 _model->getMultibodySystem().realize(s, Stage::Acceleration);
420 _model->getMatterSubsystem().calcMobilizerReactionForces(s, reactionForces);
421
422
423 const JointSet &joints = _model->getJointSet();
424
425 //Separate SimTK SpatialVecs to Forces and Torques
426 // SpatialVec = Vec2<Vec3 torque, Vec3 force>
427 for(int i=0; i<nj; i++){
428 const SimTK::MobilizedBodyIndex& ix =
429 joints[i].getChildFrame().getMobilizedBodyIndex();
430
431 rTorques[i] = reactionForces[ix](0);
432 rForces[i] = reactionForces[ix](1);
433 }
434 }
435
436 //--------------------------------------------------------------------------
437 // UTILITY
438 //--------------------------------------------------------------------------
439 //_____________________________________________________________________________
440 /**
441 * Transform a vector from one body to another
442 *
443 * @param aBodyFrom the body in which the vector is currently expressed
444 * @param aPos the vector to be transformed
445 * @param aBodyTo the body the vector will be transformed into
446 * @param rPos the vector in the aBodyTo frame is returned here
447 */
transform(const SimTK::State & s,const PhysicalFrame & aBodyFrom,const double aVec[3],const PhysicalFrame & aBodyTo,double rVec[3]) const448 void SimbodyEngine::transform(const SimTK::State& s, const PhysicalFrame &aBodyFrom, const double aVec[3], const PhysicalFrame &aBodyTo, double rVec[3]) const
449 {
450 OPENSIM_THROW_IF_FRMOBJ(
451 dynamic_cast<const PhysicalOffsetFrame*>(&aBodyFrom),
452 PhysicalOffsetFrameIsInvalidArgument);
453
454 OPENSIM_THROW_IF_FRMOBJ(
455 dynamic_cast<const PhysicalOffsetFrame*>(&aBodyTo),
456 PhysicalOffsetFrameIsInvalidArgument);
457
458 if(&aBodyFrom == &aBodyTo) { for(int i=0; i<3; i++) { rVec[i]=aVec[i]; } return; }
459 const Body* bFrom = (const Body*)&aBodyFrom;
460 const Body* bTo = (const Body*)&aBodyTo;
461
462 //Get input vector as a Vec3 to make the call down to Simbody and update the output vector
463 Vec3::updAs(rVec) = _model->getMatterSubsystem().getMobilizedBody(bFrom->getMobilizedBodyIndex()).expressVectorInAnotherBodyFrame(s, Vec3::getAs(aVec), _model->getMatterSubsystem().getMobilizedBody(bTo->getMobilizedBodyIndex()));
464 }
465
466 //_____________________________________________________________________________
467 /**
468 * Transform a vector from one body to another
469 *
470 * @param aBodyFrom the body in which the vector is currently expressed
471 * @param aPos the vector to be transformed
472 * @param aBodyTo the body the vector will be transformed into
473 * @param rPos the vector in the aBodyTo frame is returned here
474 */
transform(const SimTK::State & s,const PhysicalFrame & aBodyFrom,const Vec3 & aVec,const PhysicalFrame & aBodyTo,Vec3 & rVec) const475 void SimbodyEngine::transform(const SimTK::State& s, const PhysicalFrame &aBodyFrom, const Vec3& aVec, const PhysicalFrame &aBodyTo, Vec3& rVec) const
476 {
477 OPENSIM_THROW_IF_FRMOBJ(
478 dynamic_cast<const PhysicalOffsetFrame*>(&aBodyFrom),
479 PhysicalOffsetFrameIsInvalidArgument);
480
481 OPENSIM_THROW_IF_FRMOBJ(
482 dynamic_cast<const PhysicalOffsetFrame*>(&aBodyTo),
483 PhysicalOffsetFrameIsInvalidArgument);
484
485 if(&aBodyFrom == &aBodyTo) { rVec=aVec; return; }
486
487 // Get input vector as a Vec3 to make the call down to Simbody and update
488 // the output vector
489 rVec = aBodyFrom.getMobilizedBody().expressVectorInAnotherBodyFrame(s, aVec,
490 aBodyTo.getMobilizedBody());
491 }
492
493 //_____________________________________________________________________________
494 /**
495 * Transform a point from one body to another
496 *
497 * @param aBodyFrom the body in which the point is currently expressed
498 * @param aPos the XYZ coordinates of the point
499 * @param aBodyTo the body the point will be transformed into
500 * @param rPos the XYZ coordinates of the point in the aBodyTo frame are returned here
501 */
502 void SimbodyEngine::
transformPosition(const SimTK::State & s,const PhysicalFrame & aBodyFrom,const double aPos[3],const PhysicalFrame & aBodyTo,double rPos[3]) const503 transformPosition(const SimTK::State& s, const PhysicalFrame &aBodyFrom, const
504 double aPos[3], const PhysicalFrame &aBodyTo, double rPos[3]) const
505 {
506 OPENSIM_THROW_IF_FRMOBJ(
507 dynamic_cast<const PhysicalOffsetFrame*>(&aBodyFrom),
508 PhysicalOffsetFrameIsInvalidArgument);
509
510 OPENSIM_THROW_IF_FRMOBJ(
511 dynamic_cast<const PhysicalOffsetFrame*>(&aBodyTo),
512 PhysicalOffsetFrameIsInvalidArgument);
513
514 if(&aBodyFrom == &aBodyTo) {
515 for (int i=0; i<3; i++) rPos[i] = aPos[i];
516 return;
517 }
518
519 // Get input vector as a Vec3 to make the call down to Simbody and update
520 // the output vector.
521 Vec3::updAs(rPos) =
522 aBodyFrom.getMobilizedBody().findStationLocationInAnotherBody(s,
523 Vec3::getAs(aPos), aBodyTo.getMobilizedBody());
524 }
525
526 //_____________________________________________________________________________
527 /**
528 * Transform a point from one body to another
529 *
530 * @param aBodyFrom the body in which the point is currently expressed
531 * @param aPos the XYZ coordinates of the point
532 * @param aBodyTo the body the point will be transformed into
533 * @param rPos the XYZ coordinates of the point in the aBodyTo frame are returned here
534 */
535 void SimbodyEngine::
transformPosition(const SimTK::State & s,const PhysicalFrame & aBodyFrom,const Vec3 & aPos,const PhysicalFrame & aBodyTo,Vec3 & rPos) const536 transformPosition(const SimTK::State& s, const PhysicalFrame &aBodyFrom, const
537 Vec3& aPos, const PhysicalFrame &aBodyTo, Vec3& rPos) const
538 {
539 OPENSIM_THROW_IF_FRMOBJ(
540 dynamic_cast<const PhysicalOffsetFrame*>(&aBodyFrom),
541 PhysicalOffsetFrameIsInvalidArgument);
542
543 OPENSIM_THROW_IF_FRMOBJ(
544 dynamic_cast<const PhysicalOffsetFrame*>(&aBodyTo),
545 PhysicalOffsetFrameIsInvalidArgument);
546
547 if(&aBodyFrom == &aBodyTo) {
548 for (int i=0; i<3; i++) rPos[i] = aPos[i];
549 return;
550 }
551
552 // Get input vector as a Vec3 to make the call down to Simbody and update
553 // the output vector.
554 rPos = aBodyFrom.getMobilizedBody().findStationLocationInAnotherBody(s,
555 aPos, aBodyTo.getMobilizedBody());
556 }
557
558 //_____________________________________________________________________________
559 /**
560 * Transform a point from one body to the ground body
561 *
562 * @param aBodyFrom the body in which the point is currently expressed
563 * @param aPos the XYZ coordinates of the point
564 * @param rPos the XYZ coordinates of the point in the ground frame are returned here
565 */
transformPosition(const SimTK::State & s,const PhysicalFrame & aBodyFrom,const double aPos[3],double rPos[3]) const566 void SimbodyEngine::transformPosition(const SimTK::State& s, const PhysicalFrame &aBodyFrom, const double aPos[3], double rPos[3]) const
567 {
568 OPENSIM_THROW_IF_FRMOBJ(
569 dynamic_cast<const PhysicalOffsetFrame*>(&aBodyFrom),
570 PhysicalOffsetFrameIsInvalidArgument);
571
572 //const Body* bFrom = (const Body*)&aBodyFrom;
573
574 // Get input vector as a Vec3 to make the call down to Simbody and update
575 // the output vector.
576 Vec3::updAs(rPos) =
577 aBodyFrom.getMobilizedBody().findStationLocationInGround(s,
578 Vec3::getAs(aPos));
579 }
580
581 //_____________________________________________________________________________
582 /**
583 * Transform a point from one body to the ground body
584 *
585 * @param aBodyFrom the body in which the point is currently expressed
586 * @param aPos the XYZ coordinates of the point
587 * @param rPos the XYZ coordinates of the point in the ground frame are returned here
588 */
589 void SimbodyEngine::
transformPosition(const SimTK::State & s,const PhysicalFrame & aBodyFrom,const Vec3 & aPos,Vec3 & rPos) const590 transformPosition(const SimTK::State& s, const PhysicalFrame& aBodyFrom,
591 const Vec3& aPos, Vec3& rPos) const
592 {
593 OPENSIM_THROW_IF_FRMOBJ(
594 dynamic_cast<const PhysicalOffsetFrame*>(&aBodyFrom),
595 PhysicalOffsetFrameIsInvalidArgument);
596
597 _model->getMultibodySystem().realize(s, SimTK::Stage::Position);
598 rPos = aBodyFrom.getMobilizedBody().findStationLocationInGround(s, aPos);
599 }
600
601 //_____________________________________________________________________________
602 /**
603 * Calculate the distance between a point on one body and a point on another body
604 *
605 * @param aBody1 the body that the first point is expressed in
606 * @param aPoint1 the XYZ coordinates of the first point
607 * @param aBody2 the body that the second point is expressed in
608 * @param aPoint2 the XYZ coordinates of the second point
609 * @return the distance between aPoint1 and aPoint2
610 */
611 double SimbodyEngine::
calcDistance(const SimTK::State & s,const PhysicalFrame & aBody1,const Vec3 & aPoint1,const PhysicalFrame & aBody2,const Vec3 & aPoint2) const612 calcDistance(const SimTK::State& s, const PhysicalFrame& aBody1,
613 const Vec3& aPoint1, const PhysicalFrame& aBody2, const Vec3& aPoint2)
614 const
615 {
616 OPENSIM_THROW_IF_FRMOBJ(
617 dynamic_cast<const PhysicalOffsetFrame*>(&aBody1),
618 PhysicalOffsetFrameIsInvalidArgument);
619
620 OPENSIM_THROW_IF_FRMOBJ(
621 dynamic_cast<const PhysicalOffsetFrame*>(&aBody2),
622 PhysicalOffsetFrameIsInvalidArgument);
623
624 return aBody1.getMobilizedBody().calcStationToStationDistance(s, aPoint1,
625 aBody2.getMobilizedBody(), aPoint2);
626 }
627
628 //_____________________________________________________________________________
629 /**
630 * Calculate the distance between a point on one body and a point on another body
631 *
632 * @param aBody1 the body that the first point is expressed in
633 * @param aPoint1 the XYZ coordinates of the first point
634 * @param aBody2 the body that the second point is expressed in
635 * @param aPoint2 the XYZ coordinates of the second point
636 * @return the distance between aPoint1 and aPoint2
637 */
calcDistance(const SimTK::State & s,const PhysicalFrame & aBody1,const double aPoint1[3],const PhysicalFrame & aBody2,const double aPoint2[3]) const638 double SimbodyEngine::calcDistance(const SimTK::State& s, const PhysicalFrame&
639 aBody1, const double aPoint1[3], const PhysicalFrame& aBody2, const
640 double aPoint2[3]) const
641 {
642 OPENSIM_THROW_IF_FRMOBJ(
643 dynamic_cast<const PhysicalOffsetFrame*>(&aBody1),
644 PhysicalOffsetFrameIsInvalidArgument);
645
646 OPENSIM_THROW_IF_FRMOBJ(
647 dynamic_cast<const PhysicalOffsetFrame*>(&aBody2),
648 PhysicalOffsetFrameIsInvalidArgument);
649
650 return aBody1.getMobilizedBody().calcStationToStationDistance(s,
651 Vec3::getAs(aPoint1), aBody2.getMobilizedBody(),
652 Vec3::getAs(aPoint2));
653 }
654
655 //_____________________________________________________________________________
656 /**
657 * Convert angles to direction cosines.
658 * @param aE1 1st Euler angle.
659 * @param aE2 2nd Euler angle.
660 * @param aE3 3rd Euler angle.
661 * @param rDirCos Vector of direction cosines.
662 */
convertAnglesToDirectionCosines(double aE1,double aE2,double aE3,double rDirCos[3][3]) const663 void SimbodyEngine::convertAnglesToDirectionCosines(double aE1, double aE2, double aE3, double rDirCos[3][3]) const
664 {
665 Vec3 angs(aE1, aE2, aE3);
666 Rotation aRot;
667 aRot.setRotationToBodyFixedXYZ(angs);
668 Mat33::updAs(&rDirCos[0][0]) = aRot.asMat33();
669 }
670
671 //_____________________________________________________________________________
672 /**
673 * Convert angles to direction cosines.
674 * @param aE1 1st Euler angle.
675 * @param aE2 2nd Euler angle.
676 * @param aE3 3rd Euler angle.
677 * @param rDirCos Vector of direction cosines.
678 */
convertAnglesToDirectionCosines(double aE1,double aE2,double aE3,double * rDirCos) const679 void SimbodyEngine::convertAnglesToDirectionCosines(double aE1, double aE2, double aE3, double *rDirCos) const
680 {
681 if(rDirCos==NULL) return;
682
683 Vec3 angs(aE1, aE2, aE3);
684 Rotation aRot;
685 aRot.setRotationToBodyFixedXYZ(angs);
686 Mat33::updAs(&rDirCos[0]) = aRot.asMat33();
687 }
688
689 //_____________________________________________________________________________
690 /**
691 * Convert direction cosines to angles.
692 * @param aDirCos Vector of direction cosines.
693 * @param rE1 1st Euler angle.
694 * @param rE2 2nd Euler angle.
695 * @param rE3 3rd Euler angle.
696 */
convertDirectionCosinesToAngles(double aDirCos[3][3],double * rE1,double * rE2,double * rE3) const697 void SimbodyEngine::convertDirectionCosinesToAngles(double aDirCos[3][3], double *rE1, double *rE2, double *rE3) const
698 {
699 Vec3 ang = Rotation(Rotation::getAs(&aDirCos[0][0])).convertRotationToBodyFixedXYZ();
700 *rE1 = ang[0];
701 *rE2 = ang[1];
702 *rE3 = ang[2];
703 }
704
705 //_____________________________________________________________________________
706 /**
707 * Convert direction cosines to angles.
708 * @param aDirCos Vector of direction cosines.
709 * @param rE1 1st Euler angle.
710 * @param rE2 2nd Euler angle.
711 * @param rE3 3rd Euler angle.
712 */
convertDirectionCosinesToAngles(double * aDirCos,double * rE1,double * rE2,double * rE3) const713 void SimbodyEngine::convertDirectionCosinesToAngles(double *aDirCos, double *rE1, double *rE2, double *rE3) const
714 {
715 if(!aDirCos) return;
716 Vec3 ang = Rotation(Rotation::getAs(aDirCos)).convertRotationToBodyFixedXYZ();
717 *rE1 = ang[0];
718 *rE2 = ang[1];
719 *rE3 = ang[2];
720 }
721
722 //_____________________________________________________________________________
723 /**
724 * Convert direction cosines to quaternions.
725 * @param aDirCos Vector of direction cosines.
726 * @param rQ1 1st Quaternion.
727 * @param rQ2 2nd Quaternion.
728 * @param rQ3 3rd Quaternion.
729 * @param rQ4 4th Quaternion.
730 */
convertDirectionCosinesToQuaternions(double aDirCos[3][3],double * rQ1,double * rQ2,double * rQ3,double * rQ4) const731 void SimbodyEngine::convertDirectionCosinesToQuaternions(double aDirCos[3][3], double *rQ1, double *rQ2, double *rQ3, double *rQ4) const
732 {
733 Quaternion quat = Rotation(Rotation::getAs(&aDirCos[0][0])).convertRotationToQuaternion();
734 *rQ1 = quat[0];
735 *rQ2 = quat[1];
736 *rQ3 = quat[2];
737 *rQ4 = quat[3];
738 }
739
740 //_____________________________________________________________________________
741 /**
742 * Convert direction cosines to quaternions.
743 * @param aDirCos Vector of direction cosines.
744 * @param rQ1 1st Quaternion.
745 * @param rQ2 2nd Quaternion.
746 * @param rQ3 3rd Quaternion.
747 * @param rQ4 4th Quaternion.
748 */
convertDirectionCosinesToQuaternions(double * aDirCos,double * rQ1,double * rQ2,double * rQ3,double * rQ4) const749 void SimbodyEngine::convertDirectionCosinesToQuaternions(double *aDirCos, double *rQ1, double *rQ2, double *rQ3, double *rQ4) const
750 {
751 if(aDirCos==NULL) return;
752 Quaternion quat = Rotation(Rotation::getAs(aDirCos)).convertRotationToQuaternion();
753 *rQ1 = quat[0];
754 *rQ2 = quat[1];
755 *rQ3 = quat[2];
756 *rQ4 = quat[3];
757 }
758
759 //_____________________________________________________________________________
760 /**
761 * Convert quaternions to direction cosines.
762 * @param aQ1 1st Quaternion.
763 * @param aQ2 2nd Quaternion.
764 * @param aQ3 3rd Quaternion.
765 * @param aQ4 4th Quaternion.
766 * @param rDirCos Vector of direction cosines.
767 */
convertQuaternionsToDirectionCosines(double aQ1,double aQ2,double aQ3,double aQ4,double rDirCos[3][3]) const768 void SimbodyEngine::convertQuaternionsToDirectionCosines(double aQ1, double aQ2, double aQ3, double aQ4, double rDirCos[3][3]) const
769 {
770 Rotation R;
771 R.setRotationFromQuaternion(Quaternion(Vec4(aQ1, aQ2, aQ3, aQ4)));
772
773 Mat33::updAs(&rDirCos[0][0]) = R.asMat33();
774 }
775
776 //_____________________________________________________________________________
777 /**
778 * Convert quaternions to direction cosines.
779 * @param aQ1 1st Quaternion.
780 * @param aQ2 2nd Quaternion.
781 * @param aQ3 3rd Quaternion.
782 * @param aQ4 4th Quaternion.
783 * @param rDirCos Vector of direction cosines.
784 */
convertQuaternionsToDirectionCosines(double aQ1,double aQ2,double aQ3,double aQ4,double * rDirCos) const785 void SimbodyEngine::convertQuaternionsToDirectionCosines(double aQ1, double aQ2, double aQ3, double aQ4, double *rDirCos) const
786 {
787 if(rDirCos==NULL) return;
788 Rotation R;
789 R.setRotationFromQuaternion(Quaternion(Vec4(aQ1, aQ2, aQ3, aQ4)));
790
791 Mat33::updAs(rDirCos) = R.asMat33();
792 }
793
794
795 //--- Private Utility Methods Below Here ---
796
797
798 //=============================================================================
799 // CONFIGURATION
800 //=============================================================================
801 void SimbodyEngine::
formCompleteStorages(const SimTK::State & s,const OpenSim::Storage & aQIn,OpenSim::Storage * & rQComplete,OpenSim::Storage * & rUComplete) const802 formCompleteStorages( const SimTK::State& s, const OpenSim::Storage &aQIn,
803 OpenSim::Storage *&rQComplete,OpenSim::Storage *&rUComplete) const
804 {
805 int i;
806 int nq = _model->getNumCoordinates();
807 int nu = _model->getNumSpeeds();
808
809 // Get coordinate file indices
810 Array<string> columnLabels, speedLabels, coordStateNames;
811 columnLabels.append("time");
812 speedLabels = columnLabels;
813
814 Array<int> index(-1,nq);
815 const CoordinateSet& coordinateSet = _model->getCoordinateSet();
816 int sizeCoordSet = coordinateSet.getSize();
817 for(i=0;i<sizeCoordSet;i++) {
818 Coordinate& coord = coordinateSet.get(i);
819 coordStateNames = coord.getStateVariableNames();
820 columnLabels.append(coordStateNames[0]);
821 speedLabels.append(coordStateNames[1]);
822 int fix = aQIn.getStateIndex(coord.getName());
823 if (fix < 0) {
824 fix = aQIn.getStateIndex(columnLabels[i+1]);
825 }
826
827 index[i] = fix;
828 if(index[i]<0) {
829 string msg = "Model::formCompleteStorages(): WARNING- Did not find column ";
830 msg += coordStateNames[0];
831 msg += " in storage object.\n";
832 cout << msg << endl;
833 }
834 }
835
836 // Extract Coordinates
837 double time;
838 Array<double> data(0.0);
839 Array<double> q(0.0,nq);
840 Storage *qStore = new Storage();
841 qStore->setInDegrees(aQIn.isInDegrees());
842 qStore->setName("GeneralizedCoordinates");
843 qStore->setColumnLabels(columnLabels);
844 int size = aQIn.getSize();
845 StateVector *vector;
846 int j;
847 for(i=0;i<size;i++) {
848 vector = aQIn.getStateVector(i);
849 data = vector->getData();
850 time = vector->getTime();
851
852 for(j=0;j<nq;j++) {
853 q[j] = 0.0;
854 if(index[j]<0) continue;
855 q[j] = data[index[j]];
856 }
857
858 qStore->append(time,nq,&q[0]);
859 }
860
861 // Convert to radians
862 if (aQIn.isInDegrees())
863 convertDegreesToRadians(*qStore);
864
865 // Compute generalized speeds
866 GCVSplineSet tempQset(5,qStore);
867 std::unique_ptr<Storage> uStore{tempQset.constructStorage(1)};
868
869 // Compute constraints
870 Array<double> qu(0.0,nq+nu);
871 rQComplete = new Storage();
872 rUComplete = new Storage();
873 State constrainedState = s;
874 _model->getMultibodySystem().realize(constrainedState, s.getSystemStage());
875 for(i=0;i<size;i++) {
876 qStore->getTime(i,time);
877 qStore->getData(i,nq,&qu[0]);
878 uStore->getData(i,nu,&qu[nq]);
879 for (int j = 0; j < nq; j++) {
880 Coordinate& coord = coordinateSet.get(j);
881 coord.setValue(constrainedState, qu[j], false);
882 coord.setSpeedValue(constrainedState, qu[nq+j]);
883 }
884 _model->assemble(constrainedState);
885 for (int j = 0; j < nq; j++) {
886 Coordinate& coord = coordinateSet.get(j);
887 qu[j] = coord.getValue(constrainedState);
888 qu[nq+j] = coord.getSpeedValue(constrainedState);
889 }
890 rQComplete->append(time,nq,&qu[0]);
891 rUComplete->append(time,nu,&qu[nq]);
892 }
893
894 delete qStore;
895
896 // Set column labels before returning
897 rQComplete->setColumnLabels(columnLabels);
898 rUComplete->setColumnLabels(speedLabels);
899 }
900
901 //=============================================================================
902 // UTILITY
903 //=============================================================================
904 //_____________________________________________________________________________
905 /**
906 */
scaleRotationalDofColumns(Storage & rStorage,double factor) const907 void SimbodyEngine::scaleRotationalDofColumns(Storage &rStorage, double factor) const
908 {
909 const Array<std::string>& columnLabels = rStorage.getColumnLabels();
910 int ncols = columnLabels.getSize();
911 if(ncols == 0)
912 throw Exception("SimbodyEngine.scaleRotationalDofColumns: ERROR- storage has no labels, can't determine coordinate types for deg<->rad conversion",
913 __FILE__,__LINE__);
914
915 // Loop through the coordinates in the model. For each one that is rotational,
916 // see if it has a corresponding column of data. If it does, multiply that
917 // column by the given scaling factor.
918 std::string shortName = "";
919 std::string prefix = "";
920 int index = -1;
921 const CoordinateSet& coordinateSet = _model->getCoordinateSet();
922
923 // first column is time, so skip
924 for (int i = 1; i < ncols; i++) {
925 const std::string& name = columnLabels[i];
926 index = coordinateSet.getIndex(name);
927 if (index < 0){
928 std::string::size_type back = name.rfind("/");
929 prefix = name.substr(0, back);
930 shortName = name.substr(back+1, name.length()-back);
931 index = coordinateSet.getIndex(shortName);
932 // This is a necessary hack to use new component naming,
933 // but SimbodyEngine will be deprecated and so will this code- aseth
934 if (index < 0){ // could be a speed then trim off _u
935 back = prefix.rfind("/");
936 shortName = prefix.substr(back+1, prefix.length()-back);
937 index = coordinateSet.getIndex(shortName);
938 }
939 }
940 if (index >= 0){
941 const Coordinate& coord = coordinateSet.get(index);
942 if (coord.getMotionType() == Coordinate::Rotational) {
943 // assumes first data column is 0 whereas labels has time as 0
944 rStorage.multiplyColumn(i-1, factor);
945 }
946 }
947 }
948 }
949
scaleRotationalDofColumns(TimeSeriesTable & table,double factor) const950 void SimbodyEngine::scaleRotationalDofColumns(TimeSeriesTable& table,
951 double factor) const {
952 size_t ncols = table.getNumColumns();
953 if(ncols == 0)
954 throw Exception("SimbodyEngine.scaleRotationalDofColumns: ERROR- storage has no labels, can't determine coordinate types for deg<->rad conversion",
955 __FILE__,__LINE__);
956
957 // Loop through the coordinates in the model. For each one that is rotational,
958 // see if it has a corresponding column of data. If it does, multiply that
959 // column by the given scaling factor.
960 std::string shortName = "";
961 std::string prefix = "";
962 int index = -1;
963 const CoordinateSet& coordinateSet = _model->getCoordinateSet();
964
965 // first column is time, so skip
966 for (size_t i = 0; i < ncols; i++) {
967 const std::string& name = table.getColumnLabel(i);
968 index = coordinateSet.getIndex(name);
969 if (index < 0){
970 std::string::size_type back = name.rfind("/");
971 prefix = name.substr(0, back);
972 shortName = name.substr(back+1, name.length()-back);
973 index = coordinateSet.getIndex(shortName);
974 // This is a necessary hack to use new component naming,
975 // but SimbodyEngine will be deprecated and so will this code- aseth
976 if (index < 0){ // could be a speed then trim off _u
977 back = prefix.rfind("/");
978 shortName = prefix.substr(back+1, prefix.length()-back);
979 index = coordinateSet.getIndex(shortName);
980 }
981 }
982 if (index >= 0){
983 const Coordinate& coord = coordinateSet.get(index);
984 if (coord.getMotionType() == Coordinate::Rotational) {
985 // assumes first data column is 0 whereas labels has time as 0
986 table.updDependentColumnAtIndex(i) *= factor;
987 }
988 }
989 }
990 }
991 //_____________________________________________________________________________
992 /**
993 * Convert the rotational generalized coordinates or speeds from units of
994 * degrees to units of radians for all the state-vectors in an Storage
995 * object. Coordinates/speeds are identified by column names.
996 *
997 * @param rStorage Storage object.
998 */
convertDegreesToRadians(Storage & rStorage) const999 void SimbodyEngine::convertDegreesToRadians(Storage &rStorage) const
1000 {
1001 assert(rStorage.isInDegrees());
1002 scaleRotationalDofColumns(rStorage, SimTK_DEGREE_TO_RADIAN);
1003 rStorage.setInDegrees(false);
1004 }
1005 //_____________________________________________________________________________
1006 /**
1007 * Convert the rotational generalized coordinates or speeds from units of
1008 * radians to units of degrees for all the state-vectors in an Storage
1009 * object. Coordinates/speeds are identified by column names.
1010 *
1011 * @param rStorage Storage object.
1012 */
convertRadiansToDegrees(Storage & rStorage) const1013 void SimbodyEngine::convertRadiansToDegrees(Storage &rStorage) const
1014 {
1015 assert(!rStorage.isInDegrees());
1016 scaleRotationalDofColumns(rStorage, SimTK_RADIAN_TO_DEGREE);
1017 rStorage.setInDegrees(true);
1018 }
1019
convertRadiansToDegrees(TimeSeriesTable & table) const1020 void SimbodyEngine::convertRadiansToDegrees(TimeSeriesTable& table) const {
1021 if (table.hasTableMetaDataKey("inDegrees")) {
1022 OPENSIM_THROW_IF(
1023 table.getTableMetaData<std::string>("inDegrees") == "yes",
1024 Exception,
1025 "Columns of the table provided are already in degrees.");
1026 table.removeTableMetaDataKey("inDegrees");
1027 }
1028
1029 scaleRotationalDofColumns(table, SimTK_RADIAN_TO_DEGREE);
1030 table.addTableMetaData("inDegrees", std::string{"yes"});
1031 }
1032
convertDegreesToRadians(TimeSeriesTable & table) const1033 void SimbodyEngine::convertDegreesToRadians(TimeSeriesTable& table) const {
1034 if (table.hasTableMetaDataKey("inDegrees")) {
1035 OPENSIM_THROW_IF(
1036 table.getTableMetaData<std::string>("inDegrees") == "no",
1037 Exception,
1038 "Columns of the table provided are already in radians.");
1039 table.removeTableMetaDataKey("inDegrees");
1040 scaleRotationalDofColumns(table, SimTK_DEGREE_TO_RADIAN);
1041 table.addTableMetaData("inDegrees", std::string{ "no" });
1042 }
1043 else {
1044 OPENSIM_THROW(Exception,
1045 "Table provided does not specify rotations to be in degrees.\n"
1046 "No conversion can be applied.");
1047 }
1048 }
1049
1050 //_____________________________________________________________________________
1051 /**
1052 * Convert an array of Q/U values from degrees to radians. The sizes of the
1053 * arrays are assumed to be equal to the number of Us.
1054 *
1055 * @param aQDeg Array of values, in degrees
1056 * @param rQRad Array of values, in radians
1057 */
convertDegreesToRadians(double * aQDeg,double * rQRad) const1058 void SimbodyEngine::convertDegreesToRadians(double *aQDeg, double *rQRad) const
1059 {
1060 const CoordinateSet& coordinateSet = _model->getCoordinateSet();
1061
1062 // The arrays aQDeg and rQRad are assumed to be of size getNumSpeeds() or greater.
1063 // It is also assumed that aQDeg[N] corresponds to the first N coordinates
1064 // in the model, whether those N values are coordinates or speeds.
1065 for (int i = 0; i < _model->getNumSpeeds(); i++)
1066 {
1067 if (coordinateSet.get(i).getMotionType() == Coordinate::Rotational)
1068 rQRad[i] = aQDeg[i] * SimTK_DEGREE_TO_RADIAN;
1069 else
1070 rQRad[i] = aQDeg[i];
1071 }
1072 }
1073 //_____________________________________________________________________________
1074 /**
1075 * Convert an array of Q/U values from radians to degrees. The sizes of the
1076 * arrays are assumed to be equal to the number of Us.
1077 *
1078 * @param aQRad Array of values, in radians
1079 * @param rQDeg Array of values, in degrees
1080 */
convertRadiansToDegrees(double * aQRad,double * rQDeg) const1081 void SimbodyEngine::convertRadiansToDegrees(double *aQRad, double *rQDeg) const
1082 {
1083 const CoordinateSet& coordinateSet = _model->getCoordinateSet();
1084
1085 // The arrays aQRad and rQDeg are assumed to be of size getNumSpeeds() or greater.
1086 // It is also assumed that aQRad[N] corresponds to the first N coordinates
1087 // in the model, whether those N values are coordinates or speeds.
1088 for (int i = 0; i < _model->getNumSpeeds(); i++)
1089 {
1090 if (coordinateSet.get(i).getMotionType() == Coordinate::Rotational)
1091 rQDeg[i] = aQRad[i] * SimTK_RADIAN_TO_DEGREE;
1092 else
1093 rQDeg[i] = aQRad[i];
1094 }
1095 }
1096