0

我正在使用 Qt(当前为 PyQt)在 Python 中编写一个应用程序,该应用程序通过 UDP 接收遥测(位置数据)并显示该数据。它可以处理几种数据包类型,每种类型都有几个数据字段。我有一个单独的线程来处理接收和解码数据包,它们会发出信号将该数据发送到 Qt GUI 进行显示。数据以多种方式显示:当前值显示、滚动日志显示和 3D 视图显示。所有这一切都运行得很好,并且每个数据包都以高达约 30Hz 的频率实时更新。

我想添加另一个显示实时图的选项卡。用户可以选择要绘制的数据流,它将实时更新,显示最后 60 秒数据的滑动窗口(可能在某些时候可配置)。但是我的第一种方法特别慢。它几乎没有跟上我们的模拟器只绘制一条运行速度远低于 30Hz 的线。

我正在使用 pyqtgraph 进行绘图。我在我的 .ui 文件中实例化了一个 PlotWidget,并为可以在绘图上绘制的每条数据线创建一个 PlotDataItem。我正在使用双端队列来存储要绘制的数据,包括值和时间。这样,我可以在数据进入时快速添加数据,并在数据超出滑动窗口时将其删除。我将所有这些存储在每个数据包类型和字段的字典中:

    self.plotData[pktType][field] = {}
    self.plotData[pktType][field]['curve'] = self.pwPlot.plot()
    self.plotData[pktType][field]['starttime'] = time
    self.plotData[pktType][field]['data'] = coll.deque()
    self.plotData[pktType][field]['realtime'] = coll.deque()
    self.plotData[pktType][field]['time'] = coll.deque()

'starttime' 存储用于计算经过的秒数的初始日期时间值。'realtime' 存储收到每个数据包的日期时间值(我目前没有使用它,所以如果可以节省时间,我可以放弃它)。'time' 存储从 'starttime' 开始经过的秒数,以便于绘图,'data' 存储值。

当一个数据包进来时,我将数据存储在我可能想要解析的每个字段的双端队列中。然后我修剪掉滑动窗口之外的所有数据。最后,双端队列被打包在一个 numpy 数组中并传递给 PlotDataItem 的 setData 方法。这是为每个接收到的数据包运行的代码的简化版本:

def updatePlot(self, time, pktData):
    pktType = pktData['ptype']

    keys = self.getPlottableFromType(pktType) # list of fields that can be plotted
    if keys == None:
        return
    for k in keys:
        self.plotData[pktType][k]['data'].append(pktData[k])
        self.plotData[pktType][k]['realtime'].append(time)
        runtime = (time - self.plotData[pktType][k]['starttime']).total_seconds()
        if self.plotRate == 0:
            self.plotData[pktType][k]['time'].append(runtime)
        else:
            if self.plotData[pktType][k]['time']: # has items
                nexttime = self.plotData[pktType][k]['time'][-1] + 1. / self.plotRate
            else:
                nexttime = 0

            self.plotData[pktType][k]['time'].append(nexttime)

        while (self.plotData[pktType][k]['time'][-1] - self.plotData[pktType][k]['time'][0]) > self.plotRangeSec:
            self.plotData[pktType][k]['data'].popleft()
            self.plotData[pktType][k]['realtime'].popleft()
            self.plotData[pktType][k]['time'].popleft()

        self.drawPlot(pktType, k)

def drawPlot(self, pktType, k):
    if self.plotIsEnabled(pktType, k) and self.plotData[pktType][k]['time']: # has items
        npt = np.array(self.plotData[pktType][k]['time'])
        npd = np.array(self.plotData[pktType][k]['data'])
        self.plotData[pktType][k]['curve'].setData(npt, npd)
    else:
        self.plotData[pktType][k]['curve'].clear()

self.plotRate 可用于绘制数据,使用墙上时间或强制时间轴为固定更新率。这对于与模拟器一起使用很有用,因为它比真实系统运行得慢。

我可能应该做的第一件事不是每次都为没有绘制的图调用 .clear() (只是使逻辑复杂一点)。除此之外,有人有任何提高性能的技巧吗?numpy 数组的双端队列是一个好策略吗?有没有更好的方法来更新数据,因为我每行只更改几个值(添加一个点并可能删除一两个点)?

4

0 回答 0