Search code examples
pythonmatplotlibmatplotlib-animation

FuncAnimation: RuntimeError: The animation function must return a sequence of Artist objects


I'm trying to plot real-time temperature data with the animation module from matplotlib in Python. I get the temperature data via USB connection from a Lake Shore 224 temperature monitor

Here is my adjusted code (source: https://learn.sparkfun.com/tutorials/graph-sensor-data-with-python-and-matplotlib/update-a-graph-in-real-time):

import datetime as dt
import matplotlib.pyplot as plt
import matplotlib.animation as animation
from lakeshore import Model224

# You can ignore the following try and except block, it's a workaround for a different small problem
try:
    from IPython import get_ipython
    get_ipython().magic('clear')
    get_ipython().magic('reset -f')
except:
    pass

# Create figure for plotting

fig = plt.figure()
ax = fig.add_subplot(1, 1, 1)
xs = []
ys = []

myinstrument = Model224()

# This function is called periodically from FuncAnimation
def animate(i, xs, ys):
    # Read temperature from port A
    temperature_A = myinstrument.get_kelvin_reading('A')
    
    # Add x and y to lists
    xs.append(dt.datetime.now().strftime('%H:%M:%S.%f'))
    ys.append(temperature_A)
    
    # Limit x and y lists to 20 items
    xs = xs[-20:]
    ys = ys[-20:]
    
    # Draw x and y lists
    ax.clear()
    ax.plot(xs, ys)
    
    # Format plot
    plt.xticks(rotation=45, ha='right')
    plt.subplots_adjust(bottom=0.30)
    plt.ylabel('Temperature [K]')

# Set up plot to call animate() function periodically

ani = animation.FuncAnimation(fig = fig, func =  animate, fargs=(xs,ys), interval=10000, blit = True)
plt.show()

However, I get the following error message:

RuntimeError: The animation function must return a sequence of Artist objects.

At first, I did set as described in this case here:

blit = False

Which gives me no error message, but a blank white plot. Interestingly, it never enters the animate-Function.

Afterwards, I adjusted the code and added:

blit = True

To the function input, I got at least the following plot:

enter image description here

And I get the RuntimeError described above.

Still, I'm expecting a real-time temperature graph just like in the original source.


Solution

  • When using blitting, as per the documentation, you need to return the artists that change. The general approach for using FuncAnimation is to create a blank plot and save the Line2D object that it returns. Each loop you can update the data for that line using line.set_data() and then return the line for blitting. When using FuncAnimation properly, you don't need to worry about clearing the figure.

    Since I don't have a sensor, I just produced some data at the start and slowly plot that in my example below.

    import matplotlib.pyplot as plt
    import matplotlib.animation as animation
    import numpy as np
    
    plt.close("all")
    
    rng = np.random.default_rng(42)
    N = 1000
    xdata = np.linspace(0, 10, N)
    ydata = np.sin(5*xdata)
    
    fig, ax = plt.subplots()
    line, = ax.plot([], [])
    plt.xticks(rotation=45, ha="right")
    ax.set_ylabel("Temperature [K]")
    ax.set_ylim(-1.5, 1.5)
    
    xs = []
    ys = []
    
    def animate(i, xs, ys):    
        xs.append(xdata[i])
        ys.append(ydata[i])
        
        xs = xs[-20:]
        ys = ys[-20:]
        
        line.set_data(xs, ys)
        ax.set_xlim(min(xs), max(xs))
        
        return line,
    
    ani = animation.FuncAnimation(fig, animate, frames=N, fargs=(xs, ys), interval=10, blit=True)
    fig.show()