前言
工业控制中通常需要测量系统的实时状态,并通过曲线进行显示。在有噪声的情况下,还需要对其进行滤波。本篇文章首先介绍了如何使用QCustomPlot绘制实时显示的动态曲线。其次介绍了两种滤波方法,巴特沃斯滤波器和跟踪微分器,并在Qt中对其进行了实现。详细程序见我上传的资源《 使用QCustomPlot绘制实时动态曲线并对数据进行滤波》。一、QCustomPlot绘制实时显示的动态曲线
使用QCustomPlot之前,需要在官网下载相应的头文件和源文件http://www.qcustomplot.com/index.php/download。
下载好之后,放到工程文件下面。
使用步骤如下:
1.在QtCreator中右击工程文件名,添加现有文件,将qcustomplot.h和qcustomplot.cpp添加进工程中,然后在.pro文件中添加QT += widgets printsupport,最后在对应窗口头文件中#include“qcustomplot.h”。
2.在ui界面中拖入一个widget容器,右击
->然后提升为qcustomplot->添加后提升。
3.定义一个qCustomPlot类,并使其与ui界面的相关联。
代码如下:
qCustomPlot = ui->customPlot;
4.在构造函数中设置相应曲线,每添加一条曲线addGraph一次,设置相应曲线颜色,设置坐标轴范围为时间。
qCustomPlot->addGraph();//每画一条曲线都需要添加一次addGraph
qCustomPlot->graph(0)->setPen(QPen(QColor(40, 110, 255)));//输入信号
qCustomPlot->graph(0)->setName("输入信号");
qCustomPlot->addGraph();
qCustomPlot->graph(1)->setPen(QPen(QColor(255, 110, 40)));//红线
qCustomPlot->graph(1)->setName("巴特沃斯滤波");
qCustomPlot->addGraph();
qCustomPlot->graph(2)->setPen(QPen(QColor(0, 0, 0)));//跟踪微分器滤波
qCustomPlot->graph(2)->setName("跟踪微分器滤波");
qCustomPlot->legend->setVisible(true);//使能图例
qCustomPlot->axisRect()->insetLayout()->setInsetAlignment(0,Qt::AlignRight|Qt::AlignTop);//设置图例位置
//坐标轴使用时间刻度
QSharedPointer<QCPAxisTickerTime> timeTicker(new QCPAxisTickerTime);//使用秒作为横坐标刻度
timeTicker->setTimeFormat("%s");
qCustomPlot->xAxis->setTicker(timeTicker);
//四边安上坐标轴
qCustomPlot->axisRect()->setupFullAxesBox();
//使对称坐标轴相同范围
connect(qCustomPlot->xAxis, SIGNAL(rangeChanged(QCPRange)), qCustomPlot->xAxis2, SLOT(setRange(QCPRange)));
connect(qCustomPlot->yAxis, SIGNAL(rangeChanged(QCPRange)), qCustomPlot->yAxis2, SLOT(setRange(QCPRange)));
//支持鼠标拖拽轴的范围、滚动缩放轴的范围,
qCustomPlot->setInteractions(QCP::iRangeDrag | QCP::iRangeZoom | QCP::iSelectPlottables);
//连接定时器和对应槽函数
connect(timer0,SIGNAL(timeout()),this,SLOT(Timer0Out()));
5.在定时器中给相应曲线添加数据。
static QTime time(QTime::currentTime());
double currentTime = time.elapsed() / 1000.0;//单位毫秒
static double lastTime = 0;
if(currentTime - lastTime > 0.001)//每0.001秒刷新一次图像
{
//向graph中添加数据,曲线0为带随机噪声的正弦函数
qCustomPlot->graph(0)->addData(currentTime,1000 * qSin(currentTime) + qrand()%50 );// (double)RAND_MAX*1*qSin(currentTime/0.3843)
//qCustomPlot->graph(1)->addData(currentTime,qCos(currentTime));
//曲线1为对曲线0进行巴特沃斯2阶滤波后的曲线
qCustomPlot->graph(1)->addData(currentTime,transfer(filter1,(1000 * qSin(currentTime) + qrand()%50)));
//曲线2为对曲线0通过跟踪微分器滤波后滤波的函数
qCustomPlot->graph(2)->addData(currentTime,TrackingDifference(1000 * qSin(currentTime) + qrand()%50));
lastTime = currentTime;
//qDebug()<<QString::number(TrackingDifference(1000 * qSin(currentTime) + qrand()%50));
//当界面中动态画图勾选框被勾选,X轴自己滚动,X轴长度为8秒
if(ui->checkBoxRoll->isChecked() == true)
{
qCustomPlot->xAxis->setRange(currentTime,8,Qt::AlignRight);
}
}
6.使用qCustomPlot->replot()绘制曲线。
二、巴特沃斯滤波器的实现
代码如下:
/巴特沃斯二阶滤波器,选取不同的参数过滤不同的噪声
double Widget::butter2jie(double x[3],double y[2])
{
double ycurrent;
// double b[3]={ 0.206572083826148,0.413144167652296,0.206572083826148};//200Hz
// double a[3]={ 1.000000000000000,-0.369527377351241,0.195815712655833};//200Hz
//double b[3]={0.020083365564211,0.040166731128423,0.020083365564211};//50Hz
//double a[3]={1.000000000000000,-1.561018075800718,0.641351538057563};//50Hz
// double a[3]={1.0000000000,-1.9955571243,0.9955669721};//频率为0.5
// double b[3]={0.0000024619,0.0000049239,0.0000024619};//频率为0.5
double b[3]={0.000944691843840,0.001889383687680,0.000944691843840};//5Hz
double a[3]={1.000000000000000,-1.911197067426073,0.914975834801434};//5Hz
ycurrent=b[0]*x[2]+b[1]*x[1]+b[2]*x[0]-a[1]*y[1]-a[2]*y[0];
return ycurrent;
}
//配合巴特沃斯二阶滤波器使用函数,Rdata为需要被滤波的数据
double Widget::transfer(double *interimarray,double Rdata)
{
int i=0;
double c=0;
double a[3]={0};
double b[2]={0};
for (i=0;i<2;i++)
{
interimarray[i]=interimarray[i+1];
}
interimarray[2]=Rdata;
for (i=0;i<3;i++)
{
a[i]=interimarray[i];
}
for (i=0;i<2;i++)
{
b[i]=interimarray[i+3];
}
c = butter2jie(a,b);
for (i=0;i<1;i++)
{
interimarray[i+3]=interimarray[i+4];
}
interimarray[4]=c;
return c;
}
三、跟踪微分器的实现
代码如下:
//跟踪微分器滤波,realData
double Widget::TrackingDifference(double realData)
{
static double temp = 0;
static double x[2] = {0};
x[0] = x[0] + 0.001 * x[1];
x[1] = x[1] + 0.001 * fhan(x,realData);
temp = x[0];
return temp;
}
//最速跟踪函数
double Widget::fhan(double x[2],double rD)
{
static double r0 = 0;
static double h = 0.001;
//static double x[2] = {0};
static double d = 0;
static double a0 = 0;
static double y = 0;
static double a1 = 0;
static double a2 = 0;
static double a = 0;
static double f = 0;
r0 = 60000.00;
h = 0.001;
d = r0 * h * h;
a0 = h * x[1];
y = x[0] - rD + a0;//x[0] - rD x[1]
a1 = (double)sqrtf((d + 8 * (double)fabs(y)) * d);
a2 = a0 + sign(y) * (a1 - d) / 2;
a = (a0 + y) * (sign( y+ d) - sign(y - d)) / 2 + a2 * (1 - (sign(y + d) - sign(y - d)) / 2);
f = -r0 * (a / d) * (sign(a + d) - sign(a - d)) / 2 - r0 * sign(a) * (1 - (sign(a + d) - sign(a - d)) / 2);
return f;
}
//符号函数
double Widget::sign(double a)
{
double c = 0;
if(a > 0)
{
c = 1;
}
if(a < 0 )
{
c = -1;
}
if(a == 0)
{
c = 0;
}
qDebug()<<QString::number(c);
return c;
}
总结
本文提供了常规的动态画图曲线DEMO,其可以左键选取局部放大,可以通过勾选框充满纵轴,可以显示需要画的曲线、可以清空数据。还提供了两个常规滤波的方法,供大家借鉴。