You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.22 20:15:16