【QT】OpenGL显示六轴陀螺仪3D实时姿态

这篇具有很好参考价值的文章主要介绍了【QT】OpenGL显示六轴陀螺仪3D实时姿态。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

参考博客

https://blog.csdn.net/qq_35629971/article/details/126203543?spm=1001.2014.3001.5506

效果图

6轴陀螺仪 姿态调试软件,QT,qt,3d,ui

新建opengl工程

新建一个qt的空白工程,附带UI界面,我的工程名称就叫my_3d
6轴陀螺仪 姿态调试软件,QT,qt,3d,ui
UI界面可以可以放一些自己想要的按键、文本或者其他控件。这个不影响3D效果的展示,这些控件都会展示在3D效果图的上层,不会被3D效果覆盖
首先我们新添加一个类:
6轴陀螺仪 姿态调试软件,QT,qt,3d,ui
6轴陀螺仪 姿态调试软件,QT,qt,3d,ui
6轴陀螺仪 姿态调试软件,QT,qt,3d,ui
这样就会添加好了我刚开始界面上显示的myopenglwidget.cpp和myopenglwidget.h,
我们对头文件的类进行修改,继承其他类:

#ifndef MYOPENGLWIDGET_H
#define MYOPENGLWIDGET_H
#include <QOpenGLBuffer>
#include <QOpenGLFunctions>
#include <QOpenGLShaderProgram>
#include <QOpenGLTexture>
#include <QOpenGLWidget>
class QOpenGLShaderProgram;
class QOpenGLTexture;

/* 同时继承自QOpenGLWidget类和QOpenGLFunctions类 */
class MyOpenGLWidget : public QOpenGLWidget, protected QOpenGLFunctions {
    Q_OBJECT
public:
    explicit MyOpenGLWidget(QWidget* parent = 0);

protected:
    void initializeGL();//初始化函数
    void paintGL();//画图函数,每次刷新页面都会调用
    void resizeGL(int width, int height);//页面大小设置函数

public:
    GLfloat pitch, yaw, roll;//航向角、翻滚角和俯仰角

private:
    QOpenGLShaderProgram* program; //声明了一个QOpenGLShaderProgram对象指针,作为着色器程序。
    QOpenGLBuffer cache;//缓存器
    QOpenGLTexture* texture[6];//纹理地址
};

#endif // MYOPENGLWIDGET_H

暂且不对上述代码内的变量做解释,在后面会在cpp文件内调用说明

initializeGL()函数

void MyOpenGLWidget::initializeGL()
{
    // 绑定QOpenGLFunctions函数
    initializeOpenGLFunctions();
    // 增加图像深度
    glEnable(GL_DEPTH_TEST);

    // 创建顶点着色器
    QOpenGLShader* peak_shader = new QOpenGLShader(QOpenGLShader::Vertex, this);

    const char* peak_shader_code = "                                          \n"
                                   "#version 330                              \n"
                                   "in vec4 peak;                        \n"
                                   "in vec4 intexture;                        \n"
                                   "out vec4 outtexture;                        \n"
                                   "uniform mat4 matrix;                      \n"
                                   "void main() {                             \n"
                                   "   outtexture = intexture;                  \n"
                                   "   gl_Position = matrix * peak;      \n"
                                   "}                                         \n";
    peak_shader->compileSourceCode(peak_shader_code);
    // 创建片段着色器
    QOpenGLShader* texture_shader = new QOpenGLShader(QOpenGLShader::Fragment, this);
    //设置源码并编译  //参数:R G B 透明度(1.0为不透明)
    const char* texture_shader_code = "#version 330                              \n"
                                      "uniform sampler2D texture;                     \n"
                                      "in vec4 outtexture;                          \n"
                                      "out vec4 real_texture;                           \n"
                                      "void main() {                              \n"
                                      "   real_texture = texture2D(texture, outtexture.st);        \n"
                                      "}                                          \n";
    texture_shader->compileSourceCode(texture_shader_code); //为着色器设置源码并编译

    // 创建着色器程序
    program = new QOpenGLShaderProgram;
    program->addShader(peak_shader);
    program->addShader(texture_shader);
    program->link(); //将所有加人到程序中的着色器链接到一起,最后
    program->bind(); //将该着色器程序绑定到当前OpenGL环境中。
    //for (int i = 0; i < 6; i++)
    //textures[i] = new QOpenGLTexture(QImage(QString(":/new/side/IMU图/side%1.png").arg(i + 1)).mirrored());
    texture[0] = new QOpenGLTexture(QImage(QString(":/new/side/IMU图/side1.png")));
    texture[1] = new QOpenGLTexture(QImage(QString(":/new/side/IMU图/side2.png")));
    texture[2] = new QOpenGLTexture(QImage(QString(":/new/side/IMU图/side3.png")));
    texture[3] = new QOpenGLTexture(QImage(QString(":/new/side/IMU图/side4.png")));
    texture[4] = new QOpenGLTexture(QImage(QString(":/new/side/IMU图/side5.png")));
    texture[5] = new QOpenGLTexture(QImage(QString(":/new/side/IMU图/side6.png")));
}

