PR

PythonとRaspberry PiでXYテーブル制御GUIを作る単軸動作・片道運動・安全終了の実装【第10回】

スポンサーリンク
RaspberryPi
スポンサーリンク

はじめに

第9回では、XYテーブルに「原点復帰」や「ソフトリミット」を実装し、基本的な制御ができるようになりました。
しかし、実際に装置を運用してみると、現場レベルではさらに一歩踏み込んだ機能が欲しくなります。

  • 「X軸だけを微調整したい」
  • 「往復じゃなく、決まった位置にスッと移動させたい」
  • 「今、具体的に何mmの位置にいるのか一目で知りたい」
  • 「×ボタンで閉じたら、裏でモーターが変な動きをした…」

本記事では、これらの課題をすべて解決し、「実用的な制御アプリ」としての完成度を仕上げていきます。

完成したGUIイメージ

x軸移動量(mm)
X軸サイクル数
Y軸移動量(mm)
Y軸サイクル数
X:???mm Y:???mm
ボタン「XY同時実行」
ボタン「緊急停止」
ボタン「原点復帰」
ボタン「X軸サイクル」「Y軸サイクル」
ボタン「X軸片道」「Y軸片道」
ボタン「プログラムの終了」

今回追加・改善する機能

新しくなったGUIには、以下の機能を盛り込みます。

  1. 単軸動作(X軸・Y軸のみ)
  2. 片道移動(位置決め操作)
  3. 現在位置のリアルタイム表示
  4. 安全な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テーブル製作シリーズはこちらから

コメント