00001
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 ) { 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
00038 float den = std::max(std::abs(lastResponse),std::abs(x));
00039 if(diff/den >= .01f) {
00040
00041 bodyA.activate();
00042 bodyB.activate();
00043 lastResponse=x;
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
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 , 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 ) {
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
00111
00112
00113
00114
00115
00116
00117
00118
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 , 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 , 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
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;
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
00253
00254
00255
00256
00257 #endif