Skip to content

Commit 8bc1c8e

Browse files
authored
Merge pull request #2030 from erwincoumans/master
some fixes in inverse dynamics, PyBullet example comparing explicit pd, stable pd control, position control (constraint)
2 parents 4a66d6c + d477d18 commit 8bc1c8e

File tree

14 files changed

+201
-48
lines changed

14 files changed

+201
-48
lines changed

Extras/InverseDynamics/btMultiBodyTreeCreator.cpp

+10-2
Original file line numberDiff line numberDiff line change
@@ -202,8 +202,16 @@ int btMultiBodyTreeCreator::createFromBtMultiBody(const btMultiBody *btmb, const
202202
link.parent_r_parent_body_ref(2) = bt_link.m_eVector[2];
203203
break;
204204
case btMultibodyLink::eSpherical:
205-
bt_id_error_message("spherical joints not implemented\n");
206-
return -1;
205+
206+
link.joint_type = SPHERICAL;
207+
link.parent_r_parent_body_ref(0) = bt_link.m_eVector[0];
208+
link.parent_r_parent_body_ref(1) = bt_link.m_eVector[1];
209+
link.parent_r_parent_body_ref(2) = bt_link.m_eVector[2];
210+
// random unit vector
211+
link.body_axis_of_motion(0) = 0;
212+
link.body_axis_of_motion(1) = 0;
213+
link.body_axis_of_motion(2) = 1;
214+
break;
207215
case btMultibodyLink::ePlanar:
208216
bt_id_error_message("planar joints not implemented\n");
209217
return -1;

examples/Importers/ImportURDFDemo/URDF2Bullet.cpp

+4
Original file line numberDiff line numberDiff line change
@@ -31,13 +31,17 @@ static btVector4 colors[4] =
3131

3232
static btVector4 selectColor2()
3333
{
34+
#ifdef BT_THREADSAFE
3435
static btSpinMutex sMutex;
3536
sMutex.lock();
37+
#endif
3638
static int curColor = 0;
3739
btVector4 color = colors[curColor];
3840
curColor++;
3941
curColor &= 3;
42+
#ifdef BT_THREADSAFE
4043
sMutex.unlock();
44+
#endif
4145
return color;
4246
}
4347

examples/OpenGLWindow/SimpleOpenGL2Renderer.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -436,7 +436,7 @@ void SimpleOpenGL2Renderer::updateTexture(int textureIndex, const unsigned char*
436436
}
437437

438438
b3Assert(glGetError() == GL_NO_ERROR);
439-
glGenerateMipmap(GL_TEXTURE_2D);
439+
//glGenerateMipmap(GL_TEXTURE_2D);
440440
b3Assert(glGetError() == GL_NO_ERROR);
441441
}
442442
}

