2015年8月26日水曜日

WiiリモコンとArduinoでQuadcopterを飛ばす(4):Arduinoを受信機にする

【ご注意】

ここで紹介する方法を使って、周囲に人や建物がある場所で飛ばすのはお奨めしません。

Wiiリモコンのセンサ等はラジコンに用いることを想定されておらず、

操作不能に陥り想定外の場所に暴走したり墜落することがあります。

十分に安全な対策を取ってください。

・・・と言うより、実際に飛ばすのはお奨めしません。




前回、ざっと組み立てましたが、MultiWii や Arduino が空っぽなので中身を作っていきます。

MultiWii をフライトコントローラーとして動作させる方法は

他のサイトにも詳しく載っていますので先に Arduino の方について説明します。



今回の Arduino の用途


MultiWii はフライトコントローラーとして Quadcopter の姿勢制御に使用します。

通常、プロポからの電波をプロポの受信機で受信し、制御信号として MultiWii に入力します。



・・・が、今回はプロポの代わりに Wii リモコンを使用しますので、

MultiWii に入力できるような Wii リモコンは存在しません。

そこで、Arduino UNO を Wii リモコン受信機として動作させます。



MultiWii への入力信号




MultiWii への入力信号には次の7チャンネルあり、前半の4チャンネルが必須です。

これらにアナログ(PWM)入力することが多いようです。

  • Roll

    進行方向を軸にした回転

    つまり、右旋回/左旋回のための傾き制御


  • Pitch

    進行方向に垂直で地面に水平な方向(つまり左右方向)を軸にした回転

    つまり、機首の上げ下げ


  • Yaw

    地面に垂直な方向を軸にした回転

    つまり、旋回無しでの方向転換


  • Throttle

    推力

    つまり、Roll, Pitch が中央値の場合は上昇/下降


  • AUX1, AUX2, AUX3

    追加の制御信号

    例えば、高度計での自動制御 ON/OFF やカメラのシャッター制御など

Arduino UNO には PWM 出力ピンが6本ありますのでこれで、、、と言いたいところですが、駄目です。



と言うのも、Wii リモコンとの通信用に USB Bluetooth ドングルを使います。

そのために USB Host Shield を使う訳ですが、USB Host Shield は Arduino UNO の

PWM 出力6本のうちの3本を潰しますので MultiWii と接続するためのピンが足りません。



なお、USB Bluetooth ドングルと USB Host Shield の利用方法について、

詳しくはArduino+Wiiリモコンでラジコンを作る回で説明しています。



信号線1本でMultiWiiに入力する


Arduino UNO のピンが足りないのでどうするかと言うと PPM という方式を使って

信号線+電力線+GNDの3本だけで全ての信号を送ります。



PPM信号についてはこのあたりのサイトが分かりやすいです。

ワンチップマイコンによるラジコン信号のデコード

魔法の大鍋

特に魔法の大鍋さんは PS3 リモコンで MultiWii を制御していてなかなか熱いです。



これらのサイトで説明されていることをざっくりまとめると、各チャンネルの値をパルス幅で

表現して1本の信号線上で順番に送信する方式が PPM です。



ArduinoでPPM信号をMultiWiiに送る


Arduino の Timer を使って正確に割り込みすると PPM 信号を生成することが可能です。

具体的には次のようなコードになります。

#include <TimerOne.h>

#define CHANNELS 6

#define ROLL     0
#define PITCH    1
#define THROTTLE 2
#define YAW      3
#define AUX1     4
#define AUX2     5

#define MIN_PULSE_TIME  1000 // 1000us
#define MAX_PULSE_TIME  2000 // 2000us

#define SYNC_PULSE_TIME  3050 // 3000us

#define PIN_PPM 5

unsigned int pw[CHANNELS];

void setPulse(unsigned int r, unsigned int p, unsigned int t, unsigned int y, unsigned int a1, unsigned int a2){
pw[ROLL] = r;
pw[PITCH] = p;
pw[THROTTLE] = t;
pw[YAW] = y;
pw[AUX1] = a1;
pw[AUX2] = a2;
}

void setup() {
pinMode(PIN_PPM, OUTPUT);
pinMode(PIN_CAMERA, OUTPUT);
digitalWrite( PIN_CAMERA, LOW );

Serial.begin(38400);

activate = 0;

// Start timer with sync pulse
Timer1.initialize(SYNC_PULSE_TIME);
Timer1.attachInterrupt(isr_sendPulses);
isr_sendPulses();
}

// Sync pulse first
volatile int currentChannel = 0;

void isr_sendPulses() {
digitalWrite(PIN_PPM, LOW);
if( ! activate ){ return; }

if (currentChannel == CHANNELS) {
// After last channel
Timer1.setPeriod(SYNC_PULSE_TIME);
currentChannel = 0; // Will be 0 on next interrupt
} else {
if(pw[currentChannel] < MIN_PULSE_TIME){ pw[currentChannel] = MIN_PULSE_TIME; }
if(pw[currentChannel] > MAX_PULSE_TIME){ pw[currentChannel] = MAX_PULSE_TIME; }
Timer1.setPeriod(pw[currentChannel]);
currentChannel++;
}

digitalWrite(PIN_PPM, HIGH);
}