side1-side6这6张图是我对着陀螺仪拍的他六个面的图片,用来贴到后面我们画的长方体的六个面上,这样我们看到的就是一个3D的陀螺仪,这六张图我加入了我的工程的资源文件里,而不是用的绝对/相对文件路径的形式,这样这个软件即使在其他机器上使用也可以正常显示3D图。
如果是多面体,都想要贴纹理,那么可以用上述for循环的方法批量处理纹理

resizeGL(int,int)函数

这个其实是设置窗口大小的,但是我没有用到,这里我也会提到两个方式去显示3D图。
一个是主窗体显示,一个是小窗体显示,因为OpenGL默认的原点在窗体的中心点,所以如果放在主窗体的话,他就会占据窗体中心,导致我很多控件什么的都没法有比较好的布局显示,所以我用的是嵌入式显示,就是增加一个窗体专门用于显示这个3D图,看下两个的区别:
主窗体显示:
6轴陀螺仪 姿态调试软件,QT,qt,3d,ui
小窗体嵌入显示:
6轴陀螺仪 姿态调试软件,QT,qt,3d,ui
如果是在主窗体显示,那么就注释掉原来主窗体的显示:

#include "my_3d.h"
#include "myopenglwidget.h"
#include <QApplication>
int main(int argc, char* argv[])
{
    QApplication a(argc, argv);
    // my_3d w;
    // w.show();
    MyOpenGLWidget w;
    w.show();
    return a.exec();
}

如果想要嵌入式小窗体显示那就在UI界面创建一个widget的控件,将这个控件提升为类myopenglwidget:
6轴陀螺仪 姿态调试软件,QT,qt,3d,ui
之后3D的显示就会显示在这个新的widget窗体中,而不会显示在主窗体,已达到嵌入显示的效果

paintGL()函数

