QCustomPlot 海量数据绘图

Posted

技术标签:

【中文标题】QCustomPlot 海量数据绘图【英文标题】:QCustomPlot Huge Amount of Data Plotting 【发布时间】:2017-03-11 23:45:23 【问题描述】:

我正在尝试使用 qcustomplot 类在我的 Qt Gui 程序上绘制一些串行数据。当我尝试绘制 100 个数据/秒的低采样频率数据时,我没有遇到任何问题。该图非常酷,并且可以流畅地绘制数据。但是在 1000 数据/秒这样的高采样率下,绘图仪成为串行读取功能的瓶颈。它减慢了串行速度,距离设备有 4-5 秒的巨大延迟。直截了当,绘图仪无法达到数据流速度。那么,有什么我不知道的常见问题或任何建议吗?

我认为这些场景,

1- 将整个程序分成 2 个或 3 个线程。例如,串行部分在一个线程中运行,绘图部分在另一个线程中运行,并且两个线程与一个 QSemaphore 通信

qcustom 绘图的 2- fps 是有限的。但是应该有一个解决方案,因为 NI LABVIEW 可以毫无延迟地绘制多达 2k 的数据

3- 在 USB 协议中设计一个新的虚拟串行设备。现在,我正在使用 ft232rl 串口转 USB 转换器。

4- 更改编程语言。 C# 或 java 中实时绘图的情况和类支持是什么? (我知道这就像一个孩子说的,但这是其他语言体验的借口)

我的串口设备发送数据功能(它是用于实验的foo设备,没有严重的编码)简单地说:

void progTask()


DelayMsec(1); //my delay function, milisecond

//read value from adc13
Adc13Read(adcValue.ui32Part);

sendData[0] = (char)'a';
sendData[1] = (char)'k';
sendData[2] = adcValue.bytes[0];
sendData[3] = (adcValue.bytes[1] & 15);

Qt程序读取功能是:

//send test data
UARTSend(UART6_BASE,&sendData[0],4);


union
unsigned char bytes[2];
unsigned int intPart;
unsigned char *ptr;
serData;

void MedicalSoftware::serialReadData()


    if(serial->bytesAvailable()<4)
    
     //if the frame size is less than 4 bytes return and 
     //wait to full serial receive buffer
     //note: serial->setReadBufferSize(4)!!!!
     return;
    

QByteArray serialInData = serial->readAll();

//my algorithm
if(serialInData[0] == 'a' && serialInData[1] == 'k')

    serData.bytes[0] = serialInData[2];
    serData.bytes[1] = serialInData[3];

else if(serialInData[2] == 'a' && serialInData[3] == 'k')

    serData.bytes[0] = serialInData[0];
    serData.bytes[1] = serialInData[1];

else if(serialInData[1] == 'a' && serialInData[2] == 'k')

    serial->read(1);
    return;
else if(serialInData[0] == 'k' && serialInData[3] == 'a')

    serData.bytes[0] = serialInData[1];
    serData.bytes[1] = serialInData[2];



plotMainGraph(serData.intPart);

serData.intPart = 0;

而qcustom绘图设置功能是:

void MedicalSoftware::setGraphsProperties()

//MAIN PLOTTER
ui->mainPlotter->addGraph();
ui->mainPlotter->xAxis->setRange(0,2000);
ui->mainPlotter->yAxis->setRange(-0.1,3.5);
ui->mainPlotter->xAxis->setLabel("Time(s)");
ui->mainPlotter->yAxis->setLabel("Magnitude(mV)");
QSharedPointer<QCPAxisTickerTime> timeTicker(new QCPAxisTickerTime());
timeTicker->setTimeFormat("%h:%m:%s");
ui->mainPlotter->xAxis->setTicker(timeTicker);
ui->mainPlotter->axisRect()->setupFullAxesBox();

QPen pen;
pen.setColor(QColor("blue"));
ui->mainPlotter->graph(0)->setPen(pen);

