Qt间接平差法进行直线拟合(Eigen运算、Qcustomplot绘图)

间接平差法进行直线拟合,使用Qt、Qcustomplot、Eigen

仔细读一定会有所收获,不要着急,很快就学会啦

间接平差法直线拟合原理

间接平差是在确定多个未知量的最或然值时,选择它们之间不存在任何条件关系的独立量作为未知量组成用未知量表达测量的函数关系、列出误差方程式,按最小二乘法原理求得未知量的最或然值的平差方法。间接平差为平差计算最常用的方法,其数学模型比较简单,便于评定平差值及其函数的精度。
在这里插入图片描述
对于多个点也是类似的做法

工具介绍

Qt

Qt 是一种更快,更智能的方式来为多个屏幕创建创新的设备、现代用户界面和应用程序。它是一个跨平台的C++应用程序开发框架。它提供给开发者建立图形用户界面所需的功能,广泛用于开发图形用户界面程序,也可用于开发非图形用户界面(比如命令行界面)程序。Qt是完全面向对象的,很容易扩展,并且允许真正地组件编程。

基本上,Qt 同 X11上的GTK、Motif、Openwin和Windows上的MFC,OWL,VCL,ATL 是同类型的东西,但是 Qt 支持更多的平台(包括Windows、GNU/Linux、Mac OS X、Android、iOS、WinCE、Unix家族等),面向对象且模块化程度更高(Qt 提供了一种称为 signals/slots 的安全类型来替代 callback,这使得各个元件 之间的协同工作变得十分简单)。

丰富的API(Qt 包括多达 250 个以上的 C++ 类,还替供基于模板的 collections, serialization, file, I/O device, directory management, date/time 类。甚至还包括正则表达式的处理 功能),支持 2D/3D 图形渲染,支持 OpenGL、大量的开发文档、XML支持等。

这里给出Qt提供的各种类This is a list of all Qt classes. ,开发时按照官方文档走准没错。

Qcustomplot

QCustomPlot 是一个基于Qt的画图和数据可视化C++控件。QCustomPlot 致力于提供美观的界面,高质量的2D画图、图画和图表,同时为实时数据可视化应用提供良好的解决方案。

Eigen

Eigen 是一个线性算术的C++模板库,包括:vectors, matrices, 以及相关算法。功能强大、快速、优雅以及支持多平台。

准备工作

下载Eigen,下载最新的就行,然后放在你能找到的地方,在项目文件.pro里加入Eigen安装路径:

INCLUDEPATH += C:\Users\Documents\Qt\Line_fitting\eigen-3.3.9

在这里插入图片描述

下载Qcustomplot,同理下载下来,这个是两个文件qcustomplot.h和qcustomplot.cpp,直接导入项目就行,同时在第三行后面加入printsupport提供绘图支持:
在这里插入图片描述
在需要使用到两个功能的地方引入即可,例如:

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "QFileDialog"
#include <Eigen/Dense>
#include "qcustomplot.h"

using namespace Eigen;

我们需要做的是三件事:

  1. 读取坐标文件存入矩阵
  2. 矩阵运算求出结果
  3. 绘图

1.Qt读取文件存入矩阵

Qt提供了QFileDialog类,可以调用窗口选择文件

	//定义文件对话框类
    QFileDialog *fileDialog = new QFileDialog(this);
    //定义文件对话框标题
    fileDialog->setWindowTitle(QStringLiteral("选择数据文件"));
    //设置默认文件路径
    fileDialog->setDirectory("C:\\users");
    //设置文件过滤器
    fileDialog->setNameFilter(tr("Text files (*.txt)"));
    //设置可以选择多个文件,默认为只能选择一个文件QFileDialog::ExistingFiles
    fileDialog->setFileMode(QFileDialog::ExistingFiles);
    //设置视图模式
    fileDialog->setViewMode(QFileDialog::Detail);
    if (fileDialog->exec()) {
        //读取文件路径
        QStringList filePaths;//可设为全局变量,存储文件路径名
        filePaths = fileDialog->selectedFiles();
    }

效果如下图:
在这里插入图片描述
下面要用QFile把刚才打开的txt文件内容读取到矩阵中,矩阵由Eigen提供

//读取坐标并存储
        QVector<QString> XY;
        QVector<QString> X;
        QVector<QString> Y;
        QFile file(filePaths.at(0));
        if(!file.open(QIODevice::ReadOnly|QIODevice::Text))
        {
            return;
        }
        QTextStream in(&file);
        while (!in.atEnd())//数据流in按行读取
        {
           QString line=in.readLine();
           XY.append(line);
        }
        for (int i=0;i<XY.length();i++)//遍历一遍XY中的信息
        {
            QString line=XY.at(i);
            line=line.trimmed();
            QStringList linesplit=line.split(" ");//将数据以空格为间隔分开,以QString数组形式存储
            X.append(linesplit.at(0));//X中存坐标第一列
            Y.append(linesplit.at(1));//Y中存坐标第二列
        }

        //将坐标信息存入矩阵X、Y
        MatrixXd B(X.length(),2);//行数按照数据的行数,列数为2
        MatrixXd l(Y.length(),1);

        for (int i=0;i<X.length() ;i++ ) {
            B(i,0)=X.at(i).toDouble();
            B(i,1)=1;
            l(i,0)=Y.at(i).toDouble();
        }

