Tekkotsu Homepage
Demos
Overview
Downloads
Dev. Resources
Reference
Credits

KinematicJoint.h

Go to the documentation of this file.
00001 //-*-c++-*-
00002 #ifndef INCLUDED_KinematicJoint_h_
00003 #define INCLUDED_KinematicJoint_h_
00004 
00005 #include "SensorInfo.h"
00006 
00007 #include "Shared/plist.h"
00008 #include "Shared/plistSpecialty.h"
00009 #include "Shared/fmatSpatial.h"
00010 #include "Shared/BoundingBox.h"
00011 
00012 #include "Planners/PlannerObstacles.h"
00013 
00014 #include <set>
00015 #include <algorithm>
00016 
00017 class KinematicJoint;
00018 class IKSolver;
00019 
00020 //! these let you build the link from a series of #model and #material settings instead of one static model/material for the whole link
00021 /*! note recursive structure... in this case, each is relative to its recursive parent instead of preceeding item in the array (as is done for KinematicJoint's serialization) */
00022 class LinkComponent : virtual public plist::Dictionary {
00023   friend class KinematicJoint;
00024   
00025 public:
00026   //! constructor
00027   LinkComponent()
00028     : plist::Dictionary(), mass(0), centerOfMass(), visible(true), material(), 
00029     model("CollisionModel"), modelScale(1,1,1), modelRotation(), modelOffset(),
00030     collisionModel(), collisionModelScale(1,1,1), collisionModelRotation(), collisionModelOffset(),
00031     parent(NULL),
00032     bbDirty(false), boundingBox(),
00033     collisionModelListener(collisionModel,*this,&LinkComponent::dirtyBB,false),
00034     collisionModelScaleListener(collisionModelScale,*this,&LinkComponent::dirtyBB,false),
00035     collisionModelRotationListener(collisionModelRotation,*this,&LinkComponent::dirtyBB,false),
00036     collisionModelOffsetListener(collisionModelOffset,*this,&LinkComponent::dirtyBB,false)
00037   {
00038     init();
00039   }
00040   
00041   //! explicit copy constructor needed to set bbLowListener and bbHighListener (which aren't copied)
00042   LinkComponent(const LinkComponent& c)
00043     : plist::Dictionary(), mass(c.mass), centerOfMass(c.centerOfMass), visible(c.visible), material(c.material), 
00044     model(c.model), modelScale(c.modelScale), modelRotation(c.modelRotation), modelOffset(c.modelOffset),
00045     collisionModel(c.collisionModel), collisionModelScale(c.collisionModelScale), collisionModelRotation(c.collisionModelRotation), collisionModelOffset(c.collisionModelOffset),
00046     parent(NULL),
00047     bbDirty(c.bbDirty), boundingBox(c.boundingBox), 
00048     collisionModelListener(collisionModel,*this,&LinkComponent::dirtyBB,false),
00049     collisionModelScaleListener(collisionModelScale,*this,&LinkComponent::dirtyBB,false),
00050     collisionModelRotationListener(collisionModelRotation,*this,&LinkComponent::dirtyBB,false),
00051     collisionModelOffsetListener(collisionModelOffset,*this,&LinkComponent::dirtyBB,false)
00052   {
00053     init();
00054   }
00055   
00056   //! assignment, except for parent
00057   LinkComponent& operator=(const LinkComponent& link) {
00058     if(&link==this)
00059       return *this;
00060     mass = link.mass;
00061     centerOfMass = link.centerOfMass;
00062     visible = link.visible;
00063     material = link.material;
00064     model = link.model;
00065     modelScale = link.modelScale;
00066     modelRotation = link.modelRotation;
00067     modelOffset = link.modelOffset;
00068     collisionModel = link.collisionModel;
00069     collisionModelScale = link.collisionModelScale;
00070     collisionModelRotation = link.collisionModelRotation;
00071     collisionModelOffset = link.collisionModelOffset;
00072     // note no parent copy
00073     bbDirty = link.bbDirty;
00074     boundingBox = link.boundingBox;
00075     return *this;
00076   }
00077   
00078   virtual ~LinkComponent() {}
00079   
00080   plist::Primitive<float> mass; //!< the mass of the component in kilograms
00081   plist::Point centerOfMass; //!< the center of mass of this component
00082   
00083   plist::Primitive<bool> visible; //!< if false, will only be shown in the Mirage host's flying camera
00084   
00085   plist::Primitive<std::string> material; //!< mesh files specify "default" textures, but this can override with a different material
00086   
00087   plist::Primitive<std::string> model; //!< name of an OGRE mesh file; if set to "CollisionModel" will display collisionModel (and apply values from #collisionModelScale, #collisionModelRotation, and #collisionModelOffset)
00088   plist::Point modelScale; //!< scaling factor to apply to #model
00089   plist::Point modelRotation; //!< rotation to apply to #model (specifies axis component of quaternion, e.g. from axis/angle: axis * sin(angle/2) )
00090   plist::Point modelOffset; //!< a translation to apply to #model
00091   
00092   plist::Primitive<std::string> collisionModel; //!< name of an OGRE mesh file, or one of a set of primitives (Cube, Cylinder, Sphere)
00093   plist::Point collisionModelScale; //!< scaling factor to apply to #collisionModel
00094   plist::Point collisionModelRotation; //!< rotation to apply to #collisionModel (specifies axis component of quaternion, e.g. from axis/angle: axis * sin(angle/2) )
00095   plist::Point collisionModelOffset; //!< a translation to apply to #collisionModel
00096   
00097   //! Returns a 2D planner obstacle of this component's collision object, or NULL if no collision model is specified
00098   /*! This is a new allocation, caller is responsible for deallocation. */
00099   PlannerObstacle2D* getObstacle(const fmat::Transform& worldT) const;
00100   
00101   //! Inserts 2D projections of this link's collision components into @a obs, if @a recurse is set, recurses on branches
00102   void getObstacles(const fmat::Transform& worldT, HierarchicalObstacle& obs, bool recurse) const;
00103   
00104   //! Returns the axis-aligned bounding box (relative to link frame) of this component only (i.e. for KinematicJoint, ignores subcomponents)
00105   virtual BoundingBox3D getOwnAABB() const { return getAABB(); }
00106   
00107   //! Returns the axis-aligned bounding box (relative to link frame) of this component and any subcomponents
00108   const BoundingBox3D& getAABB() const { if(bbDirty) updateBB(); return boundingBox; }
00109   
00110   //! Computes the tightest-fitting 2D rectangle for this link (writing the result into @a ro).
00111   /*! @ro will be in the world coordinates, projecting the link collision model down the world's Z into its XY plane. */
00112   bool getOwnBB2D(const fmat::Transform& worldT, RectangularObstacle& ro) const;
00113     
00114     //! Computes a 3D box for this link (writing the result into @a bo).
00115   /*! @ro will be in the world coordinates */
00116   bool getOwnBB3D(const fmat::Transform& worldT, BoxObstacle& bo) const;
00117   
00118   //! Computes a 2D rectangle for this link and its subcomponents (writing the result into @a ro).
00119   /*! @ro will be in the world coordinates, projecting the link collision model down the world's Z into its XY plane.
00120    The rectangle will be aligned to the link axis (i.e. this is getAABB() projected into the world.) */
00121   bool getBB2D(const fmat::Transform& worldT, RectangularObstacle& ro) const;
00122     
00123     //! Computes a 3D box for this link and its subcomponents (writing the result into @a bo).
00124   /*! @ro will be in the world coordinates.
00125    The rectangle will be aligned to the link axis (i.e. this is getAABB() projected into the world.) */
00126   bool getBB3D(const fmat::Transform& worldT, BoxObstacle& bo) const;
00127   
00128   //! return the center of mass of this component, ONLY, as a homogenous scaled vector
00129   /*! The last element (homogeneous scale factor) is left as the total mass, so divide by this value to normalize.
00130    *  Otherwise, just access #centerOfMass directly. */
00131   fmat::Column<4> getMassVector() const { return fmat::pack(mass*centerOfMass[0],mass*centerOfMass[1],mass*centerOfMass[2],mass); }
00132   
00133   //! returns the center of mass of this link only, not including any branches; if this is actually a KinematicJoint, will include subcomponents
00134   virtual void sumLinkCenterOfMass(fmat::Column<3>& cOfM, float& totalMass) const;
00135   
00136   //! returns the unnormalized center of mass of this link only, not including any branches; if this is actually a KinematicJoint, will include subcomponents
00137   /*! The last element (homogeneous scale factor) is left as the total mass, so divide by this value to normalize. */
00138   virtual fmat::Column<4> sumLinkCenterOfMass() const { return getMassVector(); }
00139   
00140   //! returns true if #mass is greater than zero, of if this is a KinematicJoint, if any of the components have mass greater than zero
00141   virtual bool hasMass() const { return mass>0; }
00142   
00143   //! converts the model rotation and offset members into the supplied fmat::Transform
00144   void getModelTransform(fmat::Transform& tr) const;
00145   
00146   //! returns the model rotation and offset members as a fmat::Transform
00147   fmat::Transform getModelTransform() const {
00148     fmat::Transform tr; getModelTransform(tr); return tr;
00149   }
00150   
00151   //! converts the collision model rotation and offset members into the supplied fmat::Transform
00152   void getCollisionModelTransform(fmat::Transform& tr) const;
00153   
00154   //! returns the model rotation and offset members as a fmat::Transform
00155   fmat::Transform getCollisionModelTransform() const {
00156     fmat::Transform tr; getCollisionModelTransform(tr); return tr;
00157   }
00158   
00159   //! returns #parent joint, or NULL if this is the first joint, see addBranch()/removeBranch()
00160   KinematicJoint* getParent() const { return parent; }
00161   
00162   //! returns the next mobile ancestor joint in the kinematic chain
00163   KinematicJoint* getNextMobileAncestor() const;
00164   
00165   PLIST_CLONE_DEF(LinkComponent,new LinkComponent(*this));
00166   
00167 protected:
00168   KinematicJoint* parent; //!< if non-NULL, the parent joint to which this one is attached
00169   
00170   mutable bool bbDirty; //!< indicates bounding boxes need to be recomputed
00171   mutable BoundingBox3D boundingBox; //!< bounding box of this link (including subcomponents, but not child links)
00172   
00173   virtual void dirtyBB(); //!< sets #bbDirty to true to cause it to be recomputed on next getAABB() call
00174   virtual void updateBB() const; //!< recomputes #boundingBoxLow and #boundingBoxHigh based on collision model parameters
00175   void computeOwnAABB(BoundingBox3D& bb) const;
00176   static void computeBB2D(const fmat::Transform& fullT, RectangularObstacle& ro, const fmat::Column<3>& obD);
00177   
00178   void init() {
00179     addEntry("Mass",mass,"Mass of the component, in kilograms.  If 0, indicates static object, does not move but can still collide. (default 0)");
00180     addEntry("CenterOfMass",centerOfMass,"Position of average mass relative to parent frame.");
00181     addEntry("Visible",visible,"If true, indicates component should be rendered for simulated cameras, otherwise only appears in the Mirage user window. (default true)");
00182     addEntry("Material",material,"Name of an Ogre material, found in mirage/media/*.material files.");
00183     addEntry("Model",model,"An Ogre .mesh file, or \"CollisionModel\" to render the collision primitive. (default CollisionModel)");
00184     addEntry("ModelScale",modelScale,"Scales the graphics mesh loaded by Model.");
00185     addEntry("ModelRotation",modelRotation,"Rotates the graphics mesh loaded by Model, relative to parent frame. (supply axis component of quaternion, e.g. from axis/angle: axis * sin(angle/2) )");
00186     addEntry("ModelOffset",modelOffset,"Positions the graphics mesh loaded by Model, relative to parent frame.");
00187     addEntry("CollisionModel",collisionModel,"A Bullet primitive collision shape: { Cube | Cylinder | Sphere | Plane }");
00188     addEntry("CollisionModelScale",collisionModelScale,"Scales the CollisionModel, which by default is 1x1x1, so this sets the object dimensions.");
00189     addEntry("CollisionModelRotation",collisionModelRotation,"Rotates the CollisionModel relative to parent frame. (supply axis component of quaternion, e.g. from axis/angle: axis * sin(angle/2) )");
00190     addEntry("CollisionModelOffset",collisionModelOffset,"Positions the CollisionModel, relative to parent frame.");
00191     setLoadSavePolicy(FIXED,SYNC);
00192   }
00193 
00194   // listener callbacks should go last to ensure they are activated after members are initialized, and deactivated before they are destructed
00195   plist::PrimitiveCallbackMember<LinkComponent> collisionModelListener; //!< if going from empty string to non-empty string, indicates bounding box values need to be rebuilt
00196   plist::CollectionCallbackMember<LinkComponent> collisionModelScaleListener; //!< indicates bounding box values need to be rebuilt
00197   plist::CollectionCallbackMember<LinkComponent> collisionModelRotationListener; //!< indicates bounding box values need to be rebuilt
00198   plist::CollectionCallbackMember<LinkComponent> collisionModelOffsetListener; //!< indicates bounding box values need to be rebuilt
00199 };
00200 
00201 
00202 //! Manages parameters which define the position and type of motion produced by an actuator (i.e. forward kinematics)
00203 class KinematicJoint : public virtual plist::PrimitiveListener, public LinkComponent {
00204   friend class KinematicJointLoader;
00205   friend class KinematicJointSaver;
00206 public:
00207   class BranchListener : public plist::Listener {
00208   public:
00209     virtual void kinematicJointBranchAdded(KinematicJoint& parent, KinematicJoint& branch) {};
00210     virtual void kinematicJointBranchRemoved(KinematicJoint& parent, KinematicJoint& branch) {};
00211     virtual void kinematicJointReconfigured(KinematicJoint& joint) {};
00212   };
00213   
00214   //! constructor
00215   KinematicJoint() : plist::Dictionary(), LinkComponent(), 
00216     jointType(REVOLUTE, jointTypeNames), theta(0), d(0), alpha(0), r(0), qOffset(0), qmin(0), qmax(0),
00217     components(), frictionForce(0.5f), anistropicFrictionRatio(1,1,1), ikSolver(), sensorInfo(), controllerInfo(), outputOffset(),
00218     branches(), branchListeners(NULL), depth(0), q(0), To(), Tq(), ik(NULL), componentsListener(components,*this)
00219   {
00220     initEntries();
00221   }
00222   //! copy constructor (deep copy, but doesn't register with parent)
00223   KinematicJoint(const KinematicJoint& kj) : plist::Dictionary(), LinkComponent(kj), 
00224     jointType(kj.jointType), theta(kj.theta), d(kj.d), alpha(kj.alpha), r(kj.r), qOffset(kj.qOffset), qmin(kj.qmin), qmax(kj.qmax),
00225     components(kj.components), frictionForce(kj.frictionForce), anistropicFrictionRatio(kj.anistropicFrictionRatio), ikSolver(kj.ikSolver), sensorInfo(kj.sensorInfo), controllerInfo(kj.controllerInfo), outputOffset(kj.outputOffset),
00226     branches(), branchListeners(NULL), depth(0), q(kj.q), To(kj.To), Tq(kj.Tq), ik(NULL), componentsListener(components,*this)
00227   {
00228     initEntries();
00229     copyBranches(kj);
00230   }
00231   //! deep copy via copyBranches(), replaces current branches.  This does not affect listener list (other than calling them), the parent or depth values
00232   KinematicJoint& operator=(const KinematicJoint& kj) {
00233     shallowCopy(kj);
00234     copyBranches(kj);
00235     return *this;
00236   }
00237   //! copies all parameters from @a kj, @e except listeners, depth, parent or children; see copyBranches()
00238   void shallowCopy(const KinematicJoint& kj) {
00239     if(&kj==this)
00240       return;
00241     LinkComponent::operator=(kj);
00242     jointType = kj.jointType;
00243     theta = kj.theta;
00244     d = kj.d;
00245     alpha = kj.alpha;
00246     r = kj.r;
00247     qOffset = kj.qOffset;
00248     qmin = kj.qmin;
00249     qmax = kj.qmax;
00250     components = kj.components;
00251     frictionForce=kj.frictionForce;
00252     anistropicFrictionRatio=kj.anistropicFrictionRatio;
00253     ikSolver = kj.ikSolver;
00254     sensorInfo = kj.sensorInfo;
00255     controllerInfo = kj.controllerInfo;
00256     outputOffset = kj.outputOffset;
00257     q = kj.q;
00258     To = kj.To;
00259     Tq = kj.Tq;
00260     fireReconfigured();
00261     // ** note what is NOT copied: **
00262     //branchListeners = kj.branchListeners;
00263     //parent = kj.parent;
00264     //depth = kj.depth;
00265     // copyBranches();
00266   }
00267   //! releases current children and clones those from @kj; does not copy parameters, see shallowCopy()
00268   void copyBranches(const KinematicJoint& kj) {
00269     if(&kj==this)
00270       return;
00271     // watch out for assigning a sub-link to an ancestor, make copies of all sub-branches from source, then clear current branches, then add previously created clones as new branches
00272     std::set<KinematicJoint*> newBranches;
00273     for(std::set<KinematicJoint*>::const_iterator it=kj.branches.begin(); it!=kj.branches.end(); ++it)
00274       newBranches.insert(new KinematicJoint(**it));
00275     clearBranches();
00276     for(std::set<KinematicJoint*>::const_iterator it=newBranches.begin(); it!=newBranches.end(); ++it)
00277       addBranch(*it);
00278   }
00279   //! destructor, recursively destroys sub-tree
00280   virtual ~KinematicJoint();
00281   
00282   //! types of joints which are supported
00283   enum JointType_t {
00284     REVOLUTE, //!< a joint which rotates around the z-axis
00285     PRISMATIC //!< a joint which slides along the z-axis
00286   };
00287   //! provides human readable names for JointType_t, for loading/saving #jointType (null terminated)
00288   static const char* jointTypeNames[3];
00289   //! the type of motion produced by this joint
00290   plist::NamedEnumeration<JointType_t> jointType;
00291   
00292   // parameters in the order they are applied to get to this joint's frame
00293   plist::Angle theta; //!< rotation about the previous joint's z-axis to point its x-axis parallel to the common normal with this joint's z-axis (aka "Modified Denavit-Hartenberg")
00294   plist::Primitive<fmat::fmatReal> d; //!< translation along the previous joint's z-axis to align its x-axis with the common normal with this joint's z-axis (aka "Modified Denavit-Hartenberg")
00295   plist::Angle alpha; //!< rotation about this joint's x-axis to make the previous joint's z-axis parallel to this one (aka "Modified Denavit-Hartenberg")
00296   plist::Primitive<fmat::fmatReal> r; //!< translation from the previous joint's z-axis along the common normal to place this joint's origin (aka "Modified Denavit-Hartenberg")
00297   plist::Angle qOffset; //!< a constant offset to the #q setting to define the physical zero-point of the joint
00298   
00299   plist::Angle qmin; //!< indicates the minimum q value which inverse kinematics may provide (if equal to #qmax, the joint is considered immobile)
00300   plist::Angle qmax; //!< indicates the maximum q value which inverse kinematics may provide (if equal to #qmin, the joint is considered immobile)
00301   
00302   //! a list of link features, for simple display or collision models with more than one primitive
00303   plist::ArrayOf<LinkComponent> components;
00304   typedef plist::ArrayOf<LinkComponent>::const_iterator component_iterator; //!< for convenience when looping over #components
00305   
00306   plist::Primitive<float> frictionForce; //!< conversion from velocity to friction force (default 0.5)
00307   plist::Point anistropicFrictionRatio; //!< direction sensitivity of friction, '1' in all directions means it is not direction sensitive
00308     
00309   plist::Primitive<std::string> ikSolver; //!< specifies the name of the inverse kinematics solver to use with this appendage
00310   
00311   plist::ArrayOf<SensorInfo> sensorInfo; //!< Stores sensor parameters for simulated input, type indicated by SensorType entry corresponding to a SensorInfo subclass
00312   
00313   //! Parameters for joint motion model, used for simulation
00314   /*! You should probably sync entries made here with entries in Planners/Dynamics/MotorController */
00315   struct ControllerInfo : virtual public plist::Dictionary {
00316     explicit ControllerInfo() : plist::Dictionary(), velocity(false), forceControl(false) { init(); } //!< constructor
00317     ControllerInfo(const ControllerInfo& ci) : plist::Dictionary(), velocity(ci.velocity), forceControl(ci.forceControl) { init(); } //!< copy constructor for cloning
00318     plist::Primitive<bool> velocity; //!< Adjusts interpretation of feedback and application of friction, false implies position control
00319     plist::Primitive<bool> forceControl; //!< If true, simulation will use force control to move the joint rather than using position constraints.  Grippers should set this to true for more realistic object interaction.
00320   protected:
00321     //! performs common initialization
00322     void init() {
00323       addEntry("Velocity",velocity,"Adjusts interpretation of feedback and application of friction, false implies position control");
00324       addEntry("ForceControl",forceControl,"If true, simulation will use force control to move the joint rather than using position constraints.  Grippers should set this to true for more realistic object interaction.");
00325     }
00326   };
00327   ControllerInfo controllerInfo; //!< Stores controller parameters for simulated input, type indicated by ControllerType entry corresponding to a ControllerInfo subclass
00328   
00329   
00330   plist::OutputSelector outputOffset; //!< name or index of the joint this maps to so we can integrate with Tekkotsu's flat arrays
00331   
00332   
00333   void getObstacles(const fmat::Transform& worldT, class HierarchicalObstacle& obs, bool recurse) const;
00334   
00335   //! Returns the axis-aligned bounding box (relative to link frame) of this component only (i.e. KinematicJoint ignores subcomponents)
00336   /*! KinematicJoint uses cache members to store the expanded subcomponent BB, so must recompute the local BB each time */
00337   virtual BoundingBox3D getOwnAABB() const { BoundingBox3D bb; computeOwnAABB(bb); return bb; }
00338   
00339   //! returns the current #q value
00340   float getQ() const { return q; }
00341   
00342   //! sets the current joint position (#q) (ignoring #qmin, #qmax) and updates the transformation matrix (if #q has changed)
00343   void setQ(float x) { if(x!=q) { q=x; updateTq(); } }
00344 
00345   //! sets the current joint position (#q) to x, clipped to [#qmin,#qmax], returning true if in range
00346   bool tryQ(float x);
00347   
00348   //! sets the joint position, and #qmin and #qmax as well to prevent inverse kinematics from using it
00349   void freezeQ(float x) { qmin=qmax=x; setQ(x); }
00350   
00351   //! returns true if x is within [#qmin,#qmax] (inclusive)
00352   bool validQ(float x) const { return (x>=qmin && x<=qmax); }
00353   
00354   //! sets the joint position and its childrens' positions from a flat array, using #outputOffset
00355   /*! @a deoffset will be subtracted from each index, and the result only added to the map if it (as unsigned int) is less than @a max.
00356    *  This is intended to allow you to extract the joint values for only a region of outputs in use, e.g. values[NumArmJoints], offset=ArmOffset and max=NumArmJoints */
00357   template<class M> void pushChildrenQIntoArray(M& values, int deoffset, unsigned int max) const {
00358     if(outputOffset!=plist::OutputSelector::UNUSED && static_cast<unsigned int>(outputOffset-deoffset)<max)
00359       values[outputOffset-deoffset]=q;
00360     for(std::set<KinematicJoint*>::const_iterator it=branches.begin(); it!=branches.end(); ++it)
00361       (*it)->pushChildrenQIntoArray(values, deoffset, max);
00362   }   
00363   
00364   //! sets the joint position and its ancestors' positions from a flat array, using #outputOffset
00365   /*! @a deoffset will be subtracted from each index, and the result only added to the map if it (as unsigned int) is less than @a max.
00366    *  This is intended to allow you to extract the joint values for only a region of outputs in use, e.g. values[NumArmJoints], offset=ArmOffset and max=NumArmJoints */
00367   template<class M> void pushAncestorsQIntoArray(M& values, int deoffset, unsigned int max) const {
00368     if(outputOffset!=plist::OutputSelector::UNUSED && static_cast<unsigned int>(outputOffset-deoffset)<max)
00369       values[outputOffset-deoffset]=q;
00370     if(parent!=NULL)
00371       parent->pushAncestorsQIntoArray(values, deoffset, max);
00372   }
00373   
00374   //! sets the joint position and its childrens' positions from a flat array, using #outputOffset
00375   /*! @a deoffset will be subtracted from each index, and the result only added to the map if it (as unsigned int) is less than @a max.
00376    *  This is intended to allow you to extract the joint values for only a region of outputs in use, e.g. values[NumArmJoints], offset=ArmOffset and max=NumArmJoints */
00377   template<class M> void pullChildrenQFromArray(M& values, int deoffset, unsigned int max) {
00378     if(outputOffset!=plist::OutputSelector::UNUSED && qmin!=qmax && static_cast<unsigned int>(outputOffset-deoffset)<max)
00379       setQ(values[outputOffset-deoffset]);
00380     for(std::set<KinematicJoint*>::const_iterator it=branches.begin(); it!=branches.end(); ++it)
00381       (*it)->pullChildrenQFromArray(values, deoffset, max);
00382   }
00383   
00384   //! sets the joint position and its ancestors' positions from a flat array, using #outputOffset
00385   /*! @a deoffset will be subtracted from each index, and the result only added to the map if it (as unsigned int) is less than @a max.
00386    *  This is intended to allow you to extract the joint values for only a region of outputs in use, e.g. values[NumArmJoints], offset=ArmOffset and max=NumArmJoints */
00387   template<class M> void pullAncestorsQFromArray(M& values, int deoffset, unsigned int max) {
00388     if(outputOffset!=plist::OutputSelector::UNUSED && qmin!=qmax && static_cast<unsigned int>(outputOffset-deoffset)<max)
00389       setQ(values[outputOffset-deoffset]);
00390     if(parent!=NULL)
00391       parent->pullAncestorsQFromArray(values, deoffset, max);
00392   }   
00393   
00394   //! sets the joint position and its childrens' to zero
00395   void zeroChildrenQ();
00396   
00397   //! sets the joint position and its ancestors' to zero
00398   void zeroAncestorQ();
00399   
00400   
00401   //! returns the tranformation matrix which converts from the link's reference frame (i.e. the frame which moves with the joint's #q) to the base frame
00402   fmat::Transform getFullT() const;
00403   
00404   //! returns the tranformation matrix which converts from the base frame to the link's reference frame (i.e. the frame which moves with the joint's #q)
00405   fmat::Transform getFullInvT() const { return getFullT().rigidInverse(); }
00406   
00407   //! returns the tranformation matrix which converts from the link's reference frame (i.e. the frame which moves with the joint's #q) to the specified joint's link frame
00408   fmat::Transform getT(const KinematicJoint& j) const;
00409   
00410   //! returns the tranformation matrix which converts from the joint's reference frame (i.e. the frame in which the link moves) to the parent's frame
00411   /*! This transformation is constant in terms of #q. */
00412   const fmat::Transform& getTo() const { return To; }
00413   
00414   //! returns the tranformation matrix which converts from the link's reference frame (i.e. the frame which moves with the joint's #q) to the parent's frame
00415   /*! This transformation is changes with #q. */
00416   const fmat::Transform& getTq() const { return Tq; }
00417   
00418   //! returns the center of mass of this link and all of its branches, given their current positions, resulting position relative to this link
00419   void sumCenterOfMass(fmat::Column<3>& cOfM, float& totalMass) const;
00420   
00421   //! returns the unnormalized center of mass of this link and all of its branches, given their current positions, relative to this link
00422   /*! The last element (homogeneous scale factor) is left as the total mass, so divide by this value to normalize. */
00423   fmat::Column<4> sumCenterOfMass() const;
00424   
00425   //! returns the unnormalized center of mass of this link only, not including any branches
00426   /*! The last element (homogeneous scale factor) is left as the total mass, so divide by this value to normalize. */
00427   virtual fmat::Column<4> sumLinkCenterOfMass() const;
00428   
00429   using LinkComponent::sumLinkCenterOfMass;
00430   
00431   virtual bool hasMass() const;
00432   
00433   //! returns a column vector indicating the joint's motion in base coordinates, at a point @a p (in base coordinates)
00434   /*! The first 3 rows indicate the ∂x/∂q, ∂y/∂q, and ∂z/∂q values.
00435    *  The last 3 rows indicate the axis of rotation (or all 0's for a prismatic joint) */
00436   fmat::Column<6> getJointJacobian(const fmat::Column<3>& p) const;
00437   
00438   //! computes the motion of this joint and all of its ancestors at a point @a p (in base coordinates), @a j should be empty on call, may wish to reserve #depth+1 
00439   /*! The first 3 rows indicate the ∂x, ∂y, and ∂z values per ∂q corresponding to each column.
00440    *  The last 3 rows indicate the axis of rotation for each joint (or all 0's for prismatic joints) */
00441   void getFullJacobian(const fmat::Column<3>& p, std::vector<fmat::Column<6> >& j) const {
00442     if(parent!=NULL)
00443       parent->getFullJacobian(p,j);
00444     j.push_back(getJointJacobian(p));
00445   }
00446 
00447   //! computes the motion of this joint and all of its ancestors at a point @a p (in base coordinates), @a j should be empty on call, may wish to reserve #depth+1 
00448   /*! The first 3 rows indicate the ∂x, ∂y, and ∂z values per ∂q corresponding to each column.
00449    *  The last 3 rows indicate the axis of rotation for each joint (or all 0's for prismatic joints) */
00450   void getMobileJacobian(const fmat::Column<3>& p, std::vector<fmat::Column<6> >& j) const {
00451     if(parent!=NULL)
00452       parent->getMobileJacobian(p,j);
00453     if(isMobile())
00454       j.push_back(getJointJacobian(p));
00455   }
00456   
00457   //! returns the position of this joint in base coordinates (as a length 3 vector)
00458   fmat::Column<3> getWorldPosition() const { return getFullT().translation(); }
00459   //! returns the orientation of this joint in base coordinates (as a 3x3 matrix)
00460   fmat::Matrix<3,3> getWorldRotation() const { return getFullT().rotation(); }
00461   
00462   //! returns the position of this joint relative to its parent's link frame
00463   fmat::Column<3> getPosition() const { return Tq.translation(); }
00464   //! returns the position of the parent origin relative to this joint's link frame
00465   fmat::Column<3> getParentPosition() const;
00466   //! returns the orientation of this joint relative to its parent's link frame (as a 3x3 matrix)
00467   fmat::Matrix<3,3> getRotation() const { return Tq.rotation(); }
00468   
00469   //! returns the orientation of this joint in base coordinates (as a quaternion (w,x,y,z))
00470   fmat::Quaternion getWorldQuaternion() const;
00471   //! returns the orientation of this joint in base coordinates (as a quaternion (w,x,y,z))
00472   fmat::Quaternion getQuaternion(const KinematicJoint& j) const;
00473   //! returns the orientation of this joint relative to its parent's link frame (as a quaternion (w,x,y,z))
00474   fmat::Quaternion getQuaternion() const { return fmat::Quaternion::fromMatrix(Tq.rotation()); }
00475   
00476   //! if the node is an array, loads the sub-tree, otherwise expects a dict to load the joint's own parameters
00477   virtual void loadXML(xmlNode* node);
00478   //! if the node is NULL, has no parent, or is a plist node, writes human-readable help text as a comment and then recurses through the subtrees (otherwise saves only this joint's parameters)
00479   virtual void saveXML(xmlNode* node, bool onlyOverwrite, std::set<std::string>& seen) const;
00480   using plist::Dictionary::saveXML;
00481   
00482   
00483   //! builds a map from output offsets to this and child joints (call it on the root node –– doesn't handle ancestors
00484   /*! @a deoffset will be subtracted from each index, and the result only added to the map if it (as unsigned int) is less than @a max.
00485    *  This is intended to allow you to extract the mapping for only a region of outputs in use, e.g. childMap[NumArmJoints], offset=ArmOffset and max=NumArmJoints */
00486   template<class M> void buildChildMap(M& childMap, int deoffset, unsigned int max) {
00487     if(outputOffset!=plist::OutputSelector::UNUSED && static_cast<unsigned int>(outputOffset-deoffset)<max && !childMap[outputOffset-deoffset])
00488       childMap[outputOffset-deoffset] = this;
00489     for(std::set<KinematicJoint*>::const_iterator it=branches.begin(); it!=branches.end(); ++it)
00490       (*it)->buildChildMap(childMap,deoffset,max);
00491   }
00492   
00493   //! builds a map from output offsets to this and child joints (call it on the root node –– doesn't handle ancestors
00494   /*! @a deoffset will be subtracted from each index, and the result only added to the map if it (as unsigned int) is less than @a max.
00495    *  This is intended to allow you to extract the mapping for only a region of outputs in use, e.g. childMap[NumArmJoints], offset=ArmOffset and max=NumArmJoints */
00496   template<class M> void buildChildMap(M& childMap, int deoffset, unsigned int max) const {
00497     if(outputOffset!=plist::OutputSelector::UNUSED && static_cast<unsigned int>(outputOffset-deoffset)<max && !childMap[outputOffset-deoffset])
00498       childMap[outputOffset-deoffset] = this;
00499     for(std::set<KinematicJoint*>::const_iterator it=branches.begin(); it!=branches.end(); ++it)
00500       (*it)->buildChildMap(childMap,deoffset,max);
00501   }
00502   
00503   //! builds a map from output offsets to this and child joints (call it on the root node –– doesn't handle ancestors
00504   /*! @a deoffset will be subtracted from each index, and the result only added to the map if it (as unsigned int) is less than @a max.
00505    *  This is intended to allow you to extract the mapping for only a region of outputs in use, e.g. childMap[NumArmJoints], offset=ArmOffset and max=NumArmJoints */
00506   template<class M> void buildMobileChildMap(M& childMap, int deoffset, unsigned int max) {
00507     if(isMobile() && outputOffset!=plist::OutputSelector::UNUSED && static_cast<unsigned int>(outputOffset-deoffset)<max)
00508       childMap[outputOffset-deoffset] = this;
00509     for(std::set<KinematicJoint*>::const_iterator it=branches.begin(); it!=branches.end(); ++it)
00510       (*it)->buildMobileChildMap(childMap,deoffset,max);
00511   }
00512   
00513   //! builds a map from output offsets to this and child joints (call it on the root node –– doesn't handle ancestors
00514   /*! @a deoffset will be subtracted from each index, and the result only added to the map if it (as unsigned int) is less than @a max.
00515    *  This is intended to allow you to extract the mapping for only a region of outputs in use, e.g. childMap[NumArmJoints], offset=ArmOffset and max=NumArmJoints */
00516   template<class M> void buildMobileChildMap(M& childMap, int deoffset, unsigned int max) const {
00517     if(isMobile() && outputOffset!=plist::OutputSelector::UNUSED && static_cast<unsigned int>(outputOffset-deoffset)<max)
00518       childMap[outputOffset-deoffset] = this;
00519     for(std::set<KinematicJoint*>::const_iterator it=branches.begin(); it!=branches.end(); ++it)
00520       (*it)->buildMobileChildMap(childMap,deoffset,max);
00521   }
00522   
00523   //! type of iterators over sub-trees
00524   typedef std::set<KinematicJoint*>::const_iterator branch_iterator;
00525   
00526   //! returns a reference to #branches so you can iterator over sub-trees
00527   const std::set<KinematicJoint*>& getBranches() const { return branches; }
00528   
00529   //! adds the joint to the #branches, control over memory (de)allocation is assumed by this instance until/unless removeBranch() is called
00530   /*! For convenience, returns @a b */
00531   KinematicJoint* addBranch(KinematicJoint* b);
00532   
00533   //! removes the specified joint from #branches, returning control of (de)allocation of @a b to caller.
00534   /*! For convenience, returns @a b */
00535   KinematicJoint* removeBranch(KinematicJoint* b);
00536   
00537   //! deletes all entries of #branches en masse
00538   void clearBranches();
00539   
00540   //! returns true if the link following this joint has more than one additional joint attached (i.e. if #branches.size() > 1)
00541   bool isBranch() const { return branches.size()>1; }
00542   
00543   //! Clones joints from this back to and including the root, not cloning side branches; returns a pointer to the @e last (i.e. leaf) joint in the cloned branch.
00544   /*! To clone a joint and all its children (instead of its direct ancestors as is done here), use the copy constructor or operator=.
00545     *  Just use getRoot() on the result if you want to store the root joint instead of the end joint. */
00546   KinematicJoint* cloneBranch() const;
00547   
00548   //! returns the 'first' element of #branches, or NULL if this is a leaf
00549   /*! The choice of 'first' is arbitrary (and not necessarily consistent run-to-run), you should not use this if you expect to encounter branching chains */
00550   KinematicJoint* nextJoint() const { return *branches.begin(); }
00551   
00552   //! returns true if qmin≠qmax (i.e. the joint is considered "mobile", and not just a abstract reference frame)
00553   bool isMobile() const { return qmin!=qmax; }
00554   
00555   //! returns root joint (will return 'this' if this is the first joint), see addBranch()/removeBranch()
00556   KinematicJoint* getRoot() { KinematicJoint* kj=this; while(kj->parent!=NULL) kj=kj->parent; return kj; }
00557   //! returns root joint (will return 'this' if this is the first joint), see addBranch()/removeBranch()
00558   const KinematicJoint* getRoot() const { const KinematicJoint* kj=this; while(kj->parent!=NULL) kj=kj->parent; return kj; }
00559   //! returns a string containing the names of all ancestors' outputOffset values separated by @a sep
00560   std::string getPath(const std::string& sep = "/") const { return (parent==NULL ? std::string() : parent->getPath(sep)) + sep + outputOffset.get(); }
00561   //! returns 0 if this is the root (no parent), otherwise one plus the parent's depth
00562   size_t getDepth() const { return depth; }
00563   
00564   void addBranchListener(BranchListener* l) const;
00565   void removeBranchListener(BranchListener* l) const;
00566   bool isBranchListener(BranchListener* l) const;
00567   
00568   //! returns an IKSolver instance, which will be cached internally for future accesses
00569   IKSolver& getIK() const;
00570   
00571   PLIST_CLONE_DEF(KinematicJoint,new KinematicJoint(*this));
00572   
00573 protected:
00574   //! add the plist entries to the dictionary superclass
00575   void initEntries() {
00576     // Since we're going to have a lot of KinematicJoints listed together, we're not going to add
00577     // a description comment here, which would be repeated for each instance.
00578     // Instead, a single comment will be dumped at the beginning of the array of joints.
00579     addEntry("JointType",jointType);
00580     addEntry("θ",theta); // aka \u03B8, but we use the character literal because we explicitly want UTF-8 in the file output
00581     addEntry("d",d);
00582     addEntry("α",alpha); // aka \u03B1, but we use the character literal because we explicitly want UTF-8 in the file output
00583     addEntry("r",r);
00584     addEntry("qOffset",qOffset);
00585     addEntry("Min",qmin);
00586     addEntry("Max",qmax);
00587     addEntry("Components",components);
00588     addEntry("FrictionForce",frictionForce);
00589     addEntry("AnistropicFrictionRatio",anistropicFrictionRatio);
00590     addEntry("IKSolver",ikSolver);
00591     addEntry("SensorInfo",sensorInfo);
00592     addEntry("ControllerInfo",controllerInfo);
00593     addEntry("Name",outputOffset);
00594     outputOffset.setRange(0,-1U); // accept anything (i.e. reference frames are fine)
00595     addSelfListener();
00596     setLoadSavePolicy(FIXED,SYNC);
00597   }
00598   
00599   //! recursively computes the full transformation matrix, converting from this joint's frame to the specified joint's frame, or the base frame if @end is NULL
00600   /*! @a t does not need to be initialized to anything prior to call, but will be 4x4 on return \n
00601     *  @a endj @e must be an ancestor of this joint or the function will segfault (NULL is the ancestor of the root, so that's valid) */
00602   void getFullT(fmat::Transform& t, const KinematicJoint* endj) const {
00603     if(parent!=endj) {
00604       parent->getFullT(t,endj);
00605       t*=Tq;
00606     } else {
00607       t=Tq;
00608     }
00609   }
00610   void getQuaternion(fmat::Quaternion& quat, const KinematicJoint* endj) const {
00611     if(parent!=endj) {
00612       parent->getQuaternion(quat,endj);
00613       quat *= getQuaternion();
00614     } else {
00615       quat = getQuaternion();
00616     }
00617   }
00618   
00619   //! shorthand for saving a specific node, forwarding call to saveXMLNode()
00620   void doSaveXMLNode(std::set<std::string>& seen, xmlNode* node, const std::string& key, const std::string& indentStr, size_t longestKeyLen) const {
00621     const_iterator it=findEntry(key);
00622     if(it==end())
00623       return;
00624     saveXMLNode(node, key, it->second, indentStr, longestKeyLen);
00625     //seen.insert(key);
00626   }
00627   virtual void plistValueChanged(const plist::PrimitiveBase& pl);
00628   virtual void addSelfListener(); //!< subscribes the instance to be notified of changes to its public plist::Primitive members, and then calls updateTo()
00629   virtual void removeSelfListener(); //!< unsubscribes the instance from its public plist::Primitive members
00630   void updateTo(); //!< regenerates #To from the a, d, alpha, and theta parameters, includes call to updateTq() as well
00631   void updateTq(); //!< updates #Tq from the q and qOffset parameters (based on current #To)
00632   virtual void updateBB() const;
00633   void updateDepth() {
00634     if(parent==NULL)
00635       depth=0;
00636     else
00637       depth = parent->depth+1;
00638     std::for_each(branches.begin(), branches.end(), std::mem_fun(&KinematicJoint::updateDepth));
00639   }
00640   
00641   //! sub-chains of joints, supporting branches but not cycles (nor NULL entries)
00642   std::set<KinematicJoint*> branches;
00643   
00644   void fireBranchAdded(KinematicJoint& val);
00645   void fireBranchRemoved(KinematicJoint& val);
00646   void fireReconfigured();
00647   mutable std::set<BranchListener*>* branchListeners;
00648   
00649   size_t depth; //!< one plus #parent's depth, or 0 if parentless
00650   
00651   fmat::fmatReal q; //!< current joint position (radian rotation about z if revolute, displacement along z if prismatic)
00652   fmat::Transform To; //!< transformation to the joint's origin
00653   fmat::Transform Tq; //!< transformation to origin, including final q rotation
00654   mutable IKSolver * ik; //!< an instance of the IKSolver corresponding to #ikSolver
00655 
00656   void setParent(LinkComponent& link) { link.parent = this; dirtyBB(); } //!< for use by ComponentsListener, work around #parent being protected
00657   void unsetParent(LinkComponent& link) { link.parent = NULL; dirtyBB(); } //!< for use by ComponentsListener, work around #parent being protected
00658   
00659   class ComponentsListener : protected plist::CollectionListener {
00660   public:
00661     ComponentsListener(const plist::ArrayOf<LinkComponent>& source, KinematicJoint& kj) : plist::CollectionListener(), comps(source), parent(kj) {
00662       comps.addCollectionListener(this);
00663     }
00664     ~ComponentsListener() { comps.removeCollectionListener(this); }
00665   protected:
00666     virtual void plistCollectionEntryAdded(plist::Collection& /*col*/, ObjectBase& primitive) { parent.setParent(dynamic_cast<LinkComponent&>(primitive)); }
00667     virtual void plistCollectionEntryRemoved(plist::Collection& /*col*/, ObjectBase& primitive) { parent.unsetParent(dynamic_cast<LinkComponent&>(primitive)); }
00668     virtual void plistCollectionEntriesChanged(plist::Collection& /*col*/) {
00669       for(component_iterator it = comps.begin(); it!=comps.end(); ++it)
00670         parent.setParent(**it);
00671     }
00672     const plist::ArrayOf<LinkComponent>& comps;
00673     KinematicJoint& parent;
00674   } componentsListener;
00675   
00676   virtual void dirtyBB() { bbDirty=true; }
00677 };
00678 
00679 //! handles the recursive loading of a tree of joints
00680 class KinematicJointLoader : public plist::ArrayOf<KinematicJoint> {
00681 public:
00682   //! constructor, start loading
00683   explicit KinematicJointLoader(KinematicJoint& root, xmlNode* node)
00684   : plist::ArrayOf<KinematicJoint>(), parent(&root)
00685   {
00686     addEntry(root);
00687     loadXML(node);
00688   }
00689 
00690 protected:
00691   //! for each node, if it's an array, start loading the subtree via recursive instantiation, otherwise load the KinematicJoint
00692   virtual bool loadXMLNode(size_t index, xmlNode* val, const std::string& comment);
00693   
00694   //! the current joint for which sub-joints are being loaded
00695   KinematicJoint* parent;
00696   
00697 private:
00698   KinematicJointLoader(const KinematicJointLoader&); //!< don't call
00699   KinematicJointLoader& operator=(const KinematicJointLoader&); //!< don't call
00700 };
00701 
00702 //! handles the recursive saving of a tree of joints
00703 class KinematicJointSaver : public plist::Array {
00704 public:
00705   //! given a single KinematicJoint, places it at the beginning of the array, followed by its children
00706   explicit KinematicJointSaver(const KinematicJoint& c, xmlNode* node=NULL) : plist::Array() {
00707     addEntry(const_cast<KinematicJoint&>(c));
00708     init(c.branches,node);
00709   }
00710   //! given a single KinematicJoint, places it at the beginning of the array, followed by its children
00711   explicit KinematicJointSaver(KinematicJoint& c, bool takeOwnership, xmlNode* node=NULL) : plist::Array() {
00712     if(takeOwnership)
00713       addEntry(&c);
00714     else
00715       addEntry(c);
00716     init(c.branches,node);
00717   }
00718   /* given a set of joints, adds the chain as elements of the array, so that they can be saved in order (by a second call from client)
00719   explicit KinematicJointSaver(const std::set<KinematicJoint*>& joints, xmlNode* node=NULL) : plist::Array() {
00720     init(joints,node);
00721   }*/
00722 protected:
00723   //! saves the array of joints, prepending human-readable help text as a comment if this is a root XML node
00724   virtual void saveXML(xmlNode* node) const;
00725   //! adds the chain of joints as elements in the array, recursing on branching nodes to insert sub-arrays
00726   void init(const std::set<KinematicJoint*>& joints, xmlNode* node);
00727 };
00728 
00729 /*! @file
00730  * @brief Describes KinematicJoint, which manages parameters defining the position and type of motion produced by an actuator (i.e. forward kinematics)
00731  * @author Ethan Tira-Thompson (ejt) (Creator)
00732  */
00733 
00734 #endif

Tekkotsu v5.1CVS
Generated Fri Mar 16 05:26:42 2012 by Doxygen 1.6.3