ラズベリーパイから秋月のユニポーラステッピングモータ(ST-42BYG020)を動かす

最近ラズベリーパイを始めました。
というのも、以前から自動でスマホを操作してくれるロボットを作りたいと考えていたからです。
また、最近流行りのラズパイが、お手軽に画像認識とアクチュエータ制御を同時にこなせるため、
この機会に触れてみようと思いました。
今回は最初のステップとして、ラズベリーパイからステッピングモータを動かしてみました。
どちらも初めての経験なので、いい機会になりました。

ステッピングモータの制御方法について

ステッピングモータには大きく分けて、ユニポーラとバイポーラという2つの方式があります。
今回使うのは秋月のユニポーラステッピングモータ(ST-42BYG020)となります。
このモータの端子は全部で6つあり、A,B,C,D,O,O'という名称がつけられております。
下図に示すように、それぞれの端子は2つのコイルと接続されており、
中間端子OもしくはO'に駆動電圧を印加した状態で、
A,B,C,D端子に決められた順番で電流を流すことでモーターが回転します。
f:id:mou-tsukareta:20190310214813j:plain


たとえば、電流を流す順番を
A→B→C→Dとすると正回転、D→C→B→Aとすると逆回転となります。
この駆動方式を1相方式と呼びます。
1端子に1度電流を与えた時の回転角度はデータシートに明記してあり、
このモータだと1.8[deg]です。


また、A→A+B→B→B+C→C→C+D→Dの順番(もしくは逆)で電流を流す方式を1-2相方式と呼びます。
この方式だとよりスムーズに動くらしいので、今回はこちらの方式を採用します。
下のタイミングチャートに示すように、こちらの方式のほうが、回転角度の分解能が細かく制御することができます。
f:id:mou-tsukareta:20190310003126j:plain
 

回路

今回実験した回路は以下のようになっています。
f:id:mou-tsukareta:20190310003517j:plain


速度や回転方向を制御するためにタクトスイッチを4つ配しています。
また、トランジスタには2SD1415を使っています。
このトランジスタについて、データシート上ではVceが9Vのときに、
Icを1A程度を流すためにはIbが0.3mA程度必要だと読み取れます。
ラズパイのポート電圧が3.3Vなので、机上計算では1.1kΩ必要ということになります。
今回は安全のために手持ちの抵抗1.5kΩ→1.1kΩ→3.3kΩと下げていくことで最適値を探りました。
結論としては、3.3kΩが最適となったので、この抵抗を採用します。


プログラム

関数rotate_spmに回転角度(マイナスで逆回転)を引数として与えることで、
決められた角度にモーターを回転できるようにしています。
rotate_spmは与えられた角度から電流を何回流せばいいかを計算し、
その回数分だけ関数enable_phaseをコールしています。
enable_phaseではタイミングに応じてA,B,C,D端子に電流を流すために、
実際にGPIOの電圧をHIGH/LOWしています。
動作速度はTPHS(1相あたりの時間)が長いほど遅く、短いほど速くなります。10[ms]~0.1[ms]まで確認しましたが、0.5[ms]が最速値だったのでこれを採用します(0.1[ms]ではモーターは回転しませんでした)。
また、タクトスイッチ毎の挙動は
90度、180度、、-90度、-180度回るように分けてみました。

import RPi.GPIO as GPIO
import time

# ポート番号の定義
SW1 = 25
SW2 = 8
SW3 = 7
SW4 = 12
SPM_A = 2
SPM_B = 4
SPM_C = 3
SPM_D = 5

# GPIOの初期化
GPIO.setmode(GPIO.BCM) # BCMモードに設定(GPIOのポート番号で指定する) BOARDモードだとピン番号(GND等含む)で指定する
GPIO.setup(SW1, GPIO.IN) # 指定ポートをinに設定
GPIO.setup(SW2, GPIO.IN) # 指定ポートをinに設定
GPIO.setup(SW3, GPIO.IN) # 指定ポートをinに設定
GPIO.setup(SW4, GPIO.IN) # 指定ポートをinに設定
GPIO.setup(SPM_A, GPIO.OUT) # 指定ポートをoutに設定
GPIO.setup(SPM_B, GPIO.OUT) # 指定ポートをoutに設定
GPIO.setup(SPM_C, GPIO.OUT) # 指定ポートをoutに設定
GPIO.setup(SPM_D, GPIO.OUT) # 指定ポートをoutに設定

