Search code examples
pythonmatplotlibmatplotlib-animation

3D Lorenz Attractor animation


I've been trying to make an animation of the Lorenz Attractor being plotted using matplotlib. I already have a successful static plot and now I am trying to animate it. I have been going off the documentation for matplotlib animation to do so but, whenever I go to run my program I get an empty box no animation, I am also using spyder ide's plots but, I also ran it in a terminal.

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

x_values = np.array([])
y_values = np.array([])
z_values = np.array([])

#^Lorenz Attractor coordinates size = 1000

fig = plt.figure(facecolor="black")
ax = fig.add_subplot(projection='3d')
ln, = ax.plot([], [], [])

 #^Plot configuration.

Above is the where I make most of my variables. x-z_values is where the points for the Lorenz Attractor points are stored

def init():
    return ln,

#initializer for animation function


def update(i):
    ln.set_data(x_values[i], y_values[i], z_values[i])
    return ln,
#Adds next values of Lorenz Attractor to ln which will be returned to animation to be plotteed
    

Lorenz()
#Calls the Lorenz function to populate the arrays.

ani = FuncAnimation(fig, 
                    update,
                    frames = np.arange(0, len(x_values)), 
                    init_func = init,
                    interval=200,
                    )
plt.show()

This is where the animation is supposed to take place.

enter image description here

And this is what I get after running the program.

Update:

I've changed some code I am no longer using ln to collect the coordinate values, so the line below for example has been removed ln.set_data(x_values[i], y_values[i], z_values[i])

Below is the new code including the new update function that uses the values from a separate function named Lorenz(), the variables which are globally available. The print() debugging suggested by Furas has validated that update() is working as expected. The only problem left to solve is the actual animation.

`

def init():
    ax.set_xlim(-100, 100)
    ax.set_ylim(-100, 100)
    ax.set_zlim(-100, 100)

#initializer for animation function


def update(i):
    global x_values 
    global y_values
    global z_values
    #Globally available variables that are poulated by Lorenz()
    print(x_values[i], y_values[i], z_values[i])
    #Print debugging
    return(x_values[:i], y_values[:i], z_values[:i])

#Adds next values of Lorenz Attractor to ln which will be returned to animation to be plotteed
Lorenz()
ani = FuncAnimation(fig, 
                    update,
                    frames = np.arange(0, len(x_values)), 
                    init_func=init,
                    interval=1,
                    blit=False,
                    repeat = False)

plt.show()

`


Solution

  • I never made animation for 3D
    but based on example Animated 3D random walk in Matplotlib documentation you need:

    def update(i):
        ln.set_data( [x_values[:i], y_values[:i]] )   # set [x, y]
        ln.set_3d_properties( z_values[:i] )          # set z
        return ln,
    

    Full working code with example data:

    import matplotlib.pyplot as plt 
    from matplotlib.animation import FuncAnimation
    import numpy as np 
    
    # --- functions ---
    
    def Lorenz():
        x = np.zeros(100)
        y = np.zeros(100)
        z = np.zeros(100)
    
        for i in range(100):
            x[i] = i/100
            y[i] = i/100
            z[i] = i/100
            
        return x, y, z
    
    def init():
        return ln,
    
    def update(i):
        ln.set_data([ x_values[:i], y_values[:i]])
        ln.set_3d_properties( z_values[:i] )
        return ln,
        
    # --- main ---
    
    fig = plt.figure(facecolor="black")
    ax = fig.add_subplot(projection='3d')
    ln, = ax.plot([], [], [])
    
    x_values, y_values, z_values = Lorenz()
    
    ani = FuncAnimation(fig, 
                        update,
                        frames=len(x_values),  # you don't need range (if you don't want to skip some frames)
                        init_func=init,
                        interval=20,  # make animation faster 
                        )
    plt.show()