Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
394 views
in Technique[技术] by (71.8m points)

python - 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)
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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)

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...