[ODE] non static deltaTime in the step (worldStep(world,deltaTime) instead of worldStep(world,0.05)) => problem?

Adam Rotaru adam_rotaru at yahoo.com
Fri Jan 11 11:24:02 2002


--- Frédéric Marmond <fmarmond@soleri.com> wrote:
> I posted a question yesterday, which was not very
> precise.
> 
> As my frame rate may change (depending of  the
> amount of graphic to draw, and 
> of other calculations), i introduce the notion of
> deltaTime as follow:
> 
> deltaTime = timeNow - timeLast 
> then, i make a worldStep(world,deltaTime)
> Isn't it good?????
> 
> As I got strange results, I'm trying now to use it
> without graphic at all, 
> just to see where the problem is.
> 
> The problem is that when deltaTime is too small, the
> ODE is very instable.

(Rest of email snipped, see original posting).


  Hi,

This seemed like an interesting problem, so I decided
to try it.
I use ODE in double precision mode (I think this is
important),
on a 867MHz PC with Linux
(normally I don't consider this as relevant, but there
is some
real-time stuff involved here).
My network card is an 3Com Corporation 3c905C-TX
(hint: this is a ironic comment on the habit of
supplying irrelevant
info).

So I run Frederic's simple test code, with the results
he predicted.
It ran fine with the delay loop (using timesteps of
around 0.015).
Without the delay loop (using timesteps of around
0.00003), it
become unstable (that is the sphere jumped to >10
units in one step).
I note that the instability always occured when I ran
it with output
on the console, but only sometimes when redirecting
output to a file,
which implies some timing issues.

First thing I did was to modify the timestep to be
constant (to be
reproducible!). I tried both with 0.015 and 0.00003
(and other values),
and it worked fine!
Then I looked at real-time undelayed case again, and
found the first
place where the glitch occurs:

deltaTime=0.0000189841  timeNow=0.3101089895   
Pos=0.000000    0.000000        1.016625
deltaTime=0.0000190139  timeNow=0.3101280034   
Pos=0.000000    0.000000        1.016568
deltaTime=0.0000189841  timeNow=0.3101469874   
Pos=0.000000    0.000000        1.016510
deltaTime=0.0000190139  timeNow=0.3101660013   
Pos=0.000000    0.000000        1.016452
deltaTime=0.0201959908  timeNow=0.3303619921   
Pos=0.000000    0.000000        1.016394
deltaTime=0.0000219941  timeNow=0.3303839862   
Pos=0.000000    0.000000        0.950942        Touch!
deltaTime=0.0002210140  timeNow=0.3306050003   
Pos=0.000000    0.000000        0.955848        Touch!
deltaTime=0.0000430048  timeNow=0.3306480050   
Pos=0.000000    0.000000        1.005145
deltaTime=0.0000209808  timeNow=0.3306689858   
Pos=0.000000    0.000000        1.014737
deltaTime=0.0000550151  timeNow=0.3307240009   
Pos=0.000000    0.000000        1.019416
deltaTime=0.0000199974  timeNow=0.3307439983   
Pos=0.000000    0.000000        1.031687
deltaTime=0.0000190139  timeNow=0.3307630122   
Pos=0.000000    0.000000        1.036148
deltaTime=0.0000189841  timeNow=0.3307819963   
Pos=0.000000    0.000000        1.040389
deltaTime=0.0000190139  timeNow=0.3308010101   
Pos=0.000000    0.000000        1.044623
deltaTime=0.0000189841  timeNow=0.3308199942   
Pos=0.000000    0.000000        1.048864

Two things two note: the step *before* the first touch
is 0.02019 long,
around 1000 times longer than other steps (due to the
collision detection and handling?).
The instability might be caused by a long time step,
not a very short one.
The ball is penetrating for two steps. After that, it
starts to raise at a much
higher rate than it was falling (that's why it bounces
up).

With all this, I still don't have a clue what is
causing the instabiliy.
It might be that we are doing something wrong, or a
bug in ODE.
However, !!! I have a solution.
I think the basic problem is that you are simulating
too fast, and the exact
size of the time step depends on the execution time of
this (and other)
programs.  Simulating too fast is also a waste of CPU!
I suggest limiting the time step to minimum.
Keep track of the 'simulated physics time', and if
this is behind by only
a very small amount, do not make a physics simulation
step (together with collision,
of course).
The modified code is below.
I tried, with min step size of 0.001, and works fine.
(Note that if it does not simulate, it just sits in a
tight loop burning
CPU, which is of course not bad.  It should
nanosleep() for a few microseconds.).

I have to say that with min. size set to 0.0001, the
instability
happens.

deltaTime=0.0001010001  timeNow=0.2739050090   
Pos=0.000000    0.000000        1.122674
deltaTime=0.0001010001  timeNow=0.2740060091   
Pos=0.000000    0.000000        1.122402
deltaTime=0.0670830011  timeNow=0.3410890102   
Pos=0.000000    0.000000        1.122131
deltaTime=0.0001010001  timeNow=0.3411900103   
Pos=0.000000    0.000000        0.897666        Touch!
deltaTime=0.0002039969  timeNow=0.3413940072   
Pos=0.000000    0.000000        0.907899        Touch!
deltaTime=0.0001010001  timeNow=0.3414950073   
Pos=0.000000    0.000000        0.928568        Touch!

