使用PYQT5和VTK实现一个六轴跟随的电路板转动动画效果

news2024/11/28 1:38:53

在这里插入图片描述

实现过程:

关于六轴:

线下有一个带有六轴姿态传感器的硬件设备,将采集到的三轴加速度和角速度的值每隔1秒通过串口发送给电脑,电脑上位机使用的是pyqt5,在python中调用serial模块进行串口数据的接收,接收程序如下:

class scsThread(QThread):
	    update_ui_signal = pyqtSignal(int)
	
	    def __init__(self, window):
	        super(scsThread, self).__init__()
	        self.CONTROLLER_COM = None
	
	        self.window = window
	
	    def run(self):
	        com_list = run.get_COM()
	        if com_list != None:
	            self.CONTROLLER_COM = com_list[1]
	
	            timeout = 3 * 10  # 3秒超时
	            ctr_serial = serial.Serial(self.CONTROLLER_COM, 115200)
	            while timeout > 0:  # 判断是否接收超时
	                # print(timeout)
	                timeout = timeout - 1
	                time.sleep(0.1)
	                n = ctr_serial.inWaiting()
	
	                if n:
	                    data = ctr_serial.read(n)
	                    data = data.decode('utf-8')
	                    if data != '':
	                        # print(data)
	                        data_num = data.find("test data is:")
	                        if data_num >= 0:
	                            date_list = re.split(r'[,\s]+', data[data_num-28:])
	                            self.data["euler"] = date_list[16] + " " + date_list[17] + " " + date_list[18]  # 欧拉角
	
	                            timeout = 3 * 10
	                                                           self.window.STL_Thread_object.update_ui_signal.emit(self.data["euler"])  # 姿态

从帧中解析出原始数据,将其转换为欧拉角,转换程序如下:

// 定义全局变量
double pitch, roll, yaw;

double q0 = 1.0;
double q1 = 0.0;
double q2 = 0.0;
double q3 = 0.0;
double exInt = 0.0;
double eyInt = 0.0;
double ezInt = 0.0;

// 定义常量
const double halfT 0.001; // half of sample period, in seconds
const double Kp = 100;
const double Ki = 0.002;


//欧拉角计算
void update_IMU(double ax, double ay, double az, double gx, double gy, double gz, double* pitch, double* roll, double* yaw) {
    // 测量正常化
    double norm = sqrt(ax * ax + ay * ay + az * az);

    // 单元化
    ax = ax / norm;
    ay = ay / norm;
    az = az / norm;

    // 估计方向的重力
    double vx = 2 * (q1 * q3 - q0 * q2);
    double vy = 2 * (q0 * q1 + q2 * q3);
    double vz = q0 * q0 - q1 * q1 - q2 * q2 + q3 * q3;

    // 错误的领域和方向传感器测量参考方向之间的交叉乘积的总和
    double ex = (ay * vz - az * vy);
    double ey = (az * vx - ax * vz);
    double ez = (ax * vy - ay * vx);

    // 积分误差比例积分增益
    exInt = exInt + ex * Ki;
    eyInt = eyInt + ey * Ki;
    ezInt = ezInt + ez * Ki;

    // 调整后的陀螺仪测量
    gx = gx + Kp * ex + exInt;
    gy = gy + Kp * ey + eyInt;
    gz = gz + Kp * ez + ezInt;

    // 整合四元数
    q0 = q0 + (-q1 * gx - q2 * gy - q3 * gz) * halfT;
    q1 = q1 + (q0 * gx + q2 * gz - q3 * gy) * halfT;
    q2 = q2 + (q0 * gy - q1 * gz + q3 * gx) * halfT;
    q3 = q3 + (q0 * gz + q1 * gy - q2 * gx) * halfT;

    // 正常化四元数
    norm = sqrt(q0 * q0 + q1 * q1 + q2 * q2 + q3 * q3);
    q0 = q0 / norm;
    q1 = q1 / norm;
    q2 = q2 / norm;
    q3 = q3 / norm;

    // 获取欧拉角 pitch、roll、yaw
    *pitch = asin(-2 * q1 * q3 + 2 * q0 * q2) * 57.3;
    *roll = atan2(2 * q2 * q3 + 2 * q0 * q1, -2 * q1 * q1 - 2 * q2 * q2 + 1) * 57.3;
    *yaw = atan2(2 * (q1 * q2 + q0 * q3), q0 * q0 + q1 * q1 - q2 * q2 - q3 * q3) * 57.3;
}

将欧拉角发送给VTK的修改模型角度的函数接口(单独一个线程,见下面核心程序)。

关于3D模型:

电路板使用Altium Designer的话可以导出step格式的3D模型文件,然后用FreeCAD将其转换为STL格式文件,VTK库直接加载STL文件即可。

关于VTK:

下载方式是:pip install VTK-8.1.2-cp37-cp37m-win_amd64.whl
下载链接:https://vtk.org/download/
实例参考:https://github.com/lorensen/VTKExamples
DEMO:https://github.com/kimtaikee/QVTKDemo

