機械系エンジニアの備忘録

20代独身社会人。仕事では機械・機構の研究開発を行っているエンジニアが、自分の専門分野ではないpythonを扱って楽しむブログです。

MENU

【python】tkinterを使ってPID制御を簡単に試せるアプリを作ってみた

tkinterとmatplotlibとpython-controllを使って、PID制御の各パラメータに対応したスライダーを動かすと、リアルタイムでグラフが変化するアプリを作る

f:id:stjun:20200118234538g:plain

※2020/01/19追記:PID制御の説明とpythonでの書き方は以下記事に追加したのでご覧ください。

www.stjun.com

 


 

1. 誰に向けた記事か

pythontkinterを勉強してる人

tkinterでどんなアプリが作れるのか興味がある人

・PID制御の各パラメータがどんな影響を与えるのか直感的に理解できるアプリがあったらいいなと思ってた人

 

2. はじめに

これまでmatplotlibとtkinterを使って、tkinterで作ったウインド上にグラフを載せたり、アニメーションを作成する方法を紹介しました。

stjun.hatenablog.com

stjun.hatenablog.com

今回は応用編として、PID制御を題材に冒頭で紹介したアプリを作成します。

 

3. コード・実行結果

3.1 コード
import tkinter as tk
import tkinter.ttk as ttk
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import (
    FigureCanvasTkAgg, NavigationToolbar2Tk)
import numpy as np
from control.matlab import *

root = tk.Tk()#ウインドの作成
root.title("pid soft")#ウインドのタイトル
root.geometry("650x350") #ウインドの大きさ
frame_1 = tk.LabelFrame(root,labelanchor="nw",text="グラフ",foreground="green")
frame_1.grid(row=0, column=0)
frame_2 = tk.LabelFrame(root,labelanchor="nw",text="パラメータ",foreground="green")
frame_2.grid(row=0, column=1, sticky="nwse")

#スケールバーが動いたらその値を読み取りグラフを更新する
def graph(*args):
    ax.cla()
    Kp=scale_var.get()
    Ti=scale_var_Ti.get()
    Td=scale_var_Td.get()
    value_Kp=f"{Kp:.2f}"
    value_Ti=f"{Ti:.2f}"
    value_Td=f"{Td:.2f}"
