ここで紹介する方法は飽くまで個々の技術を組み合わせた例です。
Wiiリモコンのセンサ等はラジコンに用いることを想定されておらず、
操作不能に陥り想定外の場所に暴走したり墜落することがあります。
規制区域はもちろんのこと、安易な飛行は非常に危険ですので
重々ご注意ください。
遅々として進まないままだらだら書いているうちにドローンの飛行が
規制されるようになってしまって、こんなのどこで飛ばすんだよ状態ですが
一応書き進めます。
前回はArduinoに安全装置を実装する方法について説明しました。
今回は Arduino 部分の残りのコードを完成させてWiiリモコンから操作できるようにします。
付いているだけの普通のものです。
本当はクラシックコントローラで
アナログ操作したかったのですが
Arduinoから認識させられませんでした。
ヌンチャクは認識するのですが操作中に
接続が切れまくって使い物にならないので断念。
PS3のDUALSHOCK3を使うとWiiリモコンよりは
操作しやすいかもしれません。
今回のプログラムは使えませんが。
ソースコード全体
GitHub にソースコードを置きました。
https://github.com/onthehand/Quad_PPM/blob/master/Quad_PPM.ino
ほとんどの部分はこれまでに説明済みなので、
おさらいも兼ねて loop 関数を一通り見ていきます。
Wiiリモコンとの接続
接続確認。
接続が切れているときは何もしません。
void loop() { Usb.Task(); if(! Wii.wiimoteConnected || Wii.getButtonPress(HOME)){ activate = 0; return; }
一定時間おきに加速度センサをチェックします。
前回チェック時と同一の値だったら cc をインクリメントし、
cc が一定値を超えたら disarm しています。
詳しくは前回の安全装置その1をご参照ください。
WatchDog と組み合わせてタイマーをリセットした方がカッコいいですが
これ以上割り込みを増やしたくなかったので少々ダサい実装です。
if( millis() > uptime ){ // check connecition if( pp == Wii.getPitch() && pr == Wii.getRoll() ){ cc ++ ; }else{ cc = 0; } pp = Wii.getPitch(); pr = Wii.getRoll(); if(Wii.getButtonPress(RIGHT)){ currentTh++; }else if(Wii.getButtonPress(LEFT)){ currentTh--; } uptime = millis() + SPAN; } if ( cc > WII_TIMEOUT / SPAN ){ activate = 0; return; } activate = 1;
ところで、WiiリモコンのRIGHTボタンを押すと
スロットル(currentTh)をインクリメントしています。
LEFTボタンならデクリメント。
何故 UP, DOWN ボタンではないのかと言うと、
Wiiリモコンを横持ちするからです。
横持ちすると RIGHT ボタンが上に来ます。
上ボタンを押しっぱなしにした時のスロットル加速度は
SPAN 変数の値で決まります。
Wiiリモコン接続確認間隔も SPAN 変数を共用しているので
スロットル加速度を下げると接続切れ対応が遅くなってしまいます。
このあたりも実装がダサいですね。
カメラ操作
前々回のカメラについての説明をご参照ください。
分かりにくい実装ですが、Wiiリモコンの「2」ボタンを押すとカメラにパルスを飛ばします。
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 ); }
ロール/ピッチ/ヨーの制御
コードがどんどんカオスになっていきます。
y, r, p はそれぞれYaw, Roll, Pitch用の変数です。
ヨーは左右の方向制御なので単純に UP/DOWN ボタン
(リモコン横持ちで左右ボタン)に割り当てました。
checkButton関数はただ単に次の対応に従って値を返しているだけです。
- DOWNボタン押下→HIGH_PULSE_TIME(Yawが右)
- UPボタン押下→LOW_PULSE_TIME(Yawが左)
- 何も押されていない→HALF_PULSE_TIME(Yawが真ん中)
RollとPitchは加速度センサの値をMultiWiiのパルス時間幅に引き延ばして使用しています。
一見、変数割り当てが逆になっているように見えますが、これもリモコン横持ちの影響です。
Wiiリモコンは長手方向を軸にした回転が Rollです。
横持ちした場合に前後の操作が Wii リモコンの Roll 回転に相当しますが、
この方向の操作は Quadcopter の仰角制御に割り当てるのが自然なので、
Quadcopter から見ると Pitch 方向の回転に相当します。
複雑。
unsigned int y = checkButton(DOWN,UP,HIGH_PULSE_TIME,HALF_PULSE_TIME,LOW_PULSE_TIME); unsigned int r = map((unsigned int)(Wii.getPitch()*100.0), 9000, 27000, MAX_PULSE_TIME, MIN_PULSE_TIME); unsigned int p = map((unsigned int)(Wii.getRoll()*100.0), 9000, 27000, MIN_PULSE_TIME, MAX_PULSE_TIME);
おまけ機能
リモコンの「1」ボタンを押すとMultiWiiのAUX1がHIGHになるようにしました。
AUX1, AUX2 と言うのは本物のプロポに付いているトグルスイッチだそうです。
MultiWii側でAUX1にBAROを割り当てておくと、WiiリモコンからBAROのON/OFFを操作できます。
ちなみに、BAROと言うのは高度計です。
BAROをONにすると気圧センサを使って高度を一定に自動制御してくれる、、、はず
なのですが誤差がひどくてあまりアテになりません。
AUX2 も一応使えるようになっていますが実際には使っていません。
と言うのも、MultiWii はカメラにパルスを送る機能を持っていて、
AUX2 を割り当てようとしていたのですが、ただでさえ割り込みに敏感な
MultiWii でカメラを操作するよりも Arduino 側で直接カメラ操作した方が
安全かつ確実だという判断です。
// BARO is supposed to be activated by AUX1. unsigned int a1 = (Wii.getButtonPress(ONE)) ? HIGH_PULSE_TIME : HALF_PULSE_TIME; unsigned int a2 = (Wii.getButtonPress(TWO)) ? HIGH_PULSE_TIME : HALF_PULSE_TIME; if( Wii.getWiiState() & 0x01 ){ a2 = LOW_PULSE_TIME; }
ARM/DISARM
通常であれば ARM, DISARM はプロポの左右レバーの組み合わせで操作するのですが、
Wiiリモコンでそれをやるのは不可能なのでショートカットを作ってあります。
今回はBボタンとプラスマイナスボタンの同時押しという、
偶然の誤操作があり得ない組み合わせにしています。
if(Wii.getButtonPress(B)){ r = p = HALF_PULSE_TIME; if(Wii.getButtonPress(PLUS)){ // arm currentTh = 1000; y = 2000; }else if(Wii.getButtonPress(MINUS)){ // disarm currentTh = y = 1000; } } }
そんなこんなで普通に買ったら数万円するプロポを
数千円そこそこのWiiリモコンで実現できました。
・・・と言いたいところですが、そんなに上手くは行きません。
WiiリモコンのBluetoothの通信可能距離はせいぜい10m程度なので
ちょっと飛ばしたらあっという間に通信が切れて制御不能になります。
また10m以内でも頻繁に通信切れが発生してかなり危険なので
実用性は皆無だと考えた方が良いでしょう。
双葉のプロポを馬鹿にすることなかれ。
次回はMultiWii側について説明します。
気が向いたら。
SECRET: 0
返信削除PASS: 74be16979710d4c4e7c6647856088456
Multiwiiが搭載されているドローンAUXレバーONすると自動でroll値を左右に動かすプログラムを考えています。
何か良い知恵があれば教えてください。
SECRET: 0
返信削除PASS: 74be16979710d4c4e7c6647856088456
ん~、、、どうやっても本来の roll の信号とぶつかってしまうので結構面倒なことになりそうですね。
とりあえず、Multiwiiをいじる、受信時に上書き、送信時の上書き、の3択なんじゃないかと思います。
Multiwii のソース自体をいじれば、あらかじめ roll 値を書いておいて
AUX を受け取ったときに強引に roll 値をロードしたりできるかもしれませんが、
左右に振っている間は制御が効かなくなるなどかなり危険なことになりそうな気もします。
ハードウェアを増やしても良ければ、受信機と Multiwii の間に
別の Arduino を挟んでおいて、普段は受信信号をそのまま Multiwii に流し、
AUX を受け取った時だけあらかじめ覚えている roll を流すとか。
ただ、これも Multiwii よりも Arduino の方がチップのクロックが高くないと
パルスが歯抜けになって制御不能に陥るかもしれません。
送信側でやるのは電波飛ばさないといけないのでかなり難しいでしょうね。
プロポのレバーをモーターで動かすとか、、、って、だんだん何か別の物に。。。。
SECRET: 0
返信削除PASS: 74be16979710d4c4e7c6647856088456
このブログを見て前々から興味のあった、ドローンの自作を始め、受信機をArduinoを使って作ろうとしています。
MultiwiiはCRIUS Multiwii v2.6を使っているのですが、たけやすさんが作られたプログラムを実行してもconfigの画面では入力は確認できません、、、
一応config.hの#define SERIAL_SUM_PPMと#define SERIAL_SUM_PPMをコメントアウトしているのですが、Multiwii側で何か他に設定しなければいけないこと等があった教えてください。
SECRET: 0
返信削除PASS: 74be16979710d4c4e7c6647856088456
度々すいません!
ただの配線ミスでした、、、
SECRET: 0
返信削除PASS: 74be16979710d4c4e7c6647856088456
コメントありがとうございます。
Bluetooth経由でコントローラーを繋いでも
安定した操縦は難しいのでくれぐれもご注意くださいませ。
吹っ飛ばないように紐を繋ぐとか、
暴走しても問題ない場所で試すとか。
SECRET: 0
返信削除PASS: 74be16979710d4c4e7c6647856088456
初めまして。
拝見させていただきました。
arduinoとmultiwiiとwiiリモコンでドローンを製作しようと考えています。
上記のQuad_PPMをダウンロードさせていただき、実行してみましたが、31行目に
Quad_PPM:31: error: 'Button' was not declared in this scope
Quad_PPM:31: error: expected primary-expression before 'unsigned'
Quad_PPM:31: error: expression list treated as compound expression in initializer [-fpermissive]
Quad_PPM:31: error: 'unsigned int checkButton' previously defined here
というエラーと、49行目に
Quad_PPM:49: error: redefinition of 'unsigned int checkButton'
Quad_PPM:49: error: 'Button' was not declared in this scope
Quad_PPM:49: error: expected primary-expression before 'unsigned'
'Button' was not declared in this scope
というエラーが出たのですが、このエラーを解決の仕方を教えていただきたく、コメントさせていただきました。
ご返事お願いします。
SECRET: 0
返信削除PASS: 74be16979710d4c4e7c6647856088456
��MIさん
USB HostShield ライブラリはインストールしましたでしょうか?
もしインストールしていなければ、下記の過去ログの「USB Host Shield ライブラリ」のところをご参照ください。
Button クラスが定義されていないというエラーになっています。
Button は冒頭で include されている Wii.h の中で定義されているはずです。
で、 Wii.h はUSB HostShieldライブラリの一部です。
つまり、USB HostShieldライブラリを正しく読めていないのではないかと思います。
SECRET: 0
返信削除PASS: 74be16979710d4c4e7c6647856088456
返信ありがとうございます。
無事動作させることができました。
ありがとうございました。