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