2D Angle Joint Limits (Box2D-Style)

Please don't post Bullet support questions here, use the above forums instead.
Post Reply
crashlander
Posts: 41
Joined: Sat Apr 08, 2006 11:20 am

2D Angle Joint Limits (Box2D-Style)

Post by crashlander » Mon Aug 06, 2007 2:52 pm

I have a physics engine that uses the concepts in Box2D at it's core.

I'm trying to implement 2D JointLimits.

I already implemented an Angle joint that constrains two bodies to remain at some fixed angle. For this I used the equations:

difference = (_body2.TotalRotation - _body1.TotalRotation) - targetAngle;
_bias = -_biasFactor * inverseDt * difference;

All is good with this joint.

For the joint limit constraint, I'd like to provide and Lower Limit and an Upper Limit and constrain the angle between 2 bodies to remain between these 2 angles.

Should I just implement this as 2 in equality constraints, one for each limit, all within a single LimitedAngleJoint class

I'd also need to allow for slop, similar to the contact constraints.

Anything else I need to take into consideration?

For reference, here is the key code from my regular AngleJoint:

Code: Select all

        public override void PreStep(float inverseDt) {
            float difference;
            difference = (_body2.TotalRotation - _body1.TotalRotation) - _targetAngle;

            _bias = -_biasFactor * inverseDt * difference;

            _accumulatedAngularImpulse *= _relaxation;

            _body1.AngularVelocity -= _body1.InverseMomentOfInertia * _accumulatedAngularImpulse;
            _body2.AngularVelocity += _body2.InverseMomentOfInertia * _accumulatedAngularImpulse;
        }

        public override void Update() {
            float angularImpulse;
            angularImpulse = (_bias - _body2.AngularVelocity + _body1.AngularVelocity) / (_body1.InverseMomentOfInertia  + _body2.InverseMomentOfInertia);
            _accumulatedAngularImpulse += angularImpulse;

            _body1.AngularVelocity -= _body1.InverseMomentOfInertia * angularImpulse;
            _body2.AngularVelocity += _body2.InverseMomentOfInertia * angularImpulse;
        }

Dirk Gregorius
Posts: 875
Joined: Sun Jul 03, 2005 4:06 pm
Location: Kirkland, WA

Post by Dirk Gregorius » Mon Aug 06, 2007 3:17 pm

The only other thing you might need to take into consideration is to reset the accumulated impulse, when either the constraint is no more violated or when in the last frame the lower limit was exceeded and now in the next frame the upper limit is exceeded.

Dirk Gregorius
Posts: 875
Joined: Sun Jul 03, 2005 4:06 pm
Location: Kirkland, WA

Post by Dirk Gregorius » Mon Aug 06, 2007 3:19 pm

I would also remove the relaxation...

crashlander
Posts: 41
Joined: Sat Apr 08, 2006 11:20 am

Post by crashlander » Tue Aug 07, 2007 11:34 am

Thanks Dirk, that helped.

I have my angle limits working (sort of). They work fine for certain configurations, but are very unstable in others. Things seem to become more unstable the further the anchor is from the involved bodies.

I'm wondering if this is just the nature of the beast or if I have something that isn't implemented quite correct.

Here is what I have:

Code: Select all

        float max;
        float difference;
        float error;
        public override void PreStep(float inverseDt) {
            if (isDisposed) { return; }
            difference = (body2.totalRotation - body1.totalRotation);

            if (difference > upperLimit) {
                upperLimitViolated = true;
                if (lowerLimitViolated) { _accumulatedAngularImpulse = 0; lowerLimitViolated = false; }
                error =  difference - upperLimit;
                max = Math.Max(0, error - _slop); //error and slop are both positive
                _bias = -_biasFactor * inverseDt * max;
            }
            else
                if (difference < lowerLimit) {
                    lowerLimitViolated = true;
                    if (upperLimitViolated) { _accumulatedAngularImpulse = 0; upperLimitViolated = false; }
                    error = lowerLimit - difference;
                    max = Math.Max(0, error - _slop);
                    _bias = _biasFactor * inverseDt * max;
                }
            else {
                upperLimitViolated = false;
                lowerLimitViolated = false;
                _accumulatedAngularImpulse = 0;
                _bias = 0;
            }
        }
        
        public override void Update() {
            if (isDisposed) { return; }

            if (upperLimitViolated || lowerLimitViolated) {
 
                float angularImpulse = 0;
                angularImpulse = (_bias-(body2.angularVelocity - body1.angularVelocity)) / (body1.inverseMomentOfInertia + body2.inverseMomentOfInertia);                
                
                _accumulatedAngularImpulse += angularImpulse;

                body1.angularVelocity -= body1.inverseMomentOfInertia * _accumulatedAngularImpulse; //angularImpulse;
                body2.angularVelocity += body2.inverseMomentOfInertia * _accumulatedAngularImpulse; //angularImpulse;
            }
        }

crashlander
Posts: 41
Joined: Sat Apr 08, 2006 11:20 am

Post by crashlander » Tue Aug 07, 2007 5:50 pm

I thought this had been discussed before:

http://www.continuousphysics.com/Bullet ... .php?t=517

Looks like I'm missing the clamping!

Post Reply