はじめに
XYテーブル製作シリーズ 第8回
はじめに:ウィンドウ(アプリ)からXYテーブルを操作する
前回まではPythonプログラムからXYテーブルを自動で動かしていました。実際の装置では「移動量を変えたい」「途中で止めたい」「手動操作したい」といった操作が必要です。今回は Python 標準GUIライブラリ tkinter で操作ソフト(アプリ)を作ります。
この記事で分かること
- Tkinter の基本要素(Label・Entry・Button)と
grid()配置 - Entry から移動量・サイクル数を
float()/int()で取り出してモーター関数へ渡す - 「XY同時実行」ボタンで
threadingにより2軸を同時起動する(GUIをフリーズさせない) - フラグ方式の「緊急停止」ボタンと、A4988の電流OFF(
EN=HIGH) - フルコード(
XY_gui.py、ZIP配布)
完成する操作画面:X移動量(mm)/Xサイクル/Y移動量(mm)/Yサイクル の入力欄+「XY同時実行」「緊急停止」ボタン
使用環境とGPIOピン設定
| 項目 | 内容 |
|---|---|
| マイコン | Raspberry Pi |
| モータードライバ ×2 | A4988 |
| モーター ×2 | NEMA17 |
| 電源 | 12V 5A |
| Python | 3.x(tkinter は標準搭載) |
# X軸 X_STEP=21 X_DIR=20 X_EN=16 X_MS1=17 X_MS2=27 X_MS3=22 X_RESET=5 X_SLEEP=6 X_LIMIT=4
# Y軸 Y_STEP=2 Y_DIR=26 Y_EN=18 Y_MS1=23 Y_MS2=24 Y_MS3=25 Y_RESET=12 Y_SLEEP=13 Y_LIMIT=14
Tkinterの基本と各機能
Tkinterの基本要素
| 部品 | 用途 |
|---|---|
| Label | 文字表示 |
| Entry | 入力欄 |
| Button | ボタン |
root = tk.Tk()
tk.Label(root, text="X移動量").grid(row=0, column=0)
entry = tk.Entry(root)
entry.grid(row=0, column=1)
root.mainloop()移動量の入力
distance = float(entry_x.get()) # 入力欄の値を数値化してモーター関数へ渡すXY同時制御(threading)
XとYを同時に動かすには threading.Thread を使います。GUIメインスレッドでモーター処理を直接回すとウィンドウが固まるためです(詳しくは第8.5回)。
tx = threading.Thread(target=run_motor, args=(...))
ty = threading.Thread(target=run_motor, args=(...))
tx.start()
ty.start()緊急停止(フラグ方式)
# ボタン側
stop_flag = True
# モーター側(ループ内で毎回チェック)
if stop_flag:
return
# さらにA4988の電流をOFF
GPIO.output(X_EN, GPIO.HIGH)第8回 完成コード
# ============================================
# 第8回
# XYテーブルGUI操作
# ============================================
import RPi.GPIO as GPIO
import time
import threading
import tkinter as tk
GPIO.setmode(GPIO.BCM)
# ============================================
# GPIO設定
# ============================================
X_STEP = 21
X_DIR = 20
X_EN = 16
X_MS1 = 17
X_MS2 = 27
X_MS3 = 22
X_RESET = 5
X_SLEEP = 6
X_LIMIT = 4
Y_STEP = 2
Y_DIR = 26
Y_EN = 18
Y_MS1 = 23
Y_MS2 = 24
Y_MS3 = 25
Y_RESET = 12
Y_SLEEP = 13
Y_LIMIT = 14
# ============================================
# モーション設定
# ============================================
BELT_PITCH = 2.0
PULLEY_TEETH = 32
MM_PER_REV = BELT_PITCH * PULLEY_TEETH
FULL_STEPS = 200
MICROSTEP = 16
STEPS_PER_REV = FULL_STEPS * MICROSTEP
STEPS_PER_MM = STEPS_PER_REV / MM_PER_REV
ACC_DEC_STEPS = 100
stop_flag = False
# ============================================
# GPIO初期化
# ============================================
def gpio_init_axis(STEP,DIR,EN,MS1,MS2,MS3,RESET,SLEEP):
pins=[STEP,DIR,EN,MS1,MS2,MS3,RESET,SLEEP]
for p in pins:
GPIO.setup(p,GPIO.OUT)
GPIO.output(RESET,GPIO.HIGH)
GPIO.output(SLEEP,GPIO.HIGH)
GPIO.output(MS1,GPIO.HIGH)
GPIO.output(MS2,GPIO.HIGH)
GPIO.output(MS3,GPIO.HIGH)
GPIO.output(EN,GPIO.LOW)
# ============================================
# 加減速
# ============================================
def get_intervals(total_steps,acc_steps,total_time):
const_steps=max(total_steps-acc_steps*2,1)
total_ratio=acc_steps*0.5+const_steps+acc_steps*0.5
base_interval=total_time/total_ratio/2
intervals=[]
for i in range(acc_steps):
intervals.append(base_interval*(acc_steps-i)/acc_steps)
for i in range(const_steps):
intervals.append(base_interval)
for i in range(acc_steps):
intervals.append(base_interval*(i+1)/acc_steps)
return intervals
# ============================================
# モーター制御
# ============================================
def run_motor(axis_name,
STEP,DIR,EN,
MS1,MS2,MS3,
RESET,SLEEP,
distance_mm,
cycles):
global stop_flag
gpio_init_axis(STEP,DIR,EN,MS1,MS2,MS3,RESET,SLEEP)
steps=int(distance_mm*STEPS_PER_MM)
intervals=get_intervals(steps,ACC_DEC_STEPS,1.5)
for c in range(cycles):
if stop_flag:
return
if axis_name=="X軸":
GPIO.output(DIR,GPIO.LOW)
else:
GPIO.output(DIR,GPIO.HIGH)
for t in intervals:
if stop_flag:
return
GPIO.output(STEP,GPIO.HIGH)
time.sleep(t)
GPIO.output(STEP,GPIO.LOW)
time.sleep(t)
GPIO.output(EN,GPIO.HIGH)
# ============================================
# XY同時実行
# ============================================
def start_xy():
x_dist=float(entry_x_dist.get())
x_cycle=int(entry_x_cycle.get())
y_dist=float(entry_y_dist.get())
y_cycle=int(entry_y_cycle.get())
tx=threading.Thread(
target=run_motor,
args=("X軸",
X_STEP,X_DIR,X_EN,
X_MS1,X_MS2,X_MS3,
X_RESET,X_SLEEP,
x_dist,x_cycle)
)
ty=threading.Thread(
target=run_motor,
args=("Y軸",
Y_STEP,Y_DIR,Y_EN,
Y_MS1,Y_MS2,Y_MS3,
Y_RESET,Y_SLEEP,
y_dist,y_cycle)
)
tx.start()
ty.start()
# ============================================
# 緊急停止
# ============================================
def stop_motor():
global stop_flag
stop_flag=True
GPIO.output(X_EN,GPIO.HIGH)
GPIO.output(Y_EN,GPIO.HIGH)
# ============================================
# GUI
# ============================================
root=tk.Tk()
root.title("XY Table Controller")
tk.Label(root,text="X移動量(mm)").grid(row=0,column=0)
entry_x_dist=tk.Entry(root)
entry_x_dist.grid(row=0,column=1)
tk.Label(root,text="Xサイクル").grid(row=1,column=0)
entry_x_cycle=tk.Entry(root)
entry_x_cycle.grid(row=1,column=1)
tk.Label(root,text="Y移動量(mm)").grid(row=2,column=0)
entry_y_dist=tk.Entry(root)
entry_y_dist.grid(row=2,column=1)
tk.Label(root,text="Yサイクル").grid(row=3,column=0)
entry_y_cycle=tk.Entry(root)
entry_y_cycle.grid(row=3,column=1)
tk.Button(root,
text="XY同時実行",
command=start_xy,
bg="lightblue").grid(row=4,column=0,columnspan=2,pady=10)
tk.Button(root,
text="緊急停止",
command=stop_motor,
bg="red",
fg="white").grid(row=5,column=0,columnspan=2,pady=10)
root.mainloop()動作手順
- Raspberry Pi で
python xy_gui.pyを実行 - GUIが起動する
- 移動量・サイクル数を入力する
- 「XY同時実行」で動作、「緊急停止」で停止
まとめと次回予告
Pythonだけで「移動量入力・XY同時制御・緊急停止」を備えた装置操作ソフトの基礎が完成しました。ただし「GUIが固まる」問題を正しく理解しておく必要があります。次回・第8.5回でThreadの使い方とGUIが固まる理由を掘り下げ、第9回で原点復帰GUI・現在位置表示・ソフトリミットを追加します。
次に読む記事
🧵 次回はこちら(第8.5回) PythonのGUIが固まる理由とThreadの使い方|Tkinterでモーター制御中もボタンを効かせる なぜThreadが必要なのかを図解。 → 🗺️ 全体像 自作XYテーブルの全手順ロードマップ|Raspberry Pi+ステッピングモーターで2軸制御 第0回〜第11回の全工程をまとめたロードマップ。 →よくある質問(FAQ)
Qtkinter がインポートできません(No module named ‘tkinter’)。
ARaspberry Pi OS では通常標準搭載ですが、無い場合は sudo apt install python3-tk でインストールします。仮想環境を使っている場合はシステムのtkが見えないことがあるので、–system-site-packages で環境を作り直すか、システムのpython3で実行します。
Qボタンを押すとGUIが固まって動かなくなります。
Acommand に指定した関数の中でモーター処理(forループ+time.sleep)を直接実行しているためです。threading.Thread でモーター処理を別スレッドに逃がすと、GUIメインスレッドはイベント処理を続けられます。第8.5回で詳しく解説します。
Q緊急停止ボタンを押しても止まりません。
Aモーターのforループ内で毎ステップ if stop_flag: return をチェックしているか確認します。ループの外にチェックがあると、1回の移動が終わるまで止まりません。あわせて GPIO.output(EN, GPIO.HIGH) でドライバの電流も切ります。
QEntry が空のまま実行するとエラーで落ちます。
Afloat(entry.get()) は空文字だと ValueError になります。try/except ValueError で囲んでメッセージを出すか、入力チェック(未入力なら実行しない)を関数の先頭に入れます。
QGUIの見た目が古い/ボタンを大きくしたい。
Atkinter は素朴な見た目です。ttk(tkinter.ttk)のウィジェットを使うとOSネイティブ風になります。ボタンサイズは width / height / padx / pady、色は bg / fg で調整します。凝ったUIが必要なら CustomTkinter も選択肢です。
XYテーブル製作シリーズ(全12回)
- Raspberry Pi ImagerでのOSインストール手順|microSD書き込みから初期設定まで
- ラズパイでLチカ|PythonとRPi.GPIOでLEDを点灯・点滅させる方法
- ラズパイでステッピングモーターを回す方法|A4988の配線とPython制御・Vref調整
- ステッピングモーターをmm単位で動かす計算式とPython実装|step/mmの求め方
- PythonでX軸・Y軸のステッピングモーターを2軸制御する方法|A4988ドライバー2台
- ステッピングモーターの脱調を防ぐ加減速制御(台形駆動)をPythonで実装する方法
- Raspberry PiでXYテーブルを2軸同時制御|Pythonのthreadingでステッピングモーターを同時に動かす
- Raspberry Pi+PythonでXYテーブルの原点復帰(Homing)を実装|リミットスイッチ・バックオフ処理
- PythonのTkinterでXYテーブルをGUI操作する|移動量入力・XY同時実行・緊急停止ボタン(この記事)
- PythonのGUIが固まる理由とThreadの使い方|Tkinterでモーター制御中もボタンを効かせる
- XYテーブルGUIに原点復帰・現在位置表示・ソフトリミットを実装|Queueでスレッド安全にGUI更新
- XYテーブル制御GUIに単軸動作・片道移動・安全なプログラム終了を実装
- 自作XYテーブルの完成構成と設計資料(図面・BOM・結線図)|完成版Pythonコード配布
補足記事
⚡ 第1.5回・補足 A4988の電流制限(Vref)調整方法|焼損を防ぐ計算式とテスターでの設定手順 モーターを回す前に必ずやる A4988 の電流調整。 → 🔀 番外編 L6470が動かない原因|A4988との違いとSPI通信・デイジーチェーンの罠 高機能ドライバ L6470 を A4988 と同じ感覚で使ってハマった話。 → 🗺️ 全体像 自作XYテーブルの全手順ロードマップ|Raspberry Pi+ステッピングモーターで2軸制御 第0回〜第11回の全工程をまとめたロードマップ。 →Follow me on X
本ブログの中の人「ニンジン🥕」はXやってます。新着記事はいち早くポストでお知らせしているので、見逃したくない方は @ninjin_py_vba をフォローしてみてください🥕
@ninjin_py_vba をフォローシリーズ記事一覧
電子工作・自動化ツールの製作記録をシリーズごとにまとめています。気になるテーマからお読みください。
-
FILE.01 — IoT
Raspberry Pi Pico W × GASラズパイPico W×GASで自作!「LINEで動く温湿度&スマートリモコン」完全ロードマップ
「外出中のペットの室温が心配…」そんな思いからスタートしたIoT自作プロジェクトの総集編。温湿度監視からエアコン遠隔操作まで、仕組みをゼロから作りたい方のための全5回の開発記録です。
シリーズを読む -
FILE.02 — CNC
Arduino × レーザー刻印CNCCNC自作シリーズ
高精度な加工を目指し、本格的な自作CNC製作に挑戦中。現在は基幹パーツであるオリエンタルモーターの納品を待つ「設計・準備編」を公開。ハードとソフトの両面から、理想のマシンを形にする過程をリアルタイムにお届けします。
シリーズを読む -
FILE.03 — XY軸制御
Raspberry Pi × ステッピングモーターXYテーブルシリーズ
Raspberry Piとステッパモーターを使い、ゼロから2軸制御に挑む記録。OS設定から回路設計、多軸制御のPythonコードまで、躓きやすいポイントを徹底図解。電子工作初心者が「動く感動」を味わうための実戦ガイドです。
シリーズを読む -
FILE.04 — 業務自動化
Python × Excel × CustomTkinter【Python開発記】NINJIN Mail制作記
PythonとExcelを連携させ、実務で即戦力となるメール送信ツールを開発。CustomTkinterによるUI構築や、ミスを防ぐテンプレート活用術など、現場の「痒い所に手が届く」自動化ノウハウを細部まで丁寧に解説します。
シリーズを読む -
FILE.05 — 業務自動化
Python × Excel × GUIアプリ化Invoice Maker(請求書・見積書自動作成)
手作業の請求書作成から卒業!PythonでExcelデータを読み込み一括PDF化する「Invoice_Maker」の作り方を全3回で解説。基本ロジックからGUIアプリ化まで、実務で役立つ自動化ノウハウが満載です。
シリーズを読む

コメント