Physics of a boat+rudder

AliB
Posts: 1
Joined: Mon Nov 15, 2010 2:19 pm

Physics of a boat+rudder

Post by AliB »

Hey all!
I'm a fairly experienced programmer, but new to both physics programming in general and to Bullet in particular. I'm presently working on a small, simple, project to teach myself. Essentially, it's a simplified sailing simulator. For now, I don't deal with buoyancy, for instance, and the rest is essentially done in 2d (the objects are locked so they can only move on the xz-plane and can only rotate around their y-axis).

I've only actually gotten to implementing water resistance on the hull, and a controllable rudder that's supposed to steer the thing. The hull will move through the water fine, and even comes to a halt due to the water resistance if no force is applied. As long as the rudder points straight backwards. If, however, the rudder is turned at all, it only ever results in the hull spinning uncontrollably around its y-axis. It seems reasonable to assume that's because there's no angular water friction implemented yet (though I've tried to do just that, to no avail), but it might very well be something else for all I know. So, having been stuck on this for a week or so, I figured I'd see if there was some kind soul around willing to help.

The general physics-structure so far:
- Objects are loaded from file, linked to a btRigidBody (which I've given a collision shape, mass, and local inertia) and added to my engine
- Each iteration of the main loop, an update method is called on every physics object. This method will set forces on the btRigidBodies linked to the object.
- Then, stepSimulation is called on the dynamics world
- Loop

And here's the code for the hull's update method:

Code: Select all

void Hull::update()
{
        btTransform xform;
        // mMotionState is the motion state of the btRigidBody linked to the hull
        mMotionState->getWorldTransform(xform);

        btVector3 v = mBody->getLinearVelocity();
        float s = v.length();
        if(s > 0.f)
        {
                v /= s;

                // surfaceArea() returns the area of the submerged part of the hull
                float subArea = surfaceArea();

                /* This is a formula for water resistance I found somewhere
                 * Technically, I think there's supposed to be some residual forces as well, but for now
                 * I'm ignoring them */
                float Rn = s*mCharactersticLength*waterDensity()/waterViscosity();
                float Cf = 0.075f/pow(log(Rn)-2.f, 2.f);
                float Rf = 0.5f*waterDensity()*s*s*Cf;
                float RfH = subArea*Rf;

                /* mRudderDir is a vector describing, in hull-local coordinates, where to the rudder points,
                 * so rud is supposed to be its world-space representation. mRudderDir is about half the
                 * length of the rudder, as seen from the top, to yield a good-enough spot to apply the
                 * turning force to
                 */
                btVector3 rud = xform*mRudderDir - xform*btVector3(0,0,0);

                /* This I'm not sure about, but it seemed reasonable to make the force applied due to the
                 * rudder a sine function of the angle between the rudder and the direction of travel, so that
                 * if the rudder points directly backward, no force is applied, while otherwise there will be
                 * proportionally more force the more the rudder is angled.
                 */
                float RfR = abs(sin(acos(rud.normalized().dot(v))))*mRudderArea*Rf;
    
                btVector3 f(-v.getX(), 0, -v.getZ());

                // Water friction on hull
                mBody->applyCentralForce(f*RfH);

                btVector3 rF = f*RfR;
                // mRudderAnchor is the point on the hull around which the rudder rotates
                btVector3 rP = xform*mRudderAnchor + rud;

                // Water friction on rudder
                mBody->applyForce(rF, rP);
        }
        // That should have taken care of linear water resistance, but what do I do about angular?
        // I haven't found any resources (that I can understand) on that... I have, however, tried some things along the lines of
        v = mBody->getAngularVelocity();
        s = v.length();
        if(s > 0.f)
        {
                 v /= s;
                 // Various calculations on the speed, submerged area, characteristic length etc, that I quite frankly don't know how to do
                 float magnitude = torque_to_apply;
                 mBody->applyTorque(btVector3(0, -v.y()*magnitude, 0));
        }

        // Test to see how the hull would behave if given forward propulsion (-z in hull-space is "forward")
        mBody->applyCentralForce(xform*btVector3(0,0,-200)-xform*btVector3(0,0,0));
}
Any help is much appreciated!
sparkprime
Posts: 508
Joined: Fri May 30, 2008 2:51 am
Location: Ossining, New York

Re: Physics of a boat+rudder

Post by sparkprime »

In order to stop it spinning out of control you have to debug it very carefully to see whether your model is accurate and find out where obscure values are coming from (either a typo in the maths or a more fundamental problem).

For bouyancy, there is a neat trick where you cast rays from some nominal 'lowest' plane up at the boat, underwater. This scans out a heightmap showing the submerged part of the boat (you ignore everything that is higher than the water's surface). From the heightmap you can calculate the displacement at each point of the scanned grid.

For the rudder, perhaps your sin() approximation is not good enough. I would start with the simplest model -- applying a torque to the boat.

It may be that you need a dagger/centreboard -- this causes significantly more drag in the sideways component of the hull's local velocity than in the forwards component. Without it, the boat will drift sideways if you turn it, which you don't want. To implement this, simply break the velocity into these two components and apply a force to retard the latitudinal velocity component.