1 #ifndef SimTK_SIMBODY_RIGID_BODY_NODE_H_
2 #define SimTK_SIMBODY_RIGID_BODY_NODE_H_
3
4 /* -------------------------------------------------------------------------- *
5 * Simbody(tm) *
6 * -------------------------------------------------------------------------- *
7 * This is part of the SimTK biosimulation toolkit originating from *
8 * Simbios, the NIH National Center for Physics-Based Simulation of *
9 * Biological Structures at Stanford, funded under the NIH Roadmap for *
10 * Medical Research, grant U54 GM072970. See https://simtk.org/home/simbody. *
11 * *
12 * Portions copyright (c) 2005-15 Stanford University and the Authors. *
13 * Authors: Michael Sherman *
14 * Contributors: Derived from IVM code written by Charles Schwieters *
15 * *
16 * Licensed under the Apache License, Version 2.0 (the "License"); you may *
17 * not use this file except in compliance with the License. You may obtain a *
18 * copy of the License at http://www.apache.org/licenses/LICENSE-2.0. *
19 * *
20 * Unless required by applicable law or agreed to in writing, software *
21 * distributed under the License is distributed on an "AS IS" BASIS, *
22 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
23 * See the License for the specific language governing permissions and *
24 * limitations under the License. *
25 * -------------------------------------------------------------------------- */
26
27 #include "simbody/internal/common.h"
28 #include "SimbodyTreeState.h"
29
30 #include <cassert>
31
32 using SimTK::Vector;
33 using SimTK::Vector_;
34 using SimTK::Vec3;
35 using SimTK::SpatialVec;
36 using SimTK::Transform;
37 using SimTK::Rotation;
38 using SimTK::Inertia;
39 using SimTK::MassProperties;
40 using SimTK::Array_;
41
42 /* This is an abstract class representing the *computational* form of a body
43 and its (generic) mobilizer, that is, the joint connecting it to its parent.
44 Concrete classes are derived from this one to represent each specific type of
45 mobilizer, with the emphasis being on fast calculation since node traversals
46 are inner loops of all O(N) multibody algorithms. In particular, while the
47 generic RigidBodyNode has a variable number of mobilities, each concrete node
48 has a compile-time known size so can perform inline floating point operations
49 using stack-allocated variables.
50
51 Every body has a body frame B, and a unique inboard mobilizer frame M fixed to
52 that body. For convenience, we refer to the body frame of a body's unique
53 parent body P as the P frame. There is a frame F fixed to P which is where
54 B's inboard mobilizer attaches. The transform X_FM(q) tracks the across-
55 mobilizer change in configuration induced by the generalized coordinates q.
56 When all the mobilizer coordinates are 0 (=1000 for quaternions), M and F take
57 on their "reference configuration" relationship. Usually M==F in the reference
58 configuration, but sometimes only the axes are aligned (X_FM(0).R()=Identity)
59 while the origins are separated (X_FM(0).p() != 0); the ellipsoid joint is an
60 example.
61
62 The mobilizer frame M is fixed with respect to B, and F is fixed with
63 respect to P. In some cases M and B or F and P will be the same, but not always.
64 The constant (TODO: Instance stage) transforms X_BM and X_PF provide the
65 configuration of the mobilizer frames with respect to the body and parent
66 frames. With these definitions we can easily calculate X_PB as
67 X_PB = X_PF*X_FM*X_MB, where X_FM is the q-dependent cross-mobilizer transform
68 calculated at Position stage.
69
70 RigidBodyNodes know how to extract and deposit their own information from and
71 to the Simbody state variables and cache entries, but they don't know anything
72 about the State class, stages, etc. Instead they depend on being given
73 appropriate access by the caller, whose job it is to mine the State.
74
75 The RigidBodyNode abstract base class defines the interface a concrete
76 mobilized body node must implement. The nodes must implement two kinds of
77 interface methods:
78 - mobilizer-specific local computations
79 - single-node contributions to multibody tree-sweeping algorithms
80 The latter methods are called in a well-defined sequence, sweeping either
81 inwards or outwards so that a node can depend on either its child or parent
82 computations (respectively) having already been completed.
83
84 Here is the spec (TODO):
85
86 Mobilizer-specific
87
88 Mobility-dependent local kinematics calculations
89 ------------------------------------------------
90 calcQDot
91 calcQDotDot
92 calcMobilizerTransformFromQ
93 calcMobilizerVelocityFromU
94 calcMobilizerAccelerationFromUDot
95
96
97 Initialization of state variables
98 -------------------------------------
99 setMobilizerDefaultModelValues
100 setMobilizerDefaultInstanceValues
101 setMobilizerDefaultTimeValues
102 setMobilizerDefaultPositionValues
103 setMobilizerDefaultVelocityValues
104 setMobilizerDefaultDynamicsValues
105 setMobilizerDefaultAccelerationValues
106
107 Local initial condition setting (solvers)
108 -----------------------------------------
109 setQToFitTransform
110 setQToFitRotation
111 setQToFitTranslation
112 setUToFitVelocity
113 setUToFitAngularVelocity
114 setUToFitLinearVelocity
115 enforceQuaternionConstraints
116
117 Bookkeeping
118 -----------
119 getName
120 getNumU
121 getNumQ
122 isUsingQuaternion
123 */
124 class RigidBodyNode {
125 public:
126
127 // Every concrete RigidBodyNode sets this property on construction so we know
128 // whether it can use simple default implementations for kinematic equations
129 // (those involving the N matrix), which rely on qdot=u.
130 enum QDotHandling {
131 QDotIsAlwaysTheSameAsU, // can use default implementations
132 QDotMayDifferFromU // must supply custom implementation
133 };
134
135 // This is a fixed property of a given concrete RigidBodyNode.
isQDotAlwaysTheSameAsU()136 bool isQDotAlwaysTheSameAsU() const
137 { return qdotHandling==QDotIsAlwaysTheSameAsU; }
138
139 // This property tells us whether a RigidBodyNode may use a quaternion for
140 // some set of modeling options. If so, we know that nq > nu, and it must
141 // also be the case that QDotMayDifferFromU. (But note that there may be
142 // mobilizers for which qdot != u but there is no quaternion.)
143 enum QuaternionUse {
144 QuaternionIsNeverUsed,
145 QuaternionMayBeUsed
146 };
147
148 // This is a fixed property of a given concrete RigidBodyNode.
isQuaternionEverUsed()149 bool isQuaternionEverUsed() const
150 { return quaternionUse==QuaternionMayBeUsed; }
151
152 virtual ~RigidBodyNode() = default;
153 RigidBodyNode(const RigidBodyNode&) = delete;
154 RigidBodyNode& operator=(const RigidBodyNode&) = delete;
155
156 // MOBILIZER-SPECIFIC VIRTUAL METHODS //
157
type()158 virtual const char* type() const {return "unknown";}
159 virtual int getDOF() const=0; //number of independent dofs
160 virtual int getMaxNQ() const=0; //dofs plus extra quaternion coordinate if any
161
162 virtual int getNQInUse(const SBModelVars&) const=0; //actual number of q's
163 virtual int getNUInUse(const SBModelVars&) const=0; // actual number of u's
164
165 // This depends on the mobilizer type and modeling options. If it returns
166 // true it also returns the first generalized coordinate of the quaternion
167 // (numbering locally), which is assumed to occupy that generalized coordinate
168 // and the next three contiguous ones. Quaternion-using mobilizers should be
169 // assigned a slot in the quaternion pool.
170 virtual bool isUsingQuaternion(const SBStateDigest&,
171 MobilizerQIndex& startOfQuaternion) const=0;
172
173 // This depends on the mobilizer type and modeling options. This is the amount
174 // of position-cache storage this mobilizer wants us to set aside for
175 // precalculations involving its q's, in units of number of Reals. The meaning
176 // of the entries in this pool is known only to the node.
177 virtual int calcQPoolSize(const SBModelVars&) const = 0;
178
179 // This operator is the first step in realizePosition() for this RBNode. Taking
180 // modeling information from the supplied state digest, perform calculations
181 // that can be done knowing only the current modeling parameters and the
182 // values of the q's. The results go into the posCache and qErr arrays which
183 // have already been set to the first entry belonging exclusively to this
184 // RBNode. DO NOT write directly into the provided State. (Typically
185 // the output arrays will be referring to cache entries in the same State, but
186 // they don't have to.) Either of those output arrays may be null, and the
187 // expected lengths are passed in for consistency checking -- at least in Debug
188 // mode you should make sure they are what you're expecting.
189 //
190 // The only mandatory calculation here is that this routine *must* calculate
191 // constraint errors if there are any mobilizer-local constraints among
192 // the q's -- typically that is the quaternion normalization error where
193 // qerr=|q|-1. For many mobilizers, it is a good idea to precalculate
194 // expensive operations like sin(q), cos(q), or 1/norm(q) so that these don't
195 // need to be recalculated when forming X_FM, N, NInv, and NDot later.
196 // Note that if you want to save such precalculations, you must have asked
197 // for space to be set aside during realizeModel().
198 //
199 // The default implementation here checks the parameters and then does
200 // nothing.
201 virtual void performQPrecalculations(const SBStateDigest& sbs,
202 const Real* q, int nq,
203 Real* posCache, int nPosCache,
204 Real* qErr, int nQErr) const = 0;
205
206 // This mandatory routine calculates the across-joint transform X_FM generated
207 // by the supplied q values. This may depend on sines & cosines or normalized
208 // quaternions already having been calculated and placed into the supplied
209 // posCache. If you just have q's, call the calcAcrossJointTransform() method
210 // instead because it knows how to fill in the posCache if need be.
211 //
212 // This method constitutes the *definition* of the generalized coordinates for
213 // a particular mobilizer.
214 //
215 // Note: this calculates the transform between the *as defined* frames; we
216 // might reverse the frames in use. So we call this X_F0M0 while the possibly
217 // reversed version is X_FM.
218 virtual void calcX_FM(const SBStateDigest& sbs,
219 const Real* q, int nq,
220 const Real* posCache, int nPos,
221 Transform& X_F0M0) const = 0;
222
223 // This is a pure operator form of calcX_FM(). The StateDigest must have been
224 // realized to model stage. The result depends only on the passed-in q,
225 // not anything in the State beyond model stage.
calcAcrossJointTransform(const SBStateDigest & sbs,const Real * q,int nq,Transform & X_F0M0)226 void calcAcrossJointTransform(
227 const SBStateDigest& sbs,
228 const Real* q, int nq,
229 Transform& X_F0M0) const
230 {
231 const int poolz = getQPoolSize(sbs.getModelCache());
232 Real* qPool = poolz ? new Real[poolz] : 0;
233 Real qErr;
234 const int nQErr = isQuaternionInUse(sbs.getModelCache()) ? 1 : 0;
235 performQPrecalculations(sbs, q, nq, qPool, poolz, &qErr, nQErr);
236 calcX_FM(sbs, q, nq, qPool, poolz, X_F0M0);
237 delete[] qPool;
238 }
239
240 // An alternate signature for when you have all the Q's together; this
241 // method will find just ours and call the above method.
calcAcrossJointTransform(const SBStateDigest & sbs,const Vector & q,Transform & X_F0M0)242 void calcAcrossJointTransform(
243 const SBStateDigest& sbs,
244 const Vector& q,
245 Transform& X_F0M0) const
246 {
247 const SBModelCache mc = sbs.getModelCache();
248 const int nq = getNumQInUse(mc);
249 const Real* qp = nq ? &q[getFirstQIndex(mc)] : (Real*)0;
250 calcAcrossJointTransform(sbs, qp, nq, X_F0M0);
251 }
252
253
254 // This operator pulls N(q) from the StateDigest if necessary and calculates
255 // qdot=N(q)*u from the supplied argument. For many mobilizers it
256 // can simply copy u to qdot without referencing the state at all.
calcQDot(const SBStateDigest &,const Real * u,Real * qdot)257 virtual void calcQDot
258 (const SBStateDigest&, const Real* u, Real* qdot) const
259 { SimTK_THROW2(Exception::UnimplementedVirtualMethod, "RigidBodeNode", "calcQDot");}
260 // This operator pulls N(q) and NDot(q,u) from the StateDigest if necessary
261 // and calculates qdotdot=N*udot + NDot*u from the supplied argument.
262 // For many mobilizers it can simply copy udot to qdotdot without referencing
263 // the state at all.
calcQDotDot(const SBStateDigest &,const Real * udot,Real * qdotdot)264 virtual void calcQDotDot
265 (const SBStateDigest&, const Real* udot, Real* qdotdot) const
266 { SimTK_THROW2(Exception::UnimplementedVirtualMethod, "RigidBodeNode", "calcQDotDot");}
calcMobilizerTransformFromQ(const SBStateDigest &,const Real * q)267 virtual Transform calcMobilizerTransformFromQ
268 (const SBStateDigest&, const Real* q) const
269 { SimTK_THROW2(Exception::UnimplementedVirtualMethod, "RigidBodeNode", "calcMobilizerTransformFromQ");}
calcMobilizerVelocityFromU(const SBStateDigest &,const Real * u)270 virtual SpatialVec calcMobilizerVelocityFromU
271 (const SBStateDigest&, const Real* u) const
272 { SimTK_THROW2(Exception::UnimplementedVirtualMethod, "RigidBodeNode", "calcMobilizerVelocityFromU");}
calcMobilizerAccelerationFromUDot(const SBStateDigest &,const Real * udot)273 virtual SpatialVec calcMobilizerAccelerationFromUDot
274 (const SBStateDigest&, const Real* udot) const
275 { SimTK_THROW2(Exception::UnimplementedVirtualMethod, "RigidBodeNode", "calcMobilizerAccelerationFromUDot");}
calcParentToChildTransformFromQ(const SBStateDigest &,const Real * q)276 virtual Transform calcParentToChildTransformFromQ
277 (const SBStateDigest&, const Real* q) const
278 { SimTK_THROW2(Exception::UnimplementedVirtualMethod, "RigidBodeNode", "calcParentToChildTransformFromQ");}
calcParentToChildVelocityFromU(const SBStateDigest &,const Real * u)279 virtual SpatialVec calcParentToChildVelocityFromU
280 (const SBStateDigest&, const Real* u) const
281 { SimTK_THROW2(Exception::UnimplementedVirtualMethod, "RigidBodeNode", "calcParentToChildVelocityFromU");}
calcParentToChildAccelerationFromUDot(const SBStateDigest &,const Real * udot)282 virtual SpatialVec calcParentToChildAccelerationFromUDot
283 (const SBStateDigest&, const Real* udot) const
284 { SimTK_THROW2(Exception::UnimplementedVirtualMethod, "RigidBodeNode", "calcParentToChildAccelerationFromUDot");}
285
286 // Operators involving kinematics matrix N and related matrices NInv and
287 // NDot. These methods perform operations involving just the block on the
288 // diagonal of the matrix that corresponds to this mobilizer. These blocks
289 // can be rectangular, in which case the u-dimension is always the number
290 // of mobilizers dofs (generalized speeds) but the q-dimension may depend on
291 // modeling options (specifically, whether the mobilizer orientation is
292 // modeled with 4 quaternions or 3 Euler angles). These normally treat the
293 // input as a column vector and multiply with the matrix on the left.
294 // Optionally they will treat the input as a row vector and multiply with
295 // the matrix on the right. (The latter is equivalent to multiplication on
296 // the left by the transpose of the matrix.)
297 //
298 // multiplyByN
299 // Left out_q = N * in_u
300 // Right out_u = in_q * N
301 //
302 // multiplyByNInv
303 // Left out_u = inv(N) * in_q
304 // Right out_q = in_u * inv(N)
305 //
306 // multiplyByNDot
307 // Left out_q = NDot * in_u
308 // Right out_u = in_q * NDot
309 // (not invertible but inverse isn't needed; see below)
310 //
311 // where 'in_q' and 'in_u' etc. indicate q-like and u-like vectors or row
312 // vectors, not the actual state variables of the same names. Note that the
313 // interpretation of the in and out arrays is different depending on whether
314 // we're multiplying on the left or right; which is q-like and which is
315 // u-like reverses.
316 //
317 // Note that N=N(q), NInv=Ninv(q), and NDot=NDot(q,u) where now I am talking
318 // about the real set of generalized coordinates q and generalized speeds u
319 // but *just for this mobilizer*, whose values are taken from the supplied
320 // StateDigest.
321 //
322 // In typical usage these are used to calculate qdot=N*u,
323 // qdotdot=N*udot + NDot*u. These can be inverted to calculate u=inv(N)*qdot,
324 // and udot=inv(N)*(qdotdot - NDot*u); note that inv(NDot) is not needed (a
325 // good thing since it is likely to be singular!).
326 //
327 // WARNING: these routines do not normalize quaternions before using them,
328 // so the resulting matrices are scaled by the quaternion norm (if it
329 // isn't 1). That's a feature not a bug! This is important for correct
330 // calculation of qdots because otherwise they would not be the correct
331 // derivatives for unnormalized q's.
332
333 virtual void multiplyByN (const SBStateDigest&, bool matrixOnRight,
334 const Real* in, Real* out) const = 0;
335 virtual void multiplyByNInv(const SBStateDigest&, bool matrixOnRight,
336 const Real* in, Real* out) const = 0;
337 virtual void multiplyByNDot(const SBStateDigest&, bool matrixOnRight,
338 const Real* in, Real* out) const = 0;
339
340 // This will do nothing unless the mobilizer is using a quaternion. Otherwise it
341 // will normalize its quaternion in q, and if qErrest has non-zero length then
342 // it will remove the component of the error estimate which was along the
343 // direction of the quaternion, since that error will now be zero. That is,
344 // we'll set
345 // q = q_fixed = q/|q|
346 // and qErrest -= dot(qErrest,q_fixed)*q_fixed
347 virtual bool enforceQuaternionConstraints(
348 const SBStateDigest& sbs,
349 Vector& q,
350 Vector& qErrest) const=0;
351
352 // Convert from quaternion to Euler angle representations.
convertToEulerAngles(const Vector & inputQ,Vector & outputQ)353 virtual void convertToEulerAngles(const Vector& inputQ, Vector& outputQ) const
354 { SimTK_THROW2(Exception::UnimplementedVirtualMethod, "RigidBodeNode", "convertToEulerAngles");};
355 // Convert from Euler angle to quaternion representations.
convertToQuaternions(const Vector & inputQ,Vector & outputQ)356 virtual void convertToQuaternions(const Vector& inputQ, Vector& outputQ) const
357 { SimTK_THROW2(Exception::UnimplementedVirtualMethod, "RigidBodeNode", "convertToQuaternions");};
358
359 // Called after Model variables are allocated by realizeTopology()
setMobilizerDefaultModelValues(const SBTopologyCache &,SBModelVars &)360 virtual void setMobilizerDefaultModelValues
361 (const SBTopologyCache&, SBModelVars&) const {}
362
363 // All the rest are called right after realizeModel() since that's when all the
364 // remaining state variables are allocated.
setMobilizerDefaultInstanceValues(const SBModelVars &,SBInstanceVars &)365 virtual void setMobilizerDefaultInstanceValues
366 (const SBModelVars&, SBInstanceVars&) const {}
setMobilizerDefaultTimeValues(const SBModelVars &,SBTimeVars &)367 virtual void setMobilizerDefaultTimeValues
368 (const SBModelVars&, SBTimeVars&) const {}
setMobilizerDefaultPositionValues(const SBModelVars &,Vector & q)369 virtual void setMobilizerDefaultPositionValues
370 (const SBModelVars&, Vector& q) const {}
setMobilizerDefaultVelocityValues(const SBModelVars &,Vector & u)371 virtual void setMobilizerDefaultVelocityValues
372 (const SBModelVars&, Vector& u) const {}
setMobilizerDefaultDynamicsValues(const SBModelVars &,SBDynamicsVars &)373 virtual void setMobilizerDefaultDynamicsValues
374 (const SBModelVars&, SBDynamicsVars&) const {}
setMobilizerDefaultAccelerationValues(const SBModelVars &,SBAccelerationVars &)375 virtual void setMobilizerDefaultAccelerationValues
376 (const SBModelVars&, SBAccelerationVars&) const {}
377
378 // These attempt to set the mobilizer's internal configuration or velocity
379 // to a specified value. This is intended to be a fast, local calculation that
380 // produces an answer to machine precision *if* the mobilizer can represent the
381 // given quantity exactly. The answer is returned in the appropriate slots of a
382 // caller-provided "q-like" or "u-like" Vector; the implementation must not
383 // look at or change any other slots.
384 //
385 // It is OK for the implementation to use the current values of the coordinates
386 // or speeds, and it is required to preserve any of these that are not needed
387 // to satisfy the request. If the mobilizer can't satisfy the request to
388 // machine precision it should just do nothing or the best it can, with the
389 // only general rule being that it shouldn't make things worse. In particular,
390 // it does not need to work hard on an approximate solution.
391 //
392 // Note: these are non-virtual wrappers which arrange to reverse the request
393 // for reversed mobilizers, so that the mobilizers themselves do not need to
394 // know they have been reversed. The corresponding pure virtuals are protected.
395
setQToFitTransform(const SBStateDigest & sbs,const Transform & X_FM,Vector & q)396 void setQToFitTransform
397 (const SBStateDigest& sbs, const Transform& X_FM, Vector& q) const
398 { setQToFitTransformImpl(sbs, isReversed() ? Transform(~X_FM) : X_FM, q); }
399
setQToFitRotation(const SBStateDigest & sbs,const Rotation & R_FM,Vector & q)400 void setQToFitRotation
401 (const SBStateDigest& sbs, const Rotation& R_FM, Vector& q) const
402 { setQToFitRotationImpl(sbs, isReversed() ? Rotation(~R_FM) : R_FM, q); }
403
404 // Reversing a translation requires that we obtain the current orientation,
405 // which we'll do assuming there is something meaningful in the rotational
406 // part of the q's.
setQToFitTranslation(const SBStateDigest & sbs,const Vec3 & p_FM,Vector & q)407 void setQToFitTranslation
408 (const SBStateDigest& sbs, const Vec3& p_FM, Vector& q) const
409 {
410 if (!isReversed()) {setQToFitTranslationImpl(sbs, p_FM, q); return;}
411
412 Transform X_MF; // note reversal of frames
413 calcAcrossJointTransform(sbs, q, X_MF);
414 setQToFitTranslationImpl(sbs, X_MF.R()*(-p_FM), q);
415 }
416
setUToFitVelocity(const SBStateDigest & sbs,const Vector & q,const SpatialVec & V_FM,Vector & u)417 void setUToFitVelocity
418 (const SBStateDigest& sbs, const Vector& q, const SpatialVec& V_FM, Vector& u) const
419 { if (!isReversed()) {setUToFitVelocityImpl(sbs, q, V_FM, u); return;}
420 Transform X_MF; // note reversal of frames
421 calcAcrossJointTransform(sbs, q, X_MF);
422 setUToFitVelocityImpl(sbs, q, reverseSpatialVelocity(~X_MF,V_FM), u);
423 }
424
setUToFitAngularVelocity(const SBStateDigest & sbs,const Vector & q,const Vec3 & w_FM,Vector & u)425 void setUToFitAngularVelocity
426 (const SBStateDigest& sbs, const Vector& q, const Vec3& w_FM, Vector& u) const
427 { if (!isReversed()) {setUToFitAngularVelocityImpl(sbs, q, w_FM, u); return;}
428 Transform X_MF; // note reversal of frames
429 calcAcrossJointTransform(sbs, q, X_MF);
430 setUToFitAngularVelocityImpl(sbs, q, reverseAngularVelocity(~X_MF.R(),w_FM), u);
431 }
432
setUToFitLinearVelocity(const SBStateDigest & sbs,const Vector & q,const Vec3 & v_FM,Vector & u)433 void setUToFitLinearVelocity
434 (const SBStateDigest& sbs, const Vector& q, const Vec3& v_FM, Vector& u) const
435 { if (!isReversed()) {setUToFitLinearVelocityImpl(sbs, q, v_FM, u); return;}
436 Transform X_MF; // note reversal of frames
437 calcAcrossJointTransform(sbs, q, X_MF);
438 //TODO: we have to assume angular velocity is zero here. Should be some
439 // way to get the joint to compute w_FM.
440 const SpatialVec V_FM( Vec3(0), v_FM );
441 setUToFitLinearVelocityImpl(sbs, q, reverseSpatialVelocity(~X_MF,V_FM)[1], u);
442 }
443
444
445 // VIRTUAL METHODS FOR SINGLE-NODE OPERATOR CONTRIBUTIONS //
446
447 virtual void realizeModel(
448 SBStateDigest& sbs) const=0;
449
450 virtual void realizeInstance(
451 const SBStateDigest& sbs) const=0;
452
453 // The multibody tree cannot have any time dependence; hence no
454 // realizeTime().
455
456 // Introduce new values for generalized coordinates and calculate
457 // all the position-dependent kinematic terms, including position
458 // constraint errors. Must be called base to tip.
459 virtual void realizePosition(
460 const SBStateDigest& sbs) const=0;
461
462 // Introduce new values for generalized speeds and calculate
463 // all the velocity-dependent kinematic terms. Assumes realizePosition()
464 // has already been called on all nodes. Must be called base to tip.
465 virtual void realizeVelocity(
466 const SBStateDigest& sbs) const=0;
467
468 // Calculate base-to-tip velocity-dependent terms which will be used
469 // in Dynamics stage operators. Assumes realizeVelocity()
470 // has already been called on all nodes, as well as any Dynamics
471 // stage calculations which must go tip-to-base (e.g. articulated
472 // body inertias).
473 virtual void realizeDynamics(
474 const SBStateDigest& sbs) const=0;
475
476 // There is no realizeAcceleration().
477
478 virtual void realizeReport(
479 const SBStateDigest& sbs) const=0;
480
481 virtual void realizeArticulatedBodyInertiasInward(
482 const SBInstanceCache& ic,
483 const SBTreePositionCache& pc,
484 SBArticulatedBodyInertiaCache& abc) const=0;
485
486 virtual void realizeYOutward(
487 const SBInstanceCache& ic,
488 const SBTreePositionCache& pc,
489 const SBArticulatedBodyInertiaCache& abc,
490 SBDynamicsCache& dc) const=0;
491
492 // This has a default implementation that is good for everything
493 // but Ground.
494 virtual void calcCompositeBodyInertiasInward(
495 const SBTreePositionCache& pc,
496 Array_<SpatialInertia,MobilizedBodyIndex>& R) const;
497
multiplyBySystemJacobian(const SBTreePositionCache & pc,const Real * v,SpatialVec * Jv)498 virtual void multiplyBySystemJacobian(
499 const SBTreePositionCache& pc,
500 const Real* v,
501 SpatialVec* Jv) const
502 { SimTK_THROW2(Exception::UnimplementedVirtualMethod, "RigidBodeNode",
503 "multiplyBySystemJacobian"); }
504
multiplyBySystemJacobianTranspose(const SBTreePositionCache & pc,SpatialVec * zTmp,const SpatialVec * X,Real * JtX)505 virtual void multiplyBySystemJacobianTranspose(
506 const SBTreePositionCache& pc,
507 SpatialVec* zTmp,
508 const SpatialVec* X,
509 Real* JtX) const
510 { SimTK_THROW2(Exception::UnimplementedVirtualMethod, "RigidBodeNode",
511 "multiplyBySystemJacobianTranspose"); }
512
calcEquivalentJointForces(const SBTreePositionCache & pc,const SBTreeVelocityCache & vc,const SpatialVec * bodyForces,SpatialVec * allZ,Real * jointForces)513 virtual void calcEquivalentJointForces(
514 const SBTreePositionCache& pc,
515 const SBTreeVelocityCache& vc,
516 const SpatialVec* bodyForces,
517 SpatialVec* allZ,
518 Real* jointForces) const
519 { SimTK_THROW2(Exception::UnimplementedVirtualMethod, "RigidBodeNode",
520 "calcEquivalentJointForces"); }
521
522 virtual void calcUDotPass1Inward(
523 const SBInstanceCache& ic,
524 const SBTreePositionCache& pc,
525 const SBArticulatedBodyInertiaCache& abc,
526 const SBArticulatedBodyVelocityCache& abvc,
527 const Real* jointForces,
528 const SpatialVec* bodyForces,
529 const Real* allUDot,
530 SpatialVec* allZ,
531 SpatialVec* allGepsilon,
532 Real* allEpsilon) const=0;
533 virtual void calcUDotPass2Outward(
534 const SBInstanceCache& ic,
535 const SBTreePositionCache& pc,
536 const SBArticulatedBodyInertiaCache& abc,
537 const SBTreeVelocityCache& vc,
538 const SBDynamicsCache& dc,
539 const Real* epsilonTmp,
540 SpatialVec* allA_GB,
541 Real* allUDot,
542 Real* allTau) const=0;
543
544 virtual void multiplyByMInvPass1Inward(
545 const SBInstanceCache& ic,
546 const SBTreePositionCache& pc,
547 const SBArticulatedBodyInertiaCache& abc,
548 const Real* f,
549 SpatialVec* allZ,
550 SpatialVec* allGepsilon,
551 Real* allEpsilon) const=0;
552 virtual void multiplyByMInvPass2Outward(
553 const SBInstanceCache& ic,
554 const SBTreePositionCache& pc,
555 const SBArticulatedBodyInertiaCache& abc,
556 const Real* epsilonTmp,
557 SpatialVec* allA_GB,
558 Real* allUDot) const=0;
559
560 // Also serves as pass 1 for inverse dynamics.
calcBodyAccelerationsFromUdotOutward(const SBTreePositionCache & pc,const SBTreeVelocityCache & vc,const Real * allUDot,SpatialVec * allA_GB)561 virtual void calcBodyAccelerationsFromUdotOutward(
562 const SBTreePositionCache& pc,
563 const SBTreeVelocityCache& vc,
564 const Real* allUDot,
565 SpatialVec* allA_GB) const
566 { SimTK_THROW2(Exception::UnimplementedVirtualMethod, "RigidBodeNode", "calcBodyAccelerationsFromUdotOutward"); }
calcInverseDynamicsPass2Inward(const SBTreePositionCache & pc,const SBTreeVelocityCache & vc,const SpatialVec * allA_GB,const Real * jointForces,const SpatialVec * bodyForces,SpatialVec * allFTmp,Real * allTau)567 virtual void calcInverseDynamicsPass2Inward(
568 const SBTreePositionCache& pc,
569 const SBTreeVelocityCache& vc,
570 const SpatialVec* allA_GB,
571 const Real* jointForces,
572 const SpatialVec* bodyForces,
573 SpatialVec* allFTmp,
574 Real* allTau) const
575 { SimTK_THROW2(Exception::UnimplementedVirtualMethod, "RigidBodeNode", "calcInverseDynamicsPass2Inward"); }
576
multiplyByMPass1Outward(const SBTreePositionCache & pc,const Real * allUDot,SpatialVec * allA_GB)577 virtual void multiplyByMPass1Outward(
578 const SBTreePositionCache& pc,
579 const Real* allUDot,
580 SpatialVec* allA_GB) const
581 { SimTK_THROW2(Exception::UnimplementedVirtualMethod, "RigidBodeNode", "multiplyByMPass1Outward"); }
multiplyByMPass2Inward(const SBTreePositionCache & pc,const SpatialVec * allA_GB,SpatialVec * allFTmp,Real * allTau)582 virtual void multiplyByMPass2Inward(
583 const SBTreePositionCache& pc,
584 const SpatialVec* allA_GB,
585 SpatialVec* allFTmp,
586 Real* allTau) const
587 { SimTK_THROW2(Exception::UnimplementedVirtualMethod, "RigidBodeNode", "multiplyByMPass2Inward"); }
588
589 // Note that this requires columns of H to be packed like SpatialVec.
getHCol(const SBTreePositionCache &,int j)590 virtual const SpatialVec& getHCol(const SBTreePositionCache&, int j) const
591 {SimTK_THROW2(Exception::UnimplementedVirtualMethod, "RigidBodeNode", "getHCol");}
592
getH_FMCol(const SBTreePositionCache &,int j)593 virtual const SpatialVec& getH_FMCol(const SBTreePositionCache&, int j) const
594 {SimTK_THROW2(Exception::UnimplementedVirtualMethod, "RigidBodeNode", "getH_FMCol");}
595
596
597 // BASE CLASS METHODS //
598
599
600 static RigidBodyNode* createGroundNode();
601
602 // Register the passed-in node as a child of this one.
603 void addChild(RigidBodyNode* child);
setParent(RigidBodyNode * p)604 void setParent(RigidBodyNode* p) {parent=p;}
setNodeNum(MobilizedBodyIndex n)605 void setNodeNum(MobilizedBodyIndex n) {nodeNum=n;}
setLevel(int i)606 void setLevel(int i) {level=i;}
607
608 // TOPOLOGICAL INFO: no State needed
609
getParent()610 RigidBodyNode* getParent() const {return parent;}
getNumChildren()611 int getNumChildren() const {return (int)children.size();}
getChild(int i)612 RigidBodyNode* getChild(int i) const {return (i<(int)children.size()?children[i]:0);}
613
isReversed()614 bool isReversed() const {return reversed;}
615
616 // Return this node's level, that is, how many ancestors separate it from
617 // the Ground node at level 0. Level 1 nodes (directly connected to the
618 // Ground node) are called 'base' nodes.
getLevel()619 int getLevel() const {return level;}
620
621 // This is the unique "body index" for this (body,mobilizer) node. It is used to index
622 // arrays of body quantities.
getNodeNum()623 MobilizedBodyIndex getNodeNum() const {return nodeNum;}
624
isGroundNode()625 bool isGroundNode() const { return level==0; }
isBaseNode()626 bool isBaseNode() const { return level==1; }
627
628 // TODO: these should come from the model cache.
getUIndex()629 UIndex getUIndex() const {return uIndex;}
getQIndex()630 QIndex getQIndex() const {return qIndex;}
631
632
633 // Access routines for plucking the right per-body data from the pool in the State.
fromB(const Array_<Transform,MobilizedBodyIndex> & x)634 const Transform& fromB(const Array_<Transform,MobilizedBodyIndex>& x) const {return x[nodeNum];}
fromB(const Array_<PhiMatrix,MobilizedBodyIndex> & p)635 const PhiMatrix& fromB(const Array_<PhiMatrix,MobilizedBodyIndex>& p) const {return p[nodeNum];}
fromB(const Array_<MassProperties,MobilizedBodyIndex> & m)636 const MassProperties& fromB(const Array_<MassProperties,MobilizedBodyIndex>& m) const {return m[nodeNum];}
fromB(const Array_<Inertia,MobilizedBodyIndex> & i)637 const Inertia& fromB(const Array_<Inertia,MobilizedBodyIndex>& i) const {return i[nodeNum];}
fromB(const Array_<UnitInertia,MobilizedBodyIndex> & i)638 const UnitInertia& fromB(const Array_<UnitInertia,MobilizedBodyIndex>& i) const {return i[nodeNum];}
fromB(const Array_<int,MobilizedBodyIndex> & i)639 int fromB(const Array_<int,MobilizedBodyIndex>& i) const {return i[nodeNum];}
fromB(const Array_<SpatialVec,MobilizedBodyIndex> & v)640 const SpatialVec& fromB(const Array_<SpatialVec,MobilizedBodyIndex>& v) const {return v[nodeNum];}
fromB(const Array_<SpatialMat,MobilizedBodyIndex> & m)641 const SpatialMat& fromB(const Array_<SpatialMat,MobilizedBodyIndex>& m) const {return m[nodeNum];}
fromB(const Array_<SpatialInertia,MobilizedBodyIndex> & m)642 const SpatialInertia& fromB(const Array_<SpatialInertia,MobilizedBodyIndex>& m) const {return m[nodeNum];}
fromB(const Array_<ArticulatedInertia,MobilizedBodyIndex> & m)643 const ArticulatedInertia& fromB(const Array_<ArticulatedInertia,MobilizedBodyIndex>& m) const {return m[nodeNum];}
fromB(const Array_<Vec3,MobilizedBodyIndex> & v)644 const Vec3& fromB(const Array_<Vec3,MobilizedBodyIndex>& v) const {return v[nodeNum];}
645
646
toB(Array_<Transform,MobilizedBodyIndex> & x)647 Transform& toB(Array_<Transform,MobilizedBodyIndex>& x) const {return x[nodeNum];}
toB(Array_<PhiMatrix,MobilizedBodyIndex> & p)648 PhiMatrix& toB(Array_<PhiMatrix,MobilizedBodyIndex>& p) const {return p[nodeNum];}
toB(Array_<MassProperties,MobilizedBodyIndex> & m)649 MassProperties& toB(Array_<MassProperties,MobilizedBodyIndex>& m) const {return m[nodeNum];}
toB(Array_<Inertia,MobilizedBodyIndex> & i)650 Inertia& toB(Array_<Inertia,MobilizedBodyIndex>& i) const {return i[nodeNum];}
toB(Array_<UnitInertia,MobilizedBodyIndex> & i)651 UnitInertia& toB(Array_<UnitInertia,MobilizedBodyIndex>& i) const {return i[nodeNum];}
toB(Array_<int,MobilizedBodyIndex> & i)652 int& toB(Array_<int,MobilizedBodyIndex>& i) const {return i[nodeNum];}
toB(Array_<SpatialVec,MobilizedBodyIndex> & v)653 SpatialVec& toB(Array_<SpatialVec,MobilizedBodyIndex>& v) const {return v[nodeNum];}
toB(Array_<SpatialMat,MobilizedBodyIndex> & m)654 SpatialMat& toB(Array_<SpatialMat,MobilizedBodyIndex>& m) const {return m[nodeNum];}
toB(Array_<SpatialInertia,MobilizedBodyIndex> & m)655 SpatialInertia& toB(Array_<SpatialInertia,MobilizedBodyIndex>& m) const {return m[nodeNum];}
toB(Array_<ArticulatedInertia,MobilizedBodyIndex> & m)656 ArticulatedInertia& toB(Array_<ArticulatedInertia,MobilizedBodyIndex>& m) const {return m[nodeNum];}
toB(Array_<Vec3,MobilizedBodyIndex> & v)657 Vec3& toB(Array_<Vec3,MobilizedBodyIndex>& v) const {return v[nodeNum];}
658
659 // Elementwise access to Vectors is relatively expensive; use sparingly.
fromB(const Vector_<SpatialVec> & v)660 const SpatialVec& fromB(const Vector_<SpatialVec>& v) const {return v[nodeNum];}
fromB(const Vector_<SpatialMat> & m)661 const SpatialMat& fromB(const Vector_<SpatialMat>& m) const {return m[nodeNum];}
fromB(const Vector_<Vec3> & v)662 const Vec3& fromB(const Vector_<Vec3>& v) const {return v[nodeNum];}
toB(Vector_<SpatialMat> & m)663 SpatialMat& toB(Vector_<SpatialMat>& m) const {return m[nodeNum];}
toB(Vector_<Vec3> & v)664 Vec3& toB(Vector_<Vec3>& v) const {return v[nodeNum];}
toB(Vector_<SpatialVec> & v)665 SpatialVec& toB(Vector_<SpatialVec>& v) const {return v[nodeNum];}
666
667 // MODELING INFO
getUseEulerAngles(const SBModelVars & mv)668 bool getUseEulerAngles(const SBModelVars& mv) const {return mv.useEulerAngles;}
669
670 // Find cache resources allocated to this RigidBodyNode.
671 const SBModelPerMobodInfo&
getModelInfo(const SBModelCache & mc)672 getModelInfo(const SBModelCache& mc) const
673 { return mc.getMobodModelInfo(nodeNum); }
674
getFirstQIndex(const SBModelCache & mc)675 QIndex getFirstQIndex(const SBModelCache& mc) const
676 { return getModelInfo(mc).firstQIndex; }
getNumQInUse(const SBModelCache & mc)677 int getNumQInUse(const SBModelCache& mc) const
678 { return getModelInfo(mc).nQInUse; }
679
getFirstUIndex(const SBModelCache & mc)680 UIndex getFirstUIndex(const SBModelCache& mc) const
681 { return getModelInfo(mc).firstUIndex; }
getNumUInUse(const SBModelCache & mc)682 int getNumUInUse(const SBModelCache& mc) const
683 { return getModelInfo(mc).nUInUse; }
684
685 // Return value will be invalid if there are no quaternions currently in use here.
getQuaternionPoolIndex(const SBModelCache & mc)686 QuaternionPoolIndex getQuaternionPoolIndex(const SBModelCache& mc) const
687 { return getModelInfo(mc).quaternionPoolIndex; }
isQuaternionInUse(const SBModelCache & mc)688 bool isQuaternionInUse(const SBModelCache& mc) const
689 { return getModelInfo(mc).hasQuaternionInUse; }
690
691 // Return value will be invalid if this node is not currently using any space
692 // in the q-pool position cache entry.
getQPoolIndex(const SBModelCache & mc)693 MobodQPoolIndex getQPoolIndex(const SBModelCache& mc) const
694 { return getModelInfo(mc).startInQPool; }
getQPoolSize(const SBModelCache & mc)695 int getQPoolSize(const SBModelCache& mc) const
696 { return getModelInfo(mc).nQPoolInUse; }
697
698 // Return a pointer to the first slot in the q cache pool that is
699 // assigned to this node, or null if there are none.
getQPool(const SBModelCache & mc,const SBTreePositionCache & pc)700 const Real* getQPool(const SBModelCache& mc,
701 const SBTreePositionCache& pc) const
702 { const MobodQPoolIndex ix = getQPoolIndex(mc);
703 return ix.isValid() ? &pc.mobilizerQCache[ix] : (Real*)0; }
704
705 // INSTANCE INFO
706
707 // TODO: These ignore State currently since they aren't parametrizable.
getMassProperties_OB_B()708 const MassProperties& getMassProperties_OB_B() const {return massProps_B;}
getMass()709 const Real& getMass () const {return massProps_B.getMass();}
getCOM_B()710 const Vec3& getCOM_B () const {return massProps_B.getMassCenter();}
getUnitInertia_OB_B()711 const UnitInertia& getUnitInertia_OB_B() const {return massProps_B.getUnitInertia();}
getX_BM()712 const Transform& getX_BM () const {return X_BM;}
getX_PF()713 const Transform& getX_PF () const {return X_PF;}
714
715 // These are calculated on construction.
716 // TODO: principal axes
getInertia_CB_B()717 const Inertia& getInertia_CB_B () const {return inertia_CB_B;}
getX_MB()718 const Transform& getX_MB () const {return X_MB;}
719
720 // This says whether this mobilizer has prescribed *acceleration*, and if so
721 // whether the acceleration is known to be zero.
isUDotKnown(const SBInstanceCache & ic)722 bool isUDotKnown(const SBInstanceCache& ic) const
723 { return ic.getMobodInstanceInfo(nodeNum).udotMethod!=Motion::Free; }
isUDotKnownToBeZero(const SBInstanceCache & ic)724 bool isUDotKnownToBeZero(const SBInstanceCache& ic) const
725 { return ic.getMobodInstanceInfo(nodeNum).udotMethod==Motion::Zero; }
726
727 // POSITION INFO
728
729 // Extract from the cache X_FM, the cross-mobilizer transformation matrix giving the configuration
730 // of this body's mobilizer frame M, measured from and expressed in the corresponding outboard
731 // mobilizer frame F attached to the parent. This transformation is defined to be zero (that is, F=M)
732 // in the reference configuration where the joint coordinates are all 0 (or 1,0,0,0 for quaternions).
733 // This is NOT a spatial (ground frame) transformation.
getX_FM(const SBTreePositionCache & pc)734 const Transform& getX_FM(const SBTreePositionCache& pc) const {return fromB(pc.bodyJointInParentJointFrame);}
updX_FM(SBTreePositionCache & pc)735 Transform& updX_FM(SBTreePositionCache& pc) const {return toB (pc.bodyJointInParentJointFrame);}
736
737 // Return X_F0M0, the cross-joint mobilizer transform *as it was defined* in the mobilizer
738 // specification. If the mobilizer has been reversed, then X_F0M0=~X_FM, otherwise it is
739 // just X_FM.
findX_F0M0(const SBTreePositionCache & pc)740 Transform findX_F0M0(const SBTreePositionCache& pc) const
741 { return isReversed() ? Transform(~getX_FM(pc)) // 18 flops
742 : getX_FM(pc); }
743
744 // Extract from the cache X_PB, the cross-joint transformation matrix giving the configuration
745 // of this body's frame B measured from and expressed in its *parent* frame P. Thus this is NOT
746 // a spatial (ground frame) transformation.
getX_PB(const SBTreePositionCache & pc)747 const Transform& getX_PB(const SBTreePositionCache& pc) const {return fromB(pc.bodyConfigInParent);}
updX_PB(SBTreePositionCache & pc)748 Transform& updX_PB(SBTreePositionCache& pc) const {return toB (pc.bodyConfigInParent);}
749
750 // Extract from the cache X_GB, the transformation matrix giving the spatial configuration of this
751 // body's frame B measured from and expressed in ground. This consists of a rotation matrix
752 // R_GB, and a ground-frame vector r_OG_OB from ground's origin to the origin point of frame B.
getX_GB(const SBTreePositionCache & pc)753 const Transform& getX_GB(const SBTreePositionCache& pc) const {
754 return fromB(pc.bodyConfigInGround);
755 }
updX_GB(SBTreePositionCache & pc)756 Transform& updX_GB(SBTreePositionCache& pc) const {
757 return toB(pc.bodyConfigInGround);
758 }
759
760 // Extract from the cache the body-to-parent shift matrix "phi".
getPhi(const SBTreePositionCache & pc)761 const PhiMatrix& getPhi(const SBTreePositionCache& pc) const {return fromB(pc.bodyToParentShift);}
updPhi(SBTreePositionCache & pc)762 PhiMatrix& updPhi(SBTreePositionCache& pc) const {return toB (pc.bodyToParentShift);}
763
764 // Extract this body's spatial inertia matrix from the cache. This contains
765 // the mass properties measured from (and about) the body frame origin, but
766 // expressed in the Ground frame.
getMk_G(const SBTreePositionCache & pc)767 const SpatialInertia& getMk_G(const SBTreePositionCache& pc) const {return fromB(pc.bodySpatialInertiaInGround);}
updMk_G(SBTreePositionCache & pc)768 SpatialInertia& updMk_G(SBTreePositionCache& pc) const {return toB (pc.bodySpatialInertiaInGround);}
769
770 // Extract from the cache the location of the body's center of mass, measured from the ground
771 // origin and expressed in Ground.
getCOM_G(const SBTreePositionCache & pc)772 const Vec3& getCOM_G(const SBTreePositionCache& pc) const {return fromB(pc.bodyCOMInGround);}
updCOM_G(SBTreePositionCache & pc)773 Vec3& updCOM_G(SBTreePositionCache& pc) const {return toB (pc.bodyCOMInGround);}
774
775 // Extract from the cache the vector from body B's origin to its center of
776 // mass, reexpressed in Ground.
getCB_G(const SBTreePositionCache & pc)777 const Vec3& getCB_G(const SBTreePositionCache& pc) const
778 { return getMk_G(pc).getMassCenter(); }
779
780 // Extract from the cache the body's inertia about the body origin OB, but
781 // reexpressed in Ground.
getUnitInertia_OB_G(const SBTreePositionCache & pc)782 const UnitInertia& getUnitInertia_OB_G(const SBTreePositionCache& pc) const
783 { return getMk_G(pc).getUnitInertia(); }
784
785 // Extract from the cache the spatial (ground-relative) location and orientation of this body's
786 // *parent's* body frame P.
getX_GP(const SBTreePositionCache & pc)787 const Transform& getX_GP(const SBTreePositionCache& pc) const {assert(parent); return parent->getX_GB(pc);}
788
789 // These depend only on PositionKinematics but they are delayed until needed.
790
791 // Composite body inertias.
getR(const SBCompositeBodyInertiaCache & cbc)792 const SpatialInertia& getR(const SBCompositeBodyInertiaCache& cbc) const {return fromB(cbc.compositeBodyInertia);}
updR(SBCompositeBodyInertiaCache & cbc)793 SpatialInertia& updR(SBCompositeBodyInertiaCache& cbc) const {return toB (cbc.compositeBodyInertia);}
794
795 // Articulated body inertias and related calculations
getP(const SBArticulatedBodyInertiaCache & abc)796 const ArticulatedInertia& getP(const SBArticulatedBodyInertiaCache& abc) const {return fromB(abc.articulatedBodyInertia);}
updP(SBArticulatedBodyInertiaCache & abc)797 ArticulatedInertia& updP(SBArticulatedBodyInertiaCache& abc) const {return toB (abc.articulatedBodyInertia);}
798
getPPlus(const SBArticulatedBodyInertiaCache & abc)799 const ArticulatedInertia& getPPlus(const SBArticulatedBodyInertiaCache& abc) const {return fromB(abc.pPlus);}
updPPlus(SBArticulatedBodyInertiaCache & abc)800 ArticulatedInertia& updPPlus(SBArticulatedBodyInertiaCache& abc) const {return toB (abc.pPlus);}
801
802 // VELOCITY INFO
803
getV_FM(const SBTreeVelocityCache & vc)804 const SpatialVec& getV_FM(const SBTreeVelocityCache& vc) const {return fromB(vc.mobilizerRelativeVelocity);}
updV_FM(SBTreeVelocityCache & vc)805 SpatialVec& updV_FM(SBTreeVelocityCache& vc) const {return toB (vc.mobilizerRelativeVelocity);}
806
807 // Given V_AB, the spatial velocity of frame B in A expressed in A, return V_BA, the spatial
808 // velocity of frame A in B, expressed in B. The reversal also requires knowing X_AB, the spatial
809 // position and orientation of B in A. Theory:
810 // V_BA = -R_BA * [ w_AB ]
811 // [ v_AB + p_AB x w_AB ]
812 // This costs 45 flops. See reverseAngularVelocity() if that's all you need; it's a lot cheaper.
reverseSpatialVelocity(const Transform & X_AB,const SpatialVec & V_AB)813 static SpatialVec reverseSpatialVelocity(const Transform& X_AB, const SpatialVec& V_AB) {
814 const Rotation& R_AB = X_AB.R();
815 const Vec3& p_AB = X_AB.p();
816 const Vec3& w_AB = V_AB[0];
817 const Vec3& v_AB = V_AB[1];
818
819 return ~R_AB * SpatialVec( -w_AB, (w_AB % p_AB) - v_AB );
820 }
821
822 // Given w_AB, the angular velocity of frame B in A expressed in A, return w_BA, the angular
823 // velocity of frame A in B, expressed in B. The reversal also requires knowing R_AB, the
824 // orientation of B in A. Theory:
825 // w_BA = -R_BA*w_AB
826 // This is just a subset of what reverseSpatialVelocity does, but it is cheap and
827 // often all you need is the angular velocity.
828 // This costs 18 flops.
reverseAngularVelocity(const Rotation & R_AB,const Vec3 & w_AB)829 static Vec3 reverseAngularVelocity(const Rotation& R_AB, const Vec3& w_AB) {
830 return ~R_AB * (-w_AB);
831 }
832
833 // Return V_F0M0, the cross-joint mobilizer velocity *as it was defined* in the mobilizer
834 // specification. If the mobilizer has not been reversed, this is just V_FM.
835 // 45 flops if reversed.
findV_F0M0(const SBTreePositionCache & pc,const SBTreeVelocityCache & vc)836 SpatialVec findV_F0M0(const SBTreePositionCache& pc, const SBTreeVelocityCache& vc) const {
837 return isReversed() ? reverseSpatialVelocity(getX_FM(pc), getV_FM(vc))
838 : getV_FM(vc);
839 }
840
841 // Return w_F0M0, the cross-joint mobilizer angular velocity *as it was defined*
842 // in the mobilizer specification. If the mobilizer has not been reversed, this is just w_FM.
843 // This is a useful and much cheaper subset of findV_F0M0.
844 // 18 flops if reversed.
find_w_F0M0(const SBTreePositionCache & pc,const SBTreeVelocityCache & vc)845 Vec3 find_w_F0M0(const SBTreePositionCache& pc, const SBTreeVelocityCache& vc) const {
846 return isReversed() ? reverseAngularVelocity(getX_FM(pc).R(), getV_FM(vc)[0])
847 : getV_FM(vc)[0];
848 }
849
850 // Extract from the cache V_GB, the spatial velocity of this body's frame B measured in and
851 // expressed in ground. This contains the angular velocity of B in G, and the linear velocity
852 // of B's origin point OB in G, with both vectors expressed in G.
getV_GB(const SBTreeVelocityCache & vc)853 const SpatialVec& getV_GB (const SBTreeVelocityCache& vc) const {return fromB(vc.bodyVelocityInGround);}
updV_GB(SBTreeVelocityCache & vc)854 SpatialVec& updV_GB (SBTreeVelocityCache& vc) const {return toB (vc.bodyVelocityInGround);}
855
856 // Extract from the cache V_PB_G, the *spatial* velocity of this body's frame B, that is the
857 // cross-joint velocity measured with respect to the parent frame, but then expressed in the
858 // *ground* frame. This contains the angular velocity of B in P, and the linear velocity
859 // of B's origin point OB in P, with both vectors expressed in *G*.
getV_PB_G(const SBTreeVelocityCache & vc)860 const SpatialVec& getV_PB_G (const SBTreeVelocityCache& vc) const {return fromB(vc.bodyVelocityInParent);}
updV_PB_G(SBTreeVelocityCache & vc)861 SpatialVec& updV_PB_G (SBTreeVelocityCache& vc) const {return toB (vc.bodyVelocityInParent);}
862
getV_GP(const SBTreeVelocityCache & vc)863 const SpatialVec& getV_GP(const SBTreeVelocityCache& vc) const {assert(parent); return parent->getV_GB(vc);}
864
getSpatialVel(const SBTreeVelocityCache & vc)865 const SpatialVec& getSpatialVel (const SBTreeVelocityCache& vc) const {return getV_GB(vc);}
getSpatialAngVel(const SBTreeVelocityCache & vc)866 const Vec3& getSpatialAngVel(const SBTreeVelocityCache& vc) const {return getV_GB(vc)[0];}
getSpatialLinVel(const SBTreeVelocityCache & vc)867 const Vec3& getSpatialLinVel(const SBTreeVelocityCache& vc) const {return getV_GB(vc)[1];}
868
869 // Extract from the cache VD_PB_G, the *spatial* velocity derivative remainder term
870 // generated by H_PB_G_Dot*u, where H_PB_G_Dot = d/dt H_PB_G with the derivative taken
871 // in the Ground frame. This is used in calculation of coriolis acceleration.
872 // CAUTION: our definition for the H matrix is transposed from those used by Jain and Schwieters.
getVD_PB_G(const SBTreeVelocityCache & vc)873 const SpatialVec& getVD_PB_G (const SBTreeVelocityCache& vc) const
874 {return fromB(vc.bodyVelocityInParentDerivRemainder);}
updVD_PB_G(SBTreeVelocityCache & vc)875 SpatialVec& updVD_PB_G (SBTreeVelocityCache& vc) const
876 {return toB (vc.bodyVelocityInParentDerivRemainder);}
877
getGyroscopicForce(const SBTreeVelocityCache & vc)878 const SpatialVec& getGyroscopicForce(const SBTreeVelocityCache& vc) const {return fromB(vc.gyroscopicForces);}
updGyroscopicForce(SBTreeVelocityCache & vc)879 SpatialVec& updGyroscopicForce(SBTreeVelocityCache& vc) const {return toB (vc.gyroscopicForces);}
880
getMobilizerCoriolisAcceleration(const SBTreeVelocityCache & vc)881 const SpatialVec& getMobilizerCoriolisAcceleration(const SBTreeVelocityCache& vc) const {return fromB(vc.mobilizerCoriolisAcceleration);}
updMobilizerCoriolisAcceleration(SBTreeVelocityCache & vc)882 SpatialVec& updMobilizerCoriolisAcceleration(SBTreeVelocityCache& vc) const {return toB (vc.mobilizerCoriolisAcceleration);}
883
getTotalCoriolisAcceleration(const SBTreeVelocityCache & vc)884 const SpatialVec& getTotalCoriolisAcceleration(const SBTreeVelocityCache& vc) const {return fromB(vc.totalCoriolisAcceleration);}
updTotalCoriolisAcceleration(SBTreeVelocityCache & vc)885 SpatialVec& updTotalCoriolisAcceleration(SBTreeVelocityCache& vc) const {return toB (vc.totalCoriolisAcceleration);}
886
getTotalCentrifugalForces(const SBTreeVelocityCache & vc)887 const SpatialVec& getTotalCentrifugalForces(const SBTreeVelocityCache& vc) const {return fromB(vc.totalCentrifugalForces);}
updTotalCentrifugalForces(SBTreeVelocityCache & vc)888 SpatialVec& updTotalCentrifugalForces(SBTreeVelocityCache& vc) const {return toB (vc.totalCentrifugalForces);}
889
890 // This requires both velocity kinematics (from SBTreeVelocityCache) and
891 // articulated body inertias (from SBArticulatedBodyInertiaCache).
getArticulatedBodyCentrifugalForces(const SBArticulatedBodyVelocityCache & abvc)892 const SpatialVec& getArticulatedBodyCentrifugalForces
893 (const SBArticulatedBodyVelocityCache& abvc) const
894 { return fromB(abvc.articulatedBodyCentrifugalForces); }
updArticulatedBodyCentrifugalForces(SBArticulatedBodyVelocityCache & abvc)895 SpatialVec& updArticulatedBodyCentrifugalForces
896 (SBArticulatedBodyVelocityCache& abvc) const
897 { return toB(abvc.articulatedBodyCentrifugalForces); }
898
899
900 // DYNAMICS INFO
901
getY(const SBDynamicsCache & dc)902 const SpatialMat& getY(const SBDynamicsCache& dc) const {return fromB(dc.Y);}
updY(SBDynamicsCache & dc)903 SpatialMat& updY(SBDynamicsCache& dc) const {return toB (dc.Y);}
904
905
906
907
908 // ACCELERATION INFO
909
getZ(const SBTreeAccelerationCache & tac)910 const SpatialVec& getZ(const SBTreeAccelerationCache& tac) const {return fromB(tac.z);}
updZ(SBTreeAccelerationCache & tac)911 SpatialVec& updZ(SBTreeAccelerationCache& tac) const {return toB (tac.z);}
912
getZPlus(const SBTreeAccelerationCache & tac)913 const SpatialVec& getZPlus(const SBTreeAccelerationCache& tac) const {return fromB(tac.zPlus);}
updZPlus(SBTreeAccelerationCache & tac)914 SpatialVec& updZPlus(SBTreeAccelerationCache& tac) const {return toB (tac.zPlus);}
915
916 // Extract from the cache A_GB, the spatial acceleration of this body's frame B measured in and
917 // expressed in ground. This contains the inertial angular acceleration of B in G, and the
918 // linear acceleration of B's origin point OB in G, with both vectors expressed in G.
getA_GB(const SBTreeAccelerationCache & ac)919 const SpatialVec& getA_GB (const SBTreeAccelerationCache& ac) const {return fromB(ac.bodyAccelerationInGround);}
updA_GB(SBTreeAccelerationCache & ac)920 SpatialVec& updA_GB (SBTreeAccelerationCache& ac) const {return toB (ac.bodyAccelerationInGround);}
921
getSpatialAcc(const SBTreeAccelerationCache & ac)922 const SpatialVec& getSpatialAcc (const SBTreeAccelerationCache& ac) const {return getA_GB(ac);}
getSpatialAngAcc(const SBTreeAccelerationCache & ac)923 const Vec3& getSpatialAngAcc(const SBTreeAccelerationCache& ac) const {return getA_GB(ac)[0];}
getSpatialLinAcc(const SBTreeAccelerationCache & ac)924 const Vec3& getSpatialLinAcc(const SBTreeAccelerationCache& ac) const {return getA_GB(ac)[1];}
925
926
927
928 // These are called just after new state variables are allocated,
929 // in case there are any node-specific default values.
930 // We can handle the "body" variables (like mass) here, but we have to forward the
931 // request to the mobilizers to handle their own variables. At the Position
932 // stage, for example, mobilizers which use quaternions will set the default ball
933 // joint q's to 1,0,0,0.
934 // Most of these will use the default implementations here, i.e. do nothing.
935
936 // Called after Model variables are allocated by realizeTopology()
setNodeDefaultModelValues(const SBTopologyCache & tc,SBModelVars & mv)937 void setNodeDefaultModelValues(const SBTopologyCache& tc, SBModelVars& mv) const {
938 // no body model variables at the moment; TODO: should forward to Body type
939 setMobilizerDefaultModelValues(tc, mv);
940 }
941
942 // All the rest are called right after realizeModel() since that's when all the
943 // remaining state variables are allocated.
setNodeDefaultInstanceValues(const SBModelVars & mv,SBInstanceVars & iv)944 void setNodeDefaultInstanceValues(const SBModelVars& mv, SBInstanceVars& iv) const {
945 // mass properties, inb and outb frame are handled here
946 toB(iv.bodyMassProperties) = getMassProperties_OB_B();
947 toB(iv.outboardMobilizerFrames) = getX_BM();
948 toB(iv.inboardMobilizerFrames) = getX_PF();
949 setMobilizerDefaultInstanceValues(mv,iv);
950 }
setNodeDefaultTimeValues(const SBModelVars & mv,SBTimeVars & tv)951 void setNodeDefaultTimeValues(const SBModelVars& mv, SBTimeVars& tv) const {
952 // no body model variables at the moment; TODO: should forward to Body type
953 setMobilizerDefaultTimeValues(mv, tv);
954 }
setNodeDefaultPositionValues(const SBModelVars & mv,Vector & q)955 void setNodeDefaultPositionValues(const SBModelVars& mv, Vector& q) const {
956 // no body model variables at the moment; TODO: should forward to Body type
957 setMobilizerDefaultPositionValues(mv, q);
958 }
setNodeDefaultVelocityValues(const SBModelVars & mv,Vector & u)959 void setNodeDefaultVelocityValues(const SBModelVars& mv, Vector& u) const {
960 // no body model variables at the moment; TODO: should forward to Body type
961 setMobilizerDefaultVelocityValues(mv, u);
962 }
setNodeDefaultDynamicsValues(const SBModelVars & mv,SBDynamicsVars & dv)963 void setNodeDefaultDynamicsValues(const SBModelVars& mv, SBDynamicsVars& dv) const {
964 // no body model variables at the moment; TODO: should forward to Body type
965 setMobilizerDefaultDynamicsValues(mv, dv);
966 }
setNodeDefaultAccelerationValues(const SBModelVars & mv,SBAccelerationVars & av)967 void setNodeDefaultAccelerationValues(const SBModelVars& mv, SBAccelerationVars& av) const {
968 // no body model variables at the moment; TODO: should forward to Body type
969 setMobilizerDefaultAccelerationValues(mv, av);
970 }
971
972
973 // Calculate kinetic energy (from spatial quantities only).
974 Real calcKineticEnergy(
975 const SBTreePositionCache& pc,
976 const SBTreeVelocityCache& vc) const;
977
978 // Calculate all spatial configuration quantities, assuming availability of
979 // joint-specific relative quantities.
980 void calcJointIndependentKinematicsPos(
981 SBTreePositionCache& pc) const;
982
983 // Calcluate all spatial velocity quantities, assuming availability of
984 // joint-specific relative quantities and all position kinematics.
985 void calcJointIndependentKinematicsVel(
986 const SBTreePositionCache& pc,
987 SBTreeVelocityCache& vc) const;
988
989 // Calculate velocity-dependent quantities that involve articulated body
990 // inertias and are needed for computing accelerations.
991 void realizeArticulatedBodyVelocityCache(
992 const SBTreePositionCache& pc,
993 const SBTreeVelocityCache& vc,
994 const SBArticulatedBodyInertiaCache& abc,
995 SBArticulatedBodyVelocityCache& abvc) const;
996
997
998 protected:
999 // This is the constructor for the abstract base type for use by the derived
1000 // concrete types in their constructors.
1001 RigidBodyNode(const MassProperties& mProps_B,
1002 const Transform& xform_PF,
1003 const Transform& xform_BM,
1004 QDotHandling qdotType,
1005 QuaternionUse quatUse,
1006 bool reverse=false)
1007 : parent(0), children(), level(-1),
1008 massProps_B(mProps_B),
1009 inertia_CB_B(mProps_B.isFinite()
1010 ? mProps_B.calcCentralInertia()
1011 : (mProps_B.isInf() ? Inertia(Infinity) : Inertia())),
1012 X_BM(xform_BM), X_PF(xform_PF), X_MB(~xform_BM),
1013 qdotHandling(qdotType), quaternionUse(quatUse), reversed(reverse)
1014 {
1015 // If a quaternion might be used, it can't possibly be true that qdot is
1016 // always the same as u.
1017 assert(!(quatUse==QuaternionMayBeUsed && qdotType==QDotIsAlwaysTheSameAsU));
1018 }
1019
1020 // These have wrappers above which call them and should not be
1021 // called directly from outside this class and its descendents. Note that
1022 // the requests are couched in terms of the way the mobilizer was
1023 // *defined*, not as it is after reversal (hence F0 instead of F and
1024 // M0 instead of M). The q's and u's have identical meanings for both
1025 // forward and reverse mobilizers.
1026 virtual void setQToFitTransformImpl
1027 (const SBStateDigest&, const Transform& X_F0M0, Vector& q) const = 0;
1028 virtual void setQToFitRotationImpl
1029 (const SBStateDigest&, const Rotation& R_F0M0, Vector& q) const = 0;
1030 virtual void setQToFitTranslationImpl
1031 (const SBStateDigest&, const Vec3& p_F0M0, Vector& q) const = 0;
1032
1033 virtual void setUToFitVelocityImpl
1034 (const SBStateDigest&, const Vector& q, const SpatialVec& V_F0M0, Vector& u) const = 0;
1035 virtual void setUToFitAngularVelocityImpl
1036 (const SBStateDigest&, const Vector& q, const Vec3& w_F0M0, Vector& u) const = 0;
1037 virtual void setUToFitLinearVelocityImpl
1038 (const SBStateDigest&, const Vector& q, const Vec3& v_F0M0, Vector& u) const = 0;
1039
1040 typedef Array_<RigidBodyNode*> RigidBodyNodeList;
1041
1042 QIndex qIndex; // index into internal coord pos array
1043 UIndex uIndex; // index into internal coord vel,acc arrays
1044 USquaredIndex uSqIndex; // index into array of DOF^2 objects
1045 QuaternionPoolIndex quaternionIndex; // if this mobilizer has a quaternion, this is our slot
1046
1047 RigidBodyNode* parent;
1048 RigidBodyNodeList children;
1049 int level; //how far from base
1050 MobilizedBodyIndex nodeNum; //unique ID number in SimbodyMatterSubsystemRep
1051
1052 // These are the default body properties, all supplied or calculated on
1053 // construction. TODO: they should be
1054 // (optionally?) overrideable by Instance-level state variable entries.
1055
1056 // This is the mass, center of mass, and inertia as supplied at construction.
1057 // Here the inertia is taken about the B origin OB.
1058 const MassProperties massProps_B;
1059
1060 // This is the supplied inertia, shifted to the center of mass. It is still
1061 // a constant expressed in B, but is taken about the COM.
1062 const Inertia inertia_CB_B;
1063
1064 // Orientation and location of inboard mobilizer frame M, measured
1065 // and expressed in body frame B.
1066 const Transform X_BM;
1067 const Transform X_MB; // inverse of X_BM, calculated on construction
1068
1069 // This is set when we attach this node to its parent in the tree. This is the
1070 // configuration of the parent's outboard mobilizer attachment frame corresponding
1071 // to body B (F) measured from and expressed in the parent frame P. It is
1072 // a constant in frame P. TODO: make it parameterizable.
1073 const Transform X_PF;
1074
1075 // Concrete RigidBodyNodes should set this flag on construction to indicate whether they can guarantee
1076 // that their mobilizer's qdots are just the generalized speeds u, for all possible
1077 // modeling options. That is the same as saying nq=nu and qdot=u, or that the block of the kinematic
1078 // matrix N corresponding to this node is an nuXnu identity matrix; NInv is the same; and
1079 // NDot is nuXnu zero. These conditions allows the use of default implementations of many
1080 // methods which would otherwise have to be overridden. The default implementations assert
1081 // that qdotHandling==QDotIsAlwaysTheSameAsU.
1082 const QDotHandling qdotHandling;
1083
1084 // Concrete RigidBodyNodes should set this flag on construction to indicate whether they
1085 // can guarantee never to use a quaternion in their generailized coordinates for any set
1086 // of modeling options.
1087 const QuaternionUse quaternionUse;
1088
1089 // This is true if the mobilizer is specified in the reverse direction,
1090 // that is from child to parent.
1091 const bool reversed;
1092
1093
1094 };
1095
1096 #endif // SimTK_SIMBODY_RIGID_BODY_NODE_H_
1097