btGeneric6DofConstraint - Weld/Parent

Post Reply
User avatar
KKlouzal
Posts: 57
Joined: Thu Mar 06, 2014 5:56 am
Location: USA - Arizona

btGeneric6DofConstraint - Weld/Parent

Post by KKlouzal »

Trying to create a btGeneric6DofConstraint between two rigid bodies.
Attempting to restrict movement and rotation of the constraint in all 3 axis.
I'm able to get the positioning of the rigid bodies correct, however rotations I'm not sure about...

Code: Select all

				PrimaryNode->_RigidBody->activate();
				SecondaryNode->_RigidBody->activate();

				btTransform PrimaryFrame, SecondaryFrame;
				PrimaryFrame = PrimaryNode->_RigidBody->getWorldTransform();
				SecondaryFrame = SecondaryNode->_RigidBody->getWorldTransform();
				//	==========	Calculate Frame Positions	==========
				//
				//	Get the position of our two nodes
				btVector3 PrimaryPos = PrimaryFrame.getOrigin();
				btVector3 SecondaryPos = SecondaryFrame.getOrigin();
				//
				//	Get the distance between each node
				btScalar Dist = btDistance(PrimaryPos, SecondaryPos);
				//
				//	Calculate the vector pointing towards each node
				btVector3 PrimaryDir = (SecondaryPos - PrimaryPos).normalize();
				btVector3 SecondaryDir = (PrimaryPos - SecondaryPos).normalize();
				//
				//	Set the origin for our constraints equal to
				//	the midpoint position between each node
				PrimaryFrame.setOrigin(PrimaryDir * (Dist/2));
				SecondaryFrame.setOrigin(SecondaryDir * (Dist/2));
				//
				//	==========	Calculate Frame Rotations	==========
				//
				btQuaternion PrimaryRot = PrimaryFrame.getRotation();
				btQuaternion SecondaryRot = SecondaryFrame.getRotation();
				//
				//	not sure what to do :P
				SecondaryFrame.setRotation(PrimaryRot - SecondaryRot);
				PrimaryFrame.setRotation(SecondaryRot - PrimaryRot);

				btGeneric6DofConstraint* Constraint = new btGeneric6DofConstraint(
					*PrimaryNode->_RigidBody,
					*SecondaryNode->_RigidBody,
					PrimaryFrame,
					SecondaryFrame,
					true
				);

				Constraint->setAngularLowerLimit(btVector3(0, 0, 0));
				Constraint->setAngularUpperLimit(btVector3(0, 0, 0));
				Constraint->setLinearLowerLimit(btVector3(0, 0, 0));
				Constraint->setLinearUpperLimit(btVector3(0, 0, 0));

				_Physics->dynamicsWorld->addConstraint(Constraint, true);

			}
Upon creating the constraint, each rigid body rotation quickly snaps to what feels like a random value causing the constrained pair of bodies to go flying off in a random direction with considerable force.

Not sure what maths needs to be done to adjust rotation of 'PrimaryFrame' and 'SecondaryFrame' so that when the constraint is created, the rigid bodies rotations are unaffected.
https://i.imgur.com/cVBW812.mp4
Upon selecting the first node, then selecting the second node we apply the constraint. Each node should not move or rotate after the constraint is applied. They should appear to be 'welded/parented' together. Unfortunately each nodes rotation goes crazy, applying a random force, and shooting the pair of nodes off into the distance..
User avatar
KKlouzal
Posts: 57
Joined: Thu Mar 06, 2014 5:56 am
Location: USA - Arizona

Re: btGeneric6DofConstraint - Weld/Parent

Post by KKlouzal »

Was able to dig through the forums and finally find a helpful post.

