4

念頭に置いておくべきアプリケーションの種類は、オシロスコープまたは高速データ ロガーです。必要な情報を取得する関数があります。それを何度も何度も、非常に迅速かつ高精度で呼び出す方法を考え出す必要があります。

time.sleep() には制限がありますが、それが進むべき道だとは思いません。

組み込みのイベント スケジューラを調べましたが、精度が十分ではなく、私のニーズを十分に満たしていないと思います。

このための要件は次のとおりです。

  • 高速サンプリング。10ms が最も要求されます。
  • 高精度間隔。10ms の場合、10% の誤差は許容されます (±1ms)。
  • CPU 使用率がかなり低く、10 ミリ秒で多少の負荷は許容されますが、100 ミリ秒間隔以降は 5% 未満である必要があります。これが主観的なものであることは承知しています。私が言っているのは、CPU を占有することは受け入れられないということだと思います。
  • 理想的には、タイマーはインターバル時間で初期化され、必要に応じて開始されます。次に、タイマーが停止するまで、必要な関数を正しい間隔で何度も呼び出す必要があります。
  • Windowsマシンでのみ実行されます(必須ではありません)。

これらの要件を満たす既存のライブラリはありますか? 車輪の再発明はしたくありませんが、必要に応じて、おそらく Windows マルチメディア タイマー (winmm.dll) を使用します。それに関するコメント/提案はありますか?

4

2 に答える 2

7

私は自分の質問に答えるのに遅れていることを知っていますが、誰かの助けになることを願っています.

純粋にテストとして、Windows マルチメディア タイマーのラッパーを作成しました。うまく機能しているように見えますが、コードは完全にはテストされておらず、最適化もされていません。

mmtimer.py:

from ctypes import *
from ctypes.wintypes import UINT
from ctypes.wintypes import DWORD

timeproc = WINFUNCTYPE(None, c_uint, c_uint, DWORD, DWORD, DWORD)
timeSetEvent = windll.winmm.timeSetEvent
timeKillEvent = windll.winmm.timeKillEvent


class mmtimer:
    def Tick(self):
        self.tickFunc()

        if not self.periodic:
            self.stop()

    def CallBack(self, uID, uMsg, dwUser, dw1, dw2):
        if self.running:
            self.Tick()

    def __init__(self, interval, tickFunc, stopFunc=None, resolution=0, periodic=True):
        self.interval = UINT(interval)
        self.resolution = UINT(resolution)
        self.tickFunc = tickFunc
        self.stopFunc = stopFunc
        self.periodic = periodic
        self.id = None
        self.running = False
        self.calbckfn = timeproc(self.CallBack)

    def start(self, instant=False):
        if not self.running:
            self.running = True
            if instant:
                self.Tick()

            self.id = timeSetEvent(self.interval, self.resolution,
                                   self.calbckfn, c_ulong(0),
                                   c_uint(self.periodic))

    def stop(self):
        if self.running:
            timeKillEvent(self.id)
            self.running = False

            if self.stopFunc:
                self.stopFunc()

定期テスト コード:

from mmtimer import mmtimer
import time

def tick():
    print("{0:.2f}".format(time.clock() * 1000))

t1 = mmtimer(10, tick)
time.clock()
t1.start(True)
time.sleep(0.1)
t1.stop()

ミリ秒単位の出力:

0.00
10.40
20.15
29.91
39.68
50.43
60.19
69.96
79.72
90.46
100.23

ワンショット テスト コード:

from mmtimer import mmtimer
import time

def tick():
    print("{0:.2f}".format(time.clock() * 1000))

t1 = mmtimer(150, tick, periodic=False)
time.clock()
t1.start()

ミリ秒単位の出力:

150.17

結果からわかるように、それはかなり正確です。ただし、これはあくまでも使用time.clock()なので、塩少々でお召し上がりください。

10 ミリ秒の定期タイマーを使用した長時間のテストでは、私の古いデュアル コード 3 GHz マシンの CPU 使用率は約 3% 以下です。ただし、マシンはアイドル時にもそれを使用しているように見えるため、追加の CPU 使用率は最小限であると言えます。

于 2013-05-01T08:36:23.850 に答える
2

編集:以下のものを書いた後、私はpythonイベントスケジューラに対して同様のテストを実装する傾向があります。なぜあなたがそれが不十分に正確であると思うのか分かりません。

次のようなものは、私と一緒にLinuxでかなりうまく機能しているようです(そして、Windowsでは機能しないと思う理由はありません)。10msごとにon_timer_event()呼び出され、リアルタイムクロックに基づいて最後の呼び出しからの時間を出力します。これは、タイマーのおおよその精度を示しています。最後に、合計時間が印刷され、ドリフトがないことが示されます。

以下のコードには1つの問題があるようで、イベントが誤って(そして短い間隔で)表示されることがあります。これがなぜなのかはわかりませんが、演奏することで信頼できるものになることは間違いありません。このようなアプローチが道だと思います。

import pygame
import time

pygame.init()
TIMER_EVENT = pygame.USEREVENT+1

pygame.time.set_timer(TIMER_EVENT, 10)

timer_count = 0
MAX_TIMER_COUNT = 1000

def on_timer_event():
    global last_time
    global timer_count

    new_time = time.time()

    print new_time - last_time
    last_time = new_time

    timer_count += 1

    if timer_count > MAX_TIMER_COUNT:
        print last_time - initial_time
        pygame.event.post(pygame.event.Event(pygame.QUIT, {}))

initial_time = time.time()
last_time = initial_time
while True:
    event = pygame.event.wait()
    if event.type == TIMER_EVENT:
        on_timer_event()

    elif event.type == pygame.QUIT:
        break
于 2012-05-23T11:26:39.990 に答える