※なにか気になる点がありましたらコメント欄にご記入ください。また、工作や回路を製作する場合には、細かい作業などに対して、細心の注意を払われるようお願いいたします。
目次
1.はじめに
前回の投稿でカルマンフィルターに関する記事を載せましたが、今回はその動特性をご覧いただきたいと思います。
プログラム言語のPythonにて記述しており、Tkinterとよばれるモジュールを用いました。(ほどんどコピペです)
ソースコードは以下にあるものを参考にしました。とても役に立ちました。
※これらの記事の中に出てくるカルマンフィルターについては、トランジスタ技術2019年7月号の記事を出典としています。
ご参考:前回の記事
tanuki-bayashin.hatenablog.com
出典:トランジスタ技術 2019年7月号 第1章第2節(P.39~)のあたり
toragi.cqpub.co.jp
2.ソースコード
以下にソースコードを掲載します。
import tkinter as tk from matplotlib.figure import Figure from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2Tk import numpy as np import threading, serial, time """ グローバル変数の定義 """ value = [0.0, 0.0] y1 = np.zeros(100) y2 = np.zeros(100) x = np.linspace(0, 10, 100) ser = None thread_button = None thread_uart = None class Application(tk.Frame): def __init__(self, master=None): super().__init__(master) self.master = master self.master.title('matplotlib graph') #----------------------------------------------- # matplotlib配置用フレーム frame = tk.Frame(self.master) # matplotlibの描画領域の作成 fig = Figure(facecolor='azure') # 座標軸の作成 self.ax = fig.add_subplot(1, 1, 1) self.ax.set_xlim(0, 10) self.ax.set_ylim(-10, 100) # matplotlibの描画領域とウィジェット(Frame)の関連付け self.fig_canvas = FigureCanvasTkAgg(fig, frame) # matplotlibのツールバーを作成 self.toolbar = NavigationToolbar2Tk(self.fig_canvas, frame) # matplotlibのグラフをフレームに配置 self.fig_canvas.get_tk_widget().pack(fill=tk.BOTH, expand=True) # フレームをウィンドウに配置 frame.pack() # ボタンの作成 button = tk.Button(self.master, text = "Draw Graph", command = self.button_click) # 配置 button.pack(side = tk.BOTTOM) #----------------------------------------------- def button_click(self): global thread_button, value # 表示するデータの作成 for i in range(99): y1[99-i] = y1[98-i] y2[99-i] = y2[98-i] y1[0] = float(value[0]) y2[0] = float(value[1]) # グラフの描画 self.ax.cla() self.ax.set_facecolor((1.0,1.0,0.0,0.2)) self.ax.plot(x, y1, color="g") self.ax.plot(x, y2, color="r") # 表示 self.fig_canvas.draw() thread_button = threading.Timer(0.01, self.button_click) thread_button.start() # str文字列がfloat()変換できるかどうかを判定する def is_float(s): try: float(s) except: return False return True def uart(): global value, error, ser, thread_uart line = ser.readline() data = line.decode('utf-8', errors='ignore').strip() data = data.split(',') term = [0.0, 0.0] for i in range(2): if len(data) < 2: break if is_float(data[i]): if ((float(data[i]) < -180.0) or (float(data[i]) > 180.0)): continue term[i] = float(data[i]) value[i] = '{0:7.4f}'.format(term[i]) thread_uart = threading.Timer(0.01, uart) thread_uart.start() #================================================= # プログラムの起点 #================================================= if __name__ == '__main__': COM="COM17" bitRate=115200 ser = serial.Serial(COM, bitRate, timeout=0.1) thread_uart = threading.Thread(target=uart) thread_uart.start() root = tk.Tk() app = Application(master=root) app.mainloop() print('program end') ser.close()
解説:
1ー5行: 必要なモジュールをimportしています
7行ー: グローバル変数の定義です
18行ー: クラスを定義しています。
実を言いますとここから48行目まで何をしているのかはっきりとは分かっていないです。なんとなく、Tkinterで描くウィンドウ内にmatplotlibにて表示する部分を確保しているのかな、というぐらいです。なので詳しい説明はできないです。悪しからず。
52行ー: クラス内の関数(メソッド) button_click() を定義しています。
55-58行 表示するデータを1つずつずらしています
60,61行 下で定義された関数 uart() にて取得したデータ valueを受け取っています。
63-70行 表示されているグラフを1度消去した後、再描画しています。
72,73行 0.01秒おきにこの処理を繰り返すための処理です。
75行-: クラス外で定義されている関数です。文字列sがfloat に型変換できるかそうでないかを判別する関数です。
83行ー: これもクラス外の関数です。uart の機能により、ラズベリーパイPicoから送られてくるデータを受信しています。
送られてくるデータは2つ一組で、それらを別々の変数 value に格納しています。
こちらもよく分かっていない部分があるので、詳しい説明はできないです。
85-87行 送られてきたデータを一旦dataというリスト型変数に格納します。
89-98行 それらがfloat型に変換できることと、-180~180以内に収まっていることを確認した後、float型に変換し、valuという変数に格納します。
100ー101行 0.01秒おきにこの処理を繰り返すための処理です。
104行ー: このブログラムはここから始まります。
109-111行 シリアル通信に関する処理です。ここではbps115200にてCOM17を開いています。
113-114行 上で定義した関数 uart() をスレッドとしてスタートしています。
116-118行 Tkinter のオブジェクトを生成し、クラスで定義した処理(オブジェクト)も生成し、mainループとして無限に繰り返す手続きをしているようです。
120-121行 終了時の処理です。(シリアル通信のオブジェクトを閉じています)
解説は以上です。