examples/SharedMemory/PhysicsServerCommandProcessor.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -9756,7 +9756,7 @@ bool PhysicsServerCommandProcessor::processCalculateInverseKinematicsCommand(con
97569756
numDofs, clientCmd.m_calculateInverseKinematicsArguments.m_endEffectorLinkIndex,
97579757
&q_new[0], ikMethod, &jacobian_linear[0], &jacobian_angular[0], jacSize * 2, targetDampCoeff);
97589758
}
9759-
serverCmd.m_inverseKinematicsResultArgs.m_bodyUniqueId = clientCmd.m_calculateInverseDynamicsArguments.m_bodyUniqueId;
9759+
serverCmd.m_inverseKinematicsResultArgs.m_bodyUniqueId = clientCmd.m_calculateInverseKinematicsArguments.m_bodyUniqueId;
97609760
for (int i = 0; i < numDofs; i++)
97619761
{
97629762
serverCmd.m_inverseKinematicsResultArgs.m_jointPositions[i] = q_new[i];
+61-22
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,49 @@
11

22
import pybullet as p
3+
from pdControllerExplicit import PDControllerExplicit
4+
from pdControllerExplicit import PDControllerStable
5+
36
import time
47

8+
59
useMaximalCoordinates=False
610
p.connect(p.GUI)
7-
pole = p.loadURDF("cartpole.urdf", useMaximalCoordinates=useMaximalCoordinates)
8-
for i in range (p.getNumJoints(pole)):
11+
pole = p.loadURDF("cartpole.urdf", [0,0,0], useMaximalCoordinates=useMaximalCoordinates)
12+
pole2 = p.loadURDF("cartpole.urdf", [0,1,0], useMaximalCoordinates=useMaximalCoordinates)
13+
pole3 = p.loadURDF("cartpole.urdf", [0,2,0], useMaximalCoordinates=useMaximalCoordinates)
14+
pole4 = p.loadURDF("cartpole.urdf", [0,3,0], useMaximalCoordinates=useMaximalCoordinates)
15+
16+
exPD = PDControllerExplicit(p)
17+
sPD = PDControllerStable(p)
18+
19+
20+
for i in range (p.getNumJoints(pole2)):
921
#disable default constraint-based motors
1022
p.setJointMotorControl2(pole,i,p.POSITION_CONTROL,targetPosition=0,force=0)
11-
print("joint",i,"=",p.getJointInfo(pole,i))
23+
p.setJointMotorControl2(pole2,i,p.POSITION_CONTROL,targetPosition=0,force=0)
24+
p.setJointMotorControl2(pole3,i,p.POSITION_CONTROL,targetPosition=0,force=0)
25+
p.setJointMotorControl2(pole4,i,p.POSITION_CONTROL,targetPosition=0,force=0)
26+
27+
#print("joint",i,"=",p.getJointInfo(pole2,i))
1228

1329

30+
timeStepId = p.addUserDebugParameter("timeStep",0.001,0.1,0.01)
1431
desiredPosCartId = p.addUserDebugParameter("desiredPosCart",-10,10,2)
1532
desiredVelCartId = p.addUserDebugParameter("desiredVelCart",-10,10,0)
16-
kpCartId = p.addUserDebugParameter("kpCart",0,500,300)
33+
kpCartId = p.addUserDebugParameter("kpCart",0,500,1300)
1734
kdCartId = p.addUserDebugParameter("kdCart",0,300,150)
1835
maxForceCartId = p.addUserDebugParameter("maxForceCart",0,5000,1000)
1936

37+
textColor = [1,1,1]
38+
shift = 0.05
39+
p.addUserDebugText("explicit PD", [shift,0,.1],textColor,parentObjectUniqueId=pole,parentLinkIndex=1)
40+
p.addUserDebugText("explicit PD plugin", [shift,0,-.1],textColor,parentObjectUniqueId=pole2,parentLinkIndex=1)
41+
p.addUserDebugText("stablePD", [shift,0,.1],textColor,parentObjectUniqueId=pole4,parentLinkIndex=1)
42+
p.addUserDebugText("position constraint", [shift,0,-.1],textColor,parentObjectUniqueId=pole3,parentLinkIndex=1)
2043

2144
desiredPosPoleId = p.addUserDebugParameter("desiredPosPole",-10,10,0)
2245
desiredVelPoleId = p.addUserDebugParameter("desiredVelPole",-10,10,0)
23-
kpPoleId = p.addUserDebugParameter("kpPole",0,500,200)
46+
kpPoleId = p.addUserDebugParameter("kpPole",0,500,1200)
2447
kdPoleId = p.addUserDebugParameter("kdPole",0,300,100)
2548
maxForcePoleId = p.addUserDebugParameter("maxForcePole",0,5000,1000)
2649

@@ -29,34 +52,50 @@
2952

3053
p.setGravity(0,0,-10)
3154

32-
useRealTimeSim = True
55+
useRealTimeSim = False
3356

3457
p.setRealTimeSimulation(useRealTimeSim)
3558

36-
p.setTimeStep(0.001)
59+
timeStep = 0.001
3760

3861

3962
while p.isConnected():
63+
p.getCameraImage(320,200)
64+
timeStep = p.readUserDebugParameter(timeStepId)
65+
p.setTimeStep(timeStep)
66+
67+
desiredPosCart = p.readUserDebugParameter(desiredPosCartId)
68+
desiredVelCart = p.readUserDebugParameter(desiredVelCartId)
69+
kpCart = p.readUserDebugParameter(kpCartId)
70+
kdCart = p.readUserDebugParameter(kdCartId)
71+
maxForceCart = p.readUserDebugParameter(maxForceCartId)
72+
73+
desiredPosPole = p.readUserDebugParameter(desiredPosPoleId)
74+
desiredVelPole = p.readUserDebugParameter(desiredVelPoleId)
75+
kpPole = p.readUserDebugParameter(kpPoleId)
76+
kdPole = p.readUserDebugParameter(kdPoleId)
77+
maxForcePole = p.readUserDebugParameter(maxForcePoleId)
78+
79+
taus = exPD.computePD(pole, [0,1], [desiredPosCart,desiredPosPole],[desiredVelCart,desiredVelPole], [kpCart,kpPole], [kdCart,kdPole],[maxForceCart,maxForcePole], timeStep)
80+
p.setJointMotorControlArray(pole, [0,1], controlMode=p.TORQUE_CONTROL, forces=taus)
81+
4082
if (pd>=0):
41-
desiredPosCart = p.readUserDebugParameter(desiredPosCartId)
42-
desiredVelCart = p.readUserDebugParameter(desiredVelCartId)
43-
kpCart = p.readUserDebugParameter(kpCartId)
44-
kdCart = p.readUserDebugParameter(kdCartId)
45-
maxForceCart = p.readUserDebugParameter(maxForceCartId)
4683
link = 0
47-
p.setJointMotorControl2(bodyUniqueId=pole,jointIndex=link,controlMode=p.PD_CONTROL,targetPosition=desiredPosCart,targetVelocity=desiredVelCart,force=maxForceCart, positionGain=kpCart, velocityGain=kdCart)
48-
49-
desiredPosPole = p.readUserDebugParameter(desiredPosPoleId)
50-
desiredVelPole = p.readUserDebugParameter(desiredVelPoleId)
51-
kpPole = p.readUserDebugParameter(kpPoleId)
52-
kdPole = p.readUserDebugParameter(kdPoleId)
53-
maxForcePole = p.readUserDebugParameter(maxForcePoleId)
84+
p.setJointMotorControl2(bodyUniqueId=pole2,jointIndex=link,controlMode=p.PD_CONTROL,targetPosition=desiredPosCart,targetVelocity=desiredVelCart,force=maxForceCart, positionGain=kpCart, velocityGain=kdCart)
5485
link = 1
55-
p.setJointMotorControl2(bodyUniqueId=pole,jointIndex=link,controlMode=p.PD_CONTROL,targetPosition=desiredPosPole,targetVelocity=desiredVelPole,force=maxForcePole, positionGain=kpPole, velocityGain=kdPole)
86+
p.setJointMotorControl2(bodyUniqueId=pole2,jointIndex=link,controlMode=p.PD_CONTROL,targetPosition=desiredPosPole,targetVelocity=desiredVelPole,force=maxForcePole, positionGain=kpPole, velocityGain=kdPole)
87+
88+
89+
90+
91+
taus = sPD.computePD(pole4, [0,1], [desiredPosCart,desiredPosPole],[desiredVelCart,desiredVelPole], [kpCart,kpPole], [kdCart,kdPole],[maxForceCart,maxForcePole], timeStep)
92+
p.setJointMotorControlArray(pole4, [0,1], controlMode=p.TORQUE_CONTROL, forces=taus)
5693

57-
94+
p.setJointMotorControl2(pole3,0, p.POSITION_CONTROL, targetPosition=desiredPosCart, targetVelocity=desiredVelCart, positionGain=timeStep*(kpCart/150.), velocityGain=0.5, force=maxForceCart)
95+
p.setJointMotorControl2(pole3,1, p.POSITION_CONTROL, targetPosition=desiredPosPole, targetVelocity=desiredVelPole, positionGain=timeStep*(kpPole/150.), velocityGain=0.5, force=maxForcePole)
96+
5897
if (not useRealTimeSim):
5998
p.stepSimulation()
60-
time.sleep(1./240.)
99+
time.sleep(timeStep)
61100

62101

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import numpy as np
2+
3+
class PDControllerExplicit(object):
4+
def __init__(self, pb):
5+
self._pb = pb
6+
7+
def computePD(self, bodyUniqueId, jointIndices, desiredPositions, desiredVelocities, kps, kds, maxForces, timeStep):
8+
numJoints = self._pb.getNumJoints(bodyUniqueId)
9+
jointStates = self._pb.getJointStates(bodyUniqueId, jointIndices)
10+
q1 = []
11+
qdot1 = []
12+
for i in range (numJoints):
13+
q1.append(jointStates[i][0])
14+
qdot1.append(jointStates[i][1])
15+
q = np.array(q1)
16+
qdot=np.array(qdot1)
17+
qdes = np.array(desiredPositions)
18+
qdotdes = np.array(desiredVelocities)
19+
qError = qdes - q
20+
qdotError = qdotdes - qdot
21+
Kp = np.diagflat(kps)
22+
Kd = np.diagflat(kds)
23+
forces = Kp.dot(qError) + Kd.dot(qdotError)
24+
maxF = np.array(maxForces)
25+
forces = np.clip(forces, -maxF , maxF )
26+
return forces
27+
28+
29+
class PDControllerStable(object):
30+
def __init__(self, pb):
31+
self._pb = pb
32+
33+
def computePD(self, bodyUniqueId, jointIndices, desiredPositions, desiredVelocities, kps, kds, maxForces, timeStep):
34+
numJoints = self._pb.getNumJoints(bodyUniqueId)
35+
jointStates = self._pb.getJointStates(bodyUniqueId, jointIndices)
36+
q1 = []
37+
qdot1 = []
38+
zeroAccelerations = []
39+
for i in range (numJoints):
40+
q1.append(jointStates[i][0])
41+
qdot1.append(jointStates[i][1])
42+
zeroAccelerations.append(0)
43+
q = np.array(q1)
44+
qdot=np.array(qdot1)
45+
qdes = np.array(desiredPositions)
46+
qdotdes = np.array(desiredVelocities)
47+
qError = qdes - q
48+
qdotError = qdotdes - qdot
49+
Kp = np.diagflat(kps)
50+
Kd = np.diagflat(kds)
51+
p = Kp.dot(qError)
52+
d = Kd.dot(qdotError)
53+
forces = p + d
54+
55+
M1 = self._pb.calculateMassMatrix(bodyUniqueId,q1)
56+
M2 = np.array(M1)
57+
M = (M2 + Kd * timeStep)
58+
c1 = self._pb.calculateInverseDynamics(bodyUniqueId, q1, qdot1, zeroAccelerations)
59+
c = np.array(c1)
60+
A = M
61+
b = -c + p + d
62+
qddot = np.linalg.solve(A, b)
63+
tau = p + d - Kd.dot(qddot) * timeStep
64+
maxF = np.array(maxForces)
65+
forces = np.clip(tau, -maxF , maxF )
66+
#print("c=",c)
67+
return tau

src/BulletCollision/NarrowPhaseCollision/btConvexCast.h

+20-1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,19 @@ subject to the following restrictions:
2222
class btMinkowskiSumShape;
2323
#include "LinearMath/btIDebugDraw.h"
2424

25+
#ifdef BT_USE_DOUBLE_PRECISION
26+
#define MAX_ITERATIONS 64
27+
#define MAX_EPSILON (SIMD_EPSILON * 10)
28+
#else
29+
#define MAX_ITERATIONS 32
30+
#define MAX_EPSILON btScalar(0.0001)
31+
#endif
32+
///Typically the conservative advancement reaches solution in a few iterations, clip it to 32 for degenerate cases.
33+
///See discussion about this here http://continuousphysics.com/Bullet/phpBB2/viewtopic.php?t=565
34+
//will need to digg deeper to make the algorithm more robust
35+
//since, a large epsilon can cause an early termination with false
36+
//positive results (ray intersections that shouldn't be there)
37+
2538
/// btConvexCast is an interface for Casting
2639
class btConvexCast
2740
{
@@ -44,7 +57,9 @@ class btConvexCast
4457
CastResult()
4558
: m_fraction(btScalar(BT_LARGE_FLOAT)),
4659
m_debugDrawer(0),
47-
m_allowedPenetration(btScalar(0))
60+
m_allowedPenetration(btScalar(0)),
61+
m_subSimplexCastMaxIterations(MAX_ITERATIONS),
62+
m_subSimplexCastEpsilon(MAX_EPSILON)
4863
{
4964
}
5065

@@ -57,6 +72,10 @@ class btConvexCast
5772
btScalar m_fraction; //input and output
5873
btIDebugDraw* m_debugDrawer;
5974
btScalar m_allowedPenetration;
75+
76+
int m_subSimplexCastMaxIterations;
77+
btScalar m_subSimplexCastEpsilon;
78+
6079
};
6180

6281
/// cast a convex against another convex object

src/BulletCollision/NarrowPhaseCollision/btGjkEpaPenetrationDepthSolver.cpp

+2-2
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,8 @@ bool btGjkEpaPenetrationDepthSolver::calcPenDepth(btSimplexSolverInterface& simp
3131
(void)simplexSolver;
3232

3333
btVector3 guessVectors[] = {
34-
btVector3(transformB.getOrigin() - transformA.getOrigin()).normalized(),
35-
btVector3(transformA.getOrigin() - transformB.getOrigin()).normalized(),
34+
btVector3(transformB.getOrigin() - transformA.getOrigin()).safeNormalize(),
35+
btVector3(transformA.getOrigin() - transformB.getOrigin()).safeNormalize(),
3636
btVector3(0, 0, 1),
3737
btVector3(0, 1, 0),
3838
btVector3(1, 0, 0),

src/BulletCollision/NarrowPhaseCollision/btSubSimplexConvexCast.cpp

+4-18
Original file line numberDiff line numberDiff line change
@@ -28,13 +28,7 @@ btSubsimplexConvexCast::btSubsimplexConvexCast(const btConvexShape* convexA, con
2828
{
2929
}
3030

31-
///Typically the conservative advancement reaches solution in a few iterations, clip it to 32 for degenerate cases.
32-
///See discussion about this here http://continuousphysics.com/Bullet/phpBB2/viewtopic.php?t=565
33-
#ifdef BT_USE_DOUBLE_PRECISION
34-
#define MAX_ITERATIONS 64
35-
#else
36-
#define MAX_ITERATIONS 32
37-
#endif
31+
3832
bool btSubsimplexConvexCast::calcTimeOfImpact(
3933
const btTransform& fromA,
4034
const btTransform& toA,
@@ -60,7 +54,7 @@ bool btSubsimplexConvexCast::calcTimeOfImpact(
6054
btVector3 supVertexA = fromA(m_convexA->localGetSupportingVertex(-r * fromA.getBasis()));
6155
btVector3 supVertexB = fromB(m_convexB->localGetSupportingVertex(r * fromB.getBasis()));
6256
v = supVertexA - supVertexB;
63-
int maxIter = MAX_ITERATIONS;
57+
int maxIter = result.m_subSimplexCastMaxIterations;
6458

6559
btVector3 n;
6660
n.setValue(btScalar(0.), btScalar(0.), btScalar(0.));
@@ -69,20 +63,12 @@ bool btSubsimplexConvexCast::calcTimeOfImpact(
6963

7064
btScalar dist2 = v.length2();
7165

72-
#ifdef BT_USE_DOUBLE_PRECISION
73-
btScalar epsilon = SIMD_EPSILON * 10;
74-
#else
75-
//todo: epsilon kept for backward compatibility of unit tests.
76-
//will need to digg deeper to make the algorithm more robust
77-
//since, a large epsilon can cause an early termination with false
78-
//positive results (ray intersections that shouldn't be there)
79-
btScalar epsilon = btScalar(0.0001);
80-
#endif //BT_USE_DOUBLE_PRECISION
66+
8167

8268
btVector3 w, p;
8369
btScalar VdotR;
8470

85-
while ((dist2 > epsilon) && maxIter--)
71+
while ((dist2 > result.m_subSimplexCastEpsilon) && maxIter--)
8672
{
8773
supVertexA = interpolatedTransA(m_convexA->localGetSupportingVertex(-v * interpolatedTransA.getBasis()));
8874
supVertexB = interpolatedTransB(m_convexB->localGetSupportingVertex(v * interpolatedTransB.getBasis()));

src/BulletDynamics/ConstraintSolver/btHingeConstraint.cpp

+6
Original file line numberDiff line numberDiff line change
@@ -764,6 +764,12 @@ void btHingeConstraint::getInfo2InternalUsingFrameOffset(btConstraintInfo2* info
764764
btVector3 ax1A = trA.getBasis().getColumn(2);
765765
btVector3 ax1B = trB.getBasis().getColumn(2);
766766
btVector3 ax1 = ax1A * factA + ax1B * factB;
767+
if (ax1.length2()<SIMD_EPSILON)
768+
{
769+
factA=0.f;
770+
factB=1.f;
771+
ax1 = ax1A * factA + ax1B * factB;
772+
}
767773
ax1.normalize();
768774
// fill first 3 rows
769775
// we want: velA + wA x relA == velB + wB x relB

src/BulletDynamics/Featherstone/btMultiBody.h

+9
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,15 @@ btMultiBody
134134
return m_baseCollider;
135135
}
136136

137+
const btMultiBodyLinkCollider *getLinkCollider(int index) const
138+
{
139+
if (index >= 0 && index < getNumLinks())
140+
{
141+
return getLink(index).m_collider;
142+
}
143+
return 0;
144+
}
145+
137146
btMultiBodyLinkCollider *getLinkCollider(int index)
138147
{
139148
if (index >= 0 && index < getNumLinks())

0 commit comments

Comments
 (0)