Box2d - Possible to get rid of accumulated impulses?

Please don't post Bullet support questions here, use the above forums instead.
coderchris
Posts: 49
Joined: Fri Aug 18, 2006 11:50 pm

Box2d - Possible to get rid of accumulated impulses?

Post by coderchris »

Iv been messing around with box2d and it seems that simply creating a new contact each step instead of updating an existing one makes it go a bit faster. THis of coarse makes the accumulated impulses 0 which leads to inaccuracy...

My question is, is there any way to make it more stable without using any sort of accumulated impulse? (calculating the impulse from scratch each frame)

Edit:
I also realize that the code added to compensate for not having the accumulated impulse would probably make it go slower, but im still interested to know if there is any way of doing it
Erin Catto
Posts: 316
Joined: Fri Jul 01, 2005 5:29 am
Location: Irvine

Post by Erin Catto »

Please keep in mind that Box2D code was written as a tutorial, so it has not been optimized.

Before dropping accumulated impulses, I would try to make persistence faster. On the other hand, if you don't care about stacking you can drop it, but then Box2D might not be the right engine for you.
coderchris
Posts: 49
Joined: Fri Aug 18, 2006 11:50 pm

Post by coderchris »

Speed is not really a concern for me, im just interesting in getting stable stacking without accumulating any impulses. I dont plan to actually use it for anything in particular, its just for me to learn from

Getting rid of the accumulation does indeed make the stacking unstable, but theres gota be a way to go without them and still have stable stacking..

When i get rid of them (simply dont transfer it over when an arbiter is updated), the boxes dont penetrate each other, but the stacks will sway, slide on each other and eventually topple over


If there is no way to do without them, could you recommend a contact solving method that does not require and frame to frame knolledge but also has stable stacking and accurate behaviour?
Erin Catto
Posts: 316
Joined: Fri Jul 01, 2005 5:29 am
Location: Irvine

Post by Erin Catto »

The solver is trying to solve a nonlinear system of equations iteratively. By using accumulated impulses, it is able to spread the cost of an accurate solution over several time steps.

What you are trying to do is achieve the same accuracy within a single time step. I don't know how to do this without a huge additional cost.
coderchris
Posts: 49
Joined: Fri Aug 18, 2006 11:50 pm

Post by coderchris »

so in theory using a smaller timestep would do the trick?
User avatar
Erwin Coumans
Site Admin
Posts: 4221
Joined: Sun Jun 26, 2005 6:43 pm
Location: California, USA

Post by Erwin Coumans »

Hi,

I think it's better to distinguish two separate/different concepts: accumulating the impulse during the iterations. And secondly, 'warmstarting': assigning the previous frames values for the accumulated impulse at the start of the frame.

I have good stacking results in Bullet (in 3D) even when warmstarting is disabled (in fact, it is disabled by default at the moment). ODE also has no warmstarting (all contact points are re-created every frame), and no stacking issues there. So I'm curious how many iterations you use? How do you create the contact ponts? What timestep? fixed/variable? And also, what friction model do you use? How do you interleave friction with contact impulses, and what value do you use for friction: based on normal impulse of this iteration, or accumulated impulse?

Thanks,
Erwin
Erin Catto
Posts: 316
Joined: Fri Jul 01, 2005 5:29 am
Location: Irvine

Post by Erin Catto »

Yes, a smaller time step would help.

Erwin, you are correct about the two concepts. I've been talking about warm starting.

In Box2D Demo 3:

dt = 1/60 (fixed), iterations = 10
Warm starting: the vertical stack is easily stable with 10 boxes
No warm starting: the vertical stack is not stable with only 3 boxes

To turn off warm starting:
Change Arbiter.cpp (66) from
if (k > -1)
To
if (false)

When testing stacking, be sure to test vertical stacks. Walls and pyramids are much more stable and don't reveal stability problems as well.

Stacking stability was one of the main problems I had with ODE's QuickStep algorithm. That is why I pursued warm starting.

Friction:
- point-wise
- dependent on accumulated normal impulse of the current iteration
- interleaved
User avatar
Erwin Coumans
Site Admin
Posts: 4221
Joined: Sun Jun 26, 2005 6:43 pm
Location: California, USA

Post by Erwin Coumans »

