Donkey Car から PCA9685 を取っ払って、ESC・サーボをラズパイに直結する

#はじめに

Donkey Car はESC・サーボの制御にPCA9685(16ch PWM出力ボード)を使用しています。
しかし、Raspberry Pi単体でもサーボ制御信号（PWM信号）は出力できるはずです。
PCA9685はいらない子なのではないか？
そう思って調べてみたところ、割と簡単に外せました。

#ラズパイのPWM出力

ラズパイからPWM出力する方法はいくつかあります。
pythonから利用しやすいものだと、**RPi.GPIO** や **pigpio** などがあります。
**RPi.GPIO** はソフトウェアでやっているのか、精度が悪いです。パルス幅が安定せず、サーボがプルプルしてします。（←聞いた話）
**pigpio** が本命でしょうか。

しかし、```manage.py```を読んでみると、[ServoBlaster](https://github.com/richardghirst/PiBits/tree/master/ServoBlaster) を利用している箇所があります。これを使えば、**Donkey Car 用の Parts を作らなくてもよさそう**です。

というわけで、**ServoBlaster** を使うことに決めました。

#ServoBlaster ビルド

```apt``` で拾ってこれないので、```git clone``` して ```make``` します。

```
cd ~
git clone https://github.com/richardghirst/PiBits.git
cd ~/PiBits/ServoBlaster/user/
make servod
```

（```make install```でサービス化できるようなのですが、どうもうまく動かないので推奨しません（後述））

#ServoBlaster 起動

```
sudo ~/PiBits/ServoBlaster/user/servod --idle-timeout=2000 --step-size=4
```

で起動します。

オプション```--idle-timeout=2000``` は、使ってないときにサーボへの出力を止めるオプションです。これにより、```manage.py``` を動かしてないときの消費電力が若干減ると思います。

オプション```--step-size=4``` は、STEERING_LEFT_PWMなどの1が何マイクロ秒相当かという設定です。このオプションにより、**PCA9685の場合とほぼ同じPWM設定値を流用することができます！** （ほぼ同じですが、若干違います（後述））

#manage.py 追記

manage.pyの、```elif cfg.DRIVE_TRAIN_TYPE == "SERVO_HBRIDGE_PWM":``` の**ブロックの後ろ**に、下記コードを追加します。

```python
    elif cfg.DRIVE_TRAIN_TYPE == "SERVO_ESC_ONBOARD":
        from donkeycar.parts.actuator import ServoBlaster, PWMSteering, PWMThrottle

        steering_controller = ServoBlaster(cfg.STEERING_CHANNEL)
        steering = PWMSteering(controller=steering_controller,
                                        left_pulse=cfg.STEERING_LEFT_PWM,
                                        right_pulse=cfg.STEERING_RIGHT_PWM)

        throttle_controller = ServoBlaster(cfg.THROTTLE_CHANNEL)
        throttle = PWMThrottle(controller=throttle_controller,
                                        max_pulse=cfg.THROTTLE_FORWARD_PWM,
                                        zero_pulse=cfg.THROTTLE_STOPPED_PWM,
                                        min_pulse=cfg.THROTTLE_REVERSE_PWM)

        V.add(steering, inputs=['angle'])
        V.add(throttle, inputs=['throttle'])
```

軽く説明すると、```DRIVE_TRAIN_TYPE``` に ```SERVO_ESC_ONBOARD``` というタイプを追加して、具体的にはこーしてねというのを書いてます。

#myconfig.py 修正
下記定数3つを修正します。

```python
DRIVE_TRAIN_TYPE = "SERVO_ESC_ONBOARD"
STEERING_CHANNEL = 11           #pin number on the Raspberry Pi
THROTTLE_CHANNEL = 13           #pin number on the Raspberry Pi
```

ここのピンNo.は、GPIOの番号ではなく、1~40の物理ピン番号であることに注意してください。
また、標準で使えるのは 7,11,12,13,15,16,18,22 だけです。
ここで11と13を選んだのは、I2C接続も挿さってる場合に挿しやすい場所だったからです。
この辺はお好みでどうぞ。


#配線

電源とGNDと信号線が繋がっていればオッケーなので簡単です。

|ラズパイ|ESC|サーボ|備考|
|---|---|---|---|
|11||白|GPIOでいうと17|
|13|白||GPIOでいうと27|
||赤|赤|ESCとサーボの赤同士をつなげる|
||黒|黒|ESCとサーボの黒同士をつなげる|

これを
![DSC00361.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/104360/d77e5247-8b63-ee7a-af10-560de2487f03.jpeg)
こうして
![servo_01.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/104360/9f04c4be-35cb-5d12-24d4-b79f1e7efc05.jpeg)
こうじゃ
![servo_02.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/104360/8c371de5-e779-3459-b705-c5ffa5d8db2b.jpeg)

ラズパイとサーボ・ESCのGNDが繋がってないような気がしますが、電源側で繋がってるので問題ありません。

ラズパイとサーボ・ESCの電源は繋がないのが正解です。（ESCが電源を供給して、サーボがそれを使っています。）

ラズパイは3.3V I/Oなので、サーボ・ESCからみるとI/O電圧が足りませんが、だいたい閾値がVcc/2のことが多いので、まあOKだと思います。
最近のBEC 6VのESCを積んだラジコンだと不安定になるかも？？？
ご心配ならI/O電圧を変換するナニカを挟んでくださいませ。

とりあえず、手元の Robocar Store で買ったDonkeyCar(のベース車両であるHSP 94186)では動いています。


#走行

あとはいつものように走行するだけです！

```
cd ~/mycar
python manage.py drive
```

（実は、「Webコントローラーから制御できてるな」という確認までしただけで、教師走行や自律走行は試しておりません。。。）

#細かい話：PWM設定値

PWM設定値の正確な値の話をすると、PCA9685 の場合の1は、パルス間隔を4096で割った値です（12bit PWMなので4096で割るんですヨ）。DonkeyCarではPWM周波数を60Hzにしているので、パルス間隔は1/60です。ということから計算すると

``` 1,000,000[μs]/60/4096 ≒ 4.069[μs] ```

となります。

一方、ServoBlaster の場合は設定値がマイクロ秒単位で設定できて、設定どおり4マイクロ秒です。

1.7%ぐらいの誤差となります。ニュートラルが370だったとすると、376ぐらいに変更する必要があります。

#おまけ：キャリブレーション

https://docs.robocarstore.com/guide/calibrate/ でやるキャリブレーションはPCA9685に接続されていることが前提なので、この記事の配線方法にすると使えません。

PWM出力を手動で行うことが、下記コマンドでできます。

```
echo P1-11=370 > /dev/servoblaster 
```

一応説明しておくと、P1は固定で、11がピン番号、370が出力値です。

#課題：servodのサービス化

ServoBlasterをmakeしたときのディレクトリで

```
sudo make install
```

をすると、実行ファイル```servod``` が ```/usr/local/sbin/``` にコピーされ、起動用の ```/etc/init.d/servoblaster``` が作成されます。

起動オプション ```--step-size=4``` は、```/etc/init.d/servoblaster``` の ```OPTS```に追記する形で

```
OPTS="--idle-timeout=2000 --step-size=4"
```

としておけばよさそうです。

しかし、どうもOS起動後に ```--step-size=4``` が効いてないような動作になります。
psコマンドで見ると引数は渡されているっぽいんですけれども。

```
sudo /etc/init.d/servoblaster restart
```
でサービスの再起動をしてみると、なぜか ```/dev/servoblaster``` が見えなくなってしまいます。
psコマンドで見るとプロセスは生きているっぽいんですけれども。

とりま、stop start で正常動作するようにはなります。

```
sudo /etc/init.d/servoblaster stop
sudo /etc/init.d/servoblaster start
```

OS起動後にコマンド打たなきゃいけないなら、サービス化しなくてもいいんじゃないかと思います。
サービス化してもちゃんと動くようにしたいんですけれどもね・・・


#おわりに

Donkey Car から PCA9685 を外す方法を紹介しました。

なんで設定が簡単にできるようになってないのか不思議です。しかし、ソースを読んでると、なんか**このぐらいの改造は設定のうち**というふいんきを感じます。

この記事を書いた後、 ```~/projects/donkeycar/donkeycar/parts/actuator.py``` の中に ```PiGPIO_PWM``` というクラスを見つけてしまいました。pigpio も似たような「設定」で使うことができそうです。

追記：pigpioを使う方法を書きました → https://qiita.com/rukihena/items/b3d16c2bf6e1a62b0c92