3

I'm currently reading physics in the university, and im learning python as a little hobby.

To practise both at the same time, i figured I'll write a little "physics engine" that calculates the movement of an object based on x,y and z coordinates. Im only gonna return the movement in text (at least for now!) but i want the position updates to be real-time.

To do that i need to update the position of an object, lets say a hundred times a second, and print it back to the screen. So every 10 ms the program prints the current position.

So if the execution of the calculations take 2 ms, then the loop must wait 8ms before it prints and recalculate for the next position.

Whats the best way of constructing a loop like that, and is 100 times a second a fair frequency or would you go slower, like 25 times/sec?

Jørgen
  • 3,467
  • 6
  • 33
  • 49

3 Answers3

2

Since you cannot know in advance how long each iteration will take, you need some sort of event-driven loop. A possible solution would be using the twisted module, which is based on the reactor pattern.

from twisted.internet import task
from twisted.internet import reactor

delay = 0.1

def work():
    print "called"

l = task.LoopingCall(work)
l.start(delay)

reactor.run()

However, as has been noted, don't expect a true real-time responsiveness.

Roberto Reale
  • 4,247
  • 1
  • 17
  • 21
2

The basic way to wait in python is to import time and use time.sleep. Then the question is, how long to sleep? This depends on how you want to handle cases where your loop misses the desired timing. The following implementation tries to catch up to the target interval if it misses.

import time
import random

def doTimeConsumingStep(N):
    """
    This represents the computational part of your simulation.

    For the sake of illustration, I've set it up so that it takes a random
    amount of time which is occasionally longer than the interval you want.
    """
    r = random.random()
    computationTime = N * (r + 0.2)
    print("...computing for %f seconds..."%(computationTime,))
    time.sleep(computationTime)


def timerTest(N=1):
    repsCompleted = 0
    beginningOfTime = time.clock()

    start = time.clock()
    goAgainAt = start + N
    while 1:
        print("Loop #%d at time %f"%(repsCompleted, time.clock() - beginningOfTime))
        repsCompleted += 1
        doTimeConsumingStep(N)
        #If we missed our interval, iterate immediately and increment the target time
        if time.clock() > goAgainAt:
            print("Oops, missed an iteration")
            goAgainAt += N
            continue
        #Otherwise, wait for next interval
        timeToSleep = goAgainAt - time.clock()
        goAgainAt += N
        time.sleep(timeToSleep)

if __name__ == "__main__":
    timerTest()

Note that you will miss your desired timing on a normal OS, so things like this are necessary. Note that even with asynchronous frameworks like tulip and twisted you can't guarantee timing on a normal operating system.

DanielSank
  • 3,303
  • 3
  • 24
  • 42
1

A piece of warning. You may not expect a real time on a non-realtime system. The sleep family of calls guarantees at least a given delay, but may well delay you for more.

Therefore, once you returned from sleep, query current time, and make the calculations into the "future" (accounting for the calculation time).

user58697
  • 7,808
  • 1
  • 14
  • 28