Matplotlibについて動的リアルタイム曲線を描く方法の改善ガイド


多くの場合、リアルタイムでシリアルポートを描くなどの曲線を描く必要があります。最初に思いつく解決策はMatlab種のdrawnow関数に似ています。
pythonにはMatplotlibライブラリと似た機能がありますが、drawnowという関数はありません。
既存の解決策
ネットワーク上の既存の資料Pythonに基づいて、matplotlibにおけるダイナミックな画像更新を実現します。により、Matplotlibのインタラクティブモードを開くことにより、リアルタイムの図形描画の目的を実現することができます。この場合、関数matplotlib.pylot.ionを使用する必要があります。
存在する問題
上記の方法でリアルタイムに描画すると、深刻な問題があります。時間が経つにつれて、CPUの消費が大きくなり、時間がかかるほど、プログラムのタイムアウトが多くなります。これは明らかに我々のリアルタイムの図面の要求を満たすことができません。
以下では、タイムモジュールを通じてステップ毎の消費時間を計算し、この現象を直感的に表現します。

def Method(point):
   es_time = np.zeros([point]) 
   fig=plt.figure()
   ax=fig.add_subplot(1,1,1)
   ax.axis("equal") #         XY   
   ax.set_xlabel('Horizontal Position')
   ax.set_ylabel('Vertical Position')
   ax.set_title('Vessel trajectory')
   plt.grid(True) #    
   plt.ion()  #interactive mode on
   IniObsX=0000
   IniObsY=4000
   IniObsAngle=135
   IniObsSpeed=10*math.sqrt(2)   # / 
   print('    ')
   for t in range(point):
       t0 = time.time()
       #       
       obsX=IniObsX+IniObsSpeed*math.sin(IniObsAngle/180*math.pi)*t
       obsY=IniObsY+IniObsSpeed*math.cos(IniObsAngle/180*math.pi)*t
       ax.scatter(obsX,obsY,c='b',marker='.')  #   
       #    ,     
       plt.pause(0.001)
       es_time[t] = 1000*(time.time() - t0)
   return es_time
時間が掛かる

Method
各ステップのプロット時間は、図形描画点数と直線的に関係しており、しかも、点数が増えるにつれて、時間が多く消費されることは明らかである。図形描画の点数が万から数億に達すると、コンピュータが詰まってしまうことが想像できます。
原因を分析する
上記のような現象が発生した原因は、コードax.scater(obsX,obsY,c='b',マーカー='.')によるものと推測されます。このコードはループごとに新しいカーブを描きます。前の曲線をクリアしないと、必然的に後のループにかかったCPUリソースが多くなり、最終的にはマシンが死にます。
改善の方法
なぜかというと、繰り返し絵を描くことによって、機械資源の累積的な消耗を引き起こすので、最初の解決方法を思い付きます。それは毎回図を描く前に、前の曲線を一掃します。
上記の思想により、毎回のグラフィックスコードax.scater(obsX、obsY、c='b'、マーカー='.')にクリアコードを加えてください。すなわち、

        plt.cla()
        ax.plot(obsX,obsY,'-g',marker='*')  #   
しかし、このようにすると新しい問題があります。前に定義した座標軸、タイトル、凡例などの情報は全部消去されます。解決方法は、各ステップのループにおいて、これらの情報を再定義する必要がある。
完全コード

def Method_Improve(point):
    def initial(ax):
        ax.axis("equal") #         XY   
        ax.set_xlabel('Horizontal Position')
        ax.set_ylabel('Vertical Position')
        ax.set_title('Vessel trajectory')
        plt.grid(True) #    
        return ax
    
    es_time = np.zeros([point]) 
    fig=plt.figure()
    ax=fig.add_subplot(1,1,1)
    ax = initial(ax)
    plt.ion()  #interactive mode on
    IniObsX=0000
    IniObsY=4000
    IniObsAngle=135
    IniObsSpeed=10*math.sqrt(2)   # / 
    print('    ')
    obsX = [0,]
    obsY = [4000,]
    for t in range(point):
        t0 = time.time()
        #       
        obsX.append(IniObsX+IniObsSpeed*math.sin(IniObsAngle/180*math.pi)*t)
        obsY.append(IniObsY+IniObsSpeed*math.cos(IniObsAngle/180*math.pi)*t)
        plt.cla()
        ax = initial(ax)
        ax.plot(obsX,obsY,'-g',marker='*')  #   
        #    ,     
        plt.pause(0.001)
        es_time[t] = 1000*(time.time() - t0)
    return es_time
時間が掛かる

Method_Improve
サイクル数は,消費時間と正の相関傾向を示さないことは明らかであり,一定の誤差範囲において,消費時間は安定しているといえる。
改善方法の改善
改良方法にはまだ問題があります。サイクル毎に座標軸情報をクリアする必要があるので、ループ毎に座標軸情報を再設定しなければなりません。明らかにこのようなやり方は、余分な計算力の消耗を招いています。新しい方法がありますか?答えは明らかにあります。
しかし解決の構想はやはり原始の問題から出発しなければならなくて、つまり繰り返し絵を描いて、資源の累積的な消耗を招きます。新しい考え方をさせます。一つの曲線だけを描き、毎回これらの曲線のデータを変えます。
上記のように考えた後、プログラムの先頭で座標軸情報を定義するだけで、毎回ループ内でリセットされた座標軸情報をクリアする必要はありません。
具体的には、曲線のハンドルを取って、修正するということです。

        line.set_xdata(obsX)
        line.set_ydata(obsY)
完全コード:

def ImprovedMethod_Improve(point):    
    es_time = np.zeros([point]) 
    fig=plt.figure()
    ax=fig.add_subplot(1,1,1)

    ax.set_xlabel('Horizontal Position')
    ax.set_ylabel('Vertical Position')
    ax.set_title('Vessel trajectory')
    
    line = ax.plot([0,0],[4000,4000],'-g',marker='*')[0]
    plt.grid(True) #    
    plt.ion()  #interactive mode on
    IniObsX=0000
    IniObsY=4000
    IniObsAngle=135
    IniObsSpeed=10*math.sqrt(2)   # / 
    print('    ')
    obsX = [0,]
    obsY = [4000,]
    for t in range(point):
        t0 = time.time()
        #       
        obsX.append(IniObsX+IniObsSpeed*math.sin(IniObsAngle/180*math.pi)*t)
        obsY.append(IniObsY+IniObsSpeed*math.cos(IniObsAngle/180*math.pi)*t)
        
        line.set_xdata(obsX)
        line.set_ydata(obsY)
        ax.set_xlim([-200,10*point+200])
        ax.set_ylim([3800-10*point,4200])
        #    ,     
        plt.pause(0.001)
        es_time[t] = 1000*(time.time() - t0)
    return es_time


三つの方法の対比
締め括りをつける
ここではMatplotlibについて動的なリアルタイム曲線を描いた文章を紹介します。Matplotlibに関するダイナミックなリアルタイム曲線の内容は以前の文章を検索したり、下記の関連記事を見たりしてください。これからもよろしくお願いします。