Python,Raspberry pi,每10毫秒精确地调用一个任务

Python,Raspberry pi,每10毫秒精确地调用一个任务,python,multithreading,timer,raspberry-pi,periodic-task,Python,Multithreading,Timer,Raspberry Pi,Periodic Task,我目前正在尝试使用一个称为每隔10毫秒的函数从传感器获取数据 基本上,我是从gpio中断触发回调的,但我更改了传感器,当前使用的传感器没有INT引脚来驱动回调 因此,我的目标是拥有相同的行为,但有一个由计时器生成的内部中断 我试过这个 但是当我运行它的时候,我可以看到计时器并不是很精确,它是随着时间而变化的,正如你所看到的 1494418413.1584847 stackoverflow 1494418413.1686869 stackoverflow 1494418413.1788757 st

我目前正在尝试使用一个称为每隔10毫秒的函数从传感器获取数据

基本上,我是从gpio中断触发回调的,但我更改了传感器,当前使用的传感器没有INT引脚来驱动回调

因此,我的目标是拥有相同的行为,但有一个由计时器生成的内部中断

我试过这个

但是当我运行它的时候,我可以看到计时器并不是很精确,它是随着时间而变化的,正如你所看到的

1494418413.1584847
stackoverflow
1494418413.1686869
stackoverflow
1494418413.1788757
stackoverflow
1494418413.1890721
stackoverflow
1494418413.1992736
stackoverflow
1494418413.2094712
stackoverflow
1494418413.2196639
stackoverflow
1494418413.2298684
stackoverflow
1494418413.2400634
stackoverflow
1494418413.2502584
stackoverflow
1494418413.2604961
stackoverflow
1494418413.270702
stackoverflow
1494418413.2808678
stackoverflow
1494418413.2910736
stackoverflow
1494418413.301277
stackoverflow
因此,计时器每10毫秒派生0.2毫秒,这在几秒钟后是一个很大的偏差

我知道python并不是真正为“实时”而设计的,但我认为应该有一种方法来做到这一点

如果有人已经不得不用python处理时间限制,我很乐意提供一些建议


谢谢。

我尝试了你的解决方案,但得到了奇怪的结果

这是我的密码:

from multiprocessing import Queue
import threading
import time

def work(queue, target):
    t0 = time.clock()
    print("Target\t" + str(target))
    print("Current time\t" + str(t0))
    print("Delay\t" + str(target - t0))
    print()
    threading.Timer(target - t0, work, [queue, target+0.01]).start()

myQueue = Queue()

target = time.clock() + 0.01
work(myQueue, target)
这是输出

Target  0.054099
Current time    0.044101
Delay   0.009998

Target  0.064099
Current time    0.045622
Delay   0.018477

Target  0.074099
Current time    0.046161
Delay   0.027937999999999998

Target  0.084099
Current time    0.0465
Delay   0.037598999999999994

Target  0.09409899999999999
Current time    0.046877
Delay   0.047221999999999986

Target  0.10409899999999998
Current time    0.047211
Delay   0.05688799999999998

Target  0.11409899999999998
Current time    0.047606
Delay   0.06649299999999997
所以我们可以看到目标是每10毫秒增加一次,对于第一个循环,计时器的延迟似乎很好

该点不是在当前时间+延迟时再次启动,而是在0.045622时再次启动,表示延迟为0.001521,而不是0.01000

我错过什么了吗?我的代码似乎符合你的逻辑,不是吗


@Chupo_cro的工作示例 这是我的工作示例

from multiprocessing import Queue
import RPi.GPIO as GPIO
import threading
import time
import os

INTERVAL = 0.01
ledState = True

GPIO.setmode(GPIO.BCM)
GPIO.setup(2, GPIO.OUT, initial=GPIO.LOW)

def work(queue, target):
    try:
        threading.Timer(target-time.time(), work, [queue, target+INTERVAL]).start()
        GPIO.output(2, ledState)
        global ledState
        ledState = not ledState
    except KeyboardInterrupt:
        GPIO.cleanup()

try:
    myQueue = Queue()

    target = time.time() + INTERVAL
    work(myQueue, target)
except KeyboardInterrupt:
    GPIO.cleanup()

这段代码在我的笔记本电脑上运行-记录目标时间和实际时间之间的增量-主要目的是最小化在work()函数中完成的操作,因为例如打印和滚动屏幕可能需要很长时间

关键是根据发出呼叫的时间和目标之间的差异启动下一个计时器

我将时间间隔减慢到0.1s,以便更容易看到抖动,在我的Win7 x64上,抖动可能超过10ms,这将导致将负值传递给thte Timer()调用时出现问题:-o

这将记录100个样本,然后打印它们-如果重定向到.csv文件,则可以加载到Excel以显示图形

from multiprocessing import Queue
import threading
import time

# this accumulates record of the difference between the target and actual times
actualdeltas = []

INTERVAL = 0.1