Code: Select all

				//
				btVector3 parentAxis(1.f, 0.f, 0.f);
				btVector3 childAxis(0.f, 0.f, 1.f);
				btVector3 anchor(0.f, 0.f, 0.f);
				//
				btVector3 zAxis = parentAxis.normalize();
				btVector3 yAxis = childAxis.normalize();
				btVector3 xAxis = yAxis.cross(zAxis);
				//
				btTransform frameInW;
				frameInW.setIdentity();
				frameInW.getBasis().setValue(
					xAxis[0], yAxis[0], zAxis[0],
					xAxis[1], yAxis[1], zAxis[1],
					xAxis[2], yAxis[2], zAxis[2]
				);
				frameInW.setOrigin(anchor);
				// now get constraint frame in local coordinate systems
				btTransform frameInA = PrimaryNode->_RigidBody->getCenterOfMassTransform().inverse() * frameInW;
				btTransform frameInB = SecondaryNode->_RigidBody->getCenterOfMassTransform().inverse() * frameInW;
				// now create the constraint
				btGeneric6DofConstraint* Constraint = new btGeneric6DofConstraint(
					*PrimaryNode->_RigidBody,
					*SecondaryNode->_RigidBody,
					frameInA,
					frameInB,
					true
				);

				Constraint->setAngularLowerLimit(btVector3(0, 0, 0));
				Constraint->setAngularUpperLimit(btVector3(0, 0, 0));
				Constraint->setLinearLowerLimit(btVector3(0, 0, 0));
				Constraint->setLinearUpperLimit(btVector3(0, 0, 0));

				_Physics->dynamicsWorld->addConstraint(Constraint, true);
Now the only issue is stiffness, a chain of constrained bodies are wobbly. Also, taking a single body and 'freezing it' (setting mass and local inertias to 0) creates a tornado effect..
https://i.imgur.com/szvsRRn.mp4
User avatar
KKlouzal
Posts: 57
Joined: Thu Mar 06, 2014 5:56 am
Location: USA - Arizona

Re: btGeneric6DofConstraint - Weld/Parent

Post by KKlouzal »

Perhaps I'm going about the 'freeze' incorrectly...Instead of setting mass and inertias to 0, maybe I should apply a constraint to the world with all 6 degrees of freedom locked.

EDIT: Yes that is much better, no more 'tornado'. There is still a wave that propagates through the constrained objects, I believe the constraints aren't stiff enough. This is with 30 solver iterations and a mass of 100 on each rigid body..
https://i.imgur.com/mPrMyLv.mp4
StriderH
Posts: 3
Joined: Fri Sep 10, 2021 2:54 pm

Re: btGeneric6DofConstraint - Weld/Parent

Post by StriderH »

In my experience constraints in Bullet are never fully rigid. Even just a few bodies constrained together will show considerable divergence and wobbliness.
User avatar
KKlouzal
Posts: 57
Joined: Thu Mar 06, 2014 5:56 am
Location: USA - Arizona

Re: btGeneric6DofConstraint - Weld/Parent

Post by KKlouzal »

Never fully rigid I can deal with, considerable divergence and wobbliness however is unacceptable..
There must be some values that can be adjusted?
User avatar
drleviathan
Posts: 756
Joined: Tue Sep 30, 2014 6:03 pm
Location: San Francisco

Re: btGeneric6DofConstraint - Weld/Parent

Post by drleviathan »

You could try taking smaller substeps. The more times the simulation "solves" a system of constraints the more rigid it will be.

This is fundamentally why a system of constraints gets wobbly: the Bullet constraint solver will only iterate so many times before it concludes "good enough" and moves on to the next substep. When the constraints contradict each other (solving one tends to violate another) then it requires many iterations to arrive at the "correct" answer and when the iteration exits early you get what appear to be soft (violated) constraints.

There may be some parameters of the constraint solver that can be adjusted to force it to try harder (e.g. iterate longer) per substep, however I can't point them out: you will have to search the documentation or code for the API.

I should mention: when using the MotionState interpolation transform to position your visible shapes you can get apparent softness that is not real in the physics simulation. To avoid this, or to test to see if it is contributing to your problem, only step the simulation forward with timeSteps that are integer multiple of the substep duration. This will cause the interpolation from timestep remainder to be zero and you will get the actual body transform. Alternatively, don't use the transform supplied by the MotionState and instead get it directly from the RigidBody.

Finally, if you insist on making complicated systems of constraints and you aren't using Bullet's multibody (featherstone algorithm) feature, then for some applications it would be possible to implement a custom Verlet solver to augment Bullet's solution. I was able to get this to work well enough for a jointed "ragdoll" character however it was a very custom setup and consumed a noticeable fraction of CPU: I doubt it would work well for arbitrary collections (e.g. arbitrary sizes of parts and arbitrary numbers of constraints).
Post Reply