パルス幅変調(パルスはばへんちょう、英語: pulse width modulation、PWM)
とは変調方法の一つであり、パルス波のデューティ比を変化させて変調すること。
Wikipediaより
モーターの制御を調べているとよく出てくる単語ですが、簡単に書くと
決まった周期で一定電圧をON/OFFし、パルス波形や電圧を制御する
という理解で良いと思います。
Contents
まずはPWMが何かを説明します。
PWMは電気信号のON時間とOFF時間を制御します。
・周期的に繰り返される時間をT1
・繰り返し周期のうちONしている時間をT2
とした場合にT2/T1=Duty比になります。
このON/OFF時間を変化させて様々な機器を制御しています。
サーボモータの制御方法にはPWMの他にシリアル通信などがあります。
最近はロボットの関節のような複雑な制御を行うためにシリアル通信でモータを制御したりしますが、今でもラジコンの前輪をプロポ(ラジコンのコントローラ)によって傾けるような制御をPWMで行っています。
今回は制御が簡単なRaspberry Pi Picoを使ってPWMでサーボモータを動かしてみたいと思います。
実験回路はこちらになります。
サーボモータの駆動電源は5VのACアダプタから供給します。
コントロール信号はRaspberry Pi PicoのGPIO16から出力します。
サーボモータのコントロール角度は、分かりやすいようにADコンバータ(ADC0)に接続した固定抵抗の回転量に合わせてPWM Dutyをコントロールします。
一般的なサーボモータに与えるPWM周期は10~20msで、モータセンターのパルス幅は1.5ms前後です。そして±0.5msの範囲で左右に動きます。
今回の実験では以下のPWMを生成して制御しています。
制御するプログラムを次に示します。
from machine import Pin, PWM
#Picoハード設定
pwm = PWM(Pin(16)) #PWM設定
reg = machine.ADC(0) #ADC0設定
#PWM Duty設定
pwm_max = 0x3333 # 0x3333(13107)/0xFFFF(65535) = 20%
pwm_min = 0x1999 # 0x1999(6553)/0xFFFF(65535) = 10%
pwm_center = 0x2666 # 0x2666(9830)/0xFFFF(65535) = 15%
pwm_range = pwm_max - pwm_min
#PWM 周期 100Hz(10msec)
pwm.freq(100)
while(1):
output_per=float(reg.read_u16() / 0xFFFF)
pwm.duty_u16(int(pwm_range * output_per + pwm_min))
print("{:.0%}".format(output_per))
①モジュール読み込み
from machine import Pin, PWM
machineモジュールをインポートして、Raspberry Pi PicoのPWM機能を使えるようにします。
②ハードウェア設定
#Picoハード設定
pwm = PWM(Pin(16)) #PWM設定
reg = machine.ADC(0) #ADC0設定
GPIO16をPWM出力に設定してpwmという変数で制御できるように定義します。
ADC0をADコンバータに設定してregという変数でAD取得できるようにします。
③ハードウェア設定
#PWM Duty設定
pwm_max = 0x3333 # 0x3333(13107)/0xFFFF(65535) = 20%
pwm_min = 0x1999 # 0x1999(6553)/0xFFFF(65535) = 10%
pwm_center = 0x2666 # 0x2666(9830)/0xFFFF(65535) = 15%
pwm_range = pwm_max - pwm_min
#PWM 周期 100Hz(10msec)
pwm.freq(100)
PWMのDutyは0xFFFF(65535)を100%にして設定した値に比例したDuty比が出力されます。
pwm_max=Duty20%, pwm_min=Duty10%, pwm_center=Duty15%
になるように変数を定義します。
pwm_rangeはDutyが変化する範囲を計算しています。
PWM周期はpwm.freq(100)で100Hzに設定します。
時間は周波数の逆数なので1/100 = 0.01sec = 10msecとなり一般的なサーボモータが制御できる10msec周期に設定できます。
④PWM制御
while(1):
output_per=float(reg.read_u16() / 0xFFFF)
pwm.duty_u16(int(pwm_range * output_per + pwm_min))
print("{:.0%}".format(output_per))
①〜③で使用する準備ができたらwhile(1)の中が制御の本体です。
reg.read_u16()はADC0に接続されたボリューム抵抗の電圧レベルを取得します。
これをボリューム抵抗の可変電圧の何%にあたるか計算します。
この値がoutput_perにパーセントで出力されます。
pwm_range * output_perでボリューム抵抗の回転量をPWM出力のDuty10%から20%に割り付けます。その設定値がGPIO16に設定されます。
最後にボリューム回転量の何%でモータの回転max/minを制御しているかわかるように、print関数で表示しています。
{:.0%}は%表示の小数部分の精度を設定しています。今回は”0″にしているので整数で表示されます。
最後にPWM信号を使ったラジコンサーボモータの実演です。
PWMでの制御は、モータの駆動の他に、平均化して一定電圧から任意の電圧レベルを生成したりできます。
ON/OFFだけで色々な制御ができるので様々な機器のコントロールに使用されています。
PWMを使いこなせれば今後の開発に役に立ちます。
最後までお読みいただき、ありがとうございました。