Raspberry Pi (bookworm 以降)で PWM (pulse width modulation) 制御を行う Python のスクリプトについてまとめておくことにします。
以下の環境で動作確認をしています。
環境:
・ Raspberry Pi 5 (bookworm 以降)
・ Windows パソコン(リモートデスクトップの設定済み)
・ LAN ネットワーク
背景 ~ GPIO の仕様が変更になった!
Raspberry Pi 5 (bookworm) 以降では、パルス幅変調(PWM、pulse width modulation)に関する仕様が変更になっています。
そこで、パルス幅変調を行う Python のスクリプトをまとめ、公開しておくことにします。
PWM 制御を行うことで、たとえば、以下が可能になります。
・ LED の明るさの微調整
・ DC モーターの回転速度、力量・トルクの微調整
・ ヒーター・熱源温度の微調整
・ サーボモーターの制御
・ フィードバック制御を行う
・ Raspberry Pi のプログラム上から上記の制御を行う
・ その他
手順
① PWM を行うためのフォルダ(例:”gpio1″)とファイル(”gpio_pwm1.py”)を作成し、以下のスクリプトをコピー&ペーストして保存します。
例: home/pi/gpio1/gpio_pwm1.py
② Raspberry Pi の GPIO に 動作確認のための回路を接続します。
上記の写真では、以下の接続としています。
・ GPIO03 (GND)、GPIO14 (PWM信号、3.3V)に、それぞれ、黒色、赤色のジャンパケーブルを接続し、ブレッドボードに配線を引き出します。
・ 赤色のジャンパケーブルから電流が、LED、330Ωの抵抗(橙橙茶金)を経由し、黒色のジャンパケーブルに流れるよう接続します。
※ Raspberry Pi のターミナルで pinout コマンドを実行することで、ピン番号・ピン配列を確認できます。
※ LED は足の長い側が +側(赤色ケーブル側)となるよう接続します。
③ ①のスクリプトを実行します。
例: python home/pi/gpio1/gpio_pwm1.py
※ スクリプトを実行すると、スクリプトで指定した時間(60秒)の間、キーボードの入力を受けつける状態となります。
④ スクリプト実行中に、”c” キーを押すとプログラムを終了します。”u” キーを押すと HIGH となっている期間のパルス幅が増加します。”d” キーを押すとパルス幅が狭くなります。
うまく動いたら
うまく動いたら、スクリプトをアレンジしてみてください。
PWM 出力のピン番号、周波数などを自由に変更できます。
また、GPIO 端子に接続した回路側も変更し、応用してみてください。
詳細は省略しますが、私の場合、同スクリプトを用いて以下が動いています。
・ トランジスタと外部バッテリーを使ってDCモーターの回転速度を Raspberry Pi 側から微調整する
・ ヒーターの温度の微調整を行う
※ なお、冒頭の写真は、LED の明るさを PWM で少しずつ変更したものです。わかりにくいですけれども。。
スクリプトの説明
・ スクリプトの冒頭で import PWMLED、atexit としているところでパルス幅制御に必要なモジュールをインポートしています。
・ import sys、tty、…、select としているところでは、キーボードのキー入力を受けつけるためのモジュールをインポートしています。キー入力により、パルス幅を最小値 0% から最大値 100% まで変えられるようにします。
・ def keypress1() 関数は、キー入力を受けつけて処理をするための関数です。プログラムを停止するコマンド(イベント)が発行されていなければ、キー入力を調べて、”u” (up) であれば、パルス幅を指定した値だけ増加させます。”d” (down) が入力されていれば、パルス幅を指定した値だけ減らします。
また、”c” が入力されていれば、プログラムを停止するコマンドを発行します。
もしコマンド処理がうまくいかない場合は、内部の状態を初期の状態に戻しておきます。
・ スクリプトを実行すると、関数を定義したのち、pin1 = 14 とした行以降が実行されます。
スクリプトでは、GPIO04 の 14 番ピンを使っています。他のピンを使いたい場合、出力を複数にしたい場合はこの部分を修正してください。
・ out1 = PWMLED() としたところで、ピン番号と PWM 制御の周波数 frequency, Hz を設定しています。スクリプトでは 100Hz、周期でいえば T1 = 1/100 = 10ms としています。周期、周波数を変更する場合はこの部分を修正してください。
・ stop1 = … とした行以降で、キー入力を受けつけるためのイベント処理をしています。
・ ch1 は、キー入力で入力された文字です。
・ val1 = 0.02 は、初期状態でのパルス幅です。この例では、起動時には 2% だけ 14番ピンを HIGH (3.3V) とし、残りの 98% を LOW (0V) とする設定としています。
・ del1 = 0.01 は、”d” または “u” キーを押したときのパルス幅の変化量です。
・ sec1 = 60 は、スクリプトの実行時間(秒)です。長時間、実行したい等の場合はこの値を調整してください。
・ 上記のパラメータの設定後、for ループでパルス幅制御のコマンドを繰り返し実行しています。この間、キーボードの入力を監視し、パルス幅を変更できるようにしています。
・ 末尾でパルス幅を0%としています。for ループの途中で抜けた場合であっても、終了時の外部機器の状態を初期状態に戻すようにします。モーターなどの機器が接続されているとき、制御対象を停止し特定の状態とするようにします。
まとめ
Raspberry Pi (bookworm 以降)でパルス幅変調を行うためのスクリプトについてまとめました。
今回の LED の回路図などは、以下の関連リンクに記載したものとほぼ同等です。
関心のある方は、関連リンクなども参照してみてください。
関連リンク
・ サーボモーターを動かす 【Raspberry Pi】
・ LED を ON/OFF する 【Raspberry Pi & Python】
スクリプト
PWM 制御を行うスクリプト GPIO_PWM1.py
import time as tm1
from gpiozero import PWMLED
import atexit as at1
import sys
import tty
import threading as th1
import termios as te1
import select as sl1
def keypress1(stop1):
global val1
global del1
global ch1
fi1 = sys.stdin.fileno()
old1 = te1.tcgetattr(fi1)
try:
tty.setcbreak(fi1)
while not stop1.is_set():
if sl1.select([sys.stdin], [], [], 0.1)[0]:
ch1 = sys.stdin.read(1)
if ch1 == 'c':
stop1.set()
elif ch1 == 'u':
val1 = val1 + del1
if val1 > 1.0:
val1 = 1.0
elif ch1 == 'd':
val1 = val1 - del1
if val1 < 0.0:
val1 = 0.0
finally:
restore1(fi1, old1)
def restore1(fi1, settings1):
try:
te1.tcsetattr(fi1, te1.TCSADRAIN, settings1)
except Exception as e1:
print(e1)
pin1 = 14 # GPIO14, pinout
out1 = PWMLED(pin1, frequency=100) # PWM, 10 ms
at1.register(lambda: out1.value == 0.0)
stop1 = th1.Event() # for key events
thread1 = th1.Thread(target=keypress1, args=(stop1,), daemon=True)
thread1.start()
fi1 = sys.stdin.fileno()
ini1 = te1.tcgetattr(fi1)
ch1 = "" # key
val1 = 0.02 # duty: 2 %
del1 = 0.01
out1.value = val1
sec1 = 60 # 60 seconds
try:
for i1 in range(sec1):
if stop1.is_set():
break
out1.value = val1
str1 = str(i1) + " " + str(val1) + " " + ch1
print(str1)
tm1.sleep(1)
finally:
restore1(fi1, ini1)
out1.value = 0.0
tm1.sleep(1)