[Raspberry Pi] ラズパイでステッピングモータ制御

#はじめに
ロックダウン中に屋内でできる新しい趣味でも見つけようかなあと思ってラズパイ買ってみました．モータ制御できたら後々実験で役立つかなあと思いステッパーモータも一緒に買ったので，制御してみます．定速度で回転させるだけでも面白いですが，任意の運動をできるようにすることを目標としました．電気電子回路の知識ほぼゼロからのスタートでしたが，ネットに色々情報が落ちていて簡単にできました．本当にありがたいです．いろいろな方法で制御できるようですが，私が一番簡単だと思った方法を紹介したいと思います．

#ハードウェア
以下に使用したハードウェアをリストしておきます．

* Raspberry Pi 4B 2GB
* NEMA17ステッパーモータ（[Amazon](https://www.amazon.co.jp/s?k=nema17&__mk_ja_JP=%E3%82%AB%E3%82%BF%E3%82%AB%E3%83%8A&ref=nb_sb_noss_2)で2000）
* L298N デュアルHブリッジ DCステッパーモータドライバ
    - 激安のモータドライバ．Arduino+ステッパーモータ/DCモータの制御でよく使われているみたい．[Amazon](https://www.amazon.co.jp/s?k=l298n&__mk_ja_JP=%E3%82%AB%E3%82%BF%E3%82%AB%E3%83%8A&ref=nb_sb_noss_1)見ると全然レビューついてなくて日本だとあまり人気ないみたい(?)
* 12V電源

#定速度回転
まずは基本の定速度回転です．GitHubにある[RpiMotorLib, A Raspberry pi python motor library](https://github.com/gavinlyonsrepo/RpiMotorLib)を使用します．ステッパーモータだけでなくDCモータ，サーボモータの制御もできるみたいです．ターミナルから`sudo pip install rpimotorlib`でインストールできます．
ライブラリをインストールしたら，[とても丁寧なチュートリアル](https://github.com/gavinlyonsrepo/RpiMotorLib/blob/master/Documentation/Nema11L298N.md)のとおりに配線して，コードをコピペするだけでモーターが回ります．僕は以下のように配線しました（手描きの雑な図ですみません）．
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/119868/05db6eba-30f4-32fa-1ee1-2192438f6dc4.png" width=60%>
画像の解像度が悪くて見えにくいですが，以下の表のように配線しています．

| ラズパイGPIO | ドライバ | モータ |
|:-:|:-:|:-:|
| 17 | IN1 | -  |
| 18 | IN2 | -  |
| 27 | IN3 | -  |
| 22 | IN4 | -  |
|  - | OUT1 | A- |
|  - | OUT2 | B- |
|  - | OUT3 | A+ |
|  - | OUT4 | B+ |

メインの関数は `motor_run(GpioPins, wait, steps, ccwise, verbose, steptype, initdelay)` です．各引数の説明は以下の通り．

* GpioPins: 使用するラズパイGPIOピン．左からドライバのIN1, IN2, IN3, IN4に接続するピン．
* wait: パルス間の時間．ハーフステップモードで回転速度f [Hz]で回転させたい場合，NEMA17はステップ角1.8°なので $wait = (1/2)\times(1/f)\times(1.8/360)$．
* steps: ステップ制御シグナルの数．どのステップモードでも50ステップで1回転，すなわち1ステップで7.2°動く（理由はチュートリアル参照）．
* ccwise: 反時計回り．`'True'`で反時計回り，`'False'`で時計回り．デフォルトは`'False'`．
* verbose: `'True'`にするとステータスなどが表示されるようになる．デフォルトは`'False'`．
* steptype: ステップモード，`'full'`，`'half'`，`'wave'`．それぞれの長所・短所はチュートリアル参照．
* initdelay: GPIOピンを初期化してからモータが動くまでの時間．

たとえばhalf-stepモードで回転速度0.5Hz，反時計回りに5回転させたい場合，以下のようなコードとなります．とてもシンプルです．

```Python:
import time 
import RPi.GPIO as GPIO
from RpiMotorLib import RpiMotorLib  
    
GpioPins = [17, 18, 27, 22]  # GPIOピン．左からIN1, IN2, IN3, IN4に接続するピン．

f = 0.5  # 回転速度 [Hz]
Rev = 5  # 5回転させる
s_angle = 1.8  # ステップ角 [deg]
wait = (1/f)*(s_angle/360)/2  # パルス間の時間．Half-stepモードのため2で割る

# モータに名前を振り，モータのタイプを指定（Nemaか28BYJ）．
mymotortest = RpiMotorLib.BYJMotor('MyMotorOne', 'Nema') 

# メイン．引数は左からGPIOピン，wait，ステップ数，逆回転，verbose，ステップモード，最初のディレイ [ms]
mymotortest.motor_run(GpioPins, wait, Rev*50, True, False, 'half', 0.05)

# 最後にピンをクリーンアップする
GPIO.cleanup()
```

![stepper_test.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/119868/6035364d-2603-49cc-bd7e-693c8471fa2a.gif)

楽しい！

#任意の運動をさせたい
回すことができたので，あとはコード次第でどんな運動でもできそうです．モータに任意の運動をさせたいと思います．例として正弦波のピッチ運動をさせてみます．
1ステップで7.2°回転するので，振幅$\theta_0$はその倍数がいいでしょう．今回は72°にします．周期$T$は2秒にしました．角速度は$\omega = \dot{\theta}$（この例では$\cos$）ですが，`wait`は正の値しかとれないため，絶対値をとって$wait = (1/2)\times(1/|\omega|)\times(1.8/360)$を渡します．そして$\omega < 0$のときに`ccwise`に`'True'`を渡せばいいです．$\theta$，$\omega$，$|\omega|$，$wait$は下図のようになります．

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/119868/acbb1769-08c5-3be1-21ed-b0c225043d9a.png" width=60%>

コードは以下の通り．

```Python:
GpioPins = [17, 18, 27, 22]

T = 2  # Period [s]
N_period = 2  # No. of periods
amp = 72
unit_angle = 7.2
s_angle = 1.8  # [deg]
N = int(4*amp/unit_angle)

theta = amp*sin(np.linspace(0, 2*pi, N))  # Angle time history [deg]
dt = T/len(theta)  # time-step [s]
omega = np.gradient(theta, dt)/360  # [Hz]
omega_pos = abs(omega)
pm = omega < 0  # boolean for CW/CCW 
wait = (1/omega_pos)*(s_angle/360)/2

mymotortest = RpiMotorLib.BYJMotor, 'MyMotorOne', 'Nema')
time.sleep(0.002)

for _ in np.arange(N_period):
    for k in np.arange(N):
        mymotortest.motor_run(GpioPins, wait[k], 1, pm[k], False, "half", 0)

GPIO.cleanup()

```

![Sinu_Pitch.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/119868/a7a15824-41b6-d186-24da-e01816b1c150.gif)



できた！

#他に参考にしたサイト

* [TUTORIAL - How to control a bipoloar 4 wire stepper motor](https://www.raspberrypi.org/forums/viewtopic.php?t=55580)
* [Raspberry PI L298N Dual H Bridge DC Motor](https://www.instructables.com/id/Raspberry-PI-L298N-Dual-H-Bridge-DC-Motor/)
* [Controlling Stepper Motors using Python with a Raspberry Pi](https://medium.com/@Keithweaver_/controlling-stepper-motors-using-python-with-a-raspberry-pi-b3fbd482f886)