# ステッピングモータ定数
TPHS = 0.0005 # 1相あたりの時間[s]
DPP = 0.9 # 1-2相励磁における1相あたりの回転角度[deg/phase]
NPHS = 8 # 1-2相励磁における相数

# 最後に通電したステッピングモータの相(1:A, 2:A+B, 3:B, 4:B+C, 5:C, 6:C+D, 7:D, 8:D+A)
phase = 1

# スイッチの状態をチェックする関数
def check_switch():
    ans = 0
    
    if (GPIO.input(SW1) == GPIO.HIGH):
        # チャタリング防止
        time.sleep(0.02)
        if (GPIO.input(SW1) == GPIO.HIGH):
            print("sw1")
            ans = 1
    elif (GPIO.input(SW2) == GPIO.HIGH):
        # チャタリング防止
        time.sleep(0.02)
        if (GPIO.input(SW2) == GPIO.HIGH):
            print("sw2")
            ans = 2
    elif (GPIO.input(SW3) == GPIO.HIGH):
        # チャタリング防止
        time.sleep(0.02)
        if (GPIO.input(SW3) == GPIO.HIGH):
            print("sw3")
            ans = 3
    elif (GPIO.input(SW4) == GPIO.HIGH):
        # チャタリング防止
        time.sleep(0.02)
        if (GPIO.input(SW4) == GPIO.HIGH):
            print("sw4")
            ans = 4
            
    # スイッチの状態をリターン
    return ans

# 指定相を通電する関数(1-2相励磁)
def enable_phase(phase):
    # A相の通電
    if ((phase == 1) or (phase == 2) or (phase == 8)):
        GPIO.output(SPM_A, GPIO.HIGH)
    else:
        GPIO.output(SPM_A, GPIO.LOW)

    # B相の通電
    if ((phase == 2) or (phase == 3) or (phase == 4)):
        GPIO.output(SPM_B, GPIO.HIGH)
    else:
        GPIO.output(SPM_B, GPIO.LOW)

    # C相の通電
    if ((phase == 4) or (phase == 5) or (phase == 6)):
        GPIO.output(SPM_C, GPIO.HIGH)
    else:
        GPIO.output(SPM_C, GPIO.LOW)

    # D相の通電
    if ((phase == 6) or (phase == 7) or (phase == 8)):
        GPIO.output(SPM_D, GPIO.HIGH)
    else:
        GPIO.output(SPM_D, GPIO.LOW)

    # 通電時間待機
    time.sleep(TPHS)

# 通電終了関数
def disable_phase():
    GPIO.output(SPM_A, GPIO.LOW)
    GPIO.output(SPM_B, GPIO.LOW)
    GPIO.output(SPM_C, GPIO.LOW)
    GPIO.output(SPM_D, GPIO.LOW)

    # 通電時間待機
    time.sleep(TPHS)

# 指定した角度だけステッピングモータを正回転(もしくは逆回転)させる関数(1-2相励磁)
def rotate_spm(deg):
    # グローバル変数の指定
    global phase
    
    # 相数算出
    phase_num = round(abs(deg/DPP))

    # 算出した相数分だけ現在相から進める
    for i in range(phase_num):
        # 相を1つだけ進める(もしくは戻す)
        if (deg > 0.0):
            phase += 1
        else:
            phase -= 1
            
        # 余りを計算して相のオーバーを吸収する
        phase %= NPHS

        # 現在相を通電する
        enable_phase(phase)
        
# メイン処理
try:
    # フラグの宣言
    flg = 0
    
    # 繰り返しスイッチの状態を確認してフラグをセットする
    while True:
        # スイッチの状態をチェック
        flg = check_switch()

        # フラグに応じてモータを制御する
        if (flg == 1):
            # 90度正転
            rotate_spm(90.0)
            
        elif (flg == 2):
            # 180度正転
            rotate_spm(180.0)
            
        elif (flg == 3):
            # 90度逆転
            rotate_spm(-90.0)
            
        elif (flg == 4):
            # 180度逆転
            rotate_spm(-180.0)
            
        # 0.1秒待機
        time.sleep(0.1)
        
except KeyboardInterrupt:
    GPIO.cleanup()

実際の動作試験

実際に動作させたときの動画です。
180[deg]→90[deg]→-180[deg]→-90[deg]の順番で各4回ずつ回しています。
十分スムーズに回っているように見えますね。
vimeo.com


最後に

ステッピングモータはドライバが必要で制御が難しいものだという認識でしたが、
実際に触ってみると、ドライバを使わなくとも極めて単純に制御が可能であることがわかりました。
今後の開発に活かせる実験となりました。