导入的时候如果使用import vtk,在调试的时候没啥问题,但是在封装为exe的时候,会报no named moudle vtkmodules.all或vtk.py not found vtkmodules.all或的错误,修改方法是修改为import vtkmodules.all as vtk

报ModuleNotFoundError: No module named 'vtk.qt’的解决方法是将from vtk.qt import QVTKRenderWindowInteractor修改为from vtkmodules.qt.QVTKRenderWindowInteractor import QVTKRenderWindowInteractor

关于程序:

程序环境是PyCharm Community Edition 2021.1.1 x64,用插件QtDesigner和PyUIC配置窗口步骤的略过。核心程序如下:

class stlThread(QThread): 

    update_ui_signal = pyqtSignal(str)

    def __init__(self, window):
        super(stlThread, self).__init__()
        # 信号绑定槽函数
        self.update_ui_signal.connect(self.display_stl)
        self.window = window


    def display_stl(self, stl_str): # accel + gyro
        stl_data = stl_str.split()
        # print(stl_data)
        display_data = "pitch\r\n" + stl_data[0] + "\r\n" + "roll\r\n" + stl_data[1] + "\r\n"  + "yaw\r\n" + stl_data[2]
        self.window.label_stl.setText(display_data)
        self.window.actor.SetOrientation(float(stl_data[0]), float(stl_data[1]), float(stl_data[2]))
        self.window.ren.AddActor(self.window.actor)
        self.window.ren.ResetCamera()
        self.window.iren.Initialize()
        self.window.iren.Start()

    def run(self):
    	while True:  # 让子线程一直运行,等待主线程(ui线程)下发的任务
        		time.sleep(1)
        		# print("clock thread")


class MyInsideWindow(QMainWindow, Ui_SCS_INSIDE):
    def __init__(self):
        super(MyInsideWindow, self).__init__()
        self.setupUi(self)
        self.pushButton.clicked.connect(self.inside_pro)  # 内部测试窗口操作
        ####  模型相关
        self.vtkWidget = QVTKRenderWindowInteractor(self.frame)
        self.vl = Qt.QVBoxLayout()
        self.vl.addWidget(self.vtkWidget)

        self.ren = vtk.vtkRenderer()
        self.vtkWidget.GetRenderWindow().AddRenderer(self.ren)
        self.iren = self.vtkWidget.GetRenderWindow().GetInteractor()

        source = vtk.vtkSTLReader()  # 从文件加载STL模型数据源
        source.SetFileName("PCB.stl")

        # Create a mapper
        mapper = vtk.vtkPolyDataMapper()
        mapper.SetInputConnection(source.GetOutputPort())

        # Create an actor
        self.actor = vtk.vtkActor()
        self.actor.SetMapper(mapper)

        self.actor.SetOrientation(0, 180, 0)

        self.ren.AddActor(self.actor)

        self.ren.ResetCamera()

        self.frame.setLayout(self.vl)

        self.STL_Thread_object = None


    def inside_init(self):
        self.show()
        self.iren.Initialize()
        self.iren.Start()

        if self.STL_Thread_object != None:
            self.STL_Thread_object = None


    def inside_pro(self):  # 内部窗口操作
        self.STL_Thread_object = stlThread(self)
        self.STL_Thread_object.start()

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/590543.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

专业是要选软工还是人工智能?

大家好,我是帅地。 在帅地的训练营里,也有不少 26 届的学员,不过大一即将过去,部分学校是到了大一后面或者大二才开始细分专业方向的,包括一些想要转专业的同学,也需要选择一个细分的方向,而且…

10:mysql----存储引擎--进阶篇

目录 1:MySQL体系结构 2:存储引擎简介 3:存储引擎特点 4:存储引擎选择 1:MySQL体系结构 连接层 : 最上层是一些客户端和链接服务,主要完成一些类似于连接处理、授权认证、及相关的安全方案。服务器也会为安全接入的每个客户端验证它所具有的操作权限。 服务层 :…

抽象轻松JavaScript

想象一样,现在有一个苹果,两个苹果,一箱苹果在你面前 看,上面的三种苹果,(我写的是苹果就是苹果) 语境1 例如你现在要搬运苹果! 那么现在上面有苹果,一个,两…

阿里云的数据库架构如何设计,以实现高可用性和容灾性?

阿里云的数据库架构如何设计,以实现高可用性和容灾性?   在当今的数字化时代,数据库作为应用程序的核心组件之一,对于企业的正常运行至关重要。这篇文章将为您解析阿里云如何设计其数据库架构,以实现高可用性和容灾性…

加法器种类介绍

二进制加法器 二进制加法器接收加数A和B,以及进位Ci,输出和S,以及进位输出Co.二进制加法器的真值表如下: 逻辑表达式: S A ⊕ B ⊕ C i SA⊕B⊕C_i SA⊕B⊕Ci​ C o A B B C i A C i C_oABBC_iAC_i Co​ABBCi​ACi​ 从实现的角度,可以…

好兄弟,一天面了4家公司,堪称Offer收割机...

