这个博文可以说是承接前几篇的MPU6050数据读取、姿态解算,这里主要是实现姿态的可视化(也是课程作业要求,爬了)。
鉴于博主喜欢摸鱼的特点,这个实现也是ddl前几天完成的,由于网上没找到具体实现,只好用极坐标这个沙雕方法硬算出来。
matplotlib提供了在Qt中进行显示的一个backend(应该是?),这个方法基本上是网上统一这么实现(狗头)。
将matplotlib嵌入PyQt5一般是将其下面的matplotlib.backends.backend_qt5agg中的FigureCanvasQTAgg作为一个自己创建的类(可以理解为画布)(如这里的MyFigure)的父类,然后将自己创建的MyFigure类作为一个Widget放入QMainWindow或其他容器中(如QGroupBox)。
这里的代码是直接根据这篇博文进行修改的。
import matplotlib
matplotlib.use("Qt5Agg") # 声明使用QT5
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure
class MyFigure(FigureCanvas):
def __init__(self, width=5, height=4, dpi=100):
# 第一步:创建一个创建Figure
self.fig = Figure(figsize=(width, height), dpi=dpi)
# 第二步:在父类中激活Figure窗口
super(MyFigure, self).__init__(self.fig) # 此句必不可少,否则不能显示图形
self.ax = self.fig.add_subplot(111)#放置坐标系
self.ax.plot([0,1,2,3],[0,1,2,3],'c--')
将子类创建完毕后,我们需要进行实例化,然后将其放置到容器中,这里代码还是直接搬了一部分:
from PyQt5 import QtCore, QtGui, QtWidgets
import matplotlib
matplotlib.use("Qt5Agg") # 声明使用QT5
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure
import numpy as np
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(800, 600)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.groupBox = QtWidgets.QGroupBox(self.centralwidget)
self.groupBox.setGeometry(QtCore.QRect(39, 29, 731, 421))
self.groupBox.setObjectName("groupBox")
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 800, 23))
self.menubar.setObjectName("menubar")
MainWindow.setMenuBar(self.menubar)
self.statusbar = QtWidgets.QStatusBar(MainWindow)
self.statusbar.setObjectName("statusbar")
MainWindow.setStatusBar(self.statusbar)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
self.fig = MyFigure(3, 3, 100)
self.fig.suptitle("testfig")
QtWidgets.QGridLayout(self.groupBox).addWidget(self.fig)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
self.groupBox.setTitle(_translate("MainWindow", "GroupBox"))
class MyFigure(FigureCanvas):
def __init__(self, width=5, height=4, dpi=100):
# 第一步:创建一个创建Figure
self.fig = Figure(figsize=(width, height), dpi=dpi)
# 第二步:在父类中激活Figure窗口
super(MyFigure, self).__init__(self.fig) # 此句必不可少,否则不能显示图形
self.ax = self.fig.add_subplot(111)#放置坐标系
self.ax.plot([0,1,2,3],[0,1,2,3],'c--')
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
QMainWindow = QtWidgets.QMainWindow()
ui = Ui_MainWindow()
ui.setupUi(QMainWindow)
QMainWindow.show()
sys.exit(app.exec_())
使用matplotlib绘制动态曲线目前个人了解到的有4中方式,其中有两种我并未实践过,就不多进行介绍,大家可以参考这篇博文
这种方式和第二种方式我都没进行过实际测试,只简单陈述一下:
大体就是先清除绘图区,再plot,再重绘并刷新
self.axes.cla() # 清除绘图区
self.axes.plot(t, s, 'o-r', linewidth=0.5)
self.figs.canvas.draw() # 这里注意是画布重绘,self.figs.canvas
self.figs.canvas.flush_events() # 画布刷新self.figs.canvas
单纯清除绘图区无法改变背景等设置,如果需要改变背景,那么需要清理画布,只需要改为:
self.figs.clf() # 清理画布,这里是clf()
self.axes = self.figs.add_subplot(111) # 清理画布后必须重新添加绘图区
self.axes.plot(t, s, 'o-r', linewidth=0.5)
self.figs.canvas.draw() # 这里注意是画布重绘,self.figs.canvas
self.figs.canvas.flush_events() # 画布刷新self.figs.canvas
这个是我一开始使用PyQt绘制动态曲线使用的方法,但是这个方法我只能实现一个动态曲线的绘制,而使用姿态球我需要进行fill填充操作,因此最后改为animation实现。
这个设置数据重绘我只实现了基于matplotlib.lines.Line2D的重绘,大致就是:
先导入这个类
from matplotlib.lines import Line2D
然后在前面实现的MyFigure类中加入该曲线,这里方便看又把代码搬过来了。
class MyFigure(FigureCanvas):
def __init__(self, width=5, height=4, dpi=100):
# 第一步:创建一个创建Figure
self.fig = Figure(figsize=(width, height), dpi=dpi)
# 第二步:在父类中激活Figure窗口
super(MyFigure, self).__init__(self.fig) # 此句必不可少,否则不能显示图形
self.ax = self.fig.add_subplot(111)#放置坐标系
F = MyFigure()
xlist = [i for i in range(0,4,1)]
ylist = [2*i for i in range(0,4,1)]
lineaz = Line2D(xlist, ylist)
lineaz.set_color('b')
F.ax.add_line(lineaz)
完成初始绘制之后,要想更新曲线,只需要重新设置x和y的值即可,不过如果想要写随时间变化的曲线,还需要做的一件事就是更改显示的范围,这个就是最后一行代码实现的功能。
lineaz.set_xdata(new_xlist)
lineaz.set_ydata(new_ylist)
F.ax.set_xlim(new_xlist[0],new_xlist[-1])
这个地方我在fill那里有个操作还没弄明白,先挖个坑,有时间再填
这个是借鉴dji的姿态球写的,大致是使用单独线段/箭头在极坐标中画出Yaw角,然后使用分界线的倾斜程度表示Roll角,根据填充比例表示Pitch角。示意图大概就是:
首先偏航角Yaw和剩余两个角度是分开计算的,偏航角Yaw非常容易表示,只需要从原点到(Yaw,设置的最大半径)连线即可。
然后的Roll角和Pitch角。Roll角和Pitch角是关联表示的,总体就是涂蓝的区域,而Roll角可以通过绿色线段的倾斜程度表示。
这个区域的计算逻辑是:
这个先不写,实际实现有点小问题,好像必须是严格的Pitch和Roll角才可以正常运行。