障害者e-sports用のコントローラを作成してみた

# 背景
　近年e-sportsも盛んになってきたようですが、障害者向けのe-spors(ユニバーサルe-spots)も注目されています。その流れで、障害者向けの専用コントローラもいくつか発売されているようです。

　西九州大学リハビリテーション学科の植田研究室では、ユニバーサルe-sportsを研究テーマで扱っていて、その題材の一つとしてSP5のグランツーリスモ（カーレースゲーム）を利用しています。操作のリアリティ性を高めるため、ロジクールのハンドルコントローラ（Logicool G ハンコン G923）を使っていますが、足操作が難しい人の場合、手を使ってアクセルとブレーキを操作する必要があります。
 
![ハンドルコントローラ.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33661/19a1e3b9-6255-755b-f80b-a8ace450eacc.png)


 もちろん標準のコントローラを使えば手だけの操作は可能です。ソニー製のAccess コントローラー（CFI-ZAC1J）を使えば、自由にボタンの割り付けが可能になり便利です。

![ACssec.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33661/f11b7d5c-f753-e52d-80aa-6fbe742b34dd.png)

 それでも、気分を高め、ゲームに没入したい時にはやっぱりハンドルコントローラが欲しくなりますよね？
 
　残念ながら、前述ハンドルコントローラはアクセルとブレーキは足での操作になっており、手でのみの操作にする場合、ハンドルのスイッチに割り当てるしかなく、ON/OFFになり細かなゲーム内での操作が難しくなります。
　一方、足に障害のある人が車の運転をする場合、手の操作のみで運転できるように、手でアクセルとブレーキを操作する様々な補助装置が販売されているようです。

　今回のコントローラは障害者が普段使い慣れている　車の補助装置と同じような操作で、カーレースのゲームをできたらゲームの操作性や没入感が向上するのではという発想から制作してみました。

# 改造案
　PS5の場合専用のIF規格があるらしく、ハンドルコントローラとは別にアナログのジョイスティックを追加してもうまく認識できませんでした（格闘ゲーム用のジョイスティックコントローラはUniversal Fighting Board通称UFBというものを使うらしい）。
　そこで、ハンドルコントローラに付属のアクセル、ブレーキ、（クラッチ）コントローラを置き換えて、同じ信号を手の操作で出力できるように改造することにしました。

### ハンドルコントローラの解析結果
　ハンドルコントローラに付属のアクセル、ブレーキ、（クラッチ）コントローラを分解すると、本体とはD-Sub9ピンで接続し、本体からの4.7V（実測値）を物理的にアクセルのペダルに連動したボリュームで分圧し、本体に返しているようです。何も踏まない場合は4.7Vがそのまま出力され、ベタ部無状態で0Vになっていました。また、コントローラの接続を認識するために、4.7Vをそのまま返しているようです（9pin）。

![アクセル裏.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33661/90013b3a-2d40-aff7-88e5-2057d30016a0.png)

ピン配線（D-sub　９ピン）
P1 GND
P2 アクセル
P3 ブレーキ
P4 クラッチ
P6 P9とショート（コントローラ検出）
P9 4.7V
(P6とP9は逆かもしれない)

### 案１　アクセル・ブレーキレバー自作
　おもちゃの車のリモコンのアクセル・ブレーキの構造を参考にして、プロトタイプとして、MFDで機構確認をしてみました。うまく行けば3Dプリンタの出力物に置き換えればある程度は強度を待たせられるはずです。
　ハンドル（引く）とブレーキ（押す）の重さを変えたかったので（ブレーキ側で重くする）、テコとバネでレバーの重みを変える機構で作成してみました。

![案１.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33661/74131663-047e-0c46-db79-1ed1f9177654.png)

　レバー操作感、電圧出力は期待通りの動作でしたが、3Dプリンタの出力に置き換えるとしても、強度的に若干不安が残ります。ゲームコントローラは、ゲームが熱くなるとどのような扱いを受けるかわかりませんからね。

### 案２　ジョイスティックコントローラ改造
　いちばん簡単なのが、XY２軸のジョイスティックユニットを使って、上下方向の動きを検出する方法でしょう。
ちょうど良さげな上位スティックユニットが見つかったので、レバーの部分を追加して作成してみました。結果としては、操作感がかなり軽く、しかもブレーキとアクセルで操作時の重さが同じになるのがいまいちな感じでした。
![案２.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33661/1268e3cd-fb8e-faa6-1c84-367e52e1e62d.png)


### 案３　ハンドブレーキコントローラ改造
　色々検索して探していたら、PC用のUSBハンドブレーキコントローラを見つけました。このハンドブレーキコントローラは本体がアルミ製で作りがしっかりしていて、なおかつテーブルの固定用アタッチメントがついてたので、ちょっと高価でしたが、かっこよくて気に入ったのでこれを改造してみることにしました。

https://www.amazon.co.jp/dp/B09V7XDMDR?ref=ppx_yo2ov_dt_b_fed_asin_title