text_display.set(str(value_Kp))
text_display_Ti.set(str(value_Ti))
text_display_Td.set(str(value_Td))
num=[Td,1,1/Ti] den=[1,0] G_ID=tf(num,den) G_all=feedback(G_ID*Kp*G,1) (y_s,t_s)=step(G_all,T=np.arange(0,10,0.01)) (y_in,t_in)=step(1,T=np.arange(0,10,0.01)) ax.set_xlabel('t / s') ax.set_ylabel('y') plt.style.use('ggplot') ax.plot(t_s,y_s) ax.plot(t_in,y_in,linestyle="dashed") ax.set_title('Kp='+str(value_Kp)+',Ti='+str(value_Ti)+',Td='+str(value_Td)) canvas.draw() #伝達関数の設定 #PID制御のパラメータ Kp=30 #比例ゲイン Ti=1.8 #積分ゲイン Td=0.2 #微分ゲイン num=[Td,1,1/Ti] den=[1,0] G_ID=tf(num,den) #伝達関数の設定 num = [0.1] den = [0.1, 1.0, 1] G = tf(num, den) G_all=feedback(G_ID*Kp*G,1) #ステップ応答 (y_s,t_s)=step(G_all,T=np.arange(0,10,0.01)) (y_in,t_in)=step(1,T=np.arange(0,10,0.01)) #グラフの設定 fig=plt.Figure() ax = fig.add_subplot(111) ax.plot(t_s,y_s) ax.plot(t_in,y_in,linestyle="dashed") ax.set_title('Kp='+str(Kp)+',Ti='+str(Ti)+',Td='+str(Td)) ax.set_xlabel('t / s') ax.set_ylabel('y') plt.style.use('ggplot') #tkinterのウインド上部にグラフを表示する canvas = FigureCanvasTkAgg(fig, master=frame_1) canvas.draw() canvas.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand=1) #比例ゲインのスケール作成 scale_var=tk.DoubleVar() scale_var.set(Kp) scale_var.trace("w",graph) scale=ttk.Scale(frame_2,from_=0,to=30,length=150,orient="h",variable=scale_var) scale.grid(row=1,column=0) #比例ゲインのテキスト text=tk.Label(frame_2,text="比例ゲイン:Kp") text.grid(row=0,column=0) #比例ゲインの数値表示テキスト text_display=tk.StringVar() text_display.set(str(Kp)) label=tk.Label(frame_2,textvariable=text_display) label.grid(row=1,column=1) #積分ゲインのスケール作成 scale_var_Ti=tk.DoubleVar() scale_var_Ti.set(Ti) scale_var_Ti.trace("w",graph) scale_Ti=ttk.Scale(frame_2,from_=0.01,to=3,length=150,orient="h",variable=scale_var_Ti) scale_Ti.grid(row=3,column=0) #積分ゲインのテキスト text_ti=tk.Label(frame_2,text="積分ゲイン:Ti") text_ti.grid(row=2,column=0) #積分ゲインの数値表示テキスト text_display_Ti=tk.StringVar() text_display_Ti.set(str(Ti)) label_Ti=tk.Label(frame_2,textvariable=text_display_Ti) label_Ti.grid(row=3,column=1) #微分ゲインのスケール作成 scale_var_Td=tk.DoubleVar() scale_var_Td.set(Td) scale_var_Td.trace("w",graph) scale_Td=ttk.Scale(frame_2,from_=0,to=1.0,length=150,orient="h",variable=scale_var_Td) scale_Td.grid(row=5,column=0) #微分ゲインのテキスト text_td=tk.Label(frame_2,text="微分ゲイン:Td") text_td.grid(row=4,column=0) #微分ゲインの数値表示テキスト text_display_Td=tk.StringVar() text_display_Td.set(str(Td)) label_Td=tk.Label(frame_2,textvariable=text_display_Td) label_Td.grid(row=5,column=1) root.mainloop()
3.2 実行結果

実行すると以下のアプリが表示されます。

右側のパラメータ欄にある各スライダーを操作するとグラフがその都度更新されます。

f:id:stjun:20200118233505p:plain

 ぜひコードをコピペして自分のPCで遊んでみて下さい。

※注意:python-controlはデフォルトで入ってないので、自分でpipを用いてインストールする必要があります。

もしAnacondaを使ってる方はwindowsのスタートメニューから以下のAnaconda Promptを開いてください。

f:id:stjun:20200112163457p:plain

 そこで以下のように「pip install control」と打ち込んでください

f:id:stjun:20200120181418p:plain

そうするとpython-controlが使えるようになります。

 

■ 読んで良かった本を紹介

私はAmazon kindle unlimitedというサービスを1年以上利用しています。

これは月額980円で 和書12万冊以上の電子書籍を読めるサービスです。

ビジネス本、雑誌、漫画、技術本など様々な本を読むことができます。

なおkindle unlimitedは最初の30日間無料のため、気軽に登録してみて、あまり読みたい本が無ければすぐに解約しても問題ありません。

それか30日に気になる本を全て読破すれば実質タダです。

ぜひ気になった方はチェックしてみて下さい。

なお学生さんであればkindle unlimitedよりも年2450円(月210円程度)で映画見放題、音楽聞き放題、本読み放題の「prime student」がおすすめです。

以下に私がkindle unlimitedで読んでよかった本を紹介します。kindle unlimitedに登録すれば何回読んでも何冊読んでも月額980円だけです。

 

1. 

tkinterでの基本的なパーツの作成方法(ボタンやテキストやスクロールバー等)について、初心者が読んでもすぐに分かるように丁寧に書かれています。私はtkinterを学んだ後に知りましたが、こんなに分かりやすい本があるなら初めからこの本を読んで勉強すればこんなに苦労するなかったのにと思ったくらいです...

 

2.

難しいことはわかりませんが、お金の増やし方を教えてください!