#define LENGTH 1.2f
#define HALF_LENGTH LENGTH / 2
#define WIDTH 0.9f
#define HALF_WIDTH WIDTH / 2
#define HEIGHT 0.2f
#define HALF_HEIGHT HEIGHT / 2
#define X 1.0
void MyOpenGLWidget::paintGL()
{
    // 顶点位置  两个面  每个面四个顶点  每个顶点三个坐标值
    GLfloat peak_coordinate[6][4][4] = {
        { { -HALF_LENGTH, HALF_HEIGHT, HALF_WIDTH, X }, { -HALF_LENGTH, -HALF_HEIGHT, HALF_WIDTH, X }, { HALF_LENGTH, -HALF_HEIGHT, HALF_WIDTH, X }, { HALF_LENGTH, HALF_HEIGHT, HALF_WIDTH, X } },
        { { HALF_LENGTH, HALF_HEIGHT, HALF_WIDTH, X }, { HALF_LENGTH, -HALF_HEIGHT, HALF_WIDTH, X }, { HALF_LENGTH, -HALF_HEIGHT, -HALF_WIDTH, X }, { HALF_LENGTH, HALF_HEIGHT, -HALF_WIDTH, X } },
        { { HALF_LENGTH, HALF_HEIGHT, -HALF_WIDTH, X }, { HALF_LENGTH, -HALF_HEIGHT, -HALF_WIDTH, X }, { -HALF_LENGTH, -HALF_HEIGHT, -HALF_WIDTH, X }, { -HALF_LENGTH, HALF_HEIGHT, -HALF_WIDTH, X } },
        { { -HALF_LENGTH, HALF_HEIGHT, -HALF_WIDTH, X }, { -HALF_LENGTH, -HALF_HEIGHT, -HALF_WIDTH, X }, { -HALF_LENGTH, -HALF_HEIGHT, HALF_WIDTH, X }, { -HALF_LENGTH, HALF_HEIGHT, HALF_WIDTH, X } },
        { { -HALF_LENGTH, HALF_HEIGHT, -HALF_WIDTH, X }, { -HALF_LENGTH, HALF_HEIGHT, HALF_WIDTH, X }, { HALF_LENGTH, HALF_HEIGHT, HALF_WIDTH, X }, { HALF_LENGTH, HALF_HEIGHT, -HALF_WIDTH, X } },
        { { -HALF_LENGTH, -HALF_HEIGHT, -HALF_WIDTH, X }, { -HALF_LENGTH, -HALF_HEIGHT, HALF_WIDTH, X }, { HALF_LENGTH, -HALF_HEIGHT, HALF_WIDTH, X }, { HALF_LENGTH, -HALF_HEIGHT, -HALF_WIDTH, X } },
    };
    //顶点颜色
    GLfloat texture_coordinate[6][4][2] = {
        { { 0.0f, 1.0f }, { 0.0f, 0.0f }, { 1.0f, 0.0f }, { 1.0f, 1.0f } },
        { { 0.0f, 1.0f }, { 0.0f, 0.0f }, { 1.0f, 0.0f }, { 1.0f, 1.0f } },
        { { 0.0f, 1.0f }, { 0.0f, 0.0f }, { 1.0f, 0.0f }, { 1.0f, 1.0f } },
        { { 0.0f, 1.0f }, { 0.0f, 0.0f }, { 1.0f, 0.0f }, { 1.0f, 1.0f } },
        { { 0.0f, 0.0f }, { 0.0f, 1.0f }, { 1.0f, 1.0f }, { 1.0f, 0.0f } },
        { { 0.0f, 1.0f }, { 0.0f, 0.0f }, { 1.0f, 0.0f }, { 1.0f, 1.0f } }
    };

    cache.create(); //在OpenGL服务器中创建缓存对象
    cache.bind(); //将与该对象相关联的缓存绑定到当前OpenGL环境
    cache.allocate(peak_coordinate, 144 * sizeof(GLfloat)); //在缓存中为数组分配空间并将缓存初始化为数组的内容。
    //这里以原点为中心设置了一个正方形的4个顶点,首先是左上角的顶点,然后沿逆时针方向设置了其他3个顶点,顶点顺序可以是顺时针也可以是逆时针,逆时针绘制出来的是正面,而顺时针绘制出来的是反面。
    GLuint peak = program->attributeLocation("peak");
    program->setAttributeBuffer(peak, GL_FLOAT, 0, 4, 0);
    glEnableVertexAttribArray(peak);

    cache.write(96 * sizeof(GLfloat), texture_coordinate, 48 * sizeof(GLfloat));
    GLuint intexture = program->attributeLocation("intexture");
    program->setAttributeBuffer(intexture, GL_FLOAT, 96 * sizeof(GLfloat), 2, 0);
    glEnableVertexAttribArray(intexture);
    program->setUniformValue("tex", 0);

    QMatrix4x4 matrix;
    matrix.perspective(40.0f, 1, 0.01f, 5.1f);
    matrix.translate(-0, -0, -3);
    matrix.rotate(pitch, 1.0, 0.0, 0.0);
    matrix.rotate(yaw, 0.0, 1.0, 0.0);
    matrix.rotate(roll, 0.0, 0.0, 1.0);
    program->setUniformValue("matrix", matrix);

    // 绘制
    for (int i = 0; i < 6; i++) {
        texture[i]->bind();
        glDrawArrays(GL_TRIANGLE_FAN, i * 4, 4);
    }
}

GLfloat peak_coordinate[6][4][4]

解释下这个GLfloat peak_coordinate[6][4][4]数组,这个是用来保存顶点的,那么我们知道一个正方体有6个面,每个面有四个角,这四个角就是四个顶点,确定一个面的四个顶点,就可以确定这个面的面积和位置,我们知道3D空间是三维的,所以代码里的第四个参数X可以不需要,这个不在目标范围内,不是必须的,这个可以当成是缩放比例来用,不设置他的时候他默认是1.0,我是之前想做测试,所以加了这个参数,如果不关心这个参数只需要GLfloat peak_coordinate[6][4][3]就行;
解释下3D坐标系的划分和方向:

6轴陀螺仪 姿态调试软件,QT,qt,3d,ui
Z轴为正对屏幕中心方向远离屏幕是正,接近屏幕是负;

GLfloat texture_coordinate[6][4][2]

解释下GLfloat texture_coordinate[6][4][2]这个变量:
这个是刚才我们拍的6个面的图片,我们要将这个图片贴到6个面上,这个变量里存储的就是图片的坐标,这个坐标与上述坐标不是一个坐标系,我们正对图片,图片左下角坐标为(0,0),左上角为(0,1),右上角为(1,1),右下角为(1,0),这四个坐标与每个面的四个顶点不同的组合可以显示不同的贴图效果。

