QTimer根本不准确?

时间:2017-02-23 16:48:59

标签: c++ qt

在具有非常强大的CPU(8核16线程)的Windows7 64位计算机上运行。 我使用QTimer来触发50Hz的函数调用。 但我最终得到30Hz。 函数调用本身绝对需要不到10毫秒才能完成。 整个过程发生在一个单独的线程中。

我的情况可能出现什么问题? Qt的医生说它会在5%以内准确吗?

3 个答案:

答案 0 :(得分:8)

Windows 7不是RTOS,因此无法保证计时器会在您预期触发时准确触发。这不是QTimer问题,而是操作系统。

来自QTimer documentaion:

  

计时器的准确性取决于底层操作系统和   硬件。但是,大多数平台支持1毫秒的分辨率   定时器的精度在很多方面都不等于这个分辨率   现实世界的情况。精度还取决于计时器类型。   对于Qt :: PreciseTimer,QTimer 将尝试保持精度为1   毫秒。精确的计时器也永远不会超时   预期

请注意,文档说明"将尽力保持"而不是"保证"。

答案 1 :(得分:7)

通过将timer type属性设置为Qt::PreciseTimer(默认类型为Qt::CoarseTimer),您可以获得更好的计时器精度。

来自docs

  

Qt::PreciseTimer - 精确的计时器会尝试保持毫秒级的准确性    Qt::CoarseTimer - 粗略计时器会尝试将精度保持在所需间隔的5%以内。

然而,正如@Paul和@AlgirdasPreidžius指出的那样,仍然无法保证精度完全准确。

答案 2 :(得分:2)

为了说明这个问题,我在Windows 7 64位系统PyQt5上做了一些快速和肮脏的计时,使用QTimer 和< em>没有高精度设置。

以下是使用默认精度的结果(如PyQt4中所示):

requested timer interval vs measured timer interval for default precision

点代表100个样本的median,垂直线表示范围(minmax)。理想情况下,所有点都在虚线上。

注意大约之间明显的差异。 20ms和100ms。例如,在我的情况下将QTimer设置为20ms50Hz)会导致实际(测量)间隔为大约。 31毫秒(32Hz)。确实很粗糙。

这就是使用PreciseTimer

时的样子

requested timer interval vs measured timer interval for high precision

我会说,好多了,特别是如果你对20毫秒到100毫秒之间的间隔感兴趣。

对于那些感兴趣的人,这里的代码(无疑可以大大改进):

import sys
import time
import numpy
from matplotlib import pyplot
try:
    from PyQt5 import QtCore, QtWidgets
    print 'using PyQt5'
except:
    from PyQt4 import QtCore, QtGui
    print 'using PyQt4'


class StampTimer(QtCore.QTimer):
    signal_quit = QtCore.pyqtSignal()

    def __init__(self, number_of_samples, precise=False, *args, **kwargs):
        super(StampTimer, self).__init__(*args, **kwargs)
        if precise:
            try:
                self.setTimerType(QtCore.Qt.PreciseTimer)
                print 'using precise timer (PyQt5)'
            except:
                print 'precise timer not available (PyQt4)'
        self.number_of_samples = number_of_samples
        self.stamps_ms = []
        self.timeout.connect(self.stamp)

    def start(self):
        self.stamps_ms = []
        super(StampTimer, self).start()

    def stamp(self):
        # Save a timestamp (use clock() instead of time())
        self.stamps_ms.append(1e3 * time.clock())
        # Quit when we reach the specified number of samples
        if len(self.stamps_ms) == self.number_of_samples:
            self.stop()
            self.signal_quit.emit()


try:
    app = QtWidgets.QApplication(sys.argv)
except:
    app = QtGui.QApplication(sys.argv)

# Parameters
number_of_intervals = 100
intervals_requested_ms = range(1, 10)
intervals_requested_ms.extend(range(10, 60, 2))
intervals_requested_ms.extend(range(60, 200, 10))
intervals_requested_ms.append(1000)

# Variables
intervals_measured_ms = numpy.zeros(
    (number_of_intervals, len(intervals_requested_ms)), float)

# Run
timer = StampTimer(number_of_samples=number_of_intervals+1, precise=False)
timer.signal_quit.connect(app.quit)
for index, interval_requested_ms in enumerate(intervals_requested_ms):
    # For each interval in the list, we run the timer until we obtain 
    # the specified number of samples
    timer.setInterval(interval_requested_ms)
    timer.start()
    app.exec_()
    # Calculate statistics
    intervals_measured_ms[:, index] = numpy.diff(timer.stamps_ms)
    median_ms = numpy.median(intervals_measured_ms[:, index])
    max_ms = numpy.max(intervals_measured_ms[:, index])
    min_ms = numpy.min(intervals_measured_ms[:, index])
    # Add to plot
    print 'requested: {} ms, ' \
          'measured: median(min/max) {:.1f} ({:.2f}/{:.2f}) ms'.format(
        interval_requested_ms, median_ms, min_ms, max_ms)
    pyplot.plot(interval_requested_ms, median_ms, 'ok')
    pyplot.plot([interval_requested_ms]*2, [min_ms, max_ms], '-k')

# Plot result
pyplot.plot([0, 1000], [0, 1000], 'k--')
pyplot.xscale('log', nonposx='clip')
pyplot.yscale('log', nonposy='clip')
pyplot.xlabel('interval requested [ms]')
pyplot.ylabel(
    'interval measured [ms] (median, n={})'.format(number_of_intervals))
pyplot.show()

另请查看this answer以获取有价值的见解。