難しいことはわかりませんが、お金の増やし方を教えてください!

  • 作者:山崎元,大橋弘祐
  • 出版社/メーカー: 文響社
  • 発売日: 2015/12/04
  • メディア: Kindle版
 

 投資信託について「投資信託とは?」「どうやって始めるか?」「口座はどこで開設するか?」など具体的なアクションまで書かれている本です。ど素人の著者が口座や銘柄を選ぶ過程が詳しく書いてるので非常に分かりやすかったです。

 

3. 

さおだけ屋はなぜ潰れないのか? 身近な疑問からはじめる会計学 (光文社新書)

さおだけ屋はなぜ潰れないのか? 身近な疑問からはじめる会計学 (光文社新書)

  • 作者:山田 真哉
  • 出版社/メーカー: 光文社
  • 発売日: 2005/02/16
  • メディア: 新書
 

 ジャンルは経済に入ると思いますが全然堅苦しくなく読める本です。身近な事例をもとに「成程、そういうことだったんだ」と思うような説明がされていて面白かったです。

 

4. 

多動力 (NewsPicks Book) (幻冬舎文庫)

多動力 (NewsPicks Book) (幻冬舎文庫)

  • 作者:堀江 貴文
  • 出版社/メーカー: 幻冬舎
  • 発売日: 2019/04/10
  • メディア: 文庫
 

既にベストセラーになってる本なので読んだ方も多いと思います。

堀江さんは時にキツイ物言いで炎上しますが、言ってることや考えてることはすごくまともだし、それを説明する能力にも長けているので読んでいてためになりました。 

 

5. 

「幸せをお金で買う」5つの授業 (中経出版)

「幸せをお金で買う」5つの授業 (中経出版)

 

「どういうお金の使い方をしたら幸せを感じれるのか?」「お金持ちになったら本当に幸せになれるのか?」を大きなテーマに、様々な体験談や研究事例を通してお金の賢い使い方を紹介している本です。

 

6. 雑誌系

家電、美容、アウトドア、経済など様々な雑誌があります。

こういった雑誌を買っていませんか?1つでも買っているならkindle unlimitedに登録した方が安上がりで更に読み放題です。

MONOQLO (モノクロ) 2020年 02月号 [雑誌]

MONOQLO (モノクロ) 2020年 02月号 [雑誌]

  • 作者:晋遊舎
  • 出版社/メーカー: 晋遊舎
  • 発売日: 2019/12/19
  • メディア: Kindle版
 
家電批評 2020年 1月号 [雑誌]

家電批評 2020年 1月号 [雑誌]

  • 作者: 
  • 出版社/メーカー: 晋遊舎
  • 発売日: 2019/12/03
  • メディア: Kindle版
 
ロードバイク完全メンテナンス (エイムック 3544 BiCYCLE CLUB別冊)

ロードバイク完全メンテナンス (エイムック 3544 BiCYCLE CLUB別冊)

  • 作者: 
  • 出版社/メーカー: エイ出版社
  • 発売日: 2016/11/29
  • メディア: ムック
 
繰り返し作りたくなる!  ラク弁当レシピ (エイムック 3680)

繰り返し作りたくなる! ラク弁当レシピ (エイムック 3680)

  • 作者:長谷川りえ
  • 出版社/メーカー: エイ出版社
  • 発売日: 2017/03/27
  • メディア: ムック
 
Tarzan(ターザン) 2020年1月23日号 No.779 [内臓脂肪 皮下脂肪すっきり落とす!] [雑誌]

Tarzan(ターザン) 2020年1月23日号 No.779 [内臓脂肪 皮下脂肪すっきり落とす!] [雑誌]

  • 作者: 
  • 出版社/メーカー: マガジンハウス
  • 発売日: 2020/01/04
  • メディア: Kindle版
 
DIME(ダイム) 2020年 03 月号 [雑誌]

DIME(ダイム) 2020年 03 月号 [雑誌]

  • 作者: 
  • 出版社/メーカー: 小学館
  • 発売日: 2019/12/16
  • メディア: 雑誌
 

 

7. 

この本は「1. 著者の筋トレ格言」「2. 筋トレ体験談」「3. 筋トレの効能を学術論文を引用して科学的に説明」の3部からなっています。この本で私が好きな著者の考え方は、

