ラズパイからESC&ブラシレスモータを動かす

# 前置き①：ブラシレスモータの制御

ドローンなどで使われるブラシレスモータは、ミニ四駆などで使っていたブラシ付きモータと違って、単純に電池をつなぐだけでは動作しません。

ブラシレスモータからは3本の配線(U,V,Wとします※)が出ていて、この3本に流す電流を

```
1.U→W
2.U→V
3.W→V
4.W→U
5.V→U
6.V→W
以下ループ
```
のように切り替えて行くことで動作します。

>※一般にUVWとされているようですが、その正確な由来はわからず。。 アルファベット順になっている模様です。

ただ、この6パターンを単純にスイッチしていくだけでも動作はするのですが、
カクカクとした動きになってしまうようで、
滑らかな回転を指せるためには正弦波的に3線に流す電流を緩やかに変動させていく必要があります。

[参考:Renesas Electronics BLDCモータの制御](https://www.renesas.com/jp/ja/support/technical-resources/engineer-school/brushless-dc-motor-02-inverter-pmw.html)


更にこの切替は、モータの回転位置(角度)に応じて行う必要があります。
高価/大型なブラシレスモータには回転位置を測るためのセンサーがついたものもありますが、
センサレスという、センサーは用いず、モータの回転状態に応じて発生する逆起電力から推定して制御する方式もあります。


いずれにしても、かなり面倒な電流制御が必要になります。。。。

ドローン作りなどで、これを自前で実装するのは大変なので、ESC(Electronic Speed Controller)という専用のデバイスを利用します。


# 前置き②:ESC(Electronic Speed Controller)
ESCは、入力としてのPWM制御信号を元に、出力として**前置き①**で書いたような信号を出し、ブラシレスモータを操作するためのデバイスです。

なお、モータ駆動電力として入力信号とは別に、電源も必要です。

ちょっとややこしいのは、
入力の制御信号もPWM
出力もPWMで表現した正弦波
とPWMが二箇所に出てきます。
(ESCを使う場合には前者の入力側PWMだけ意識すれば良いです。)


# 構成
>Quadceptというのを使って描いてみました。まだまだ不慣れですが、使い込めば便利そう。
>QuadceptにはMac版がなかったので、PallelsというアプリでWindows環境を作り、その上で利用しています。

<img width="864" alt="20200406_00_01_19.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/124098/906b4095-37e0-38d3-4d85-e7f56b9d6999.png">

ESCのPWMには5Vの入力を入れたいのですが、ラズパイのGPIOは3.3Vです。
とりあえず動作させたいだけなのでこのままで行きます。

ブラシレスモータとESC、ラズパイ(ゼロ)とLipoバッテリーで構成します。
用意した部品によっては配線に一部はんだ付けが必要だったりするので頑張る必要があります。


# Raspberry Pi側準備

pigpioを利用してPWM制御します。


### pigpioのインストールとデーモン起動

インストール

```:rspi
sudo apt install pigpio
```

デーモン起動
- pigpioを使ってプログラムからGPIOにアクセスするためにデーモン起動します。

```:rspi
sudo pigpiod
```



## pigpioのhardware_PWMとset_servo_pulsewidthについて
pigpioにはPWM出力操作用に**hardware_PWM**や**set_PWM_dutycycle**、**set_servo_pulsewidth**といった関数が用意されています。

[hardware_PWM](http://abyz.me.uk/rpi/pigpio/python.html#hardware_PWM)
[set_PWM_dutycycle](http://abyz.me.uk/rpi/pigpio/python.html#set_servo_pulsewidth)
[set_servo_pulsewidth](http://abyz.me.uk/rpi/pigpio/python.html#set_servo_pulsewidth)

いずれも同じようなことができるはずなのですが、
hardware_PWMやset_PWM_dutycycleでは上手くいかず(たまに動作するが不安定)、
set_servo_pulsewidthを使ってなんとか動作させられました。

### 補足：hardware_PWMやset_PWM_dutycycleで動かない件
いずれを使っても、Lチカしてみている感じではどちらも同じ様に光るし、パラメータを変更して光量が変わることも確認できています。

しかし、ESCに繋ぐと何故か"ビー","ビー","ビー"と2秒間隔ぐらいで鳴り続ける、エラー状況になってしまいます。
このエラー音を
[ESC(Electronic Speed Controller) Skywalkerシリーズの説明書翻訳](https://qiita.com/sakaeda11/items/5fc27b6ae7cd77cba1ea#トラブルシューティング)
の対応表でみると**「スロットルの信号が不規則」**とのこと。

- ラズパイのPWMの信号が不安定
- ESCが求める周波数には決まりがあって上手く指定できていなかった。(set_servo_pulsewidthはデフォルトでその周波数になっていた)
といった理由かな、と思うのですが、前者はググってもそういった情報は特に見つからず、こうしゃもESCやブラシレスモーターの説明書を見ても詳細不明だったので、とりあえずset_servo_pulsewidthを使って進めます。

### 操作の流れ

ここではGNDとGPIOの18を使います。

起動する前に、制御信号plusewidthの**最小値**(650~最大値の8割以下？、700あたりを推奨)と**最大値**(1600~2400：2000あたりを推奨)を決めておきます。

最大値：~2484

1. plusewidthを**最大値**にしておく
2. ESCとバッテリー接続する
3. ビービーと鳴るまで2秒待つ
4. "3"のあとすぐにplusewidthを**最小値**にする(時間が空きすぎるとプログラムモードになってしまうので注意)
5. 「」と鳴るのを待つ。
6. plusewidthを最小値〜最大値の範囲の適当な値にする →　モーター始動！

### plusewidthの最小値と最大値について
set_servo_pulsewidthの入力のplusewidth自体は0 or 500~2500を設定できるものです。
また、ESCの説明書を見ると、「起動時はスロットルを一番上に」、と書いてあるので最大値を入力すればよさそうなのですが、
なぜかplusewidthの最大値の2500を入力するとエラーになります。

試したところ、最大値は**1555~2484**の範囲で、
最大値を2000で始めたときに最小値は **625~1750**以上(1800だと駄目だった)の範囲なら動作するようです。
※あくまで私の環境での値なので、環境によってこの値は変化すると思われます。

# ソースコード

```:MoveBrushlessMotor.py
#!/usr/bin/python3
# -*- coding: utf-8 -*-

import time
import pigpio
import sys

PIN = 18
minPulse = 700
maxPulse = 2000

print("初期化します。バッテリーを外してください。")

pi = pigpio.pi()
pi.set_servo_pulsewidth(PIN, maxPulse)

print("バッテリーを接続して、ビービーと鳴ったらEnterを押してください。")
inp = input()
if inp == '':
    pi.set_servo_pulsewidth(PIN, minPulse)
    time.sleep(3)

print("\"stop\"")
print("\"u\" to up speed")
print("\"d\" to down speed")
speed = 1000
print("speed = %d" % speed)
while True:    
    pi.set_servo_pulsewidth(PIN, speed)
    inp = input()
    if inp == "d":
        speed -= 100
        print("speed = %d" % speed)
    elif inp == "u":    
        speed += 100    # incrementing the speed like hell
        print("speed = %d" % speed)
    elif inp == "stop":
        speed = 0
        pi.set_servo_pulsewidth(PIN, 0)
        break
    else:
        print("stop or u or d!")

# 終了処理
pi.stop()
```

# 動作デモ(youtube動画)

[![ブラシレスモータの動作デモ](http://img.youtube.com/vi/9j4toZpgqJ0/maxresdefault.jpg)](https://youtu.be/9j4toZpgqJ0)
URL: [https://youtu.be/9j4toZpgqJ0](https://youtu.be/9j4toZpgqJ0)


# 参考： gpio pin配置

![GPIO.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/124098/500c12bd-3a86-c655-cb9e-1995cbe69271.png)
画像出展:[https://www.raspberrypi.org/documentation/usage/gpio/](https://www.raspberrypi.org/documentation/usage/gpio/)