cache变量

解释下这个变量的作用,这个变量是一个缓存对象,用来存储上面两个变量的坐标数据,如果没这个缓存的话,每次刷新界面重新绘图时都会重新从这个客户内存的去复制这些数据,而有了缓存之后就会把这些数据放到图形内存,而不需要每次都去复制客户内存的数据。
cache.allocate(peak_coordinate, 144 * sizeof(GLfloat)); /
解释下这个144的由来,虽然初始化缓存的对象是peak_coordinate,但是我们后面还要写问题图片的坐标,所以我们还要考虑纹理坐标数组的大小,所以这个144是顶点坐标数量96+纹理坐标数量48

cache.write(96 * sizeof(GLfloat), texture_coordinate, 48 * sizeof(GLfloat));
这里就对应了上述的顶点坐标数量和纹理坐标数量第一个参数是96 * sizeof(GLfloat),这个是偏移量,就是之前我们初始化的时候已经吧peak_coordinate作为对象存入进去了,为了不覆盖顶点坐标的数据,所以在顶点坐标存储位置的后面存入纹理坐标数据;

	QMatrix4x4 matrix;
    matrix.perspective(40.0f, 1, 0.01f, 5.1f);
    matrix.translate(-0, -0, -3);
    matrix.rotate(pitch, 1.0, 0.0, 0.0);
    matrix.rotate(yaw, 0.0, 1.0, 0.0);
    matrix.rotate(roll, 0.0, 0.0, 1.0);
    program->setUniformValue("matrix", matrix);

perspective为透视投影设置,第一个参数40.0f也可以认为是缩放参数,数值越大,3D图像越小,第二个参数是窗口纵横比,第三个参数和第四个参数可以认为是远近比例,官方历程里设置的是0.1和100.0,我是针对我的图像做了调整,大家可以调整这些参数看看有什么区别。

translate是坐标平移,之前说过3D图像的坐标原点是屏幕中心,我这里是把Z轴往屏幕里面缩了三个单位,这个应该是和前面perspective的第四个参数有关联,translate函数的Z轴参数如果大于perspective第四个参数3D图就不可见了,也就是往里缩出了屏幕之外。

rotate就是旋转了,我是分别对三个轴进行了旋转设置,我是创建了这三个轴的变量,之后把这三个轴的变量和陀螺仪的数据绑定后就可以实时显示陀螺仪姿态了

动态显示

因为陀螺仪部分代码不可披露,所以我用按键来控制图像的三个轴的变化,效果见文章开头:
头文件,如果你创建工程的时候没有重命名,那么这个就是mainwindow.h,我是重命名了工程名称,所以生成的是my_3d.h:

#ifndef MY_3D_H
#define MY_3D_H

#include "myopenglwidget.h"
#include <QMainWindow>
namespace Ui {
class my_3d;
}

class my_3d : public QMainWindow {
    Q_OBJECT

public:
    explicit my_3d(QWidget* parent = 0);
    ~my_3d();
    void keyPressEvent(QKeyEvent* event);//按键函数
    MyOpenGLWidget* w;//指针

private:
    Ui::my_3d* ui;
};

#endif // MY_3D_H

.cpp文件:

#include "my_3d.h"
#include "QKeyEvent"
#include "ui_my_3d.h"
my_3d::my_3d(QWidget* parent)
    : QMainWindow(parent)
    , ui(new Ui::my_3d)
{
    ui->setupUi(this);
    w = ui->widget_2;
    w->show();
}

my_3d::~my_3d()
{
    delete ui;
}

void my_3d::keyPressEvent(QKeyEvent* event)
{
    switch (event->key()) {
    case Qt::Key_Up:
        w->pitch += 10;
        break;
    case Qt::Key_Left:
        w->yaw += 10;
        break;
    case Qt::Key_Right:
        w->roll += 10;
        break;
    default:
        break;
    }
    w->update();
}

这样就可以通过键盘右下角的上左右三个方向键控制3D图像的三轴转动
想显示陀螺仪的实时姿态可以加上串口数据的解析,将陀螺仪三个轴的角度数据传给这三个变量,就可以实时显示陀螺仪姿态;

作者仅仅会用,理解不深,也尝试加摄像机搞个完美视角,但是失败了,如果文章有误,请帮忙指出,谢谢。文章来源地址https://www.toymoban.com/news/detail-782586.html