「弱いのはメンタルではない、フィジカルだ。メンタルを鍛えたいならまずフィジカルを鍛えろ。」

 です。

気になる方は過去記事を見て下さい。

stjun.hatenablog.com

 

8. 

筋トレビジネスエリートがやっている最強の食べ方

筋トレビジネスエリートがやっている最強の食べ方

  • 作者:Testosterone
  • 出版社/メーカー: KADOKAWA
  • 発売日: 2017/06/30
  • メディア: Kindle版
 

 5番と同じ著者の本です。痩せるためにどういった食事をすれば良いかについて書いてある本です。筋トレ本を出してる著者ですが、意外にも「痩せたいなら筋トレより食事だ」と言い切っていて面白かったです。1日どのくらいのカロリーやたんぱく質を取れば痩せるか計算してくれるサイトのURLがあり、それ通りに食事制限すると本当痩せました..

 

4.  説明

※仕事が忙しくなったので土日に更新するよう頑張ります。

※2020/01/19追記:PID制御の説明とpythonでの書き方は以下記事に追加したのでご覧ください。

stjun.hatenablog.com

スライダーの動きと関数(何かしらの動作、今回はグラフを更新する動作)を紐づける方法は過去記事で紹介していますが、本記事でも簡単に紹介したいと思います。

stjun.hatenablog.com

 4.1 スケールバーの説明
import tkinter as tk
import tkinter.ttk as ttk

root=tk.Tk()

#スケールバーを動かしたときに呼び出される関数
def text_change(*args):
    text_display.set(str(scale_var.get()))

#数字用ラベル
text_display=tk.StringVar()
text_display.set("0")
label=tk.Label(textvariable=text_display)
label.pack()

#スケールバー
scale_var=tk.IntVar()
scale_var.set(0)
scale_var.trace("w",text_change)
scale=ttk.Scale(root,from_=0,to=100,length=100,orient="h",variable=scale_var)
scale.pack()

root.mainloop()

上記は説明用に作ったコードです。次のような動きをします。

f:id:stjun:20200121004422g:plain

まずスケールバーは以下のコードになります。

#スケールバー
scale_var=tk.IntVar() #IntVar()だと整数表示
scale_var.set(0) #スケールバーの初期値
scale_var.trace("w",text_change) #スケールバーを動かしたときに呼び出す関数
scale=ttk.Scale(root,from_=0,to=100,length=100,orient="h",variable=scale_var)
#スケールバーの作成。0~100を100刻みなので1目盛りは1。orient='h'は横向き. scale.pack() #スケールバーの配置

ここで、

scale_var.trace("w",text_change)

 で呼び出される関数は以下になります。スケールバーを動かすと、スケールバーの位置に対応した数字を読み取り、事前に作成した数字用ラベルに代入しています。

#スケールバーを動かしたときに呼び出される関数
def text_change(*args):
    text_display.set(str(scale_var.get()))
#数字用ラベルの値をスケールバーの値に更新

上記コードではラベルの値を変更しているだけですが、この関数部分に、

1. スケールバーを動かしたときにその値を読み取り

Kp=scale_var.get()
Ti=scale_var_Ti.get()
Td=scale_var_Td.get()

2. 読み取った値でPID制御の伝達関数を再計算し

num=[Td,1,1/Ti]
den=[1,0]
G_ID=tf(num,den)
G_all=feedback(G_ID*Kp*G,1)
(y_s,t_s)=step(G_all,T=np.arange(0,10,0.01))
(y_in,t_in)=step(1,T=np.arange(0,10,0.01))

3. グラフを更新

ax.set_xlabel('t / s')
ax.set_ylabel('y')
plt.style.use('ggplot')
ax.plot(t_s,y_s)
ax.plot(t_in,y_in,linestyle="dashed")
ax.set_title('Kp='+str(value_Kp)+',Ti='+str(value_Ti)+',Td='+str(value_Td))
canvas.draw()

するコードを書けば良いことになります。

 

5.  最後に

いかがだったでしょうか。何かを設計する時に「このパラメータを動かしたらどういう挙動を示すんだろう」と思う時があります。そんな時に便利な方法ですね。

20/01/20追記:Part2を書きました。 

stjun.hatenablog.com