在具有非常强大的CPU(8核16线程)的Windows7 64位计算机上运行。 我使用QTimer来触发50Hz的函数调用。 但我最终得到30Hz。 函数调用本身绝对需要不到10毫秒才能完成。 整个过程发生在一个单独的线程中。
我的情况可能出现什么问题? Qt的医生说它会在5%以内准确吗?
答案 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中所示):
点代表100个样本的median
,垂直线表示范围(min
到max
)。理想情况下,所有点都在虚线上。
注意大约之间明显的差异。 20ms和100ms。例如,在我的情况下将QTimer
设置为20ms
(50Hz
)会导致实际(测量)间隔为大约。 31毫秒(32Hz
)。确实很粗糙。
这就是使用PreciseTimer
:
我会说,好多了,特别是如果你对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以获取有价值的见解。