Erin Catto wrote:Stacking stability was one of the main problems I had with ODE's QuickStep algorithm. That is why I pursued warm starting.
I am suprised: Bullet's CcdPhysicsDemo has 2 stacks (1 single stack of 12 boxes, and one wall). The stacks are stable without warmstarting when using SequentialImpulse and ODE quickstep, 10 iterations, 60 hertz (this is not meant to brag: previously I wrongly clipped the incremental impulse instead of the accumulated, thanks to Erin).

Perhaps some other issue then? Every single piece of the puzzle seems to make a global impact on stability/convergence.

Erwin
coderchris
Posts: 49
Joined: Fri Aug 18, 2006 11:50 pm

Post by coderchris »

Ah I see, I think i may have confused my terms. Warmstarting is whay I want to get rid of and still have stable stacking. So ODE's quickstep does this without warmstarting? I thought that used LPC or somthing

So, to get stable stacking without warmstarting, does quickstep have to be used in conjunction with bullet's SequentialImpulse solver or can bullets solver already do that by itself?
User avatar
Erwin Coumans
Site Admin
Posts: 4221
Joined: Sun Jun 26, 2005 6:43 pm
Location: California, USA

Post by Erwin Coumans »

Stable stacking can be done in Bullet using one of the 2 constraint solvers. They both work fine, and are mathematically equivalent (they are both iterative LCP solvers).

Bullet's default constraint solver is the Sequential Impulse solver, see Bullet/src/BulletDynamics/Constraint/btSequentialConstraintSolver.cpp. The other solver can be found easily in Extras. You choose one of the two. Simple.

Erwin
coderchris wrote:Ah I see, I think i may have confused my terms. Warmstarting is whay I want to get rid of and still have stable stacking. So ODE's quickstep does this without warmstarting? I thought that used LPC or somthing

So, to get stable stacking without warmstarting, does quickstep have to be used in conjunction with bullet's SequentialImpulse solver or can bullets solver already do that by itself?
coderchris
Posts: 49
Joined: Fri Aug 18, 2006 11:50 pm

Post by coderchris »

Right, and bullets sequential solver is the 3d version of erin's if i remember correctly, so how come bullet can stack things with great stability without warmstarting where if I take warmstarting out of box2d, the stability crashes?
Erin Catto
Posts: 316
Joined: Fri Jul 01, 2005 5:29 am
Location: Irvine

Post by Erin Catto »

I modified appCcdPhysicsDemo in Bullet 2.30 to be roughly equivalent to Box2D Demo 3. Is warm starting on in this version? I couldn't stack more than 7 boxes. Also, there is some high frequency jitter.

It is quite difficult to turn off deactivation in Bullet. I had to do it through the debug draw mode, but this gets messed up when I press z or x. Could this be made easier?

Code: Select all

#include "btBulletDynamicsCommon.h"
#include "LinearMath/btIDebugDraw.h"
#include "GLDebugDrawer.h"
#include <stdio.h>
#include "CcdPhysicsDemo.h"
#include "GL_ShapeDrawer.h"
#include "GlutStuff.h"

float deltaTime = 1.f/60.f;
float gCollisionMargin = 0.05f;
const int maxProxies = 32766;
const int gNumObjects = 8;
#define CUBE_HALF_EXTENTS 0.5f
GLDebugDrawer debugDrawer;

btCollisionShape* shapePtr[] = 
{
	new btBoxShape(btVector3(50,10,50)),
	new btBoxShape(btVector3(CUBE_HALF_EXTENTS, CUBE_HALF_EXTENTS, CUBE_HALF_EXTENTS)),		
};

int main(int argc,char** argv)
{
	//experimental jitter damping (1 = no damping, 0 = total damping once motion below threshold)
	extern float gJitterVelocityDampingFactor;
	gJitterVelocityDampingFactor = 0.7f;
	debugDrawer.setDebugMode(btIDebugDraw::DBG_NoDeactivation);
	CcdPhysicsDemo* ccdDemo = new CcdPhysicsDemo();
	ccdDemo->initPhysics();
	ccdDemo->setCameraDistance(12.f);
	return glutmain(argc, argv,640,480,"Bullet Physics Demo. http://bullet.sf.net",ccdDemo);
}