setPulse 関数に Roll, Pitch, Yaw, Throttle 等の値を渡して一旦バッファに保持します。

上記のコードでは省略していますが、loop 関数内で Wii リモコンからの信号を受け、

setPulse 関数で更新していきます。



setPulse とは別に、起動時に setup 関数内で Timer1 をセットします。

こうすることで SYNC_PULSE_TIME 後に割り込みが発生して isr_sendPulse 関数が呼ばれます。



isr_sendPulse 関数内では、setPulse でバッファにセットされた値に応じて Timer1 の

割り込み間隔を上書きしてパルス幅を制御しています。



本当は PIN_PPM を LOW にする時間を PPM の仕様に合わせた時間幅にしないと

いけない気がしますが、何故か上記のコードでも MultiWii は正しい値を

受け取ってくれているのでそのままにしています。



MultiWii と Arduino UNO の接続


MultiWii の PPM ピンはデフォルトで D2 なので、次のように接続します。

  • Arduino PIN_PPM → MultiWii D2

  • Arduino GND → MultiWii GND

  • Arduino Vin → MultiWii 5V



MultiWii 5V と Arduino 5V なんじゃないの?と思うかもしれませんが、

MultiWii 側にバッテリーを繋ぐと MultiWii から 5V が給電され、

その状態で Arduino に USB 等を接続すると Arduino 5V にも給電されて

どちらかに逆流が発生してしまって駄目なのではないかと思います。(たぶん)



Arduino Vin と MultiWii 5V を接続することで、Arduino の USB ケーブルを

外しても MultiWii から給電されて Arduino にはバッテリー不要になります。



実は、本当にこんな使い方で合っているのか自信がないのですが、一応、これで動作しています。



オマケ:カメラの制御


MultiWii にもカメラに録画開始用パルスを送る機能があるのですが、

せっかく Arduino を使っているので、MultiWii 無しでカメラを

リモート制御してみます。



とは言え、Go Pro はそこそこの値段がしますし、

コンデジだと重すぎて不安定になります。



そこで、Walkera というラジコン用のカメラを使います。

中国製で動作は怪しいですが恐ろしく安い上に10g程度の超軽量です。



コネクタが Walkera 専用になっていますが、途中で切って

信号線3本を Arduino のデジタルピンと 5V, GND にそれぞれ繋げばOKです。


Arduino からカメラを制御する


このカメラは1.5秒前後のパルスを信号として受け取ります。

静止画モードの場合はパルス受信で1枚撮影。
動画モードの場合はパルスを受信する度に撮影開始/終了を繰り返します。



これはメーカーから公開された仕様ではなく私がイイカゲンに試して

見つけただけなので正確ではないかもしれませんが。



Arduino からこのパルスを送りたいのですが、前述の通り PPM 信号で

タイマーを1つ使っていますのでこの程度のことに割り込みを使うのは避けて

次のようにしました。

#define PIN_CAMERA 2

unsigned long camtrig = 0;
#define CAMERA_PULSE 1500

void setup() {
pinMode(PIN_CAMERA, OUTPUT);
}

void loop() {
if( 0 < camtrig && camtrig < millis() ){
camtrig = 0;
digitalWrite( PIN_CAMERA, LOW );
}
if( camtrig == 0 && Wii.getButtonPress(TWO) ){
camtrig = millis() + CAMERA_PULSE;
digitalWrite( PIN_CAMERA, HIGH );
}
}

Wii リモコンの2番ボタンを押すと 1.5 秒程度のパルスを発信します。

loop 内で時間を測っているだけなので不正確ですが

一応カメラは受け取ってくれるようです。



なお、1.5秒分 delay した方が簡単そうに見えますが、

delay は内部で Timer を使っていて PPM の

割り込みと干渉しがちなのでお奨めしません。



これで撮影するとこんな感じになります。

アクションカメラではないので画質はガタガタ。



マルチコプターが問題になるよりもずっと前に撮った映像ですが、

最近はこんな公園内で飛ばすのはNGかもしれませんね。。。





ちょっと長くなったので今回はここまで。

次回以降で Wii リモコンのボタンと制御チャンネルの割り当てや

MultiWii の設定を行っていきます。

2 件のコメント:

  1. SECRET: 1
    PASS: 5adb363e5a0abc723b0b7485fc465865
    この記事を見てwiiリモコンを使いQuadcopterを作りたいと思っているのですが、素人なのであまり専門的知識がないのでもう少し詳しく教えてもらえませんか?

    返信削除
  2. SECRET: 0
    PASS: 74be16979710d4c4e7c6647856088456
    遅筆ですみません。
    この件はまだ続きがあって、いま公開している内容は全体の半分くらいです。
    なお、冒頭にも書きましたが、実際に作成するのはお薦めしません。
    万が一、頭上に墜落したら命に関わります。冗談抜きで。
    それでも試したいのであれば自己責任の範囲としか言いようがないですが、
    急な突風や通信障害が起きても絶対に事故を起こさないよう、
    自動制御のコードを自力で書くくらいの心意気と万全な安全対策が必須です。
    という前提を踏まえた上で、私がこのブログでお伝えできるのは技術的な導入だけだと考えています。
    このご時世ですので警察のご厄介になっても、このブログのせいにしないでくださいね。(キリッ)

    返信削除