def work(queue, target):
    # first thing to do is record the jitter - the difference between target and actual time
    actualdeltas.append(time.clock()-target+INTERVAL)
#    t0 = time.clock()
#    print("Current time\t" + str(time.clock()))
#    print("Target\t" + str(target))
#    print("Delay\t" + str(target - time.clock()))
#    print()
#    t0 = time.clock()
    if len(actualdeltas) > 100:
        # print the accumulated deltas then exit
        for d in actualdeltas:
            print d
        return
    threading.Timer(target - time.clock(), work, [queue, target+INTERVAL]).start()

myQueue = Queue()

target = time.clock() + INTERVAL
work(myQueue, target)
典型输出(即不依赖Python中Windows上的毫秒计时):


它的漂移是因为你没有考虑计时器本身所花的时间和打印时间。如果您根据实际时间与下一个10毫秒到期时间之间的差异来设置计时器,那么您的计时将不会漂移。例如,获取启动循环之前的时间,并保持“目标”以10ms为增量递增,然后启动计时器一段时间(target currenttime),然后在计时器过期时,将10ms添加到目标并再次启动。你需要自己确认你得到的抖动是否可以接受-即测量所有使用场景下的平均值和峰值谢谢你的回答,我知道我不是在测量计时器的时间安排,但我认为这不会增加这样的漂移。我想说的是,我想对这10毫秒非常精确,这种方法的问题是,我总是会偏离计时器的计划。有没有办法在开始时只定义一次,然后让计时器在过期后自动重新启动?按照我建议的方法,大约10毫秒的间隔会有抖动,但不会偏离,因为在每次超时时,计时器都会以适当的延迟(小于先前的抖动)启动。当然,你完全可以随心所欲地做这件事——为什么不做一些实验,试着想出一个更好的方案呢?我试过你的方法,但我不明白你说的话,或者我的代码被窃听了。如果你能看一下,也许你会发现我做错了什么,你应该在启动计时器之前立即设置t0,或者最好在计时器调用中使用当前时间,即threading.timer(target time.clock(),…)不要忘记将数字转换为字符串、打印、滚动屏幕等会影响工作的时间()功能需要。尝试将增量增加到(比如)1s,看看基本原理是否有效,然后计算在work()函数中可以做的事情有多少-例如,将计时器值存储在1000个调用的列表中,然后打印结果。在我的win7笔记本电脑上,1s delta的抖动约为5ms,但没有漂移,因为每个计时器都是从绝对目标时间开始驱动的。非常感谢您的代码!我将尝试通过
work()
中的UART、I2C或SPI压缩发送大约60字节的数据。Regardsworth很适合使用Raspberry Pi通过I2C发送数据。但是,
multiprocessing.Queue()
似乎是多余的,因为它根本没有被使用。但是如果必须使用队列-那么它不应该是
queue.queue()
用于线程,而不是
multiprocessing.queue()
用于多处理吗?嗨@barny,最后我的问题是使用time.time()时使用time.clock(),它工作得很好。我可以在那上面呆很长时间。供你参考,我的raspberry pi和last raspbian lite绝对没有漂移,平均抖动约为200 us,非常适合我的应用。非常感谢您的帮助;)@Arkaik:您是使用您的代码(不带
队列
)还是巴尼代码(带
队列
)来解决问题的?我想用树莓皮做完全相同的事情(每10毫秒运行一次任务)。@Chupo_cro:如果你想从中得到启发,我已经在我的答案中添加了我的工作解决方案;)这在使用Raspberry Pi通过I2C发送数据时效果很好(使用
time.time()
而不是
time.clock()
)。
multiprocessing.Queue()
似乎是多余的,因为在本例中没有使用它,但是如果必须使用队列,那么它不应该是
Queue.Queue()
,用于线程,而不是
multiprocessing.Queue()
用于多处理吗?
from multiprocessing import Queue
import threading
import time

# this accumulates record of the difference between the target and actual times
actualdeltas = []

INTERVAL = 0.1

def work(queue, target):
    # first thing to do is record the jitter - the difference between target and actual time
    actualdeltas.append(time.clock()-target+INTERVAL)
#    t0 = time.clock()
#    print("Current time\t" + str(time.clock()))
#    print("Target\t" + str(target))
#    print("Delay\t" + str(target - time.clock()))
#    print()
#    t0 = time.clock()
    if len(actualdeltas) > 100:
        # print the accumulated deltas then exit
        for d in actualdeltas:
            print d
        return
    threading.Timer(target - time.clock(), work, [queue, target+INTERVAL]).start()

myQueue = Queue()

target = time.clock() + INTERVAL
work(myQueue, target)
0.00947008617187
0.0029628920052
0.0121824719378
0.00582923077099
0.00131316206917
0.0105631524709
0.00437298744466
-0.000251418553351
0.00897956530515
0.0028528821332
0.0118192949105
0.00546301269675
0.0145723546788
0.00910063698529