Tekkotsu Homepage
Demos
Overview
Downloads
Dev. Resources
Reference
Credits

MotorControllers.h

Go to the documentation of this file.
00001 //-*-c++-*-
00002 #ifndef INCLUDED_MotorControllers_h_
00003 #define INCLUDED_MotorControllers_h_
00004 
00005 #include "Shared/plist.h"
00006 #include "Motion/KinematicJoint.h"
00007 
00008 #include <set>
00009 #ifdef __APPLE__
00010 #include <BulletDynamics/btBulletDynamicsCommon.h>
00011 #else
00012 #include <btBulletDynamicsCommon.h>
00013 #endif
00014 
00015 class ConstraintInterface {
00016 public:
00017   virtual ~ConstraintInterface() {}
00018   virtual float getValue(float dt)=0;
00019   virtual void applyResponse(float dt, float x)=0;
00020 };
00021 
00022 class HingePositionInterface : public ConstraintInterface {
00023 public:
00024   explicit HingePositionInterface(btHingeConstraint& hingeConstraint) : ConstraintInterface(), hinge(hingeConstraint), lastResponse(0) {}
00025   virtual float getValue(float /*dt*/) { return hinge.getHingeAngle(); }
00026   virtual void applyResponse(float dt, float x) {
00027     if(x==0)
00028       return;
00029     x*=dt;
00030     btRigidBody& bodyA = hinge.getRigidBodyA();
00031     btRigidBody& bodyB = hinge.getRigidBodyB();
00032     btVector3 torque =  bodyA.getCenterOfMassTransform().getBasis() * hinge.getAFrame().getBasis().getColumn(2) * x;
00033     bodyA.applyTorqueImpulse(torque);
00034     bodyB.applyTorqueImpulse(-torque);
00035     float diff = std::abs(lastResponse-x);
00036     if(diff>0) {
00037       // bullet will "deactivate" objects if they don't move for a while, thus...
00038       float den = std::max(std::abs(lastResponse),std::abs(x));
00039       if(diff/den >= .01f) { // if our torque changes significantly (here, by more than 1% than last trigger)
00040         // then tell bullet to reactivate the bodies (or keep them active if already)
00041         bodyA.activate();
00042         bodyB.activate();
00043         lastResponse=x; // only store when we actually activate, this is what we want to test against to handle slow drifts
00044       }
00045     }
00046   }
00047   btHingeConstraint& hinge;
00048   float lastResponse;
00049 };
00050 
00051 class HingeVelocityInterface : public HingePositionInterface {
00052 public:
00053   explicit HingeVelocityInterface(btHingeConstraint& hingeConstraint) : HingePositionInterface(hingeConstraint), lastValue(0) {}
00054   float getValue(float dt) {
00055     if(dt==0)
00056       return lastValue;
00057     const float q = hinge.getHingeAngle();
00058     const float rot = normalizeAngle(q - lastValue);
00059     //std::cout << "rot " << rot << " from " << lastValue << " to " << q << " over " << dt << std::endl;
00060     lastValue=q;
00061     return rot / dt;
00062   }
00063   static inline float normalizeAngle(float value) {
00064     if ( value < -(float)M_PI ) 
00065       return value + 2*(float)M_PI;
00066     else if ( value > (float)M_PI)
00067       return value - 2*(float)M_PI;
00068     else
00069       return value;
00070   }   
00071   float lastValue;
00072 };
00073 
00074 class HingeAngularMotorInterface : public HingeVelocityInterface {
00075 public:
00076   explicit HingeAngularMotorInterface(btHingeConstraint& hingeConstraint) : HingeVelocityInterface(hingeConstraint) {}
00077   void applyResponse(float /*dt*/, float x) {
00078     if(x!=lastResponse) {
00079       hinge.enableAngularMotor(true,x,10);
00080       hinge.getRigidBodyA().activate();
00081       hinge.getRigidBodyB().activate();
00082       lastResponse=x;
00083     }
00084   }
00085 };
00086 
00087 class GenericAngularForceInterface : public ConstraintInterface {
00088 public:
00089   explicit GenericAngularForceInterface(btGeneric6DofConstraint& genericConstraint)
00090   : ConstraintInterface(), constraint(genericConstraint), lastResponse(0) { lastResponse=-1; applyResponse(0,0); }
00091   
00092   virtual float getValue(float /*dt*/) {
00093     const btRigidBody& bodyA = constraint.getRigidBodyA();
00094     const btRigidBody& bodyB = constraint.getRigidBodyB();
00095     const btVector3 refAxis0  = bodyA.getCenterOfMassTransform().getBasis() * constraint.getFrameOffsetA().getBasis().getColumn(0);
00096     const btVector3 refAxis1  = bodyA.getCenterOfMassTransform().getBasis() * constraint.getFrameOffsetA().getBasis().getColumn(1);
00097     const btVector3 swingAxis = bodyB.getCenterOfMassTransform().getBasis() * constraint.getFrameOffsetB().getBasis().getColumn(0);
00098     return std::atan2(swingAxis.dot(refAxis1), swingAxis.dot(refAxis0));
00099   }
00100   
00101   virtual void applyResponse(float dt, float x) {
00102     if(x==0)
00103       return;
00104     x*=dt;
00105     btRigidBody& bodyA = constraint.getRigidBodyA();
00106     btRigidBody& bodyB = constraint.getRigidBodyB();
00107     btVector3 torque =  bodyA.getCenterOfMassTransform().getBasis() * constraint.getFrameOffsetA().getBasis().getColumn(2) * x;
00108     bodyA.applyTorqueImpulse(-torque);
00109     bodyB.applyTorqueImpulse(torque);
00110     /*float diff = std::abs(lastResponse-x);
00111     if(diff>0) {
00112       // bullet will "deactivate" objects if they don't move for a while, thus...
00113       float den = std::max(std::abs(lastResponse),std::abs(x));
00114       if(diff/den >= .01f) { // if our torque changes significantly (here, by more than 1% than last trigger)
00115         // then tell bullet to reactivate the bodies (or keep them active if already)
00116         bodyA.activate();
00117         bodyB.activate();
00118         lastResponse=x; // only store when we actually activate, this is what we want to test against to handle slow drifts
00119       }
00120     }*/
00121     bodyA.activate();
00122     bodyB.activate();
00123     lastResponse=x;
00124   }
00125   btGeneric6DofConstraint& constraint;
00126   float lastResponse;
00127 };
00128 
00129 class GenericAngularMotorInterface : public GenericAngularForceInterface {
00130 public:
00131   explicit GenericAngularMotorInterface(btGeneric6DofConstraint& genericConstraint)
00132   : GenericAngularForceInterface(genericConstraint) { lastResponse=-1; applyResponse(0,0); }
00133   
00134   void applyResponse(float /*dt*/, float x) {
00135     if(x!=lastResponse) {
00136       constraint.getRotationalLimitMotor(2)->m_enableMotor = true;
00137       constraint.getRotationalLimitMotor(2)->m_targetVelocity = -x;
00138       constraint.getRotationalLimitMotor(2)->m_maxMotorForce = 10;
00139       constraint.getRigidBodyA().activate();
00140       constraint.getRigidBodyB().activate();
00141       lastResponse=x;
00142     } else if(x!=0) {
00143       constraint.getRigidBodyA().activate();
00144       constraint.getRigidBodyB().activate();
00145     }
00146   }
00147 };
00148 
00149 class GenericAngularPositionInterface : public GenericAngularForceInterface {
00150 public:
00151   explicit GenericAngularPositionInterface(btGeneric6DofConstraint& genericConstraint)
00152   : GenericAngularForceInterface(genericConstraint) { lastResponse=-1; applyResponse(0,0); }
00153   
00154   void applyResponse(float /*dt*/, float x) {
00155     constraint.setAngularLowerLimit(btVector3(0,0,-x));
00156     constraint.setAngularUpperLimit(btVector3(0,0,-x));
00157     constraint.getRigidBodyA().activate();
00158     constraint.getRigidBodyB().activate();
00159   }
00160 };
00161 
00162 
00163 //! Sync entries here with those in KinematicJoint::ControllerInfo
00164 class MotorController : virtual public plist::Dictionary {
00165 public:
00166   MotorController(const plist::Primitive<float>& tgt_, ConstraintInterface& c)
00167     : plist::Dictionary(), tgt(tgt_), constraint(c),
00168     velocity(false), forceControl(false)
00169   {
00170     addEntry("Velocity",velocity,"Adjusts interpretation of feedback and application of friction, false implies position control");
00171     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.");
00172   }
00173   virtual ~MotorController() {}
00174 
00175   virtual void updateControllerResponse(float dt)=0;
00176   
00177   const plist::Primitive<float>& tgt;
00178   ConstraintInterface& constraint;
00179   
00180   plist::Primitive<bool> velocity;
00181   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.
00182 };
00183 
00184 
00185 class LinearMotorController : virtual public plist::Dictionary, public MotorController {
00186 public:
00187   LinearMotorController(const plist::Primitive<float>& tgt_, ConstraintInterface& c, float sc=1)
00188     : plist::Dictionary(), MotorController(tgt_,c), scale(sc)
00189   {
00190     addEntry("Scale",scale,"Scales the target domain (e.g. convert wheel rim dist/s to axle rad/s)");
00191   }
00192   virtual void updateControllerResponse(float dt) {
00193     constraint.applyResponse(dt, tgt*scale);
00194   }
00195   plist::Primitive<float> scale;
00196 };
00197 
00198 class ProportionalMotorController : virtual public plist::Dictionary, public MotorController {
00199 public:
00200   ProportionalMotorController(const plist::Primitive<float>& tgt_, ConstraintInterface& c, float p_=1)
00201   : plist::Dictionary(), MotorController(tgt_,c), p(p_)
00202   {
00203     addEntry("P",p,"Proportional control parameter (scales I and D response as well)");
00204   }
00205   virtual void updateControllerResponse(float dt);
00206   plist::Primitive<float> p;
00207 };
00208 
00209 class PIDMotorController : virtual public plist::Dictionary, public MotorController {
00210 public:
00211   PIDMotorController(const plist::Primitive<float>& tgt_, ConstraintInterface& c)
00212     : plist::Dictionary(), MotorController(tgt_,c),
00213     scale(1), p(0), i(0), d(0), derrGamma(0), linearff(0), punch(0),
00214     minResponse(0), maxResponse(-1), friction(0), verbose(),
00215     lastX(0), lastErr(0), sumErr(0), avgDerr(0)
00216   {
00217     addEntry("Scale",scale,"Scales the target domain (e.g. convert wheel rim dist/s to axle rad/s)");
00218     addEntry("P",p,"Proportional control parameter (scales I and D response as well)");
00219     addEntry("I",i,"Integrative control parameter");
00220     addEntry("D",d,"Derivative control parameter");
00221     addEntry("DerrGamma",derrGamma,"Applies exponential smoothing to derivative control, use '0' to disable smoothing");
00222     addEntry("LinearFF",linearff,"Linear feed-forward term, to add scaled target value directly to response (good for velocity control)");
00223     addEntry("Punch",punch,"Adds a constant feed-forward term if response exceeds MinResponse");
00224     addEntry("MinResponse",minResponse,"Subtracted from response and clips to 0, simulates static friction");
00225     addEntry("MaxResponse",maxResponse,"Caps maximum response, -1 to disable");
00226     addEntry("Friction",friction,"Subtracts a portion of constraint value or derivative (depending on Velocity) from response");
00227     addEntry("Verbose",verbose,"Set to non-empty string to enable debugging outputs, handy for tuning; value is prepended to outputs");
00228     setLoadSavePolicy(FIXED,SYNC);
00229     updateControllerResponse(0);
00230   }
00231   
00232   virtual void updateControllerResponse(float dt);
00233   
00234   plist::Primitive<float> scale;
00235   plist::Primitive<float> p;
00236   plist::Primitive<float> i;
00237   plist::Primitive<float> d;
00238   plist::Primitive<float> derrGamma;
00239   plist::Primitive<float> linearff;
00240   plist::Primitive<float> punch;
00241   plist::Primitive<float> minResponse;
00242   plist::Primitive<float> maxResponse;
00243   plist::Primitive<float> friction;
00244   plist::Primitive<std::string> verbose;
00245   
00246   float lastX;
00247   float lastErr;
00248   float sumErr;
00249   float avgDerr;
00250 };
00251 
00252 /*! @file
00253  * @brief 
00254  * @author Ethan Tira-Thompson (ejt) (Creator)
00255  */
00256 
00257 #endif

Tekkotsu v5.1CVS
Generated Mon May 9 04:58:45 2016 by Doxygen 1.6.3