Box2dWeb revolute joint “stretching”

2019-06-09 02:04发布

问题:

I am trying to create a car-like thing in 2D using box2dweb. I have a box for the car body and two circles, connected to it by revolute joints, as the wheels. Everything works fine for a while, but gradually the gaps increase between the centers of the wheels and the points on the box they were attached to. Setting more iterations for the solver doesn't help, and sooner or later the wheels go completely away from the box. It looks like an accumulated error of some sort. Also, collision of the wheels with some bumps on the ground seem to be the most contributing factor to this problem. Initially it's like on the left picture, but after I drive it over the big ball on the ground a couple of times, it gets like on the right picture, and stays that way:

I used an older box2djs library before and didn't seem to have this problem there. The problem appeared after I ported the code to box2dweb. There are quite a few differences in how things are created in those two libraries, so I must've missed something, but don't know what.

The code for the box:

function createBox(world, x, y, width, height)
{
    var fixDef = new b2FixtureDef;
    fixDef.density = 1.0;
    fixDef.friction = 1.0;
    fixDef.restitution = 1.0;

    var bodyDef = new b2BodyDef;
    bodyDef.type = b2Body.b2_dynamicBody;
    bodyDef.position.x = x;
    bodyDef.position.y = y;

    fixDef.shape = new b2PolygonShape;
    fixDef.shape.SetAsBox(width, height);

    var b = world.CreateBody(bodyDef);
    b.CreateFixture(fixDef);
    return b;
}

For the wheels (almost same, except it creates circles):

function createBall(world, x, y, r)
{
    var fixDef = new b2FixtureDef;
    fixDef.density = 1.0;
    fixDef.friction = 1.0;
    fixDef.restitution = 1.0;

    var bodyDef = new b2BodyDef;
    bodyDef.type = b2Body.b2_dynamicBody;
    bodyDef.position.x = x;
    bodyDef.position.y = y;

    fixDef.shape = new b2CircleShape(r);

    var b = world.CreateBody(bodyDef);
    b.CreateFixture(fixDef);
    return b;
}

And for the joints:

var jointDef_rear = new b2RevoluteJointDef();   
jointDef_rear.Initialize(rear_wheel, car_body, rear_wheel.GetPosition());
jointDef_rear.maxMotorTorque = 10.0;
jointDef_rear.enableMotor = true;

rear_joint = world.CreateJoint(jointDef_rear);

var jointDef_front = new b2RevoluteJointDef();  
jointDef_front.Initialize(front_wheel, car_body, front_wheel.GetPosition());
jointDef_front.maxMotorTorque = 10.0;
jointDef_front.enableMotor = true;

front_joint = world.CreateJoint(jointDef_front);

So as much as I hate asking "what's wrong with my code"-kinda questions, what am I doing wrong here?

回答1:

Most likely, reason is in mass of the bodies.

Box2D maunal says:
Chains of bodies connected by joints may stretch if a lighter body is supporting a heavier body. For example, a wrecking ball connect to a chain of light weight bodies may not be stable. Stability degrades as the mass ratio passes 10:1.

Your front wheel is too small, and has density equal to other bodies. So, it's mass is very different from mass of car body. Try, for example, set density of the car body 0.2, density of the rear wheel over 0.5, and density front wheel over 1.5. Or better set mass manual to each body by b2Body::SetMassData. I think, it would be better to make wheels more heavy then box.



回答2:

It appears the problem was caused by zero position iterations:

world.Step(dt, iterations);

It seems that in older versions of Box2Djs the prototype of the function was exactly like that. In a newer version, however, it was changed to

function (dt, velocityIterations, positionIterations)

with all parameters defaulting to 0. Calling it the old way, like world.Step(dt, iterations) would then be equal to world.Step(dt, iterations, 0), which still "sort of" works, but the relative positions of the connected bodies aren't properly resolved. I tried calling it like world.Step(dt, iterations, iterations), and suddenly everything got fixed, even if the number of iterations is low, like 3.