void CcdPhysicsDemo::clientMoveAndDisplay()
{
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 

	if (m_dynamicsWorld)
	{
		m_dynamicsWorld->stepSimulation(deltaTime,1);
	}
	
	renderme(); 
	glFlush();
	glutSwapBuffers();
}

void CcdPhysicsDemo::displayCallback(void) {

	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 
	m_dynamicsWorld->updateAabbs();
	renderme();
	glFlush();
	glutSwapBuffers();
}

inline float Random(float lo, float hi)
{
	float r = (float)rand();
	r /= RAND_MAX;
	r = (hi - lo) * r + lo;
	return r;
}

void CcdPhysicsDemo::initPhysics()
{
	btCollisionDispatcher* dispatcher = new	btCollisionDispatcher();

	btVector3 worldAabbMin(-10000,-10000,-10000);
	btVector3 worldAabbMax(10000,10000,10000);
	btOverlappingPairCache* broadphase = new btAxisSweep3(worldAabbMin,worldAabbMax,maxProxies);
	btSequentialImpulseConstraintSolver* solver = new btSequentialImpulseConstraintSolver;
	m_dynamicsWorld = new btDiscreteDynamicsWorld(dispatcher,broadphase,solver);
	m_dynamicsWorld->setGravity(btVector3(0,-10,0));
	m_dynamicsWorld->setDebugDrawer(&debugDrawer);

	btTransform tr;
	tr.setIdentity();
	
	for (int i=0; i<gNumObjects; i++)
	{
		bool isDyna = i>0;
		btCollisionShape* shape = isDyna ? shapePtr[1] : shapePtr[0];
		shape->setMargin(gCollisionMargin);

		btTransform trans;
		trans.setIdentity();
		
		if (isDyna)
		{
			float x = Random(-0.1f, 0.1f);
			float z = Random(-0.1f, 0.1f);
			btVector3 pos(x,  0.55f + 1.1f * (i - 1.0f) - 10.0f, z);
			trans.setOrigin(pos);
		}
		else
		{
			trans.setOrigin(btVector3(0,-20,0));
		}

		float mass = isDyna ? 1.f : 0.0f;
		btRigidBody* body = localCreateRigidBody(mass,trans,shape);		
		body->setFriction(0.3f);
		body->setRestitution(0.0f);
		body->setDamping(0.0f, 0.0f);

		body->setCcdSquareMotionThreshold( CUBE_HALF_EXTENTS );
		body->setCcdSweptSphereRadius( 0.2*CUBE_HALF_EXTENTS );
	}

	clientResetScene();
}
User avatar
Erwin Coumans
Site Admin
Posts: 4221
Joined: Sun Jun 26, 2005 6:43 pm
Location: California, USA

Post by Erwin Coumans »

Erin Catto wrote: It is quite difficult to turn off deactivation in Bullet. I had to do it through the debug draw mode, but this gets messed up when I press z or x. Could this be made easier?
In the demos, you can just press 'd' to toggle deactivation. But you are right, a proper API for that would be better.

OK, I see how you make conditions worse then my default: random positions instead of a perfect aligned stack makes things a little worse indeed. Also letting the boxes fall down makes things already collapse before settling down, so just takine 0.5 + 1 * i works better. Smaller box sizes are less slightly less stable (I use 1 meter boxes, you use 0.5 meter). Bullet always uses internal timestep of 60 hertz, and interpolates transforms when passing real-time delta times. So you can just pass the realtime as first argument, instead of 1./60. Otherwise it just looks like the stack collapses soon, but it takes thousands of frames in real.

I took your demo, and without the 'random' the stack can be higher, 10 boxes at least without warmstarting, with it is about 7 indeed.

Erwin
coderchris
Posts: 49
Joined: Fri Aug 18, 2006 11:50 pm

Post by coderchris »

so, basically I can either warmstart, or not warmstart and suffer the consiquences of un-stable stacking.

I read something somewhere about "shock propagation" Supposedly that lets you have fairly stable stacking. I was unable to find any articles describing how to do it, would you happen to have any links?
raigan2
Posts: 197
Joined: Sat Aug 19, 2006 11:52 pm

Post by raigan2 »

this might be a stupid question, but why wouldn't you want to warmstart?

my (limited) understanding is that by warmstarting you're just using the solution calculated by the previous frame as the initial guess for the current frame.. which seems like it would always be a better idea/closer to the current solution than just always using 0 as an initial guess.