InvertedPendulumPDControl.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427
  1. #include "InvertedPendulumPDControl.h"
  2. #include "BulletDynamics/Featherstone/btMultiBodyLinkCollider.h"
  3. #include "BulletDynamics/Featherstone/btMultiBodyJointFeedback.h"
  4. #include "../CommonInterfaces/CommonMultiBodyBase.h"
  5. #include "../Utils/b3ResourcePath.h"
  6. #include "../CommonInterfaces/CommonParameterInterface.h"
  7. static btScalar radius(0.2);
  8. static btScalar kp = 100;
  9. static btScalar kd = 20;
  10. static btScalar maxForce = 100;
  11. struct InvertedPendulumPDControl : public CommonMultiBodyBase
  12. {
  13. btMultiBody* m_multiBody;
  14. btAlignedObjectArray<btMultiBodyJointFeedback*> m_jointFeedbacks;
  15. bool m_once;
  16. int m_frameCount;
  17. public:
  18. InvertedPendulumPDControl(struct GUIHelperInterface* helper);
  19. virtual ~InvertedPendulumPDControl();
  20. virtual void initPhysics();
  21. virtual void stepSimulation(float deltaTime);
  22. virtual void resetCamera()
  23. {
  24. float dist = 5;
  25. float pitch = -21;
  26. float yaw = 270;
  27. float targetPos[3] = {-1.34, 1.4, 3.44};
  28. m_guiHelper->resetCamera(dist, yaw, pitch, targetPos[0], targetPos[1], targetPos[2]);
  29. }
  30. };
  31. InvertedPendulumPDControl::InvertedPendulumPDControl(struct GUIHelperInterface* helper)
  32. : CommonMultiBodyBase(helper),
  33. m_once(true),
  34. m_frameCount(0)
  35. {
  36. }
  37. InvertedPendulumPDControl::~InvertedPendulumPDControl()
  38. {
  39. }
  40. btMultiBody* createInvertedPendulumMultiBody(btMultiBodyDynamicsWorld* world, GUIHelperInterface* guiHelper, const btTransform& baseWorldTrans, bool fixedBase)
  41. {
  42. btVector4 colors[4] =
  43. {
  44. btVector4(1, 0, 0, 1),
  45. btVector4(0, 1, 0, 1),
  46. btVector4(0, 1, 1, 1),
  47. btVector4(1, 1, 0, 1),
  48. };
  49. int curColor = 0;
  50. bool damping = false;
  51. bool gyro = false;
  52. int numLinks = 2;
  53. bool spherical = false; //set it ot false -to use 1DoF hinges instead of 3DoF sphericals
  54. bool canSleep = false;
  55. bool selfCollide = false;
  56. btVector3 linkHalfExtents(0.05, 0.37, 0.1);
  57. btVector3 baseHalfExtents(0.04, 0.35, 0.08);
  58. //mbC->forceMultiDof(); //if !spherical, you can comment this line to check the 1DoF algorithm
  59. //init the base
  60. btVector3 baseInertiaDiag(0.f, 0.f, 0.f);
  61. float baseMass = fixedBase ? 0.f : 10.f;
  62. if (baseMass)
  63. {
  64. //btCollisionShape *shape = new btSphereShape(baseHalfExtents[0]);// btBoxShape(btVector3(baseHalfExtents[0], baseHalfExtents[1], baseHalfExtents[2]));
  65. btCollisionShape* shape = new btBoxShape(btVector3(baseHalfExtents[0], baseHalfExtents[1], baseHalfExtents[2]));
  66. shape->calculateLocalInertia(baseMass, baseInertiaDiag);
  67. delete shape;
  68. }
  69. btMultiBody* pMultiBody = new btMultiBody(numLinks, 0, baseInertiaDiag, fixedBase, canSleep);
  70. pMultiBody->setBaseWorldTransform(baseWorldTrans);
  71. btVector3 vel(0, 0, 0);
  72. // pMultiBody->setBaseVel(vel);
  73. //init the links
  74. btVector3 hingeJointAxis(1, 0, 0);
  75. //y-axis assumed up
  76. btVector3 parentComToCurrentCom(0, -linkHalfExtents[1] * 2.f, 0); //par body's COM to cur body's COM offset
  77. btVector3 currentPivotToCurrentCom(0, -linkHalfExtents[1], 0); //cur body's COM to cur body's PIV offset
  78. btVector3 parentComToCurrentPivot = parentComToCurrentCom - currentPivotToCurrentCom; //par body's COM to cur body's PIV offset
  79. //////
  80. btScalar q0 = 1.f * SIMD_PI / 180.f;
  81. btQuaternion quat0(btVector3(1, 0, 0).normalized(), q0);
  82. quat0.normalize();
  83. /////
  84. for (int i = 0; i < numLinks; ++i)
  85. {
  86. float linkMass = 1.f;
  87. //if (i==3 || i==2)
  88. // linkMass= 1000;
  89. btVector3 linkInertiaDiag(0.f, 0.f, 0.f);
  90. btCollisionShape* shape = 0;
  91. if (i == 0)
  92. {
  93. shape = new btBoxShape(btVector3(linkHalfExtents[0], linkHalfExtents[1], linkHalfExtents[2])); //
  94. }
  95. else
  96. {
  97. shape = new btSphereShape(radius);
  98. }
  99. shape->calculateLocalInertia(linkMass, linkInertiaDiag);
  100. delete shape;
  101. if (!spherical)
  102. {
  103. //pMultiBody->setupRevolute(i, linkMass, linkInertiaDiag, i - 1, btQuaternion(0.f, 0.f, 0.f, 1.f), hingeJointAxis, parentComToCurrentPivot, currentPivotToCurrentCom, false);
  104. if (i == 0)
  105. {
  106. pMultiBody->setupRevolute(i, linkMass, linkInertiaDiag, i - 1,
  107. btQuaternion(0.f, 0.f, 0.f, 1.f),
  108. hingeJointAxis,
  109. parentComToCurrentPivot,
  110. currentPivotToCurrentCom, false);
  111. }
  112. else
  113. {
  114. btVector3 parentComToCurrentCom(0, -radius * 2.f, 0); //par body's COM to cur body's COM offset
  115. btVector3 currentPivotToCurrentCom(0, -radius, 0); //cur body's COM to cur body's PIV offset
  116. btVector3 parentComToCurrentPivot = parentComToCurrentCom - currentPivotToCurrentCom; //par body's COM to cur body's PIV offset
  117. pMultiBody->setupFixed(i, linkMass, linkInertiaDiag, i - 1,
  118. btQuaternion(0.f, 0.f, 0.f, 1.f),
  119. parentComToCurrentPivot,
  120. currentPivotToCurrentCom);
  121. }
  122. //pMultiBody->setupFixed(i,linkMass,linkInertiaDiag,i-1,btQuaternion(0,0,0,1),parentComToCurrentPivot,currentPivotToCurrentCom,false);
  123. }
  124. else
  125. {
  126. //pMultiBody->setupPlanar(i, linkMass, linkInertiaDiag, i - 1, btQuaternion(0.f, 0.f, 0.f, 1.f)/*quat0*/, btVector3(1, 0, 0), parentComToCurrentPivot*2, false);
  127. pMultiBody->setupSpherical(i, linkMass, linkInertiaDiag, i - 1, btQuaternion(0.f, 0.f, 0.f, 1.f), parentComToCurrentPivot, currentPivotToCurrentCom, false);
  128. }
  129. }
  130. pMultiBody->finalizeMultiDof();
  131. ///
  132. world->addMultiBody(pMultiBody);
  133. btMultiBody* mbC = pMultiBody;
  134. mbC->setCanSleep(canSleep);
  135. mbC->setHasSelfCollision(selfCollide);
  136. mbC->setUseGyroTerm(gyro);
  137. //
  138. if (!damping)
  139. {
  140. mbC->setLinearDamping(0.f);
  141. mbC->setAngularDamping(0.f);
  142. }
  143. else
  144. {
  145. mbC->setLinearDamping(0.1f);
  146. mbC->setAngularDamping(0.9f);
  147. }
  148. //
  149. //////////////////////////////////////////////
  150. if (numLinks > 0)
  151. {
  152. btScalar q0 = 180.f * SIMD_PI / 180.f;
  153. if (!spherical)
  154. {
  155. mbC->setJointPosMultiDof(0, &q0);
  156. }
  157. else
  158. {
  159. btQuaternion quat0(btVector3(1, 1, 0).normalized(), q0);
  160. quat0.normalize();
  161. mbC->setJointPosMultiDof(0, quat0);
  162. }
  163. }
  164. ///
  165. btAlignedObjectArray<btQuaternion> world_to_local;
  166. world_to_local.resize(pMultiBody->getNumLinks() + 1);
  167. btAlignedObjectArray<btVector3> local_origin;
  168. local_origin.resize(pMultiBody->getNumLinks() + 1);
  169. world_to_local[0] = pMultiBody->getWorldToBaseRot();
  170. local_origin[0] = pMultiBody->getBasePos();
  171. // double friction = 1;
  172. {
  173. // float pos[4]={local_origin[0].x(),local_origin[0].y(),local_origin[0].z(),1};
  174. // float quat[4]={-world_to_local[0].x(),-world_to_local[0].y(),-world_to_local[0].z(),world_to_local[0].w()};
  175. if (1)
  176. {
  177. btCollisionShape* shape = new btBoxShape(btVector3(baseHalfExtents[0], baseHalfExtents[1], baseHalfExtents[2])); //new btSphereShape(baseHalfExtents[0]);
  178. guiHelper->createCollisionShapeGraphicsObject(shape);
  179. btMultiBodyLinkCollider* col = new btMultiBodyLinkCollider(pMultiBody, -1);
  180. col->setCollisionShape(shape);
  181. btTransform tr;
  182. tr.setIdentity();
  183. //if we don't set the initial pose of the btCollisionObject, the simulator will do this
  184. //when syncing the btMultiBody link transforms to the btMultiBodyLinkCollider
  185. tr.setOrigin(local_origin[0]);
  186. btQuaternion orn(btVector3(0, 0, 1), 0.25 * 3.1415926538);
  187. tr.setRotation(orn);
  188. col->setWorldTransform(tr);
  189. bool isDynamic = (baseMass > 0 && !fixedBase);
  190. int collisionFilterGroup = isDynamic ? int(btBroadphaseProxy::DefaultFilter) : int(btBroadphaseProxy::StaticFilter);
  191. int collisionFilterMask = isDynamic ? int(btBroadphaseProxy::AllFilter) : int(btBroadphaseProxy::AllFilter ^ btBroadphaseProxy::StaticFilter);
  192. world->addCollisionObject(col, collisionFilterGroup, collisionFilterMask); //, 2,1+2);
  193. btVector4 color(0.0, 0.0, 0.5, 1);
  194. guiHelper->createCollisionObjectGraphicsObject(col, color);
  195. // col->setFriction(friction);
  196. pMultiBody->setBaseCollider(col);
  197. }
  198. }
  199. for (int i = 0; i < pMultiBody->getNumLinks(); ++i)
  200. {
  201. const int parent = pMultiBody->getParent(i);
  202. world_to_local[i + 1] = pMultiBody->getParentToLocalRot(i) * world_to_local[parent + 1];
  203. local_origin[i + 1] = local_origin[parent + 1] + (quatRotate(world_to_local[i + 1].inverse(), pMultiBody->getRVector(i)));
  204. }
  205. for (int i = 0; i < pMultiBody->getNumLinks(); ++i)
  206. {
  207. btVector3 posr = local_origin[i + 1];
  208. // float pos[4]={posr.x(),posr.y(),posr.z(),1};
  209. const btScalar quat[4] = {-world_to_local[i + 1].x(), -world_to_local[i + 1].y(), -world_to_local[i + 1].z(), world_to_local[i + 1].w()};
  210. btCollisionShape* shape = 0;
  211. if (i == 0)
  212. {
  213. shape = new btBoxShape(btVector3(linkHalfExtents[0], linkHalfExtents[1], linkHalfExtents[2])); //btSphereShape(linkHalfExtents[0]);
  214. }
  215. else
  216. {
  217. shape = new btSphereShape(radius);
  218. }
  219. guiHelper->createCollisionShapeGraphicsObject(shape);
  220. btMultiBodyLinkCollider* col = new btMultiBodyLinkCollider(pMultiBody, i);
  221. col->setCollisionShape(shape);
  222. btTransform tr;
  223. tr.setIdentity();
  224. tr.setOrigin(posr);
  225. tr.setRotation(btQuaternion(quat[0], quat[1], quat[2], quat[3]));
  226. col->setWorldTransform(tr);
  227. // col->setFriction(friction);
  228. bool isDynamic = 1; //(linkMass > 0);
  229. int collisionFilterGroup = isDynamic ? int(btBroadphaseProxy::DefaultFilter) : int(btBroadphaseProxy::StaticFilter);
  230. int collisionFilterMask = isDynamic ? int(btBroadphaseProxy::AllFilter) : int(btBroadphaseProxy::AllFilter ^ btBroadphaseProxy::StaticFilter);
  231. //if (i==0||i>numLinks-2)
  232. {
  233. world->addCollisionObject(col, collisionFilterGroup, collisionFilterMask); //,2,1+2);
  234. btVector4 color = colors[curColor];
  235. curColor++;
  236. curColor &= 3;
  237. guiHelper->createCollisionObjectGraphicsObject(col, color);
  238. pMultiBody->getLink(i).m_collider = col;
  239. }
  240. }
  241. return pMultiBody;
  242. }
  243. void InvertedPendulumPDControl::initPhysics()
  244. {
  245. {
  246. SliderParams slider("Kp", &kp);
  247. slider.m_minVal = -200;
  248. slider.m_maxVal = 200;
  249. m_guiHelper->getParameterInterface()->registerSliderFloatParameter(slider);
  250. }
  251. {
  252. SliderParams slider("Kd", &kd);
  253. slider.m_minVal = -50;
  254. slider.m_maxVal = 50;
  255. m_guiHelper->getParameterInterface()->registerSliderFloatParameter(slider);
  256. }
  257. {
  258. SliderParams slider("max force", &maxForce);
  259. slider.m_minVal = 0;
  260. slider.m_maxVal = 100;
  261. m_guiHelper->getParameterInterface()->registerSliderFloatParameter(slider);
  262. }
  263. int upAxis = 1;
  264. m_guiHelper->setUpAxis(upAxis);
  265. this->createEmptyDynamicsWorld();
  266. m_dynamicsWorld->getSolverInfo().m_jointFeedbackInWorldSpace = true;
  267. m_dynamicsWorld->getSolverInfo().m_jointFeedbackInJointFrame = true;
  268. m_guiHelper->createPhysicsDebugDrawer(m_dynamicsWorld);
  269. m_dynamicsWorld->getDebugDrawer()->setDebugMode(
  270. //btIDebugDraw::DBG_DrawConstraints
  271. +btIDebugDraw::DBG_DrawWireframe + btIDebugDraw::DBG_DrawContactPoints + btIDebugDraw::DBG_DrawAabb); //+btIDebugDraw::DBG_DrawConstraintLimits);
  272. m_dynamicsWorld->setGravity(btVector3(0, -10, 0));
  273. btTransform baseWorldTrans;
  274. baseWorldTrans.setIdentity();
  275. baseWorldTrans.setOrigin(btVector3(1, 2, 3));
  276. m_multiBody = createInvertedPendulumMultiBody(m_dynamicsWorld, m_guiHelper, baseWorldTrans, true);
  277. //for (int i=pMultiBody->getNumLinks()-1;i>=0;i--)//
  278. for (int i = 0; i < m_multiBody->getNumLinks(); i++)
  279. {
  280. btMultiBodyJointFeedback* fb = new btMultiBodyJointFeedback();
  281. m_multiBody->getLink(i).m_jointFeedback = fb;
  282. m_jointFeedbacks.push_back(fb);
  283. //break;
  284. }
  285. }
  286. char fileName[1024];
  287. static btAlignedObjectArray<btScalar> qDesiredArray;
  288. void InvertedPendulumPDControl::stepSimulation(float deltaTime)
  289. {
  290. static btScalar offset = -0.1 * SIMD_PI;
  291. m_frameCount++;
  292. if ((m_frameCount & 0xff) == 0)
  293. {
  294. offset = -offset;
  295. }
  296. btScalar target = SIMD_PI + offset;
  297. qDesiredArray.resize(0);
  298. qDesiredArray.resize(m_multiBody->getNumLinks(), target);
  299. for (int joint = 0; joint < m_multiBody->getNumLinks(); joint++)
  300. {
  301. int dof1 = 0;
  302. btScalar qActual = m_multiBody->getJointPosMultiDof(joint)[dof1];
  303. btScalar qdActual = m_multiBody->getJointVelMultiDof(joint)[dof1];
  304. btScalar positionError = (qDesiredArray[joint] - qActual);
  305. double desiredVelocity = 0;
  306. btScalar velocityError = (desiredVelocity - qdActual);
  307. btScalar force = kp * positionError + kd * velocityError;
  308. btClamp(force, -maxForce, maxForce);
  309. m_multiBody->addJointTorque(joint, force);
  310. }
  311. if (m_frameCount == 100)
  312. {
  313. const char* gPngFileName = "pendulum";
  314. if (gPngFileName)
  315. {
  316. //printf("gPngFileName=%s\n",gPngFileName);
  317. sprintf(fileName, "%s%d.png", gPngFileName, m_frameCount);
  318. b3Printf("Made screenshot %s", fileName);
  319. this->m_guiHelper->getAppInterface()->dumpNextFrameToPng(fileName);
  320. }
  321. }
  322. m_dynamicsWorld->stepSimulation(1. / 60., 0); //240,0);
  323. static int count = 0;
  324. if ((count & 0x0f) == 0)
  325. {
  326. #if 0
  327. for (int i=0;i<m_jointFeedbacks.size();i++)
  328. {
  329. b3Printf("F_reaction[%i] linear:%f,%f,%f, angular:%f,%f,%f",
  330. i,
  331. m_jointFeedbacks[i]->m_reactionForces.m_topVec[0],
  332. m_jointFeedbacks[i]->m_reactionForces.m_topVec[1],
  333. m_jointFeedbacks[i]->m_reactionForces.m_topVec[2],
  334. m_jointFeedbacks[i]->m_reactionForces.m_bottomVec[0],
  335. m_jointFeedbacks[i]->m_reactionForces.m_bottomVec[1],
  336. m_jointFeedbacks[i]->m_reactionForces.m_bottomVec[2]
  337. );
  338. }
  339. #endif
  340. }
  341. count++;
  342. /*
  343. b3Printf("base angvel = %f,%f,%f",m_multiBody->getBaseOmega()[0],
  344. m_multiBody->getBaseOmega()[1],
  345. m_multiBody->getBaseOmega()[2]
  346. );
  347. */
  348. // btScalar jointVel =m_multiBody->getJointVel(0);
  349. // b3Printf("child angvel = %f",jointVel);
  350. }
  351. class CommonExampleInterface* InvertedPendulumPDControlCreateFunc(struct CommonExampleOptions& options)
  352. {
  353. return new InvertedPendulumPDControl(options.m_guiHelper);
  354. }