[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/