Questions about the btGeneric6DofConstraint

User avatar
TomorrowPlusX
Posts: 14
Joined: Wed Apr 16, 2008 11:45 am

Questions about the btGeneric6DofConstraint

Post by TomorrowPlusX »

Hi,

I'm attempting to write a joint similar in function to ODE's "universal joint". For those not familar with the universal joint, it's like having two hinges perpendicular to eachother to form a joint between two bodies. Here's ODE's info on it:

http://opende.sourceforge.net/wiki/inde ... #Universal

I figured I could use a 6 DOF constraint set up such that the angular limits would represent the two perpendicular axes.

Here's the relevant code -- note that the function STB converts my matrix/vector types to bullet's.

Code: Select all


// create a matrix with x and y axes corresponding to universal joint parent/child axes
vec3 xAxis = normalize( parentAxis );
vec3 yAxis = normalize( childAxis );
vec3 zAxis = cross( xAxis, yAxis );     
yAxis = cross( xAxis, zAxis );

mat4 pivot;
pivot.setRow(0, vec4( xAxis,0 ) );
pivot.setRow(1, vec4( yAxis,0 ) );
pivot.setRow(2, vec4( zAxis,0 ) );

// position it at "anchor" point
pivot.setCol(3, vec4( anchor, 0 ));

// create local reference frames for the pivot
btTransform frameInA = STB( parent->transform().inverse() * pivot );
btTransform frameInB = STB( child->transform().inverse() * pivot );

_constraint = new btGeneric6DofConstraint( *bulletParent->bulletRigidBody(), *bulletChild->bulletRigidBody(), frameInA, frameInB, true ); 

const btVector3 linearLimit = STB( child->position() - pivot.position() );
_constraint->setLinearLowerLimit( linearLimit );
_constraint->setLinearUpperLimit( linearLimit );
        
_constraint->setAngularLowerLimit( btVector3( -PI/4, -PI/4, 0 ) );
_constraint->setAngularUpperLimit( btVector3( +PI/4, +PI/4, 0 ) );

Needless to say this doesn't really work. I assume that I'm just not grokking how one actually sets up a btGeneric6DofConstraint. I've gone over documentation, but I haven't found anything that helps me to understand the 6 DOF constraint.

The way I see it, I could be screwing up the frameInA & frameInB, or I could be screwing up the application of angular/linear limits afterwards -- or both.

Am I barking up the wrong tree? Perhaps there's a constraint more suited to this purpose? Either way, I clearly don't get the 6 DOF constraint and any pointers as to how to use it would help me a lot.
User avatar
rponomarev
Posts: 56
Joined: Sat Mar 08, 2008 12:37 am

Re: Questions about the btGeneric6DofConstraint

Post by rponomarev »

Hello,

Yes, the luck of documentation on 6DOF constraint is a problem.
We'll try to cover this in the near future.
Also we are going to simplify setup of ODE-like joints in the next Bullet version (2.75),
see http://code.google.com/p/bullet/issues/detail?id=225

Looks like you didn't set frames and limits in a right way
The following code works (you can drop it into appConstraintDemo and play with it)

Code: Select all

