【问题标题】:How to avoid undesired additional velocity from time step change with Verlet integrator如何使用 Verlet 积分器避免时间步长变化带来的额外速度
【发布时间】:2011-06-12 00:19:48
【问题描述】:

我知道这个标题很挑人,但这主要是一个副作用问题。我正在编写一个 Android 应用程序,我可以将它与我在物理课上学习的数学一起使用。这是一个 2D 弹跳球应用程序。我正在使用时间校正的 Verlet 积分器,在屏幕底部的地板上有一个脉冲。我正在增加摩擦力和弹力,以便球最终达到 0 速度。

当球停在地板上并且发生“重大”时间步长变化时,问题就会出现。积分器完美地调整速度并最终在地板上发射脉冲。当速度的绝对值大于 2.5 时,脉冲触发。 Android 的 GC 通常会导致时间调整为 -18 的速度。

感谢任何帮助。我意识到代码结构可能会更好,但我只是想通过可视化和应用物理来获得乐趣。谢谢。

// The loop
public void run() {
    if(mRenderables != null) {
        final long time = SystemClock.uptimeMillis();
       final long timeDelta = time - mLastTime;

        if(mLastTime != 0) {
            final float timeDeltaSeconds = timeDelta / 1000.0f; 

            if(mLastTimeDeltaSec != 0) {                    
                for(short i = 0; i < mRendLength; i++) {
                    Ball b1 = mRenderables[i];

                    // Acceleration is gauged by screen's tilt angle
                    final float gravityX = -mSV.mSensorX * b1.MASS;
                    final float gravityY = -mSV.mSensorY * b1.MASS;

                    computeVerletMethod(b1, gravityX, gravityY, timeDeltaSeconds, mLastTimeDeltaSec);
                }
            }

            mLastTimeDeltaSec = timeDeltaSeconds;
        }

        mLastTime = time;
    }
}

/*
* Time-Corrected Verlet Integration
* xi+1 = xi + (xi - xi-1) * (dti / dti-1) + a * dti * dti
*/  
public void computeVerletMethod(Renderable obj, float gravityX, float gravityY, float dt, float lDT) {
    mTmp.x = obj.pos.x;
    mTmp.y = obj.pos.y;

    obj.vel.x = obj.pos.x - obj.oldPos.x;
    obj.vel.y = obj.pos.y - obj.oldPos.y;
    // Log "1." here        

    resolveScreenCollision(obj);

    obj.pos.x += obj.FRICTION * (dt / lDT) * obj.vel.x + gravityX * (dt * dt);
    obj.pos.y += obj.FRICTION * (dt / lDT) * obj.vel.y + gravityY * (dt * dt);

    obj.oldPos.x = mTmp.x;
    obj.oldPos.y = mTmp.y;
    // Log "2." here
}

// Screen edge detection and resolver
public void resolveScreenCollision(Renderable obj) {
   final short xmax = (short) (mSV.mViewWidth - obj.width);
   final short ymax = (short) (mSV.mViewHeight - obj.height);
   final float x = obj.pos.x;
   final float y = obj.pos.y;

   // Only testing bottom of screen for now     
   if (y > ymax) {
    // ...
    } else if (y < 0.5f) {
        if(Math.abs(obj.vel.y) > 2.5f) {
            float imp = (obj.MASS * (obj.vel.y * obj.vel.y) / 2) * obj.RESTITUTION / obj.MASS;
            obj.vel.y += imp;
           // Log "bounce" here
        } else {
            obj.vel.y = obj.pos.y = obj.oldPos.y = mTmp.y = 0.0f;
        }
    }
}

当球停在地板上并且突然冲动发生时输出 (参见“log”cmets 的代码)

1.  vel.y: -0.48258796
2.  pos.y: -0.42748278 /oldpos.y: 0.0 /dt: 0.016 /ldt: 0.017

1.  vel.y: -0.42748278
dalvikvm  GC_FOR_MALLOC freed 8536 objects / 585272 byte s in 74ms
2.  pos.y: -0.48258796 /oldpos.y: 0.0 /dt: 0.017 /ldt: 0.016

1.  vel.y: -0.48258796
2.  pos.y: -18.061148 /oldpos.y: 0.0 /dt: 0.104 /ldt: 0.017

1.  vel.y: -18.061148
bounce  imp: 124.35645
2.  pos.y: 13.805508 /oldpos.y: -18.061148 /dt: 0.015 /ldt: 0.104

【问题讨论】:

    标签: java android garbage-collection physics


    【解决方案1】:

    您不应该使用基于上一次计算的时间步长的时间步长,因为如果您还没有该时间步长,那么这可能会导致诸如此类的问题和碰撞检测错误。相反,对于每次更新,您应该为每次更新设置一个“时间块”或最大时间量。例如:假设您想要 30fps 并且在纳米时间中大约是 33333333。所以 33333333 = 1 个时间块。那么你可以做一个while循环

    long difftime = System.nanoTime() - lastTime;
    static long fpstn = 1000000000 / 30;
    static int maxtimes = 10;// This is used to prevent what is commonly known as the spiral of death: the calcutaions are longer that the time you give them. in this case you have to increase the value of a base timechunk in your calculations
    for (int i = 0; i < maxtimes; i++) {
        if (difftime >= fpstn) {
            world.updateVerlet(1);
        } else {
            world.updateVerlet((float)diffTime / (float)fpstn);
        }
        difftime -= fpstn;
        if (difftime <= 0)
            break;
    }
    

    【讨论】:

    • 7 个月后...我有一个使用固定时间步长的新物理模拟器。我按照 Glenn Fiedler 的 timestep article 作为参考。
    【解决方案2】:

    很难确定,但问题似乎不是时间步的增加,而是时间步的。您将 Verlet 积分和弹跳作为单独的过程进行,因此,如果球从地板上的静止位置开始,时间步长很大,它会落入地板很远,加快速度,然后再反射到空中。保持时间步长小,你不会有这个问题......很多。

    【讨论】:

    • 我将时间步长增加到 3 和 4 的幂,并注意到当系统滞后时球的位置会跳跃。好消息是 -Y 速度足够低,不会触发脉冲。坏消息是现在一切都变慢了。嗯,它实际上也永远不会获得足够的速度来进行初始反弹。
    猜你喜欢
    • 2016-07-19
    • 1970-01-01
    • 2023-03-08
    • 1970-01-01
    • 2021-09-01
    • 1970-01-01
    • 2012-01-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多