QCustomPlot海量数据绘图
QCustomPlot Huge Amount of Data Plotting
我正在尝试使用 qcustomplot class 在我的 Qt Gui 程序上绘制一些串行数据。当我尝试绘制像 100 data/second 这样的低采样频率数据时,我没有遇到任何问题。该图真的很酷,并且可以顺利地绘制数据。但是在1000data/second这样的高采样率下,绘图仪对串行读取功能造成了瓶颈。它减慢了串行速度,除了设备之外还有 4-5 秒的巨大延迟。直截了当地说,绘图仪无法达到数据流速度。那么,有什么我不知道的常见问题或建议吗?
我认为这些场景,
1-将整个程序划分为2个或3个线程。例如,串行部分在一个线程中运行,绘图部分在另一个线程中运行,两个线程与 QSemaphore
通信
2- qcustom plot 的 fps 是有限的。但应该有一个解决方案,因为 NI LABVIEW 可以毫不延迟地绘制多达 2k 的数据
3- 在 usb 协议中设计一个新的虚拟串行设备。现在,我正在使用 ft232rl 串行到 usb 转换器。
4- 更改编程语言。 C# 中的情况和 class 支持或实时绘图的 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 plot设置函数为:
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;
}
最后一个是plot函数:
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;
}
快速回答:
你试过了吗:
ui->mainPlotter->replot(QCustomPlot::rpQueuedReplot);
根据文档,它可以在进行大量重绘时提高性能。
更长的答案:
我对你的代码的感觉是你试图尽可能频繁地重新绘制以获得 "real time" 绘图。但是,如果您使用的是带有桌面的 PC OS,则没有实时这样的东西。
你应该关心的是:
- 确保read/write到串口的代码没有延迟太多。 "Too much" 将根据连接的硬件进行解释。如果它真的变得时间紧迫(这似乎是你的情况),你必须优化你的 read/write 函数并最终将它们单独放在一个线程中。这可以为该线程保留完整的硬件 CPU 内核。
- 确保图表的刷新速度足以让人眼看到。您无需在每次收到单个数据点时都进行完全重绘。
在您的情况下,您收到 1000 data/s,每毫秒产生 1 个数据。这非常快,因为这超出了大多数桌面 OS 的默认计时器分辨率。这意味着您在调用 "serialReadData()" 时可能拥有多个数据点,并且您可以通过降低调用频率来优化它(例如,每 10 毫秒调用一次并每次读取 10 个数据点)。然后你可以每 30 毫秒调用一次 "replot()" ,每次都会添加 30 个新数据点,与你的代码相比,每 30 毫秒跳过大约 29 次 replot() 调用,并给你 ~30fps。
1- to devide whole program to 2 or 3 thread. For example, serial part
runs in one thread and plotting part runs in another thread and two
thread communicates with a QSemaphore
将 GUI 与串行部分分成 2 个线程是很好的做法,因为这样可以防止 GUI 中的瓶颈阻塞串行通信。您也可以跳过使用信号量并简单地依赖 Qt signal/slot 连接(以 Qt::QueuedConnection 模式连接)。
4- to change programming language. What is the situation and class
support in C# or java for realtime plotting? (I know it is like a kid
saying, but this is a pretex to be experienced in other languages)
在最好的情况下,更改编程语言不会改变任何内容或可能会损害您的性能,尤其是当您使用未编译为本机 CPU 指令的语言时。
另一方面,更改绘图库 可以 改变性能。你可以看看Qt Charts and Qwt。不过,我不知道它们与 QCustomPlot 相比如何。
我正在尝试使用 qcustomplot class 在我的 Qt Gui 程序上绘制一些串行数据。当我尝试绘制像 100 data/second 这样的低采样频率数据时,我没有遇到任何问题。该图真的很酷,并且可以顺利地绘制数据。但是在1000data/second这样的高采样率下,绘图仪对串行读取功能造成了瓶颈。它减慢了串行速度,除了设备之外还有 4-5 秒的巨大延迟。直截了当地说,绘图仪无法达到数据流速度。那么,有什么我不知道的常见问题或建议吗?
我认为这些场景,
1-将整个程序划分为2个或3个线程。例如,串行部分在一个线程中运行,绘图部分在另一个线程中运行,两个线程与 QSemaphore
通信2- qcustom plot 的 fps 是有限的。但应该有一个解决方案,因为 NI LABVIEW 可以毫不延迟地绘制多达 2k 的数据
3- 在 usb 协议中设计一个新的虚拟串行设备。现在,我正在使用 ft232rl 串行到 usb 转换器。
4- 更改编程语言。 C# 中的情况和 class 支持或实时绘图的 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 plot设置函数为:
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;
}
最后一个是plot函数:
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;
}
快速回答:
你试过了吗:
ui->mainPlotter->replot(QCustomPlot::rpQueuedReplot);
根据文档,它可以在进行大量重绘时提高性能。
更长的答案:
我对你的代码的感觉是你试图尽可能频繁地重新绘制以获得 "real time" 绘图。但是,如果您使用的是带有桌面的 PC OS,则没有实时这样的东西。
你应该关心的是:
- 确保read/write到串口的代码没有延迟太多。 "Too much" 将根据连接的硬件进行解释。如果它真的变得时间紧迫(这似乎是你的情况),你必须优化你的 read/write 函数并最终将它们单独放在一个线程中。这可以为该线程保留完整的硬件 CPU 内核。
- 确保图表的刷新速度足以让人眼看到。您无需在每次收到单个数据点时都进行完全重绘。
在您的情况下,您收到 1000 data/s,每毫秒产生 1 个数据。这非常快,因为这超出了大多数桌面 OS 的默认计时器分辨率。这意味着您在调用 "serialReadData()" 时可能拥有多个数据点,并且您可以通过降低调用频率来优化它(例如,每 10 毫秒调用一次并每次读取 10 个数据点)。然后你可以每 30 毫秒调用一次 "replot()" ,每次都会添加 30 个新数据点,与你的代码相比,每 30 毫秒跳过大约 29 次 replot() 调用,并给你 ~30fps。
1- to devide whole program to 2 or 3 thread. For example, serial part runs in one thread and plotting part runs in another thread and two thread communicates with a QSemaphore
将 GUI 与串行部分分成 2 个线程是很好的做法,因为这样可以防止 GUI 中的瓶颈阻塞串行通信。您也可以跳过使用信号量并简单地依赖 Qt signal/slot 连接(以 Qt::QueuedConnection 模式连接)。
4- to change programming language. What is the situation and class support in C# or java for realtime plotting? (I know it is like a kid saying, but this is a pretex to be experienced in other languages)
在最好的情况下,更改编程语言不会改变任何内容或可能会损害您的性能,尤其是当您使用未编译为本机 CPU 指令的语言时。 另一方面,更改绘图库 可以 改变性能。你可以看看Qt Charts and Qwt。不过,我不知道它们与 QCustomPlot 相比如何。