效果如下
在这里插入图片描述
现在我们算是得到了所需要的矩阵,就可以直接计算结果了

2.Eigen矩阵运算得到k、d

公式在上面,直接上代码,Eigen提供了矩阵的转置和求逆方法
由于每个点的权值都是1,所以这里的P直接取单位矩阵就行,否则需要进一步求P(不难)

		MatrixXd I=MatrixXd::Identity(X.length(),Y.length());//单位矩阵
        MatrixXd Z=((B.transpose()*I*B).inverse())*B.transpose()*I*l;//计算公式
        MatrixXd V=B*Z-l;

        double k=Z(0,0);//求出k、d值
        double d=Z(1,0);

得到k、d就可以画图了

3.Qcustomplot绘图

没啥讲的,有k、d和那么多点,直接画就行,有个要点
我们在ui设计里面使用Qcustomplot需要将widget提升为qcustomplot
首先放置一个widget在需要的地方
在这里插入图片描述
右键该widget
在这里插入图片描述

然后将其提升,我是第二次,里面直接有Qcustomplot,第一次也没关系,直接在下面提升的类名称部分按下图填就行,然后点击添加、提升即可。
在这里插入图片描述
下面代码里面是ui->panel,这个就是上面的widget提升之后我改的名字
在这里插入图片描述

你可以自己改,网上查的大多代码是Qcustomplot->,你可以这样

Qcustomplot *Qcustomplot=ui->panel;

下面将进行绘图

//绘图
        QString K=QString::number(k);
        ui->kvalue->setText(K);
        QString D=QString::number(d);
        ui->dvalue->setText(D);

        QVector<double> x,y,yt;//x,y分别为原始数据点,yt为拟合后的纵坐标,(x,yt)一定在拟合直线上
        for (int i=0;i<X.length() ;i++ ) {
            x.append(X.at(i).toDouble());
            yt.append(k*X.at(i).toDouble()+d);
            y.append(Y.at(i).toDouble());
        }

        //绘制拟合直线
        ui->panel->addGraph();
        ui->panel->graph(0)->setPen(QPen(Qt::black,2));
        ui->panel->graph(0)->setData(x, yt);
        //根据图像最高点最低点自动缩放坐标轴,下面同理
        //X、Y轴
        ui->panel->graph(0)->rescaleAxes(true);
        // 为坐标轴添加标签
        ui->panel->xAxis->setLabel("X");
        ui->panel->yAxis->setLabel("Y");

        //绘制拟合后的点,在直线上
        ui->panel->addGraph();
        ui->panel->graph(1)->setPen(QPen(Qt::red));
        ui->panel->graph(1)->setLineStyle(QCPGraph::lsNone);
        ui->panel->graph(1)->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssDisc, 4));
        ui->panel->graph(1)->setData(x, yt);
        //X、Y轴
        ui->panel->graph(1)->rescaleAxes(true);

        //绘制原始数据散点,不一定在直线上
        ui->panel->addGraph();
        ui->panel->graph(2)->setPen(QPen(Qt::green));
        ui->panel->graph(2)->setLineStyle(QCPGraph::lsNone);
        ui->panel->graph(2)->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssDisc, 4));
        ui->panel->graph(2)->setData(x, y);
        //X、Y轴
        ui->panel->graph(2)->rescaleAxes(true);

        // 重画图像
        ui->panel->replot();

在这里插入图片描述
点击此处获取代码链接

QT是一个跨平台的C++图形用户界面应用程序开发框架,而Eigen是一个C++模板库,用于线性代数计算,其中包含了许多矩阵和向量的运算。因此,使用QTEigen实现曲线拟合是一种非常方便和高效的方法。 下面是一段使用QTEigen进行曲线拟合的代码: ```cpp #include <QtGui> #include <QtCore> #include <Eigen/Dense> QVector<double> xData, yData; // 读取数据 void readData(const QString &fileName) { QFile file(fileName); if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) return; xData.clear(); yData.clear(); QTextStream in(&file); while (!in.atEnd()) { QString line = in.readLine(); QStringList fields = line.split(","); if (fields.size() != 2) continue; xData.append(fields.toDouble()); yData.append(fields.toDouble()); } } // 曲线拟合 void fitCurve() { int n = xData.size(); Eigen::MatrixXd A(n, 3); Eigen::VectorXd b(n); for (int i = 0; i < n; ++i) { A(i, 0) = xData[i] * xData[i]; A(i, 1) = xData[i]; A(i, 2) = 1; b(i) = yData[i]; } Eigen::VectorXd x = A.colPivHouseholderQr().solve(b); double a = x(0), b = x(1), c = x(2); qDebug() << "f(x) = " << a << "x^2 + " << b << "x + " << c; } int main(int argc, char *argv[]) { QApplication app(argc, argv); readData("data.txt"); fitCurve(); return app.exec(); } ``` 这段代码实现了从文件中读取数据,并使用Eigen库对数据进行曲线拟合,最后输出拟合函数的系数。具体来说,这里使用了二次函数对数据进行拟合,可以根据具体情况选择其他的函数形式。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

please叫我滚去学习

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值