Donkey Car をS.BUS対応ラジコン受信機経由で操作する

#はじめに

Donkey Car を学習走行させる方法はブラウザ経由またはゲームコントローラーから操縦する方法が用意されています。
しかし、普通のラジコンの操作に慣れている人の場合、これらの方法は操縦しにくいので、プロポで操作したくなります。
受信機からの一般的な信号であるPWM信号を読み取る方法もありますが、ここではFutaba受信機の**S.BUS信号を読み取る方法**を紹介します。

PWM信号の読み取りはチャンネル毎に配線が必要ですが、S.BUSは配線が一組で済むというメリットがあります。（一方、S.BUS対応受信機しか使えないというデメリットがあります。）

#方針

S.BUSは信号レベルが反転した100kbpsのUARTです。反転回路さえあれば、シリアル入力から読み取れます。

[vJoySerialFeeder](https://github.com/Cleric-K/vJoySerialFeeder)を使うと、S.BUS信号を仮想ジョイスティックデバイスとして見えるようにすることができます。今回はこれを使用します。

#ハードウェア

UARTはRaspberry PiのUARTを利用しようと思いましたが、なぜか失敗したので、USBシリアル変換モジュールを使います。

私は入手性から [ＦＴ２３４Ｘ　超小型ＵＳＢシリアル変換モジュール](http://akizukidenshi.com/catalog/g/gM-08461/) を使いました。

![M-08461.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/104360/08bd5698-ce79-5035-7228-e1922ca0b7d6.jpeg)


反転回路はトランジスタを用いた回路を組んでみたのですが、**実はFT234Xには反転回路が搭載されている**ので、これを使います。Windows上でFT_PROGというアプリを使って、反転回路を有効にします。
## 反転回路の有効化

この作業だけ Windows PC が必要です。

[FTDIのサイト](https://www.ftdichip.com/Support/Utilities.htm#FT_PROG) から FT_PROG をダウンロードします。

![FT_PROG_DL_LINK.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/104360/2f0298dc-addc-c53e-c331-70c8c15c2e7e.png)

インストールはNextボタンなどを数クリックするだけです。

Windows PC に FT234X を接続し、FT_PROGを起動します。

![2020-01-12 (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/104360/3054a371-cf88-c56d-53ee-943bc66c3ca9.png)

```DEVICE -> Scan and Parse``` をすると Device Tree に一覧が表示されます。

このツリーから ```Invert RS232 Signals``` を選択し、右ペインの ```Invert RXD``` のチェックボックスをONにします。

![ftdi_03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/104360/63ac27d6-f81a-bafb-eaab-6f91e2f23382.png)

```DEVICE -> Program``` で設定を書き込みます。

![2020-01-12 (3).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/104360/b9ab46e5-64a1-54c9-a93c-3e01d014d543.png)

下記ダイアログで ```Program``` クリック

![2020-01-12 (4).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/104360/ddaf06c2-95e7-5aa7-7655-036d8cebdf35.png)

以上で反転回路の有効化が完了です。

##配線

外付けの反転回路が不要になったので、配線は簡単です。

| 受信機|色|FT234X|
|:--------|:------|:-----|
| +5V | 赤| +5V |
| GND | 黒 | GND |
|-|-|TxD|
|Signal|白|RxD|

![line.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/104360/d8ca397e-1f0c-55ac-cd46-39aa8c797589.jpeg)

受信機側のコネクタは、ラジコンショップなどで入手できるサーボ延長ケーブルを流用するといいでしょう。

電子工作的な作業はこれだけです。

受信機との接続のしかたは、受信機依存です。典型的な受信機では、S.BUSを有効にすると、一番最後のチャンネルがS.BUS出力になるようです。S.BUS専用コネクタが存在する受信機もあるようです。

USBケーブルは、Type A側（Raspberry Pi側）がL字になっているものを使うといいでしょう。（ストレートコネクタだと、前に出すぎてカメラに映りこんでしまいます。）

#vJoySerialFeederのインストール

vJoySerialFeederは**C#で書かれたGUIアプリ**です。そのため、先だって Mono環境（.NET Framework互換環境）と、PIXEL環境（RaspbianのDesktop環境）のインストールが必要です。

##Mono環境のインストール
[Monoのダウンロードページ](https://www.mono-project.com/download/stable/#download-lin-raspbian) を見て ```mono-complete``` パッケージをインストールしてください。

一応、簡単に解説すると、パッケージリポジトリの追加をして、apt install するだけです。

パッケージリポジトリの追加
Raspbian buster の場合は

```
sudo apt install apt-transport-https dirmngr gnupg ca-certificates
sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 3FA7E0328081BFF6A14DA29AA6A19B38D3D831EF
echo "deb https://download.mono-project.com/repo/debian stable-raspbianbuster main" | sudo tee /etc/apt/sources.list.d/mono-official-stable.list
sudo apt update
```

Raspbian stretch の場合は

```
sudo apt install apt-transport-https dirmngr gnupg ca-certificates
sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 3FA7E0328081BFF6A14DA29AA6A19B38D3D831EF
echo "deb https://download.mono-project.com/repo/debian stable-raspbianstretch main" | sudo tee /etc/apt/sources.list.d/mono-official-stable.list
sudo apt update
```

で、その後、

```
sudo apt install mono-complete
```
です。
（パッケージてんこ盛りで10分ぐらいかかります・・・）

##PIXEL環境のインストール

PIXEL環境をapt install します。

```
sudo apt install raspberrypi-ui-mods
```

（これもパッケージてんこ盛りで10分ぐらいかかります・・・）

```sudo raspi-config``` で、

- 起動時にCLIでなくGUIが立ち上がるようにする
- VNC Server の有効化

も行いましょう。
メニューの場所は、それぞれ

- 3 Boot Options -> B1 Desktop / CLI -> B4 Desktop Autologin
- 5 Interfacing Options -> P3 VNC

 です。

（またまたVNC関連パッケージのインストールで10分ぐらいかかります・・・）

VNCサーバーはラズパイに直接ディスプレイ・キーボードを接続して操作するなら不要ですが、まあ、リモートでつないだ方が楽ですよね？
VNCクライアントは何でもいいのですが、自分は Real VNC を使用しています。

##vJoySerialFeeder本体のインストール

https://github.com/Cleric-K/vJoySerialFeeder/releases から、vJoySerialFeeder_Linux_v1.5.zip をダウンロードして展開するだけです。

展開先は適当にお願いします。

##デバイスパーミッションの設定

シリアルポートを```pi```ユーザーから使えるようにするため、次のコマンドを実行します。

```
sudo gpasswd -a pi dialout
```

```/dev/uinput``` を```pi```ユーザーから使えるようにするため、```uinput``` グループを用意して適切に設定します。

```uinput```グループの作成と ```pi``` ユーザーのグループへの追加

```
sudo groupadd -f uinput
sudo gpasswd -a pi uinput
```

```/dev/uinput```を```uinput``` グループから使えるようにするには、```/etc/udev/rules.d/99-input.rules```ファイルを作って次の内容を書き込みます。

```
SUBSYSTEM=="misc", KERNEL=="uinput", MODE="0660", GROUP="uinput"
```

これでいいはずなんですが、手元のRaspbian buster 環境ではうまくいきませんでした。

いろいろ試行錯誤してみたところ、```/etc/modules``` の最後に ```uinput``` を追記したらうまくいきました。

ここで再起動しましょう。

（パーミッションの設定が面倒とかうまくいかない場合は、後で出てくる ```mono vJoySerialFeeder.exe``` の起動を ```sudo``` でやっちゃいましょう。）


##vJoySerialFeederの設定

vJoySerialFeeder を

```
mono vJoySerialFeeder.exe
```

で起動します。あ、VNCで接続して、Terminalを開いたうえで起動してください。
（sshセッションから起動するとディスプレイが見つからない旨のエラーが出ます）

```Cannot open assembly 'vJoySerialFeeder.exe': No such file or directory.```というエラーが出る場合は、vJoySerialFeederの展開先ディレクトリに```cd```するか、引数にパスを含めて起動しましょう。


起動したら、```Protocol``` で SBUS を選択、 ```Port``` で ttyUSB0 を選択し、```Connect``` をクリックします。

![vJoy_01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/104360/05307a3f-3ef2-d259-325b-cc121a5b2675.png)

```libevdev_uinput_create_from_device() failed``` っていうエラーが出る場合は、```/dev/uinput``` のパーミッションの設定がミスってます。


```Port``` に ttyUSB0 が無い場合は・・・、接続してないですね。っていうか接続してくださいという話をし忘れてますね。
先に作ったUSBシリアル変換ケーブルをラズパイに差して、反対側はラジコン受信機に接続してください。
```Refresh``` ボタンをクリックすると、ttyUSB0が選べるようになると思います。

Connectできたら、```Add Axis``` を2回クリックしてください。

![vJoy_02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/104360/2cf7b978-99ba-2210-a767-3b556d549fb2.png)

正しく接続できていれば、 Input が 1500前後、Output が 50%前後の表示になります。
送信機のステアリングを操作すると、Input Output がそれぞれ変化します。
反応が無い場合は、次の項目を確認してみてください。

- 配線は間違っていないか
- USBシリアルの反転設定がされているか
- 送信機の電源は入っているか
- 送信機と受信機のバインド（ペアリング）がされているか
- 受信機をSBUSモードにしているか
- 3ピンコネクタがSBUS出力のところに接続されているか



次に、各チャンネルの詳細設定をしてまいります。

2) の Channel を 2 に、Axis を Ry にしてください。
![vJoy_03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/104360/21c8e211-fc37-f1fa-a293-2f7cfe61ca29.png)

なんでYじゃなくてRyなのかというと、他のジョイスティックでの設定と同じにするためです。

それぞれのチャンネルのキャリブレーションをします。

1) の ```Setup```をクリックします。

![vJoy_04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/104360/89dd25e2-b00e-3f35-4919-ec72bd1a3cba.png)

```Calibrate``` をクリックします。

ここでCenter(ニュートラル)が決まります。

次に、```Next```をクリックします。

左右のエンドポイントを決めるところです。
一旦、グラフが平たんになります。
送信機のステアリングを左右目いっぱいそれぞれ操作してください。
グラフがまた右上がりになると思います。

その状態で ```Done``` をクリックしてキャリブレーション完了です。

Ch2(スロットル)も同様にキャリブレーションしましょう。

お好みで、```Deadband``` や ```Expo``` も設定してみましょう。

スロットルの```Deadband```を 1～2%程度にするのがオススメです。学習走行時に、余計な記録がされないようになります。

```Expo``` は**エクスポーネンシャルカーブ**の話なんですが、ここでの説明は割愛します。（各自おググり下さいませ）

あと、スロットルは ```Invert``` のチェックボックスをOnにしてください。
```Invert```は、操作方向を逆転するものです。正負の方向の解釈がプロポメーカーによってイロイロなのですが、ここではS.BUS互換、つまりフタバ互換が前提なので、スロットルだけ逆転するのが正解です。（万が一前進後進が逆とか、左右が逆などの現象が出たら、反転してみてください。）

設定が完了したら、```Profile``` のところに適当な名前を入力し、```Save``` をクリックしましょう。次回のvJoySerialFeeder起動時は、最後に使用したプロファイルが読み込まれます。

#Donkey Car から vJoySerialFeeder を使う設定

項目を立てましたが、vJoySerialFeederの設定をジョイスティック互換になるようにしたので、特に ```myconfig.py``` などの編集は不要です。おそらくどのジョイスティック設定でも使えると思いますが、私は ```CONTROLLER_TYPE='F710' ``` としています。

他のジョイスティックが既に存在すると ```/dev/input/js0``` がすでに出来上がってて、vJoySerialFeeder が ```/dev/input/js1``` などになってしまうので、 ジョイスティックを外した状態で vJoySerialFeeder を起動しましょう。

念のため、js1が存在しないことを確認してください。

```
$ ls /dev/input
event0  js0  mice
```

更に念のため、送信機を操作すると js0 にデータが出力されることを確認しましょう。

```
$ hexdump /dev/input/js0
0000000 93da 0032 0000 0081 93da 0032 0000 0181
0000010 93da 0032 0000 0281 93da 0032 0000 0381
0000020 93da 0032 0000 0481 93da 0032 0000 0581
0000030 93da 0032 0000 0681 93da 0032 0000 0781
...
```


あとは、いつものように

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

で学習走行させてみましょう！

次回走行時は、```mono vJoySerialFeeder.exe``` で起動することと、```Connect```ボタンをクリックすることが必要です。

#おわりに
USBシリアル変換チップと、vJoySerialFeeder を用いて、S.BUS受信機を仮想ジョイスティックに見せる方法を紹介しました。
vJoySerialFeeder はmonoとGUI環境を必要とし、大げさなことと、OS再起動後にアプリの起動とConnectのクリックが必要なので、もうちょっと軽いドライバがあるといいなと思います。~~（だれか作ってー！）~~

追記：vJoySerialFeeder を使わない方法について書きました → [Donkey Car をS.BUS対応ラジコン受信機経由で操作する ～controller.py 改造版～](https://qiita.com/rukihena/items/6a542cb33d58675d91fc)