在Python中循环计时不准确

时间:2018-05-22 15:53:54

标签: python raspberry-pi

我正在尝试使用Raspberry Pi 3 B +以高采样率记录数据。为了达到固定的采样率,我推迟了while循环,但总是得到一个比我指定的少一点的采样率。

对于2500 Hz,我得到~2450 Hz

对于5000 Hz,我得到~4800 Hz

对于10000 Hz,我得到~9300 Hz

这是我用来延迟while循环的代码:

import time

count=0
while True:
    sample_rate=5000

    time_start=time.perf_counter()
    count+=1
    while (time.perf_counter()-time_start) < (1/sample_rate):
        pass

    if count == sample_rate:
        print(1/(time.perf_counter()-time_start))
        count=0

我也尝试过更新到Python 3.7并使用time.perf_counter_ns(),但它没有什么区别。

2 个答案:

答案 0 :(得分:1)

我认为您可以通过重新安排代码来轻松解决这个问题:

import time

count=0 
sample_rate=5000
while True:
    time_start=time.perf_counter()

    # do all the real stuff here

    while (time.perf_counter()-time_start) < (1/sample_rate):
        pass

这样python会在您执行代码之后而不是之前执行等待,因此解释程序运行它所花费的时间将不会添加到您的采样率中。正如丹尼所说,它是一种解释性语言,因此可能会引入时间不一致,但这种方式至少应该会降低你所看到的效果。

编辑以证明其有效:

import sys
import time

count=0 
sample_rate=int(sys.argv[1])
run_start = time.time()

while True:
    time_start=time.time()

    a = range(10)
    b = range(10)
    for x in a:
        for y in b:
            c = a+b
    count += 1
    if count == sample_rate*2:
        break
    while (time.time()-time_start) < (1.0/sample_rate):
        pass

real_rate = sample_rate*2/(time.time()-run_start)
print real_rate, real_rate/sample_rate     

因此,测试代码会在2秒内执行大量随机垃圾,然后打印实际费率和实际费率的百分比。以下是一些结果:

~ ><> python t.py 1000
999.378471674 0.999378471674
~ ><> python t.py 2000
1995.98713838 0.99799356919
~ ><> python t.py 5000
4980.90553757 0.996181107514
~ ><> python t.py 10000
9939.73553783 0.993973553783
~ ><> python t.py 40000
38343.706669 0.958592666726  

所以,不完美。但绝对比在期望的10000下降~700Hz更好。接受的答案肯定是正确的。

答案 1 :(得分:1)

您看到的问题是因为您的代码每次在循环中使用实时时间,因为它会在一段时间内开始每个延迟 - 因此,由于操作系统多任务处理而导致的不定时代码和抖动所花费的时间会累积,从而降低整体期间低于你想要达到的目标。

为了大大提高定时精度,请使用每个循环“应该”在应该启动后的周期(1 / sample_rate)完成的事实 - 并将该开始时间保持为绝对计算而不是实时,并且等到绝对开始时间之后的时间段,然后时间没有漂移。

我把你的时间安排到timing_orig和修改后的代码中使用绝对时间到timing_new - 结果在下面。

var array = [
    {name: "one", value:"data1", caption:"aaa"},
    {name: "two", value:"data2", caption:"bbb"},
    {name: "three", value:"data3", caption:"ccc"},
    {name: "four", value:null, caption:"ddd"},
    {name: "five", value:"", caption:"eee"}
]

var result = array.filter(item => item.value)

console.log(result);

结果:

import time

def timing_orig(ratehz,timefun=time.clock):
    count=0
    while True:
        sample_rate=ratehz

        time_start=timefun()
        count+=1
        while (timefun()-time_start) < (1.0/sample_rate):
            pass

        if count == ratehz:
            break

def timing_new(ratehz,timefun=time.clock):
    count=0
    delta = (1.0/ratehz)
    # record the start of the sequence of timed periods
    time_start=timefun()
    while True:
        count+=1
        # this period ends delta from "now" (now is the time_start PLUS  a number of deltas)
        time_next = time_start+delta
        # wait until the end time has passed
        while timefun()<time_next:
            pass
        # calculate the idealised "now" as delta from the start of this period
        time_start = time_next
        if count == ratehz:
            break

def timing(functotime,ratehz,ntimes,timefun=time.clock):
    starttime = timefun()
    for n in range(int(ntimes)):
        functotime(ratehz,timefun)
    endtime = timefun()
#   print endtime-starttime
    return ratehz*ntimes/(endtime-starttime)

if __name__=='__main__':
    print "new 5000",timing(timing_new,5000.0,10.0)
    print "old 5000",timing(timing_orig,5000.0,10.0)
    print "new 10000",timing(timing_new,10000.0,10.0)
    print "old 10000",timing(timing_orig,10000.0,10.0)
    print "new 50000",timing(timing_new,50000.0,10.0)
    print "old 50000",timing(timing_orig,50000.0,10.0)
    print "new 100000",timing(timing_new,100000.0,10.0)
    print "old 100000",timing(timing_orig,100000.0,10.0)

注意我没有使用time.sleep(),因为它引入了太多的抖动。此外,请注意,尽管这个最小的示例在我的Windows笔记本电脑上显示非常准确的时序甚至高达100khz,但如果您将更多代码放入循环中而不是执行时间,则时间将相应地变慢。

道歉我使用的是Python 2.7,它没有非常方便的time.perf_counter()函数 - 为每个对timing()的调用添加一个额外的参数timefun = time.perf_counter()