masato-ka's diary

日々思ったこととか、やったことの備忘録。

AI RC Carのプロポ信号をUSBゲームパッドに変換する

この記事について

この記事はAI RC Carアドベントカレンダー13日目の記事です。今回はRC Carのプロポの入力をUSBゲームパッドとして変換する内容を紹介します。

AI RC Carとプロポ

AI RC Carでは人がプロポを使って操作した内容をJetsonNanoなどのSBC(シングルボードコンピュータ)に記録する場合が多々あります。通常、スピコンやサーボへ接続するプロポ受信機のPWM信号をなんらかの方法でSBCへ渡すことで実現します。一般的なラジコンのPWM信号は1msから2msのパルス幅となっています。これをSBCで直接読み込む方法はありません。PWMの値を読む前にOSの割り込みなり入るため、専用ハードを用意する必要があります。RTOSなどを利用すればできるかもしれませんが、基本的にはSBCで実施するのは厳し目です。

マイコンを利用したPWM信号のデコード

こういった用途に非常に適しているのがマイコンです。一般的に多くのマイコンではこういったPWM信号のパルス幅を計測するための機能が用意されています。Arduinoなどにもようしてありますが、今回は格安マイコンのBluepill(STM32F103)を利用します。理由としては以前購入してそのまま放置していた。Amazonで格安で購入できる点です。

BluepillとSBCの接続

Bluepillはmicro USBを搭載しており、HIDにも対応しています。HIDはいわゆる、マウスやキーボード、ゲームパッドなどのインタフェースとしてホストPCへ接続できる機能です。今回はBuluepillをゲームパッドとしてSBCへ接続します。ゲームパッドジョイスティックとして認識されるためプログラムから制御可能です。Pythonであれば、PyGamesなどを使って制御できます。また、HTML5のGamePad APIも利用できます。簡単に試すにはPCへUSBで接続、以下のサイトを最新のChromeブラウザから開くとGamePadとして認識していることがわかります。

HTML5 Gamepad Tester

プログラム

Bluepillの詳しい開発方法は掲載していません。Arduino IDEで開発できるように環境を整えてください。実際に書き込みを行ったソースコードは以下のようになっています。HIDのライブラリであるUSBCompositeをあらかじめインストールしておきます。PB5とPB6にプロポの受信機のCH1, CH2をそれぞれ接続します。USBでホストに接続するとXBOX360のコントローラとして認識され、CH1が左ジョイスティックの縦操作、CH2が右ジョイスティックの横操作に対応します。

qiita.com

#include <USBComposite.h>

#define CHANNEL_NUM 2
#define DEBUG 1

const int WIDTH = 410;
byte PWM_PIN[CHANNEL_NUM] = {PB5,PB6};
int base[CHANNEL_NUM] = {0,0};
int value[CHANNEL_NUM] = {0,0};
float joystick[CHANNEL_NUM] = {0,0};
  
USBXBox360 XBox360;


void setup() {
  // put your setup code here, to run once:
  pinMode(PC13, OUTPUT);
  digitalWrite(PC13, HIGH);
  pinMode(PWM_PIN[0], INPUT);
  pinMode(PWM_PIN[1], INPUT);

  delay(100);

  base[0] = pulseIn(PWM_PIN[0], HIGH, 29412);
  base[1] = pulseIn(PWM_PIN[1], HIGH, 29412);

  XBox360.begin();
  digitalWrite(PC13, LOW);
  delay(1000);
  
}

void loop() {
  for(int i=0; i < CHANNEL_NUM; i++){
     value[i] = pulseIn(PWM_PIN[i], HIGH, 29412);
     joystick[i] = map(value[i], long(base[i] - WIDTH), long(base[i] + WIDTH), -30000, 30000);
  }
  XBox360.X(joystick[0]);
  XBox360.XRight(joystick[1]);
  
}

init内では入出力ピンの初期化処理とHIDデバイスの初期化を行っています。メインループの中ですが、逐次PWMピンの値を読み取り、-30000から30000の間に値をマッピングし直しているのみです。その後はXBox360のコントローラに対応した値をセットしてホスト側PCに入力を送っています。

まとめ

ラジコンプロポの信号をRaspberryPiやJetson Nanoで読み取る方法は以外にめんどくさく情報がありません。Arduinoで一度受け取ってからHIDで送る方法は大変お手軽で思ったよりも簡単に作れるので、AI RC Car作成の際に一つ用意しておくと良いでしょう。