简体   繁体   中英

Python: Animating a vector using mplot3d and animation

I'm trying to make a 3d plot with Matplotlib and the animation package from matplotlib. In addition, the animation should be a part of a Gui generated using PyQt and Qt-Designer. Currently I'm stuck on using the "animation.Funcnimation()" correctly, at least i think so... So here is my code:

import sys
from PyQt4.uic import loadUiType
from PyQt4 import QtGui

from matplotlib import pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib import animation

import numpy as np
import Quaternion as qt

Ui_MainWindow, QMainWindow = loadUiType('Newsphere.ui')

class Kinematic(Ui_MainWindow, QMainWindow):
    def __init__(self):
        super(Kinematic, self).__init__()
        self.setupUi(self)

        self.fig = plt.figure()
        self.ax = self.fig.add_subplot(111,projection = '3d')
        self.fig.tight_layout()

        self.ani = animation.FuncAnimation(self.fig, self.update,
                                       init_func=self.setup_plot, blit=True)

        self.canvas = FigureCanvas(self.fig)
        self.mplvl.addWidget(self.canvas)
        self.canvas.draw()

    def setup_plot(self):
        self.ax.view_init(40, 45)

        self.ax.set_xlabel('X')
        self.ax.set_ylabel('Y')
        self.ax.set_zlabel('Z')

        self.ax.set_xlim3d(-1,1)
        self.ax.set_ylim3d(-1,1)
        self.ax.set_zlim3d(-1,1)

        g_x = np.matrix([[1.0],[0.0],[0.0]])
        g_y = np.matrix([[0.0],[1.0],[0.0]])
        g_z = np.matrix([[0.0],[0.0],[1.0]])

        self.ax.plot([0,g_x[0]], [0,g_x[1]], [0,g_x[2]], label='$X_0$')
        self.ax.plot([0,g_y[0]], [0,g_y[1]], [0,g_y[2]], label='$Y_0$')
        self.ax.plot([0,g_z[0]], [0,g_z[1]], [0,g_z[2]], label='$Z_0$')

        self.vek, = self.ax.plot([0,-1], [0,0], [0,0], label='$g \cdot R$', animated=True)

        self.ax.legend(loc='best')
        self.ax.scatter(0,0,0, color='k')

        return self.vek,

    def update(self, i):
        b = self.bslider.value() / 100

        g = np.matrix([[1.0],[0.0],[0.0]])
        q = np.array([0,b,0.5,0])

        R = qt.QtoR(q)
        x, y, z = R*g

        self.vek, = self.ax.plot([0,x], [0,y], [0,z], label='$g \cdot R$', animated=True) #the rotated vector
        return self.vek,

if __name__ == '__main__':

app = QtGui.QApplication(sys.argv)

main = Kinematic()
main.show()

sys.exit(app.exec_())

You won't be able to run it by just copy-paste because you don't have the file "Newsphere.ui" (Line 13) and the Quaternion.py (Line 11). So when I run it, I get the following (actually like I wish!): Coordinate system

My goal is now to draw a vector (Line 50) and animate it (Line 66) using data which I get from the Gui-slider (Line 58). Can anyone help me with this? I'm stuck with this for days!

Since your problem is with the animation part, below you can see a snippet that animate an arrow that is rotating.

import numpy as np
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
import matplotlib.animation as animation


def data_gen(num):
    """Data generation"""
    angle = num * np.pi/36    
    vx, vy, vz = np.cos(angle), np.sin(angle), 1
    ax.cla()
    ax.quiver(0, 0, 0, vx, vy, vz, pivot="tail", color="black")
    ax.quiver(0, 0, 0, vx, vy, 0, pivot="tail", color="black",
              linestyle="dashed")
    ax.set_xlim(-1, 1)
    ax.set_ylim(-1, 1)
    ax.set_zlim(-1, 1)
    ax.view_init(elev=30, azim=60)


fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
data_gen(0)
ani = animation.FuncAnimation(fig, data_gen, range(72), blit=False)
plt.show()

The documentation on animations might not be the best. But there are several examples out there, for example, this one animates the Lorenz attractor .

So if someone is interested in a solution of the mentioned problem, here we go: (again it is not a code for copy-paste because of the missing 'Newsphere.ui', but I try to explain the important snippets)

import sys
from PyQt4.uic import loadUiType
from PyQt4 import QtGui

from matplotlib import pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib import animation

import numpy as np

Ui_MainWindow, QMainWindow = loadUiType('Newsphere.ui')

class Kinematic(Ui_MainWindow, QMainWindow):

    def __init__(self):
        super(Kinematic, self).__init__()
        self.setupUi(self)

        self.fig = plt.figure()
        self.ax = self.fig.add_subplot(111,projection = '3d')
        self.fig.tight_layout()
        self.ax.view_init(40, -45)

        # dashed coordinate system
        self.ax.plot([0,1], [0,0], [0,0], label='$X_0$', linestyle="dashed")
        self.ax.plot([0,0], [0,-1], [0,0], label='$Y_0$', linestyle="dashed")
        self.ax.plot([0,0], [0,0], [0,1], label='$Z_0$', linestyle="dashed")

        self.ax.set_xlim3d(-1,1)
        self.ax.set_ylim3d(-1,1)
        self.ax.set_zlim3d(-1,1)

        self.ax.set_xlabel('X')
        self.ax.set_ylabel('Y')
        self.ax.set_zlabel('Z')

        self.ax.scatter(0,0,0, color='k') # black origin dot

        self.canvas = FigureCanvas(self.fig)
        self.mplvl.addWidget(self.canvas)

        self.ani = animation.FuncAnimation(self.fig, self.data_gen, init_func=self.setup_plot, blit=True)

    def setup_plot(self):

        self.ax.legend(loc='best')

        self.vek = self.ax.quiver(0, 0, 0, 0, 0, 0, label='$g \cdot R$', pivot="tail", color="black")

        return self.vek,

    def data_gen(self, i):

        b = self.bslider.value() / 100

        vx, vy, vz = np.cos(b), np.sin(b), 0

        self.vek = self.ax.quiver(0, 0, 0, vx, vy, vz, label='$g \cdot R$', pivot="tail", color="black")

        self.canvas.draw()

        return self.vek,

if __name__ == '__main__':

    app = QtGui.QApplication(sys.argv)

    main = Kinematic()
    main.show()

    sys.exit(app.exec_())

By running the code I get

在此处输入图片说明

(These are two pictures combined in one showing the same process)

I generated a GUI file named Newsphere.ui following this tutorial . Basically it contains just a Widget, a QSlider and a QSpinBox. The Widget has the layout-name "mplvl", which occures in line 41. This adds the generated figure to the widget (I think so...). The QSlider is connected with the QSpinBox (done in QtDesigner) and has the name "bslider", line 55. So in this line the slider-value gets divided by 100 because I didn't found a slider that generates me a float-value. The key-line for me was line 61, where the canvas is drawn. Now the animation.FuncAnimation (line 43) draws a new vector when I change the slider value, compare the pics. Also it is important to draw the changing vector as a ax.quiver and not as a ax.plot like in my previous attempt.

If there are questions or suggestions for improvement please ask/post.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM