Matplotlib animation.FuncAnimation() 动画错过第一帧?

Matplotlib animation.FuncAnimation() animation miss first frame?

我是 Python 和 Matplotlib 的新手。我有这段代码可以在 3d Matplotlib 子图中绘制轨迹:

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation

from itertools import islice

import sympy as sym
from sympy.parsing.sympy_parser import parse_expr

from sympy import Eq, solve




class FunZ:
    
    def __init__(self, f):  
  
        self.x, self.y  = sym.symbols('x y')
        
        self.f = parse_expr(f)
        
        # print('f : ', self.f)
        
    def evalu(self, xx, yy):
    
        return float(self.f.subs({self.x: xx, self.y: yy}).evalf())
    

    def derX(self, xx, yy):        
        
        self.dx = sym.diff(self.f, self.x)
        
        # print('dx : ', self.dx)
        
        return float(self.dx.subs({self.x: xx, self.y: yy}).evalf())
    
    def derY(self, xx, yy):
        
        self.dy = sym.diff(self.f, self.y)
        
        # print('dy :', self.dy)
        
        return float(self.dy.subs({self.x: xx, self.y: yy}).evalf())
    
    def derXY(self, xx, yy):
        
        return [float(self.derX(xx, yy)), float(self.derY(xx, yy))]
    
    def minim(self):
        
        self.dx = sym.diff(self.f, self.x)
        self.dy = sym.diff(self.f, self.y)
        print('dx : ', self.dx)
        print('dy : ', self.dy)
        
        eq1 = Eq(self.dx ,0)
        
        eq2 = Eq(self.dy ,0)
        
        solu = solve((eq1,eq2), (self.x, self.y), dict= False)
        
        print(solu, type(solu))
        
        return solu

XX = np.linspace(-4, 4, 100)

YY = np.linspace(-4, 4, 100)

funz = FunZ('x**2 + y**2 + 2*y + 2')

ij = [(x, y, funz.evalu(x, y)) for x in XX for y in YY]


arr = np.array(ij, dtype=float)

# print(arr, arr.size, arr.shape, arr.dtype)


der_x = [(a, b, funz.derX(a, b)) for a in XX for b in YY] 

derX = np.array(der_x)

# print(derX, derX.size, derX.shape, derX.dtype)


der_y = [(a, b, funz.derY(a, b)) for a in XX for b in YY] 

derY = np.array(der_y)

# print(derY, derY.size, derY.shape, derY.dtype)


x = arr[:, 0]
y = arr[:, 1]

data = arr[:, 2]




fig = plt.figure()
ax = fig.add_subplot(projection='3d')
ax.set_xlim([-4,4])
ax.set_ylim([-4,4])
ax.set_zlim([-3,50])
ax.plot_trisurf(x, y, data, color="red", alpha=0.5)


### devo duplicare primo elemento della lista altrimenti skippa a visualizzare i primi due sul video

# trajectory =  [(2.5, 3.5, 27.5),(2.5, 3.5, 27.5), (2.0, 3.0, 21.0), (1.5, 2.5, 15.5), (1.0, 2.0, 11.0), (0.5, 1.5, 7.5), (0.0, 1.0, 5.0), (0.0, 0.5, 3.25), (0.0, 0.0, 2.0), (0.0, -0.5, 1.25), (0.0, -1.0, 1.0)]

trajectory =  [(2.5, 3.5, 27.5), (2.0, 3.0, 21.0), (1.5, 2.5, 15.5), (1.0, 2.0, 11.0), (0.5, 1.5, 7.5), (0.0, 1.0, 5.0), (0.0, 0.5, 3.25), (0.0, 0.0, 2.0), (0.0, -0.5, 1.25), (0.0, -1.0, 1.0)]

print('len trajectory : ', len(trajectory))


i = islice(trajectory, 0 , len(trajectory), 1)


pippo = None
def animate(j):  #funziona senza stop iteration with animation.save
    
    global pippo
    
    try: 
        coord = next(i)
        
    except:
        print('coord empty')
        return
    
    if pippo:
        pippo.remove()
    
    print(coord)
                
    x = coord[0]
    y = coord[1]
    z = coord[2]
      
    pippo = ax.scatter(x,y,z)          
    
    return pippo

ani = animation.FuncAnimation(fig, animate, interval=1000, frames=len(trajectory), repeat=False)  # e' necessario



plt.show()
plt.close('all')


fig = plt.figure()
ax = fig.add_subplot(projection='3d')
ax.set_xlim([-4,4])
ax.set_ylim([-4,4])
ax.set_zlim([-3,50])
ax.plot_trisurf(x, y, data, color="red", alpha=0.5)


print('#####################')

i = islice(trajectory, 0 , len(trajectory), 1)

ani2 = animation.FuncAnimation(fig, animate, interval=1000, frames=len(trajectory), repeat=False)

# writervideo = animation.FFMpegWriter(fps=1, codec="h264") 
# ani2.save('video_steeper.mp4', writer=writervideo)

ani2.save("movie.gif", writer=animation.PillowWriter(fps=1))

我正在努力理解为什么要制作正确的动画并保存动画我需要复制 trajectory 列表的第一个元素 ((2.5, 3.5, 27.5))。

如果我不这样做,动画和我的 gif 只显示 9 个点而不是 10 个点,请查看结果输出:

来自 10 个元素列表的电影 trajectory :

来自 11 个元素列表的电影 trajectory(第一个重复):

我已经尝试了很多来弄清楚发生了什么但无法理解,奇怪的是我在 animate 函数中的 print 语句准确地打印了 11 和 10 点两个不同的列表,但它们都错过了动画和文件中的帧。有什么线索吗?

在我看来,您的问题来自您正在创建的可迭代 i 和函数 next 的使用。我不确定为什么会发生这种情况,但是如果您不创建可迭代对象 i 而只是直接索引您的轨迹,那么您的动画会按预期工作。

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
from itertools import islice
import sympy as sym
from sympy.parsing.sympy_parser import parse_expr
from sympy import Eq, solve

class FunZ:    
    def __init__(self, f):  
        self.x, self.y  = sym.symbols('x y')
        self.f = parse_expr(f)
        
    def evalu(self, xx, yy):   
        return float(self.f.subs({self.x: xx, self.y: yy}).evalf())
  
    def derX(self, xx, yy):        
        self.dx = sym.diff(self.f, self.x)
        return float(self.dx.subs({self.x: xx, self.y: yy}).evalf())
    
    def derY(self, xx, yy):
        self.dy = sym.diff(self.f, self.y)
        return float(self.dy.subs({self.x: xx, self.y: yy}).evalf())
    
    def derXY(self, xx, yy):
        return [float(self.derX(xx, yy)), float(self.derY(xx, yy))]
    
    def minim(self):     
        self.dx = sym.diff(self.f, self.x)
        self.dy = sym.diff(self.f, self.y)
        print('dx : ', self.dx)
        print('dy : ', self.dy)      
        eq1 = Eq(self.dx ,0)   
        eq2 = Eq(self.dy ,0)
        solu = solve((eq1,eq2), (self.x, self.y), dict= False)
        print(solu, type(solu))
        return solu

XX = np.linspace(-4, 4, 100)
YY = np.linspace(-4, 4, 100)

funz = FunZ('x**2 + y**2 + 2*y + 2')
ij = [(x, y, funz.evalu(x, y)) for x in XX for y in YY]
arr = np.array(ij, dtype=float)

der_x = [(a, b, funz.derX(a, b)) for a in XX for b in YY] 
derX = np.array(der_x)
der_y = [(a, b, funz.derY(a, b)) for a in XX for b in YY] 
derY = np.array(der_y)

x = arr[:, 0]
y = arr[:, 1]
data = arr[:, 2]

fig = plt.figure()
ax = fig.add_subplot(projection='3d')
ax.set_xlim([-4,4])
ax.set_ylim([-4,4])
ax.set_zlim([-3,50])
ax.plot_trisurf(x, y, data, color="red", alpha=0.5)

trajectory =  [(2.5, 3.5, 27.5), (2.0, 3.0, 21.0), (1.5, 2.5, 15.5), (1.0, 2.0, 11.0), (0.5, 1.5, 7.5), (0.0, 1.0, 5.0), (0.0, 0.5, 3.25), (0.0, 0.0, 2.0), (0.0, -0.5, 1.25), (0.0, -1.0, 1.0)]
print('len trajectory : ', len(trajectory))

pippo = None
def animate(j):  #funziona senza stop iteration with animation.save
    
    global pippo

    x = trajectory[j][0]
    y = trajectory[j][1]
    z = trajectory[j][2]

    if pippo is not None:
        pippo.remove()
    pippo = ax.scatter(x,y,z)          
    return pippo

ani = animation.FuncAnimation(fig, animate, interval=1000, frames=len(trajectory), repeat=False)  # e' necessario
plt.show()

输出给出:

很好地摆弄了一下 FuncAnimation 添加一个 init_func :

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation

from itertools import islice

import sympy as sym
from sympy.parsing.sympy_parser import parse_expr

from sympy import Eq, solve




class FunZ:
    
    def __init__(self, f):  
  
        self.x, self.y  = sym.symbols('x y')
        
        self.f = parse_expr(f)
        
        # print('f : ', self.f)
        
    def evalu(self, xx, yy):
    
        return float(self.f.subs({self.x: xx, self.y: yy}).evalf())
    

    def derX(self, xx, yy):        
        
        self.dx = sym.diff(self.f, self.x)
        
        # print('dx : ', self.dx)
        
        return float(self.dx.subs({self.x: xx, self.y: yy}).evalf())
    
    def derY(self, xx, yy):
        
        self.dy = sym.diff(self.f, self.y)
        
        # print('dy :', self.dy)
        
        return float(self.dy.subs({self.x: xx, self.y: yy}).evalf())
    
    def derXY(self, xx, yy):
        
        return [float(self.derX(xx, yy)), float(self.derY(xx, yy))]
    
    def minim(self):
        
        self.dx = sym.diff(self.f, self.x)
        self.dy = sym.diff(self.f, self.y)
        print('dx : ', self.dx)
        print('dy : ', self.dy)
        
        eq1 = Eq(self.dx ,0)
        
        eq2 = Eq(self.dy ,0)
        
        solu = solve((eq1,eq2), (self.x, self.y), dict= False)
        
        print(solu, type(solu))
        
        return solu

XX = np.linspace(-4, 4, 100)

YY = np.linspace(-4, 4, 100)

funz = FunZ('x**2 + y**2 + 2*y + 2')

ij = [(x, y, funz.evalu(x, y)) for x in XX for y in YY]


arr = np.array(ij, dtype=float)

# print(arr, arr.size, arr.shape, arr.dtype)


der_x = [(a, b, funz.derX(a, b)) for a in XX for b in YY] 

derX = np.array(der_x)

# print(derX, derX.size, derX.shape, derX.dtype)


der_y = [(a, b, funz.derY(a, b)) for a in XX for b in YY] 

derY = np.array(der_y)

# print(derY, derY.size, derY.shape, derY.dtype)


x = arr[:, 0]
y = arr[:, 1]

data = arr[:, 2]

fig = plt.figure()
ax = fig.add_subplot(projection='3d')
ax.set_xlim([-4,4])
ax.set_ylim([-4,4])
ax.set_zlim([-3,50])
ax.plot_trisurf(x, y, data, color="red", alpha=0.5)

trajectory =  [(2.5, 3.5, 27.5), (2.0, 3.0, 21.0), (1.5, 2.5, 15.5), (1.0, 2.0, 11.0), (0.5, 1.5, 7.5), (0.0, 1.0, 5.0), (0.0, 0.5, 3.25), (0.0, 0.0, 2.0), (0.0, -0.5, 1.25), (0.0, -1.0, 1.0)]

print('len trajectory : ', len(trajectory))
i = islice(trajectory, 0 , len(trajectory), 1)

def init_animate():
    ax.plot_trisurf(x, y, data, color="green", alpha=0.5)
    
pippo = None
def animate(j):  #funziona senza stop iteration with animation.save
    
    global pippo
    
    try: 
        coord = next(i)
        
    except:
        print('coord empty')
        return
    
    if pippo:
        pippo.remove()
    
    print(coord)
                
    x = coord[0]
    y = coord[1]
    z = coord[2]
      
    pippo = ax.scatter(x,y,z)          
    
    return pippo

ani = animation.FuncAnimation(fig, animate, interval=1000, frames=len(trajectory),init_func=init_animate, repeat=False)  

plt.show()
plt.close('all')

print('#####################')

能够在 gif 上获得所有 10 分:

也能够删除 frames arg 但只是为了可视化而不是保存我的动画,请参阅:

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation

from itertools import islice

import sympy as sym
from sympy.parsing.sympy_parser import parse_expr

from sympy import Eq, solve




class FunZ:
    
    def __init__(self, f):  
  
        self.x, self.y  = sym.symbols('x y')
        
        self.f = parse_expr(f)
        
        # print('f : ', self.f)
        
    def evalu(self, xx, yy):
    
        return float(self.f.subs({self.x: xx, self.y: yy}).evalf())
    

    def derX(self, xx, yy):        
        
        self.dx = sym.diff(self.f, self.x)
        
        # print('dx : ', self.dx)
        
        return float(self.dx.subs({self.x: xx, self.y: yy}).evalf())
    
    def derY(self, xx, yy):
        
        self.dy = sym.diff(self.f, self.y)
        
        # print('dy :', self.dy)
        
        return float(self.dy.subs({self.x: xx, self.y: yy}).evalf())
    
    def derXY(self, xx, yy):
        
        return [float(self.derX(xx, yy)), float(self.derY(xx, yy))]
    
    def minim(self):
        
        self.dx = sym.diff(self.f, self.x)
        self.dy = sym.diff(self.f, self.y)
        print('dx : ', self.dx)
        print('dy : ', self.dy)
        
        eq1 = Eq(self.dx ,0)
        
        eq2 = Eq(self.dy ,0)
        
        solu = solve((eq1,eq2), (self.x, self.y), dict= False)
        
        print(solu, type(solu))
        
        return solu

XX = np.linspace(-4, 4, 100)

YY = np.linspace(-4, 4, 100)

funz = FunZ('x**2 + y**2 + 2*y + 2')

ij = [(x, y, funz.evalu(x, y)) for x in XX for y in YY]


arr = np.array(ij, dtype=float)

# print(arr, arr.size, arr.shape, arr.dtype)


der_x = [(a, b, funz.derX(a, b)) for a in XX for b in YY] 

derX = np.array(der_x)

# print(derX, derX.size, derX.shape, derX.dtype)


der_y = [(a, b, funz.derY(a, b)) for a in XX for b in YY] 

derY = np.array(der_y)

# print(derY, derY.size, derY.shape, derY.dtype)


x = arr[:, 0]
y = arr[:, 1]

data = arr[:, 2]




fig = plt.figure()
ax = fig.add_subplot(projection='3d')
ax.set_xlim([-4,4])
ax.set_ylim([-4,4])
ax.set_zlim([-3,50])
ax.plot_trisurf(x, y, data, color="red", alpha=0.5)


### devo duplicare primo elemento della lista altrimenti skippa a visualizzare i primi due sul video

trajectory =  [(2.5, 3.5, 27.5), (2.0, 3.0, 21.0), (1.5, 2.5, 15.5), (1.0, 2.0, 11.0), (0.5, 1.5, 7.5), (0.0, 1.0, 5.0), (0.0, 0.5, 3.25), (0.0, 0.0, 2.0), (0.0, -0.5, 1.25), (0.0, -1.0, 1.0)]