#if 1
{ 
	// create a universal joint using generic 6DOF constraint
	// create two rigid bodies
	// static bodyA (parent) on top:
	btTransform tr;
	tr.setIdentity();
	tr.setOrigin(btVector3(btScalar(0.), btScalar(4.), btScalar(0.)));
	btRigidBody* pBodyA = localCreateRigidBody( 0.0, tr, shape);
	pBodyA->setActivationState(DISABLE_DEACTIVATION);
	// dynamic bodyB (child) below it :
	tr.setIdentity();
	tr.setOrigin(btVector3(btScalar(0.), btScalar(0.), btScalar(0.)));
	btRigidBody* pBodyB = localCreateRigidBody(1.0, tr, shape);
	pBodyB->setActivationState(DISABLE_DEACTIVATION);
	// add some (arbitrary) data to build constraint frames
	btVector3 parentAxis(1.f, 0.f, 0.f); 
	btVector3 childAxis(0.f, 0.f, 1.f); 
	btVector3 anchor(0.f, 2.f, 0.f);
	// build frame basis
	// 6DOF constraint uses Euler angles and to define limits
	// it is assumed that rotational order is :
	// Z - first, allowed limits are (-PI,PI);
	// new position of Y - second (allowed limits are (-PI/2 + epsilon, PI/2 - epsilon), where epsilon is a small positive number 
	// used to prevent constraint from instability on poles;
	// new position of X, allowed limits are (-PI,PI);
	// So to simulate ODE Universal joint we should use parent axis as Z, child axis as Y and limit all other DOFs
	// Build the frame in world coordinate system first
	btVector3 zAxis = parentAxis.normalize();
	btVector3 yAxis = childAxis.normalize();
	btVector3 xAxis = yAxis.cross(zAxis); // we want right coordinate system
	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 = pBodyA->getCenterOfMassTransform().inverse() * frameInW;
	btTransform frameInB = pBodyB->getCenterOfMassTransform().inverse() * frameInW;
	// now create the constraint
	btGeneric6DofConstraint* pGen6DOF = new btGeneric6DofConstraint(*pBodyA, *pBodyB, frameInA, frameInB, true);
	// linear limits in our case are allowed offset of origin of frameInB in frameInA, so set them to zero
	pGen6DOF->setLinearLowerLimit(btVector3(0., 0., 0.));
	pGen6DOF->setLinearUpperLimit(btVector3(0., 0., 0.));
	// set limits for parent (axis z) and child (axis Y)
	pGen6DOF->setAngularLowerLimit(btVector3(0.f, -SIMD_HALF_PI * 0.5f, -SIMD_HALF_PI * 0.5f));
	pGen6DOF->setAngularUpperLimit(btVector3(0.f,  SIMD_HALF_PI * 0.5f,  SIMD_HALF_PI * 0.5f));
	// add constraint to world
	m_dynamicsWorld->addConstraint(pGen6DOF, true);
	// draw constraint frames and limits for debugging
	pGen6DOF->setDbgDrawSize(btScalar(10.f));
}

#endif
Hope this will help,
Roman
User avatar
projectileman
Posts: 109
Joined: Thu Dec 14, 2006 4:27 pm
Location: Colombia

Re: Questions about the btGeneric6DofConstraint

Post by projectileman »

Hi.

If you want to set the rotation axis as fixed in a 6DOF Joint, you must provide epsilons for convenience. I'm not sure, but maybe zero-length ranges could have problems in the constraint solver.

So this code:

Code: Select all

_constraint->setAngularLowerLimit( btVector3( -PI/4, -PI/4, 0 ) );
_constraint->setAngularUpperLimit( btVector3( +PI/4, +PI/4, 0 ) );


Must be fixed as following

Code: Select all

_constraint->setAngularLowerLimit( btVector3( -PI/4, -PI/4, -epsilon ) );
_constraint->setAngularUpperLimit( btVector3( +PI/4, +PI/4, epsilon ) );

// epsilon is a very little value f.e. : 0.00000001f


I hope that it solves your problem.

However, I can't understand what effect do you want with this code:

Code: Select all

const btVector3 linearLimit = STB( child->position() - pivot.position() );
_constraint->setLinearLowerLimit( linearLimit );
_constraint->setLinearUpperLimit( linearLimit );
If you want to set a separation between the linked bodies, It would be better setting the Anchor transformation (which is defined in local space reference). May child->position() returns the global position.


About the documentation, it's true that it needs some improvements. However you can find the existing documentation of btGeneric6DofConstraint by reading its header file, f.e. look at the line 204 in this file:
http://code.google.com/p/bullet/source/ ... nstraint.h

It provides info about the valid ranges for angular limits. The comments after line 217 are suposedly a table in html with the angular limit range values.

At last, I also want to give thanks to Roman. He did great improvements to the constraint system in Bullet. Thanks Roman. And if you want to improve the documentation of btGeneric6DofConstraint, just add a '>' character at line 218; just replace: "<tr" with "<tr>". That was causing rendering problems with Doxygen, so the table of values couldn't be shown. Also you must replace the sentence that says "At this moment translational motors are not supported. May be in the future" because you actually did implement translational motors in this joint.
User avatar
TomorrowPlusX
Posts: 14
Joined: Wed Apr 16, 2008 11:45 am

Re: Questions about the btGeneric6DofConstraint

Post by TomorrowPlusX »

rponomarev wrote:Hello,

Yes, the luck of documentation on 6DOF constraint is a problem.
We'll try to cover this in the near future.
Also we are going to simplify setup of ODE-like joints in the next Bullet version (2.75),
see http://code.google.com/p/bullet/issues/detail?id=225

Looks like you didn't set frames and limits in a right way
The following code works (you can drop it into appConstraintDemo and play with it)

Code: Select all

... code
Hope this will help,
Roman
Roman,

That was exactly it -- it seems I was almost on the right track regarding the frame, but was wrong regarding the euler angles. Thank you so much -- though once I power this joint I may have some more questions.

Thank you!
User avatar
TomorrowPlusX
Posts: 14
Joined: Wed Apr 16, 2008 11:45 am

Re: Questions about the btGeneric6DofConstraint

Post by TomorrowPlusX »

I have another question regarding this universal joint. Note, I've implemented something like servo control using the angular motors of the 6DOF constraint. Where you can specify a target angle, and my code sets a target velocity and torque on the angular motor to move to that target angle.

However, when the joint reaches its limit ( say, 45 degrees ), the motor for that axis seems to stop working. So it's at 45 degrees, and I tell it to move to -20, and it sits there until something "wakes up" one of the rigid bodies.

Is this normal? I've tried manually enabling those bodies after setting the motor params but it doesn't seem to actually do anything. Do I need to simply disable deactivation all together?

EDIT
A better way to put this is that when the angle has reached a limit ( low or high ) applying torque and velocity to the motor doesn't work. So if the limit's -30 to +30 degrees, everything works fine so long as the angle is within that range, say -29 to +29, but at the limits, it's as if the motor is disabled.

For some reason it wakes up if an external force is applied to the system. For example my mouse-picking code which applies external force to move objects wakes up the system.
User avatar
TomorrowPlusX
Posts: 14
Joined: Wed Apr 16, 2008 11:45 am

Re: Questions about the btGeneric6DofConstraint

Post by TomorrowPlusX »

As a followup, I've tried disabling deactivation, I've tried all sort of silliness. But as soon as the angular motor reaches its limit, powered or not, the motor simply stops dead.

Is there nothing I can do?
User avatar
rponomarev
Posts: 56
Joined: Sat Mar 08, 2008 12:37 am

Re: Questions about the btGeneric6DofConstraint

Post by rponomarev »

Hello,

Try to upgrade to the latest trunk version - I recently committed a few fixes for the 6DOF constraint.
Anyway I'll try to reproduce this situation.
A little snippet of your code will really help :-)

Thanks,
Roman
User avatar
TomorrowPlusX
Posts: 14
Joined: Wed Apr 16, 2008 11:45 am

Re: Questions about the btGeneric6DofConstraint

Post by TomorrowPlusX »

Thanks,

I may check out the trunk, but I can wait until 2.75 comes out, since I'm also struggling with a refactor of my voxel terrain system right now!

Anyway, here's what my code looks like. Maybe I'm doing something stupid:

Code: Select all

//
// The PIDs determine target velocity based on desired position vs current position, rate, etc.
//

_parentPID.step( time );
_childPID.step( time );


btRotationalLimitMotor *parentMotor = _constraint->getRotationalLimitMotor(2),
                       *childMotor = _constraint->getRotationalLimitMotor(1);   

// These are made up, and achieve nothing useful here
//childMotor->m_bounce = parentMotor->m_bounce = 0;
//childMotor->m_limitSoftness = parentMotor->m_limitSoftness = 0.25;
//childMotor->m_ERP = parentMotor->m_ERP = 0.1;

parentMotor->m_enableMotor = _parentPID.torque > 0;
parentMotor->m_targetVelocity = _parentPID.velocity;
parentMotor->m_maxMotorForce = _parentPID.torque;

childMotor->m_enableMotor = _childPID.torque > 0;
childMotor->m_targetVelocity = _childPID.velocity;
childMotor->m_maxMotorForce = _childPID.torque;
Please note: I've verified that I'm applying meaningful velocity and torques to the motors...
User avatar
rponomarev
Posts: 56
Joined: Sat Mar 08, 2008 12:37 am

Re: Questions about the btGeneric6DofConstraint

Post by rponomarev »

Hello,

Looks like your code should work with the latest version from trunk (I suppose that m_maxMotorForce is always positive)
Actually you need to update 2 files : btGeneric6DofConstraint.cpp and btGeneric6DofConstraint.h

Hope this will help,
Roman
User avatar
TomorrowPlusX
Posts: 14
Joined: Wed Apr 16, 2008 11:45 am

Re: Questions about the btGeneric6DofConstraint

Post by TomorrowPlusX »

That's encouraging -- I'll do exactly that. Thanks!

Out of curiousity, is this a bug which has been fixed? Or is it more of a behavior which made sense once, but no longer does?