Skip to main content

A simple physics/dynamics stack

Posted by herkules on September 23, 2007 at 10:09 AM PDT

Are you doing engineering using Java? Or even science? Than you sometimes might need to calculate how objects move under
the impression of forces and torques. The following might be for you then...




Maybe somebody of you, dear reader, has already tried out my flight simulator
FlyingGuns which is part of the
Distributed RealTime Simulation project on SourceForge.
The flight model of motion is very simple.
I call it physically motivated instead of physical because it relies on some heuristics
that are not modeled using forces and torques. That is appropriate for a game but not for a true simulator.




There is no framework to plug in forces that are then correctly treated. This is the main reason why
the airplane cannot land, because there is just no mean to model the forces caused by the undercarriage on the ground.




In order to change this, I started to develop a simple dynamics framework.
It currently is located in the projects sandbox
because the API is not fully settled yet.
With this package, it is easy to define bodies that behave physically correct. This is a
piece of sample code to setup a 5kg body with gravity G and a Spring:




Body body = new Body();
body.setMass(5.0);

KinematicState state = new KinematicState();
state.attitude(QuatUtil.ONEd);

CompositeForce f = new CompositeForce();
f.addForce(new G(body));

Vector3d springcenter = new Vector3d(0,0,5);
Spring s = new Spring(springcenter, 5 );
f.addForce( s );

Dynamic dynbody = new Dynamic(body, state, f);




I call it dynamics stack to keep it apart from a physics engine like
ODEJava or JOODE.
A physics engine is a far more complex beast dealing a lot with collision detection
and - as a main task - computing the appropriate forces. But any physics engine
needs some kind of dynamics code under the hood.



There is no secret in how to do that. The math is known for hundreds of years meanwhile.
Yet some aspects, esp. those concerning rotational motion are highly unintuitive
(it took me 2 years to develop kind of mental image).
Having this thoroughly solved makes the value of this package.



Stack

Why is it a stack? Because on each level of the stack there is a set of classes implementing the interface of the next lower level.
Each level of the stack can be used on its own, omitting higher levels as desired.
These are the stacked components:

  • intergrator
  • kinematics
  • dynamics
  • physics
  • application, e.g. flight model

dynamics_overview.jpg



Integration

Integration has the task to solve ordinary differential equations (ODE). It gets some initial
values and an object that can calculate the resp. derivatives in time d/dt. The integrator has no idea
what the values do mean - it has no notion of e.g. position or speed.


There are several well-known ways to do that with different quality and performance.
My package implements Euler and RungeKutta. While the algorithms are
not easy to understand, the resp. code is quite simple because the algorithms are very
well described in various textbooks
and there are many samples on the net.
Cash-Karp (see Numerical Recipes in C)
is currently under development.



The Euler integrator is the most simple one basically perfoming:



x_new = x_old + v*dt



Thats very fast, but only works if forces are small and do hardly ever change.
A common spring already may blow that approach.
RungeKutta is much more advanced but also takes (at least) four times more CPU power.




Kinematics

Kinematics gives a meaning to terms like position or speed and can transform them to
the array of values needed by the integrator. It also respects that we are dealing with second
order differential equations (eg. position is order 0, velocity is order 1, acceleration is order 2)
and transforms them into a system of equations of first order:


One second order equation


position = f( velocity, acceleration )
       

makes 2x first order equation:


position = f(velocity)
velocity = f(acceleration)
       

On the kinematics level, the interface Acceleration plays the key role. On this level,
there still is no notion of a phyical body having mass properties.




Dynamics

Dynamics introduces the phyical body having mass and inertia. A class implementing
the kinematics interface Acceleration converts forces and torques acting on a
physical body into acceleration. While this is trivial for translational properties (just divide
by mass - F=ma - remember?), the rotational parts needs some consideration concerning world-
and body-fixed coordinate systems and the transforms between them.

On the dynamics level, the interface Force plays the key role.




Physics

On this level, forces and torques are defined. It is not truely a level in the stack, but more
a collection of utilities. There are predefined forces like gravity G or a spring.
Conceptually harder is a class called RotatingPart which allows to model parts
within a body that do rotate themselves. Think of propellers or turbines as an example.
Again, this rotational things are not easy to deal with on an intuitive level.




Tests

Now how to test dynamics? I took two approaches. One is the classical unit test comparing the results of
the integration with a know analytical solution. Here is one taken from a textbook:



	
// problem 10-32 in my edition of Classical Dynamics by Marion+Thornton
@Test
public void spinningPlate()
{
    Body b = new Body();

    // I1 = 1, I2 = 2*I1, I3 = I1 + I2
    b.setInertia(new Matrix3d(1.0, 0.0, 0.0, 0.0, 2.0, 0.0, 0.0, 0.0, 3.0));

    KinematicState state = new KinematicState();
    state.omega(new Vector3d(Math.sqrt(3), 0, 1));

    // with no forces and torques, w_body_y(t) should be |w| cos(a) tanh(|w| t sin(a)).
    // a = 30 degrees, so cos(a) = sqrt(3)/2 and sin(a) = 1/2.  the expected result is
    // thus sqrt(3) * tanh(t).

    Dynamic d = new Dynamic(b, state, Force.NONE );

    double dt = 1.0/1000.0;
    double t = 0;
    for( int i = 0; i < 300; i++ )
    {
        d.progress((long)t, dt);
        t += dt;
// System.out.println(i + " " + Math.sqrt(3)*Math.tanh(t) + " " +  state.omega().y);
        assertEquals(Math.sqrt(3)*Math.tanh(t), state.omega().y, 0.05 );
    }
}




The other approach is piecewise comparison with a human judging from the visuals. E.g. assuming euler
integration works, I run the system with a RungeKutta integration. The results have to be
comparable. The same can be done to show that the calculation of acceleration from forces work correct
or that a rotating body behaves like one that does not rotate, but has a rotating part with same
inertia.



screenshot_DynamicSample.jpg




Next

Things to come (besides an application for a new flight model for FlyingGuns) is some
kind of exception handling in case of overstress. Sometime, forces may go beyond their limits
that e.g. may destroy the structure. This could be implemented using exceptions.




Links

Related Topics >>