Qt下QCustomPlot绘制传感器数据随机出现锯齿异常的排查求助
Qt+QCustomPlot传感器绘图随机锯齿状异常的排查与修复
问题场景
使用C++开发基于Qt的GUI程序,通过QCustomPlot绘制传感器数据,初始化代码如下:
void MainWindow::plotSetup(QCustomPlot *plot, QColor line_color, QString plot_name, float y_min, float y_max, QString y_label) { plot->legend->setVisible(true); QFont legendFont=font(); legendFont.setPointSize(9); plot->legend->setFont(legendFont); plot->legend->setBrush(QBrush(QColor(255,255,255,230))); plot->axisRect()->insetLayout()->setInsetAlignment(0, Qt::AlignLeft|Qt::AlignTop); plot->addGraph(); plot->graph(0)->setPen(QPen(line_color)); plot->graph(0)->setName(plot_name); QSharedPointer<QCPAxisTickerTime> timeTicker(new QCPAxisTickerTime); timeTicker->setTimeFormat("%h:%m:%s"); plot->xAxis->setTicker(timeTicker); plot->axisRect()->setupFullAxesBox(); plot->xAxis->setLabel("Time"); plot->yAxis->setRange(y_min, y_max); plot->yAxis->setLabel(y_label); connect(plot->xAxis, SIGNAL(rangeChanged(QCPRange)), plot->xAxis2, SLOT(setRange(QCPRange))); connect(plot->yAxis, SIGNAL(rangeChanged(QCPRange)), plot->yAxis2, SLOT(setRange(QCPRange))); }
数据绘制代码:
mPlot_co2->graph(0)->addData(timestamp, data.co2); mPlot_co2->xAxis->setRange(timestamp, sensorDataBufferSize, Qt::AlignRight);
多数情况下绘图正常,但会随机出现锯齿状图形(真实数据应为平滑曲线),打印的真实数据无异常,问题有时自行恢复,重启GUI也可解决,但数据流未中断。异常效果如下:
可能原因与修复方案
1. 线程安全问题(最常见)
如果传感器数据在非GUI线程中推送并直接调用绘图代码,QCustomPlot的所有操作必须在**主线程(GUI线程)**执行。非主线程直接调用addData或setRange会导致绘图上下文混乱,引发图形异常。
修复:
用Qt信号槽机制将数据传递到主线程后再更新绘图:
// 非GUI线程中发射信号 emit newSensorData(timestamp, data.co2); // MainWindow构造函数中连接信号槽(默认Qt::QueuedConnection确保在主线程执行) connect(this, &MainWindow::newSensorData, this, &MainWindow::updateCO2Plot); // 主线程中的更新函数 void MainWindow::updateCO2Plot(double timestamp, double co2Value) { mPlot_co2->graph(0)->addData(timestamp, co2Value); mPlot_co2->xAxis->setRange(timestamp, sensorDataBufferSize, Qt::AlignRight); mPlot_co2->replot(); // 显式触发重绘 }
2. 时间戳非递增
如果timestamp出现跳变、非递增的情况(比如传感器时序错误、读取时的逻辑漏洞),QCustomPlot绘制折线时会因x轴点乱序导致折线来回跳跃,呈现锯齿状。
修复:
- 检查传感器时间戳生成逻辑,确保每次传入的
timestamp严格递增; - 添加前置判断,只保留递增的时间戳数据:
double lastX = mPlot_co2->graph(0)->data()->isEmpty() ? 0 : mPlot_co2->graph(0)->data()->lastKey(); if (timestamp > lastX) { mPlot_co2->graph(0)->addData(timestamp, data.co2); } else { // 处理异常时间戳,比如丢弃或修正 }
3. 缺少显式重绘
当前代码未调用replot(),QCustomPlot依赖自动重绘时,在高频数据更新场景下可能出现重绘不及时或冲突,导致渲染异常。
修复:
在每次更新数据和轴范围后,显式调用队列重绘避免阻塞主线程:
mPlot_co2->graph(0)->addData(timestamp, data.co2); mPlot_co2->xAxis->setRange(timestamp, sensorDataBufferSize, Qt::AlignRight); mPlot_co2->replot(QCustomPlot::rpQueued);
4. 数据缓冲区溢出
如果未限制数据点数量,当数据量过大时,QCustomPlot渲染可能出现异常;或存在重复添加数据点的情况,引发折线抖动。
修复:
限制缓冲区数据点数量,超出时移除旧数据:
mPlot_co2->graph(0)->addData(timestamp, data.co2); // 保持数据点数量不超过设定范围的2倍(可根据实际调整) while (mPlot_co2->graph(0)->data()->size() > sensorDataBufferSize * 2) { mPlot_co2->graph(0)->data()->removeBefore(timestamp - sensorDataBufferSize); } mPlot_co2->xAxis->setRange(timestamp, sensorDataBufferSize, Qt::AlignRight); mPlot_co2->replot();
内容的提问来源于stack exchange,提问作者Drake P
相关产品推荐
相关产品推荐

