1 #ifndef SimTK_SIMBODY_RIGID_BODY_NODE_SPEC_UNIVERSAL_H_
2 #define SimTK_SIMBODY_RIGID_BODY_NODE_SPEC_UNIVERSAL_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-12 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 /**@file
28  * Define the RigidBodyNode that implements a Universal mobilizer, also known
29  * as a UJoint or Hooke's joint.
30  */
31 
32 #include "SimbodyMatterSubsystemRep.h"
33 #include "RigidBodyNode.h"
34 #include "RigidBodyNodeSpec.h"
35 
36     // UNIVERSAL (U-JOINT, HOOKE'S JOINT) //
37 
38 // This is a Universal Joint (U-Joint), also known as a Hooke's joint. This is
39 // identical to the joint that connects pieces of a driveshaft together under
40 // a car. Physically, you can think of this as a parent body P, hinged to a
41 // massless cross-shaped coupler, which is then hinged to the child body B. The
42 // massless coupler doesn't actually appear in the model. Instead, we use a
43 // body-fixed 1-2 Euler rotation sequence for orientation, which has the same
44 // effect: starting with frames B and P aligned (when q0=q1=0), rotate frame
45 // B about the Px(=Bx) axis by q0; then, rotate frame B further about the new
46 // By(!=Py) by q1. For generalized speeds u we use the Euler angle derivatives
47 // qdot, which are *not* the same as angular velocity components because u0 is
48 // a rotation rate around Px(!=Bx any more) while u1 is a rotation rate
49 // about By.
50 //
51 // To summarize,
52 //    q's: a two-angle body-fixed rotation sequence about x, then new y
53 //    u's: time derivatives of the q's
54 //
55 // Note that the U-Joint degrees of freedom relating the parent's F frame to
56 // the child's M frame are about x and y, with the "long" axis of the
57 // driveshaft along z.
58 template<bool noX_MB, bool noR_PF>
59 class RBNodeUJoint : public RigidBodyNodeSpec<2, false, noX_MB, noR_PF> {
60 public:
61 typedef typename RigidBodyNodeSpec<2, false, noX_MB, noR_PF>::HType HType;
type()62 virtual const char* type() { return "ujoint"; }
63 
RBNodeUJoint(const MassProperties & mProps_B,const Transform & X_PF,const Transform & X_BM,bool isReversed,UIndex & nextUSlot,USquaredIndex & nextUSqSlot,QIndex & nextQSlot)64 RBNodeUJoint(const MassProperties& mProps_B,
65                 const Transform&      X_PF,
66                 const Transform&      X_BM,
67                 bool                  isReversed,
68                 UIndex&               nextUSlot,
69                 USquaredIndex&        nextUSqSlot,
70                 QIndex&               nextQSlot)
71 :   RigidBodyNodeSpec<2, false, noX_MB, noR_PF>(mProps_B,X_PF,X_BM,nextUSlot,nextUSqSlot,nextQSlot,
72                          RigidBodyNode::QDotIsAlwaysTheSameAsU, RigidBodyNode::QuaternionIsNeverUsed,
73                          isReversed)
74 {
75     this->updateSlots(nextUSlot,nextUSqSlot,nextQSlot);
76 }
77 
setQToFitRotationImpl(const SBStateDigest & sbs,const Rotation & R_FM,Vector & q)78 void setQToFitRotationImpl(const SBStateDigest& sbs, const Rotation& R_FM,
79                            Vector& q) const {
80     // The only rotations this joint can handle are about Mx and My.
81     // TODO: isn't there a better way to come up with "the rotation around x&y
82     // that best approximates a rotation R"? Here we're just hoping that the
83     // supplied rotation matrix can be decomposed into (x,y) rotations.
84     const Vec2 angles12 = R_FM.convertRotationToBodyFixedXY();
85     this->toQ(q) = angles12;
86 }
87 
setQToFitTranslationImpl(const SBStateDigest & sbs,const Vec3 & p_FM,Vector & q)88 void setQToFitTranslationImpl(const SBStateDigest& sbs, const Vec3& p_FM,
89                               Vector& q) const {
90     // M and F frame origins are always coincident for this mobilizer so there
91     // is no way to create a translation by rotating. So the only translation
92     // we can represent is 0.
93 }
94 
95 // We can only express angular velocity that can be produced with our
96 // generalized speeds which are Fx and My rotations rates. So we'll take the
97 // supplied angular velocity expressed in F, project it on Fx and use that as
98 // the first generalized speed. Then take whatever angular velocity is
99 // unaccounted for, express it in M, and project onto My and use that as the
100 // second generalized speed.
setUToFitAngularVelocityImpl(const SBStateDigest & sbs,const Vector & q,const Vec3 & w_FM,Vector & u)101 void setUToFitAngularVelocityImpl(const SBStateDigest& sbs, const Vector& q,
102                                   const Vec3& w_FM, Vector& u) const {
103     const Rotation R_FM =
104         Rotation(BodyRotationSequence, this->fromQ(q)[0], XAxis, this->fromQ(q)[1], YAxis);  // body fixed 1-2 sequence
105     const Vec3 wyz_FM_M = ~R_FM*Vec3(0,w_FM[1],w_FM[2]);
106     this->toU(u) = Vec2(w_FM[0], wyz_FM_M[1]);
107 }
108 
setUToFitLinearVelocityImpl(const SBStateDigest & sbs,const Vector &,const Vec3 & v_FM,Vector & u)109 void setUToFitLinearVelocityImpl
110     (const SBStateDigest& sbs, const Vector&, const Vec3& v_FM, Vector& u) const
111 {
112     // M and F frame origins are always coincident for this mobilizer so there
113     // is no way to create a linear velocity by rotating. So the only linear
114     // velocity we can represent is 0.
115 }
116 
117 enum {PoolSize=4}; // number of Reals
118 enum {CosQ=0, SinQ=2};
119 // We want space for cos(q01) and sin(q01).
calcQPoolSize(const SBModelVars &)120 int calcQPoolSize(const SBModelVars&) const
121 {   return PoolSize; }
122 
123 // Precalculation of sin/cos costs around 100 flops.
performQPrecalculations(const SBStateDigest & sbs,const Real * q,int nq,Real * qCache,int nQCache,Real * qErr,int nQErr)124 void performQPrecalculations(const SBStateDigest& sbs,
125                                 const Real* q, int nq,
126                                 Real* qCache,  int nQCache,
127                                 Real* qErr,    int nQErr) const
128 {
129     assert(q && nq==2 && qCache && nQCache==PoolSize && nQErr==0);
130     Vec2::updAs(&qCache[CosQ]) = Vec2(std::cos(q[0]),std::cos(q[1]));
131     Vec2::updAs(&qCache[SinQ]) = Vec2(std::sin(q[0]),std::sin(q[1]));
132 }
133 
134 // TODO: should use precalculated sin/cos but doesn't.
calcX_FM(const SBStateDigest & sbs,const Real * q,int nq,const Real * qCache,int nQCache,Transform & X_FM)135 void calcX_FM(const SBStateDigest& sbs,
136                 const Real* q,      int nq,
137                 const Real* qCache, int nQCache,
138                 Transform&  X_FM) const
139 {
140     assert(q && nq==2 && qCache && nQCache==PoolSize);
141     // TODO: use qCache
142     // We're only updating the orientation here because a U-joint
143     // can't translate. This is a body fixed X-Y sequence.
144     X_FM.updR() = Rotation(BodyRotationSequence, q[0], XAxis, q[1], YAxis);
145     X_FM.updP() = 0;
146 }
147 
148 // The generalized speeds for this 2-dof rotational joint are the time
149 // derivatives of the body-fixed 1-2 rotation sequence defining the
150 // orientation. That is, the first speed is just a rotation rate about Fx. The
151 // second is a rotation rate about the current My, so we have to transform it
152 // into F to make H_FM uniformly expressed in F.
calcAcrossJointVelocityJacobian(const SBStateDigest & sbs,HType & H_FM)153 void calcAcrossJointVelocityJacobian(
154     const SBStateDigest& sbs,
155     HType&               H_FM) const
156 {
157     // "upd" because we're realizing positions now
158     const SBTreePositionCache& pc = sbs.updTreePositionCache();
159     const Transform  X_F0M0 = this->findX_F0M0(pc);
160 
161     // Dropping the 0's here.
162     const Rotation& R_FM = X_F0M0.R();
163 
164     H_FM(0) = SpatialVec(  Vec3(1,0,0) , Vec3(0) );
165     H_FM(1) = SpatialVec(    R_FM.y()  , Vec3(0) );
166 }
167 
168 // Since the second row of the Jacobian H_FM above is not constant in F,
169 // its time derivative is non zero. Here we use the fact that for
170 // a vector r_B_A fixed in a moving frame B but expressed in another frame A,
171 // its time derivative in A is the angular velocity of B in A crossed with
172 // the vector, i.e., d_A/dt r_B_A = w_AB % r_B_A.
calcAcrossJointVelocityJacobianDot(const SBStateDigest & sbs,HType & HDot_FM)173 void calcAcrossJointVelocityJacobianDot(
174     const SBStateDigest& sbs,
175     HType&               HDot_FM) const
176 {
177     const SBTreePositionCache& pc = sbs.getTreePositionCache();
178     // "upd" because we're realizing velocities now
179     const SBTreeVelocityCache& vc = sbs.updTreeVelocityCache();
180 
181     const Transform  X_F0M0 = this->findX_F0M0(pc);
182 
183     // Dropping the 0's here.
184     const Rotation& R_FM = X_F0M0.R();
185     const Vec3      w_FM = this->find_w_F0M0(pc,vc); // angular velocity of M in F
186 
187     HDot_FM(0) = SpatialVec(     Vec3(0)     , Vec3(0) );
188     HDot_FM(1) = SpatialVec( w_FM % R_FM.y() , Vec3(0) );
189 }
190 
191 };
192 
193 
194 
195 
196 #endif // SimTK_SIMBODY_RIGID_BODY_NODE_SPEC_UNIVERSAL_H_
197 
198