3

これが取引です。9600ボーでシリアルポートを介してデータを送信するモジュールがあり、matplotlibを使用してそのデータをリアルタイムでプロットしています。私はこのコードを書きました

#! python

############ import section ###################
import serial
import struct
import numpy as np
import matplotlib.pyplot as plt
###############################################

############ Serial Configuration #############

com = serial.Serial(14,9600)
print ("Successfully opened " + com.portstr)

###############################################
def get_new_values():
    s = com.read(20)
    raw_data =  struct.unpack('!BBBBBBBBBBBBBBBBBBBB', s)
    j = 0;
    norm_data = []
    for i in range(0,20,2):
        big_byte = raw_data[i+1]+(raw_data[i]*256)
        norm_data.append(big_byte)
        j = j+1
    return norm_data

x = range(0,10,1)
y = get_new_values()

plt.ion()
line, = plt.plot(x,y)

while(1):
    a = get_new_values()
    line.set_ydata(a)  
    plt.draw()

com.close()
print ("Port Closed Successfully")

20バイトを取得してから10バイトを作成します(2バイトのデータは送信中に2つの1バイト値として分割されます)。しかし、私はこれからリアルタイムの値を取得していないことに気づきました。それが重要な場合、私はWindows 7HomeBasicを使用しています。

なぜこれが起こっているのか手がかりはありますか?

編集

また、プロットをクリックするたびにハングします。

4

1 に答える 1

2

以下のコードは、あなたの単純な問題に対して長くて複雑すぎるように見えますが、通常、手動で最適化するとコードが増え、バグの可能性が高くなります。そのため、時期尚早の最適化はほとんどの場合間違いです。

__init__プロットを設定し、軸、キャンバス、線 (画面外に描かれた線で始まる)、およびアニメーション前の背景の参照を設定します。さらに__init__、サイズ変更とシャットダウンを処理するコールバックを登録します。on_resizeウィンドウのサイズが変更されたときにバックグラウンド (ブリットに使用) を更新するには、コールバックが必要です。on_closeコールバックは、ロックを使用して実行ステータスを更新します。これですべての競合状態を排除したわけではありませんが、これは_tkinter.TclError、終了した Tk アプリにブリットしようとすることによる原因を防ぐために機能します。私は Tk でのみ、1 台のマシンでのみテストしました。YMMV、そして私は提案を受け付けています。

メソッドに へのrun呼び出しを追加しましたcanvas.flush_events()。これにより、ウィンドウをドラッグしてサイズを変更しようとしても、プロット ウィンドウがハングするのを防ぐことができます。このメソッドの while ループはself.get_new_values()、プロットにデータを設定するために呼び出します。次に、構成された方法を使用してプロットを更新します。self.blitが true の場合は を使用しcanvas.blit、それ以外の場合は を使用しますpyplot.draw

変数spf(フレームあたりのサンプル数) は、フレームにプロットされるサンプル数を制御します。の実装でこれを使用して、get_new_values読み取るバイト数を決定できます (たとえば2 * self.spf、サンプルごとに 2 バイト)。デフォルトを 120 に設定しました。これは、データ レートが 600 サンプル/秒の場合、5 フレーム/秒です。受信データに遅れずについていきながら、グラフでスループットと時間分解能を最大化するスイート スポットを見つける必要があります。

Python リストを使用する代わりに NumPy 配列にデータを読み込むと、処理が高速化される可能性があります。また、信号をダウンサンプリングして分析するためのツールに簡単にアクセスできます。バイト文字列から直接 NumPy 配列に読み込むことができますが、エンディアンが正しいことを確認してください。

>>> data = b'\x01\xff' #big endian 256 + 255 = 511
>>> np.little_endian   #my machine is little endian
True
>>> y = np.fromstring(data, dtype=np.uint16); y  #so this is wrong
array([65281], dtype=uint16)
>>> if np.little_endian: y = y.byteswap()
>>> y #fixed
array([511], dtype=uint16)

コード:

from __future__ import division
from matplotlib import pyplot
import threading

class Main(object):
    def __init__(self, samples_per_frame=120, blit=True):
        self.blit = blit
        self.spf = samples_per_frame
        pyplot.ion()
        self.ax = pyplot.subplot(111)
        self.line, = self.ax.plot(range(self.spf), [-1] * self.spf)
        self.ax.axis([0, self.spf, 0, 65536])
        pyplot.draw()
        self.canvas = self.ax.figure.canvas
        self.background = self.canvas.copy_from_bbox(self.ax.bbox)

        self.canvas.mpl_connect('resize_event', self.on_resize)
        self.canvas.mpl_connect('close_event', self.on_close)
        self.lock = threading.Lock()
        self.run()

    def get_new_values(self):
        import time
        import random
        #simulate receiving data at 9600 bps (no cntrl/crc)
        time.sleep(2 * self.spf * 8 / 9600)
        y = [random.randrange(65536) for i in range(self.spf)]
        self.line.set_ydata(y)

    def on_resize(self, event):
        self.line.set_ydata([-1] * self.spf)
        pyplot.draw()
        self.background = self.canvas.copy_from_bbox(self.ax.bbox)

    def on_close(self, event):
        with self.lock:
            self.running = False

    def run(self):
        with self.lock:
            self.running = True
        while self.running:
            self.canvas.flush_events()
            with self.lock:
                self.get_new_values()
            if self.running:
                if self.blit:
                    #blit for a higher frame rate
                    self.canvas.restore_region(self.background)
                    self.ax.draw_artist(self.line)
                    self.canvas.blit(self.ax.bbox)
                else:
                    #versus a regular draw
                    pyplot.draw()

if __name__ == '__main__':
    Main(samples_per_frame=120, blit=True)
于 2011-10-06T08:33:39.337 に答える