　ハンドブレーキは引くブレーキ操作のみの対応なので（今回はブレーキの場合は押す操作にします）、逆方向に力を与えてフローティング状態にし、アクセル操作の場合引くの操作できるようにする必要があります。色々眺め回してどのようにすれば実現するか結構悩みましたが、バネを固定しているネジを外せばバネが働くまで少し遊びができて、この部分でアクセル操作をすれば実現できそうです。
![バネ.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33661/622636eb-1d79-8e24-af56-feff6a8b7e7b.png)


 アクセル操作（引く動作）は、今回ゴムを使って引っ張ってフローティング状態にしてみました。本当はバネを使いたいところですが、取り付け位置とか強度とかの関係で、検討するのに時間がかかりそうだったので、今回は機能的に実現できることを優先しています。
　操作量の検出ですが、もともとついていたセンサ基板は、ホールセンサでレバーのテンションを加えているバネの変形（バネの端にある磁石）による磁力変化を検出していました。耐久性を考えるとホールセンサをそのままつかいところですが、引くだけでなく押す分の可動域を広げたため、ホールセンサの検出可能領域から外れてしまって、うまくアクセル、ブレーキの両方の操作で移動量が検出できなかったので、今回は使用を見送りました。接触式のボリュームよりは非接触のホールセンサがいいので、今後の検討の余地ありです。
　結局、バネの移動量を回転式のボリュームの回転量に変換することで、レバーの操作量を検出する方法にしました。構成的には、バネの稼働量をスライド式ボリュームで検出する方法がシンプルで良さげですが、手元に回転式のポテンショメーターがあったのでこれを使いました。なんとなくメカっぽくてかっこいいという個人的な趣味も入っています。
![ブレーキボリューム.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33661/e5b53502-b551-1030-b485-b3bfce2bc36f.png)
![案３.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33661/20c7c51a-2ff6-8348-7471-80338046f00f.jpeg)

# マイコンの処理
　レバー操作でボリュームの抵抗値が変化するけど、電圧や増減の方向の関係で、そのままでは本体にそのまま返すことができません。そこで一旦マイコン経由にし、マイコンのアナログ入力で電圧の変化を検出して、ハンドルコントローラの仕様に合わせて電圧を変換して出力することにします。アナログ出力は、PWMでなくDA変換して出力したかったので（LPFなどの外付け回路が必要なのと、若干遅延が発生する懸念）、DA変換があるマイコンを探しました。その結果、ESP32はDA変換の出力が２本使えるので採用することに。なお、最近のESP32 C3とかS3はDA変換の出力がなくなっているので、注意が必要です。

### サンプルプログラム
　プログラムはArduino IDEで作成しています。プログラムとしてはシンプルで、20msecのループ内で、VRの電圧をサンプリングし、中立状態の電圧値をしきい値としてアクセル、ブレーキの値に変換しているだけです。中点時のドリフトを考慮して、検出範囲を設定しています。アクセル、ブレーキの値は、アナログ出力ポート(25,26)からdacWrite()関数を使ってアナログ値として出力されます。
 
```c:ab_cntl.ino
//EPS32
#define DAC1 25       //アクセル
#define DAC2 26       //ブレーキ
#define ANALOG_IN 33  //アナログ入力

//フィルター用
int datMax = 5;
int datCnt = 0;
float data1[5];

#define ZERO_POS 820
#define ZERO_WIDE 50

void setup()
{

  pinMode(DAC1 , OUTPUT);
  pinMode(DAC2 , OUTPUT);
  pinMode(ANALOG_IN , INPUT);

  Serial.begin(115200);
  while (!Serial) {
    delay(10);
  }
}

void loop()
{

  int sensorValue = analogRead(ANALOG_IN);

  //フィルター計算 1
  data1[datCnt] = sensorValue;
  datCnt++;
  if(datCnt > datMax){
    datCnt = 0;
  }
  float da1 = 0;
  for(int cnt = 0 ; cnt < datMax; cnt++){
    da1 += data1[cnt];
  }
  da1 = da1 / datMax;
  Serial.print(da1);
  Serial.print(",");

  int val1 = 0;
  int val2 = 0;
  if(da1 <= ZERO_POS - ZERO_WIDE){
    val1 = (ZERO_POS - ZERO_WIDE - (int)da1)*2;
    if(val1 > 255) val1 = 255;
  }else if(da1 >= ZERO_POS + ZERO_WIDE){
    val2 = ((int)da1 - (ZERO_POS + ZERO_WIDE))*2;
    if(val2 > 255) val2 = 255;
  }
  
  dacWrite(DAC1, 255 - val1); //0-255
  dacWrite(DAC2, 255 - val2); //0-255

  Serial.print(255 - val1);
  Serial.print(",");
  Serial.print(255 - val2);
  Serial.print(",");
  Serial.print(1100);
  Serial.print(",");
  Serial.println(0);

  delay(20);
}
```

# 実際の使用感
　普段手でアクセル・ブレーキを操作していないので、慣れが必要でしたが、慣れれば問題なく操作できたし、ケーム内でのコントロール性も向上したと思います。

全国都道府県対抗ｅスポーツ選手権（佐賀）での西九州大学ユニバーサルe-sportsブースでのデモの様子
![展示.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33661/8df606fd-da23-f162-bdb0-b4ef4ae7c576.png)


応用として、片手でハンドル操作とアクセル、ブレーキ操作が可能なコントローラも作成可能ですね。

#### 制作協力
西九州大学リハビリテーション学科　植田研究室
FabLab Saga