dataTimer = new QTimer;

最后是绘图功能:

void MedicalSoftware::plotMainGraph(const quint16 serData)

static QTime time(QTime::currentTime());
double key = time.elapsed()/1000.0;
static double lastPointKey = 0;
if(key-lastPointKey>0.005)


    double value0 = serData*(3.3/4096);
    ui->mainPlotter->graph(0)->addData(key,value0);
    lastPointKey = key;

ui->mainPlotter->xAxis->setRange(key+0.25, 2, Qt::AlignRight);
counter++;

        ui->mainPlotter->replot();
        counter = 0;


【问题讨论】:

【参考方案1】:

快速回答:

你试过了吗:

ui->mainPlotter->replot(QCustomPlot::rpQueuedReplot);

根据文档,它可以在进行大量重新绘制时提高性能。

更长的答案:

我对您的代码的感觉是,您正试图尽可能多地重新绘制以获得“实时”绘图。但是,如果您使用的是带有桌面操作系统的 PC,则没有实时这样的东西。

你应该关心的是:

确保读取/写入串行端口的代码不会延迟太多。 “太多”是相对于连接的硬件来解释的。如果它变得非常时间紧迫(这似乎是你的情况),你必须优化你的读/写功能,并最终将它们单独放在一个线程中。这可以为该线程保留完整的硬件 CPU 内核。 确保图表的刷新速度足以让人眼看到。您无需在每次收到单个数据点时都重新绘制。

在您的情况下,您收到 1000 个数据/秒,每毫秒产生 1 个数据。这是相当快的,因为这超出了大多数桌面操作系统的默认计时器分辨率。这意味着在调用“serialReadData()”时您可能拥有多个数据点,并且您可以通过减少调用它来优化它(例如,每 10 毫秒调用一次,每次读取 10 个数据点)。然后,您可以每 30 毫秒调用一次“replot()”,这将每次添加 30 个新数据点,与您的代码相比,每 30 毫秒跳过大约 29 次 replot() 调用,并为您提供 ~30fps。


1- 将整个程序分成 2 个或 3 个线程。例如,串行部分 在一个线程中运行,绘图部分在另一个线程中运行,两个 线程与 QSemaphore 通信

将 GUI 与串行部分分成 2 个线程是好的,因为您将防止 GUI 中出现阻塞串行通信的瓶颈。您也可以跳过使用信号量,而只依赖 Qt 信号/插槽连接(以 Qt::QueuedConnection 模式连接)。

4- 更改编程语言。什么情况和等级 在 C# 或 java 中支持实时绘图吗? (我知道这就像一个孩子 说,但这是用其他语言体验的借口)

在最好的情况下,更改编程语言不会改变任何内容或可能会损害您的性能,尤其是当您使用未编译为本机 CPU 指令的语言时。 另一方面,更改绘图库可能会改变性能。您可以查看Qt Charts 和Qwt。不过,我不知道它们与 QCustomPlot 相比如何。

【讨论】:

感谢您的评论,我意识到这些情况,1-多线程必须存在,因为串口读取数据并等待完成我的程序中的 qcustomplot 任务。同时,有来自串口的新数据,造成瓶颈。 2-你的 fps 概念对我来说是如此领先。事实上,我想,“真的,我为什么要尝试 1000 fps(!)”我将创建一个计时器信号,它绘制大约 30 fps。谢谢。 我用 30 fps 解决了这个问题。我正在调用 replot() 函数,但现在我以 100 fps 调用,它看起来很棒:-)

以上是关于QCustomPlot 海量数据绘图的主要内容,如果未能解决你的问题,请参考以下文章

使用QT绘图控件QCustomPlot绘制波形图

使用QT绘图控件QCustomPlot绘制波形图

介绍一下海量数据的处理方法

海量数据分析处理方法

SQL如何快速处理海量数据?

如何处理海量数据