# trajectory =  [(2.5, 3.5, 27.5), (2.0, 3.0, 21.0), (1.5, 2.5, 15.5), (1.0, 2.0, 11.0), (0.5, 1.5, 7.5), (0.0, 1.0, 5.0), (0.0, 0.5, 3.25), (0.0, 0.0, 2.0), (0.0, -0.5, 1.25), (0.0, -1.0, 1.0)]

print('len trajectory : ', len(trajectory))


i = islice(trajectory, 0 , len(trajectory), 1)





def init_animate():
    ax.plot_trisurf(x, y, data, color="blue", alpha=0.5)
    
pippo = None
def animate(j):  #funziona senza stop iteration with animation.save

    global pippo

    try: 
        
        coord = next(i)
        print(coord)
        
    except:
        print('coord empty')
        global ani
        global ani2
        ani = None
        ani2 = None
        return 
    
    if pippo:
        pippo.remove()
    
    # print(coord)
                
    x = coord[0]
    y = coord[1]
    z = coord[2]
      
    pippo = ax.scatter(x,y,z)          
    
    return pippo


# ani = animation.FuncAnimation(fig, animate ,frames=len(trajectory),init_func=init_animate, interval=1000, repeat=False) 
ani = animation.FuncAnimation(fig, animate, init_func=init_animate, interval=1000, repeat=False) 

plt.show()

print('test to see if everything is fine')

fig = plt.figure()
ax = fig.add_subplot(projection='3d')
ax.set_xlim([-4,4])
ax.set_ylim([-4,4])
ax.set_zlim([-3,50])
ax.plot_trisurf(x, y, data, color="red", alpha=0.5)


print('#####################')

i = islice(trajectory, 0 , len(trajectory), 1)

ani2 = animation.FuncAnimation(fig, animate, init_func=init_animate, interval=1000, repeat=False).save("movie_init_no-frames.gif", writer=animation.PillowWriter(fps=1)) 

不知道发生了什么,但算法保持 运行,输出:

len trajectory :  10
(2.5, 3.5, 27.5)
(2.0, 3.0, 21.0)
(1.5, 2.5, 15.5)
(1.0, 2.0, 11.0)
(0.5, 1.5, 7.5)
(0.0, 1.0, 5.0)
(0.0, 0.5, 3.25)
(0.0, 0.0, 2.0)
(0.0, -0.5, 1.25)
(0.0, -1.0, 1.0)
coord empty
test to see if everything is fine
#####################
(2.5, 3.5, 27.5)
(2.0, 3.0, 21.0)
(1.5, 2.5, 15.5)
(1.0, 2.0, 11.0)
(0.5, 1.5, 7.5)
(0.0, 1.0, 5.0)
(0.0, 0.5, 3.25)
(0.0, 0.0, 2.0)
(0.0, -0.5, 1.25)
(0.0, -1.0, 1.0)
coord empty
coord empty
coord empty
^Z
[9]+  Stopped 

我想这与 FuncAnimation的内部有关。 尽管如此,我还是应该花时间尝试理解 j 文档 根据 help(matplotlib.animation)

......

class FuncAnimation(TimedAnimation)
     |  FuncAnimation(fig, func, frames=None, init_func=None, fargs=None, save_count=None, *, cache_frame_data=True, **kwargs)
     |  
     |  Makes an animation by repeatedly calling a function *func*.
     |  
     |  Parameters
     |  ----------
     |  fig : `~matplotlib.figure.Figure`
     |      The figure object used to get needed events, such as draw or resize.
     |  
     |  func : callable
     |      The function to call at each frame.  The first argument will
     |      be the next value in *frames*.   Any additional positional
     |      arguments can be supplied via the *fargs* parameter.
     |  
     |      The required signature is::
     |  
     |          def func(frame, *fargs) -> iterable_of_artists
     |  
     |      If ``blit == True``, *func* must return an iterable of all artists
     |      that were modified or created. This information is used by the blitting
     |      algorithm to determine which parts of the figure have to be updated.
     |      The return value is unused if ``blit == False`` and may be omitted in
     |      that case.

......