Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

how to use FuncAnimation to update and animate multiple figures with matplotlib?

Trying to create a program that reads serial data and updates multiple figures (1 line and 2 bar charts for now but could potentially be more).

Using 3 separate calls to FuncAnimation() right now, but proving to be really slow which is not good as I still need the option of adding more animated figures in the future.

So how can I make it a single FuncAnimation (or maybe something similar) that updates all three (potentially more) figures? Alternatively, what can I do to speed it up a bit?

#figure for current
amps = plt.figure(1)
ax1 = plt.subplot(xlim = (0,100), ylim = (0,500))
line, = ax1.plot([],[])
ax1.set_ylabel('Current (A)')

#figure for voltage
volts = plt.figure(2)
ax2 = plt.subplot()
rects1 = ax2.bar(ind1, voltV, width1)
ax2.grid(True)
ax2.set_ylim([0,6])
ax2.set_xlabel('Cell Number')
ax2.set_ylabel('Voltage (V)')
ax2.set_title('Real Time Voltage Data')
ax2.set_xticks(ind1)

#figure for temperature
temp = plt.figure(3)
ax3 = plt.subplot()
rects2 = ax3.bar(ind2, tempC, width2)
ax3.grid(True)
ax3.set_ylim([0,101])
ax3.set_xlabel('Sensor Number')
ax3.set_ylabel('temperature (C)')
ax3.set_title('Real Time Temperature Data')
ax3.set_xticks(ind2)

def updateAmps(frameNum):

    try:
    #error check for bad serial data
        serialString = serialData.readline()
        serialLine = [float(val) for val in serialString.split()]
        print (serialLine)

        if (len(serialLine) == 5):
            voltV[int(serialLine[1])] = serialLine[2]
            tempC[int(serialLine[3])] = serialLine[4]
            currentA.append(serialLine[0])
            if (len(currentA)>100):
                currentA.popleft()

        line.set_data(range(100), currentA)

    except ValueError as e:
    #graphs not updated for bad serial data
        print (e)

    return line,

#function to update real-time voltage data
def updateVolts(frameNum):

    for rects, h in zip(rects1,voltV):
        rects.set_height(h)

    return rects1

#function to update real-time temperature data
def updateTemp(frameNum):

    for rects, h in zip(rects2,tempC):
        rects.set_height(h)

    return rects2

Call to funcAnimation:

anim1 = animation.FuncAnimation(amps, updateAmps,
                                interval = 20, blit = True)
anim2 = animation.FuncAnimation(volts, updateVolts, interval = 25, blit = True)
anim3 = animation.FuncAnimation(temp, updateTemp, interval = 30, blit = True)
like image 635
user3458571 Avatar asked Mar 06 '17 08:03

user3458571


People also ask

How do I create a funcanimation using matplotlib?

Either you need to create one or you can use: matplotlib.pyplot.gcf(). Which stands for “Get current figure” This is the func part that goes inside the FuncAnimation as a parameter. If you’d like to have a graph that animates you will need to define a function that updates the data that goes in the plot and also the function that creates the chart.

When to use animations in Matplotlib?

Animations make even more sense when we are working with projects (stock markets, ECG Anomaly Detection, Internet Traffic Forecasting) that depicts the time series data. The matplotlib.animation.FuncAnimation class is used to make animation calls recursively.

How to call a function to get the animation in Python?

Call to funcAnimation: anim1 = animation.FuncAnimation(amps, updateAmps, interval = 20, blit = True) anim2 = animation.FuncAnimation(volts, updateVolts, interval = 25, blit = True) anim3 = animation.FuncAnimation(temp, updateTemp, interval = 30, blit = True) pythonperformancematplotlib

How to make animation calls recursively in Matplotlib?

The matplotlib.animation.FuncAnimation class is used to make animation calls recursively. You must store the created Animation in a variable that lives as long as the animation should run.


1 Answers

Echoing @ImportanceOfBeingErnest's comment, the obvious solution would be to use 3 subplots and only one FuncAnimation() call. You simply have to make sure your callback function returns a list of ALL artists to be updated at each iteration.

One drawback is that the update will happen a the same interval in all 3 subplots (contrary to the different timings you had in your example). You could potentially work around that by using global variables that count how many time the function has been called and only do some of the plots every so often for example.

#figure 
fig = plt.figure(1)
# subplot for current
ax1 = fig.add_subplot(131, xlim = (0,100), ylim = (0,500))
line, = ax1.plot([],[])
ax1.set_ylabel('Current (A)')

#subplot for voltage
ax2 = fig.add_subplot(132)
rects1 = ax2.bar(ind1, voltV, width1)
ax2.grid(True)
ax2.set_ylim([0,6])
ax2.set_xlabel('Cell Number')
ax2.set_ylabel('Voltage (V)')
ax2.set_title('Real Time Voltage Data')
ax2.set_xticks(ind1)

#subplot for temperature
ax3 = fig.add_subplot(133)
rects2 = ax3.bar(ind2, tempC, width2)
ax3.grid(True)
ax3.set_ylim([0,101])
ax3.set_xlabel('Sensor Number')
ax3.set_ylabel('temperature (C)')
ax3.set_title('Real Time Temperature Data')
ax3.set_xticks(ind2)

def updateAmps(frameNum):

    try:
    #error check for bad serial data
        serialString = serialData.readline()
        serialLine = [float(val) for val in serialString.split()]
        print (serialLine)

        if (len(serialLine) == 5):
            voltV[int(serialLine[1])] = serialLine[2]
            tempC[int(serialLine[3])] = serialLine[4]
            currentA.append(serialLine[0])
            if (len(currentA)>100):
                currentA.popleft()

        line.set_data(range(100), currentA)

    except ValueError as e:
    #graphs not updated for bad serial data
        print (e)

    return line,

#function to update real-time voltage data
def updateVolts(frameNum):

    for rects, h in zip(rects1,voltV):
        rects.set_height(h)

    return rects1

#function to update real-time temperature data
def updateTemp(frameNum):

    for rects, h in zip(rects2,tempC):
        rects.set_height(h)

    return rects2

def updateALL(frameNum):
    a = updateAmps(frameNum)
    b = updateVolts(frameNum)
    c = updateTemp(frameNum)
    return a+b+c

animALL = animation.FuncAnimation(fig, updateALL,
                                interval = 20, blit = True)
like image 95
Diziet Asahi Avatar answered Oct 27 '22 00:10

Diziet Asahi