はじめに
第9回では、XYテーブルに「原点復帰」や「ソフトリミット」を実装し、基本的な制御ができるようになりました。
しかし、実際に装置を運用してみると、現場レベルではさらに一歩踏み込んだ機能が欲しくなります。
- 「X軸だけを微調整したい」
- 「往復じゃなく、決まった位置にスッと移動させたい」
- 「今、具体的に何mmの位置にいるのか一目で知りたい」
- 「×ボタンで閉じたら、裏でモーターが変な動きをした…」
本記事では、これらの課題をすべて解決し、「実用的な制御アプリ」としての完成度を仕上げていきます。
完成したGUIイメージ

x軸移動量(mm)
X軸サイクル数
Y軸移動量(mm)
Y軸サイクル数
X:???mm Y:???mm
ボタン「XY同時実行」
ボタン「緊急停止」
ボタン「原点復帰」
ボタン「X軸サイクル」「Y軸サイクル」
ボタン「X軸片道」「Y軸片道」
ボタン「プログラムの終了」
今回追加・改善する機能
新しくなったGUIには、以下の機能を盛り込みます。
- 単軸動作(X軸・Y軸のみ)
- 片道移動(位置決め操作)
- 現在位置のリアルタイム表示
- 安全なGUI終了シーケンス
UIは直感的に操作できるよう、軸ごとの実行ボタンを整理しました。
| カテゴリ | 項目 / ボタン |
| 設定入力 | X軸移動量(mm), X軸サイクル数, Y軸移動量(mm), Y軸サイクル数 |
| ステータス表示 | X:???.?? mm / Y:???.?? mm (リアルタイム) |
| メイン操作 | XY同時実行, 緊急停止, 原点復帰 |
| 単軸・サイクル | X軸サイクル, Y軸サイクル |
| 単軸・片道 | X軸片道, Y軸片道 |
| システム | プログラム終了 (安全停止ボタン) |
単軸動作の実装
実際の装置運用では、「X軸だけ動かしてセンサー位置を確認する」といった単独操作が非常に多く発生します。
単軸動作関数の追加
def start_x_cycle():
distance=float(entry_X_dist.get())
cycles=int(entry_X_cycle.get())
threading.Thread(
target=run_motor,
args=("X軸",
X_STEP,X_DIR,X_EN,
X_MS1,X_MS2,X_MS3,
X_RESET,X_SLEEP,
distance,cycles),
daemon=True
).start()
# Y軸も同様に作成(引数をY軸用に変更)
単軸動作用のGUIボタンの追加
単軸動作関数をつくったら、それに対応するGUIボタンを追加します。
Tkinter(as tk)でGUIを定義した箇所に以下のコードを追加します
tk.Button(root, text="X軸サイクル実行", command=start_x_cycle, bg="lightblue")\
.grid(row=10, column=0, pady=5)
tk.Button(root, text="Y軸サイクル実行", command=start_y_cycle, bg="lightblue")\
.grid(row=10, column=1, pady=5)
片道移動(位置決め)機能
「往復」ではなく「指定した距離だけ移動して止まる」機能です。ワークのセットや位置決め作業には欠かせません。
片道移動関数
第10回で新しく追加したrun_motor_oneway関数では、移動後に現在位置(current_pos)を更新するのがポイントです。
クリックして追加して片道動作の関数を表示する
# ============================================
# 片道運動の関数(第10回追加)
# ============================================
def run_motor_oneway(axis_name, STEP, DIR, EN, MS1, MS2, MS3, RESET, SLEEP,
distance_mm):
global stop_flag,current_pos
stop_flag = False
start_pos =current_pos[axis_name]
target_pos = start_pos + distance_mm
# -----------------------------------------------
# ソフトリミットチェック
if axis_name == "X軸":
if not (X_MIN_MM <= target_pos <= X_MAX_MM):
message_queue.put(("error", f"{axis_name}移動範囲外:{target_pos:.2f}mm"))
return
else:
if not (Y_MIN_MM <= target_pos <= Y_MAX_MM):
message_queue.put(("error", f"{axis_name}移動範囲外:{target_pos:.2f}mm"))
return
steps = int(distance_mm * STEPS_PER_MM)
if steps < 1:
return
intervales = get_intervals(steps,ACC_DEC_STEPS,1.5)
gpio_init_axis(STEP,DIR,EN,MS1,MS2,MS3,RESET,SLEEP)
step_count = 0 # ★ 現在位置(ステップ数)
try:
#-----正転のみ-----
if axis_name =="X軸":
GPIO.output(DIR,GPIO.LOW)
else:
GPIO.output(DIR,GPIO.HIGH)
for t in intervales:
if stop_flag:
break
GPIO.output(STEP,GPIO.HIGH)
time.sleep(t)
GPIO.output(STEP,GPIO.LOW)
time.sleep(t)
step_count += 1
if step_count % 50 ==0:
current_mm = step_count / STEPS_PER_MM
message_queue.put(("pos",axis_name,current_mm))
GPIO.output(EN,GPIO.HIGH)
#★片道なので位置を更新
current_pos[axis_name]=target_pos
message_queue.put(("pos",axis_name,current_pos[axis_name]))
message_queue.put(("info",f"{axis_name}片道移動完了"))
finally:
print(f"[{axis_name}]片道終了")
#片道運動の関数===================================
片道移動用のGUIボタンの追加
片道動作の関数をつくったら、それに対応するGUIボタンを追加します。
Tkinter(as tk)でGUIを定義した箇所に以下のコードを追加します
tk.Button(root, text="X軸 片道", command=start_x_oneway, bg="lightblue")\
.grid(row=11, column=0, pady=5)
tk.Button(root, text="Y軸 片道", command=start_y_oneway, bg="lightblue")\
.grid(row=11, column=1, pady=5)
現在位置のリアルタイム表示
「今どこにいるか」を可視化することで、操作の安心感が格段に向上します。
ステップ数から位置を計算
ステッピングモーターは送ったパルス数で移動距離がわかるため、以下の計算で現在地を割り出します。
step_count += 1
if step_count % 50 == 0:
current_mm = step_count / STEPS_PER_MM
message_queue.put(
("pos",axis_name,current_mm)
)
Point: すべてのステップでGUIを更新しようとすると、通信過多でアプリがフリーズします。% 50(剰余演算)を使って適度に間引くのがスムーズな表示のコツです。
GUI側処理
GUIではQueueからメッセージを受け取り
位置表示を更新します。
if msg[0]=="pos":
_,axis,pos_mm = msg
if axis=="X軸":
x_pos_var.set(f"x:{pos_mm:.2f}mm")
else:
y_pos_var.set(f"y:{pos_mm:.2f}mm")
これで
モーター動作中(リアルタイム)
↓
現在位置が更新
されるようになりました。
安全なGUI終了(最重要)
装置制御において、右上の「×」ボタンでいきなり閉じるのは厳禁です。
なぜ「×」ボタンは危険なのか?
- GPIOの保持: プログラムが強制終了されると、出力ピンがHIGHのまま固定され、モーターに電流が流れ続けたり、異音が発生したりします。
- ゾンビ・スレッド: GUIは消えても、バックグラウンドのスレッドが生き残り、予期せぬ動作を続ける可能性があります。
対策:×ボタンの無効化と終了ボタン
「×」ボタンを無効化し、クリーンアップ処理(GPIO解放など)を必ず通る「終了ボタン」を実装します。
×ボタンを無効化
root.protocol(
"WM_DELETE_WINDOW",
disable_close
)
終了ボタンの追加 関数とGUI
GUIを終了させる関数の追加
def quit_program():
global stop_flag
stop_flag=True
GPIO.output(X_EN,GPIO.HIGH)
GPIO.output(Y_EN,GPIO.HIGH)
root.destroy()
対応する終了ボタンを追加
btn_quit = tk.Button(root,text="プログラム終了",command=quit_program,bg="gray",fg="white")
btn_quit.grid(row=7,column=0,columnspan=2,pady=15)
これで
モーター停止
↓
GPIO解放
↓
GUI終了
という安全な終了ができます。
まとめ
第10回では、XYテーブル制御GUIを「より使いやすく、より安全に」ブラッシュアップしました。
- 単軸操作で細かな調整が可能に
- 片道移動で正確な位置決めを実現
- リアルタイム表示で動作状況を把握
- 安全終了でハードウェアを保護
これで、XYテーブルを自在に操るための基本機能がすべて揃いました!
次回予告
いよいよこのシリーズもクライマックスです。
次回は、これまでのコードをきれいに整理した「完成版フルコード」を公開します。お楽しみに!
過去のXYテーブル製作シリーズはこちらから
- 【第0回】Raspberry PiのOSインストール完全ガイド|初心者向けにゼロから解説
- 【第1回】PythonでLEDを光らせる|GPIOの基本をやさしく解説
- 【第2回】ラズパイ+A4988でステッピングモーターを回す
- 【第3回】ステッピングモーターをmm単位で動かす|ベルト駆動の計算方法まで解説
- 【第1.5回】 A4988のVref調整手順(XYテーブル製作シリーズ)
- 【番外編】L6470が動かない?A4988と同じ感覚で使ってハマった話
- 【第4回】XYテーブル化の第一歩|X軸とY軸を2台動かす
- 【第5回】Pythonでステッピングモーターの加減速制御を実装する
- 【第6回】Pythonでステッピングモーターを2軸同時に動かす
- 【第7回】Raspberry PiでXYテーブル制御|リミットスイッチと原点復帰(Homing)の実装
- 【第8回】【Python × Raspberry Pi】XYテーブルをGUI操作する(Tkinter)
- 【第9回】Python×Raspberry PiでXYテーブルを制御するGUIを作る(原点復帰・位置表示・ソフトリミット実装)


コメント