如何使用FuncAnimation在matplotlib中更新和动画化多个图形?

6

我正在尝试创建一个程序,它可以读取串行数据并更新多个图形(目前有1条线和2个条形图,但可能还会增加更多)。

目前我正在使用3个单独的FuncAnimation()调用,但证明非常慢,这不好,因为我仍然需要在将来添加更多动画图形的选项。

那么我该如何将它变成单个FuncAnimation(或类似的东西),以便更新所有三个(可能更多)图形?另外,我应该怎么做来加速它一点?

#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

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)

这里有两个机制可以限制速度:(1)读取串行数据的时间和(2)将艺术家绘制到画布上的时间。它不应该与您拥有的FuncAnimations数量有关。我建议您分别调查这两件事。如果您只使用单个动画(例如仅anim1),速度是否真的会快得多? - ImportanceOfBeingErnest
@ImportanceOfBeingErnest,我认为现在不是读取串行数据的时候。只有一个FuncAnimation似乎可以大大加快速度。当程序开始变得有点卡顿时,经过3个FuncAnimations后会有明显的差异。此外,我仍然需要添加可能另外3个实时数据图表,这就是为什么我现在需要找到一种优化它的方法,因为我认为如果按照当前的实现方式进行,情况不会很好。 - user3458571
那么下一个测试案例将是将所有三个图形作为子图放置在同一图形中,并使用单个FuncAnimation。将其与具有三个不同动画的情况进行比较。速度是否显着更快?如果是这样,请考虑是否实际上对于您的情况接受单个图形。 - ImportanceOfBeingErnest
1个回答

4
回应@ImportanceOfBeingErnest的评论,显而易见的解决方案是使用3个子图和仅一个FuncAnimation()调用。您只需确保回调函数返回每次迭代需要更新的所有艺术家列表即可。
一个缺点是所有3个子图的更新将在相同的间隔时间内发生(与您示例中的不同时间相反)。您可以通过使用全局变量计算函数被调用的次数并且只在一段时间内完成某些绘图来解决这个问题。
#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)

谢谢您提供的代码。您提到更新速率以相同间隔发生,这一点很有道理。但我并不完全理解帧与间隔之间的关系。您能否举个例子详细说明一下?谢谢。 - auro

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接