There's again a large time step which causes the
inaccuracy.
I can't explain why it works when using a *constant*
step size of 0.0001,
but not when using step sizes which are all slightly
above 0.0001, but vary.

Maybe the time step should be bounded by above too,
ie., if is is too small,
we should execute several smaller ones.

I finally managed to stabilize by limiting the step
size to between
0.00004 - 0.004.

In any case, hope this helps
--Adam


ps.: Another smart-ass comment for the end: using
'9.81' for gravity in
your physics simulation doesn't mean you understand
physics better.



----------- modified prog -----------------

#include <ode/ode.h>
#include <sys/time.h>   //for gettimeofday => time at
micro second resolution

// some constants
#define ORIG_Z 1.5
#define ORIG_MASS 8000000


static dWorldID world;
static dBodyID body;    //the body is assumed to be a
sphere, with radius of 1.
static dJointGroupID contactgroup;


static const dReal* bpos;

static  timeval tdeb;  // starting time
static  float tphys;  // physics simulation time, may
be behind
static  float tnow;
//static  float tlast;
static  float deltaTime=0.0;
static const float minDeltaTime = 0.001;

//compute the deltaTime between two frames
static void gDeltaTime()
{
        timeval tvnow;
        gettimeofday(&tvnow,0);
        
       
tnow=(tvnow.tv_sec-tdeb.tv_sec)+(tvnow.tv_usec-tdeb.tv_usec)/1000000.0;
        //deltaTime=(tnow-tlast);
        //tlast=tnow;

	float timeBehind = tnow - tphys;
	deltaTime = timeBehind;
}


//very simplist collide function:
//test if the body (sphere radius=1) touch the ground
(z=0)
static void collide()
{

  if (bpos[2]<=1.0)
  {
      printf("\tTouch!");
      dContact contact;
      contact.surface.mode = dContactSoftERP |
dContactSoftCFM;
      contact.surface.mu = dInfinity;
      contact.surface.soft_erp = 0.1;
      contact.surface.soft_cfm = 0.0;

      //depth of intrusion
      contact.geom.depth=-(bpos[2]-1.0);

      //contact is at body's position, z-=1, (body is
a sphere radius=1)
      contact.geom.pos[0]=bpos[0];
      contact.geom.pos[1]=bpos[1];
      contact.geom.pos[2]=bpos[2]-1.0;

      //the ground is the plan z=0
      contact.geom.normal[0]=0.0;
      contact.geom.normal[1]=0.0;
      contact.geom.normal[2]=1.0;

      dJointID c = dJointCreateContact
(world,contactgroup,&contact);
      //printf("\ndepth=%f",contact[i].geom.depth);
      //contact between body and the ground(static)
      dJointAttach (c,body,0);
 }
}


//basic motion
static void motion ()
{
    gDeltaTime();

    // do a complete physics step only if the physics
time it too behind
    // otherwise do nothing (this will result in a
busy loop; 
    // properly we should sleep for a small time to
give other treads a chance)
    if (deltaTime > minDeltaTime)
    {
      printf("\ndeltaTime=%.10f",deltaTime);
        printf("\ttimeNow=%.10f",tnow);
       
printf("\tPos=%f\t%f\t%f",bpos[0],bpos[1],bpos[2]);

      tphys = tnow;

      collide ();

      dWorldStep (world,deltaTime);

      // remove all contact joints
      dJointGroupEmpty (contactgroup);
    }
}


int main (int argc, char **argv)
{
  dMass m;


  // create world
  world = dWorldCreate();
  contactgroup = dJointGroupCreate (0);
  dWorldSetGravity (world,0,0,-9.81);

  // create body
  body = dBodyCreate (world);
  dBodySetPosition (body,0,0,ORIG_Z);
  dMassSetSphere (&m,1,1.0);
  dMassAdjust (&m,ORIG_MASS);
  dBodySetMass (body,&m);


  gettimeofday(&tdeb,0);
  gDeltaTime();
  gDeltaTime();
  while (true)
  {
        bpos=dBodyGetPosition(body);

        motion();

        //loop to slow down the prog.
        //if you comment this loop, the system becomes
instable!!!
        //for (long i=0;i<100000;i++)
        //{
        //        float a=cos((float)i);  //wait a
moment
        //}
  }

  dJointGroupDestroy (contactgroup);
  dWorldDestroy (world);

  return 0;
}


----------- prog ends -----------------


The hack to limit stepsize from above looks like this:

//basic motion
static void motion ()
{
    gDeltaTime();

    // do a complete physics step only if the physics
time it too behind
    // otherwise do nothing (this will result in a
busy loop;
    // properly we should sleep for a small time to
give other threads a chance)
    while (deltaTime > minDeltaTime)
    {
      float deltaTime2 = deltaTime;
      if (deltaTime2 > maxDeltaTime)
        deltaTime2 = maxDeltaTime;
      deltaTime -= deltaTime2;

      printf("\ndeltaTime=%.10f",deltaTime2);
        printf("\ttimeNow=%.10f",tnow);
       
printf("\tPos=%f\t%f\t%f",bpos[0],bpos[1],bpos[2]);

      tphys = tnow;

      collide ();

      dWorldStep (world,deltaTime2);

      // remove all contact joints
      dJointGroupEmpty (contactgroup);
    }
}


__________________________________________________
Do You Yahoo!?
Send FREE video emails in Yahoo! Mail!
http://promo.yahoo.com/videomail/