好兄弟一天面了4家公司,堪称Offer收割机… 面试感受 先说一个字 是真的 “ 累 ” 安排的太满的后果可能就是一天只吃一顿饭,一直奔波在路上 不扯这个了,给大家说说面试吧,我工作大概两年多的时间,大家可以参考下 在…

开关电源DCDC并联均流输出8V(XL4015)-2011年全国电赛题

2011年全国电赛题-开关电源模块并联供电系统,两路XL4015芯片做DCDC模块输出8V,采用主从均流法,可实现多种比例精确分配电流,效率在80%以上。 题目 设计并制作一个由两个额定输出功率均为 16W 的 8V DC/DC 模块构成的并联供电系统…

【事务】@Transactional 注解参数详解

文章目录 前言一、参数详解1.1、isolation(事务隔离级别)1.2、propagation(事务传播机制)1.3、readOnly(事务读写性)1.4、noRollbackFor 和 noRollbackForClassName(遇到时不回滚)1.…

PHPMySQL基础(四):模拟登录Login功能案例

PHP&MySQL基础(一):创建数据库并通过PHP进行连接_长风沛雨的博客-CSDN博客 PHP&MySQL基础(二):通过PHP对MySQL进行增、删、改、查_长风沛雨的博客-CSDN博客 PHP&MySQL基础(三):处理查询SQL返…

敏捷指标: 评估计划的进展

作者 | Will Hayes, Patrick Place, and Keith Korzec ——卡耐基梅隆大学 度量标准有助于实现一个运作良好的系统,评判现有流程的绩效。在项目交付契约功能时能够对其性能进行监督。本文探讨了在一个复杂的信息物理系统的迭代、增量交付过程中,政府项目…

Django从Models 10分钟建立一套RestfulApi

简介 Django是一套完善而强大的web开发框架, 结合Django Restframework我们可以非常快的搭建一套后台的api, 该api主要特点: 标准的Restful接口, 支持增删改查 每个模型分列表和详情两种接口, 列表GET获取列表/POST新建,详情接口GET获取详情/PUT修改/DELETE删除所有接口自带权…

DJ4-5 基本分段存储管理方式

目录 4.5.1 分段式存储管理方式的引入 4.5.2 分段式存储管理的基本原理 一、分段 二、段表 三、地址变换机构 4.5.3 段的共享和保护 一、分页共享 二、分段共享 4.5.4 段页式存储管理 一、段页式存储管理的引入 二、段页式存储管理 三、地址变换机构 四、评价…

小明找前缀100000(假)

题目背景 小明最近上课天天睡觉,于是啥都不会。 一天,老师终于点兵点将点到他回答问题,你能帮他渡过难关吗? 现在老师给了小明 n 个由 0、1 构成的字符串,然后有 m 次询问, 每次询问给出一个由 0、1 构…

【医学图像】图像分割系列.3 (uncertainty)

介绍几篇使用不确定性引导的医学图像分割论文:UA-MT(MICCAI2019),SSL4MIS(MICCAI2021),UG-MCL(AIIM2022). Uncertainty-aware Self-ensembling Model for Semi-supervise…

3-网络初识-协议

1.概念 协议,网络协议的简称,网络协议是网络通信(即网络数据传输)经过的所有网络设备都必须共同遵从的一组约定、规则。如怎么样建立连接、怎么样互相识别等。只有遵守这个约定,计算机之间才能相互通信交流。通常由三…

Android系统中的Binder通信机制分析(6)- Binder通信机制详解

声明 其实对于Android系统Binder通信的机制早就有分析的想法,记得2019年6、7月份Mr.Deng离职期间约定一起对其进行研究的,但因为我个人问题没能实施这个计划,留下些许遗憾…文中参考了很多书籍及博客内容,可能涉及的比较多先不具…

SER5 5500U 黑苹果系统安装教程

注意事项: 安装黑苹果系统需要一定的技术和操作经验,而且存在一定的风险,安装前请注意备硬盘内的数据,以免数据丢失,不推荐普通用户进行尝试,本系统及引导由黑果小兵独家制作,更多黑苹果教程百度…

安装CHATGPT保姆级教程(windows版)

ai包链接: 链接:https://pan.baidu.com/s/1tKuG4OfkewlDRU292vx8mw?pwdtw8t 提取码:tw8t 一、安装篇 安装python,使用软件包中的python安装程序安装后检查是否安装成功,cmd窗口运行命令: python –vers…

【九章斩题录】C/C++:替换空格(JZ5)

精品题解 🔥 《九章斩题录》 👈 猛戳订阅 📜 目录: JZ5 - 替换空格 「 法一 」暴力美学 「 法二 」另开数组 「 法三 」反向替换(利用 rfind replace) 「 整活 」不用C,Python 一行代码搞…

C#基本语法

关键字 标识符命名规则 标识符中只能出现英文字母、数字、下划线,以及这几种字符,不能出现诸如"空格,!"等这些字符;标识符名称只能以下划线,字母以及打头,不可以用数字作为标识符名…