到了这里,关于【QT】OpenGL显示六轴陀螺仪3D实时姿态的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • [Android]将实时获取的加速度计、陀螺仪、磁场数据通过卡尔曼滤波,转换为手机的姿态角

    由于需要实时获取传感器数据,我们可以使用Android系统提供的SensorManager类来获取加速度计、陀螺仪和磁场传感器的数据。然后,我们可以将这些数据传递给一个卡尔曼滤波器对象进行滤波。 以下是一段示例代码: 在这个示例代码中,我们注册了对加速度计、陀螺仪和磁场传

    2024年02月17日
    浏览(47)
  • Android使用陀螺仪

    在Android应用中使用陀螺仪可以帮助实现各种功能,比如游戏控制、虚拟现实体验、运动追踪等。以下是使用Android陀螺仪的基本步骤: 获取传感器服务 : 首先,需要获取设备上的陀螺仪传感器服务。可以通过 SensorManager 类来获取。 注册监听器 : 使用 SensorManager 注册一个陀

    2024年04月26日
    浏览(33)
  • 平衡小车——陀螺仪

    可以通过MPU6050获取加速度信息 可以通过DMP库获取角度信息 MPU6050 MPU6050是一种常用的集成电路(IC),结合了3轴陀螺仪和3轴加速度计。它用于各种需要运动跟踪和感应的电子项目和设备。MPU6050由英飞凌科技公司(InvenSense)制造,现在已被TDK收购。它的一些主要特点包括:

    2024年02月02日
    浏览(40)
  • 6轴陀螺仪姿态解算

    之前看过学长姿态解算相关代码,因为要做平衡车的项目,希望陀螺仪处理数据能够达到很好的效果,大概2个星期前,看的学长代码,当时把大部分代码看懂是用来干什么的,但原理还是一窍不通,没办法,太高深了hhhhh。用学长的代码很顺利就完成了基本工作,但当时调

    2023年04月08日
    浏览(36)
  • 陀螺仪MPU6050(IIC&源码)

    1. 陀螺仪 1.1   什么是陀螺仪? 检测角度变化的一个装置。 1.1.1  有什么用?? 用于检测角度变化,用角度变化的值判断物体的运动轨迹。 1.1.2  我们怎么用? 我们是使用这个装置(或者说设备)获取到数据,再使用这个数据得到我们想要的信息。 这里我使用陀螺仪获取板

    2024年02月13日
    浏览(40)
  • 陀螺仪小车(Forerake-Car)

    项目简介:搭建一辆有arduino UNO 与rnf24l01组成的小车;手部安装由arduino nano开发板、nrf24l01、imu构成的手势控制器,利用手势控制器检测手部状态、发送信号对小车进行前进,实现基于卡尔曼滤波的MPU6050姿态结算。 如果你想搭建一辆有Arduino UNO和nRF24L01组成的小车,并使用手势

    2024年02月14日
    浏览(81)
  • 三轴陀螺仪解算姿态(四元数)

    三轴陀螺仪可以测量载体在三个轴上的角速度分量,对这些角速度进行积分就可以得到旋转的角度,应用到载体上就可以得到载体的姿态。 假设导航坐标系为东北天,载体坐标系为右前上。 初始载体坐标系和导航坐标系重合,对应的四元数为q=[1,0,0,0],使用此四元数表示 载

    2024年02月05日
    浏览(42)
  • 课题学习(十九)----Allan方差:陀螺仪噪声分析

       Allan方差是一种分析时域数据序列的方法,用于测量振荡器的频率稳定性。该方法还可用于确定系统中作为平均时间函数的本征噪声。该方法易于计算和理解,是目前最流行的识别和量化惯性传感器数据中存在的不同噪声项的方法之一。该方法的结果与适用于惯性传感器

    2024年01月22日
    浏览(46)
  • 陀螺仪与加速度计的姿态融合——互补滤波

    本篇文章我们来讲讲如何将陀螺仪和加速度计的数据结合起来,获取更准确的姿态数据,使用的是互补滤波的方法。 阅读本文需有一定的知识基础,可以参见作者以前MPU6050的两篇文章:《MPU6050陀螺仪和加速度计数据的获取和校准》、《MPU6050官方DMP的移植和使用》,以及了解

    2024年02月03日
    浏览(49)
  • 陀螺仪mpu6050的使用(附带HAL的使用)

    我们日常见的陀螺仪模块的使用就是在平衡小车和控制小车的移动上,那么陀螺仪是怎么使用的呢,首先就是能很好的使用I2C,而看到这里,说的一切都是虚的,首先陀螺仪的配置和数据手册大家也是没少看的,但是还是跟我再了解一遍. MPU6050内部整合了三轴MEMS陀螺仪、三轴

    2024年02月13日
    浏览(55)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包