Help us understand the problem. What is going on with this article?

ミニドローン Parrot MAMBO を Node.js で飛ばす(Windows 編)

More than 3 years have passed since last update.

目的

Node.js (JavaScript) でドローン(Parrot のミニドローン)を飛ばします。
既に Mac による例Raspberry Pi による例を拝見していたので、Windows でもやってみました。Windows では他の環境に比べて少しトリッキーでしたので、自分のメモの意味も含め、まとめておきます。

環境・機材

ハードウェア

ソフトウェア

環境構築

Node.js のインストール

  • 通常どおりインストーラでインストール
    • 私は C:\nodejs にインストールしました

windows-build-tools のインストール

  • 各種の Node モジュールを Windows ネイティブでコンパイルするための環境一式で、以下を一括でインストール・各種設定できます(こちらをご参考)。なお、既に VisualStudio 、C++ Build Tools、Python がインストールされている環境でも「conflict-free」だそうです。

    • Visual C++ Build Tools 2015
    • Python 2.7.11
      • C:\Users\(ユーザ名)\.windows-build-tools\python27 にインストールされる
  • コマンドプロンプト(cmd.exe)を「管理者として実行」して、以下のコマンドを実行

npm install --global windows-build-tools
  • インストールが終わるまで気長に待ちます

node-rolling-spider のインストール

  • Parrot のミニドローンと Bluetooth でやりとりできるモジュールです
  • これからアプリケーションを作るフォルダを適当に作成
    • 例 C:\Users\(ユーザ名)\drone
  • コマンドプロンプトでそのフォルダに移動
  • 以下のコマンドを実行
npm install rolling-spider
  • 黄色い文字で「WARN」がいくつか出てくると思いますが、たいてい大丈夫です
  • 赤い文字で「ERROR」の場合はネイティブコンパイル等に失敗しているので、windows-build-tools をインストールし直したり、エラーメッセージをもとに試行錯誤してください

Bluetooth アダプタの設定

  • Bluetooth アダプタをパソコンの USB ポートにさします
    • Windows 10 の場合、普通に使う場合はドライバ不要(自動インストール)だそうです(させば、デバイスマネージャーで Bluetooth デバイスとして認識されます)が…
  • 上記 node-rolling-spider が依存している node-bluetooth-hci-socket というモジュールを通じて使う場合は、「WinUSB」というドライバに書き換える必要があります(こちらに記載)。
  • Zadig というドライバ書き換えツールをこちらからダウンロードします
  • zadig-2.3.exe をダブルクリックで実行します
  • Options メニュー -> List All Devices を選択するとデバイスの一覧が出ます

zadig.PNG

  • デバイスの一覧から Bluetooth アダプタ(今回の場合は「CSR8510 A10」)を選ぶと、左側に現在のドライバが出ます(私の場合は以前に東芝のスタックを入れていたので tosrfusb と表示されています)
  • これを「WinUSB」に書き換えるため、「Replace Driver」をクリックします

zadig2.PNG

  • 「The driver was installed successfully.」と出れば成功です。

zadig3.PNG

  • するとこれまで「デバイスマネージャー」で「Bluetooth」として認識されていたアダプタが、「ユニバーサル シリアル バス デバイス」として認識されるようになります。こうなれば OK です。

※ Bluetooth アダプタのドライバを元に戻す方法は後述します

ようやく飛ばします

既に標準のスマホ/タブレットアプリを使って当該ドローンの操作はひととおり理解していることを前提とします。

以下のコードは rolling-spider モジュール内の eg フォルダにある「keyboard.js」をベースにして、モジュールのサイトを参考に書き換えつつ、コメントを加えています。間違いがあればコメントいただけましたら幸いです。

コーディング

以下のコードをテキストエディタで入力し、さきほど rolling-spider をインストールしたフォルダ(例 C:\(ユーザ名)\drone)内に「app.js」として保存します。

app.js
'use strict';                             // 厳格モードにする

// モジュールの読み込み
const Drone = require('rolling-spider');  // rolling-spider モジュールを使う
const keypress = require('keypress');     // キーボード操作を取得する keypress モジュールを使う(rolling-spider と同時にインストールされる)

// 変数の設定
let ACTIVE = true;                        // ドローンがアクティブ状態か否か
const STEPS = 2;                          // 一度のキー操作で命令を出す回数(動かすステップ数、0-100)

// rolling-spider のインスタンスを作る
const d = new Drone();
// ドローンの初期設定
d.connect( () => {                        // BLE でドローンに接続し、接続できたらコールバック
  d.setup( () => {                        // ドローンを初期設定してサービスや特徴を取得、その後コールバック
    d.flatTrim();                         // トリムをリセット
    d.startPing();                        // 不明
    d.flatTrim();                         // なぜ二回呼ぶのかは不明
    ACTIVE = true;                        // ドローンを ACTIVE 状態とする
    console.log(d.name, 'is ready!');     // 準備OKなことをコンソール出力
  });
});

// キー操作でイベントを発生させる
keypress(process.stdin);                  // 標準入力に keypress イベントを発生させる
process.stdin.setRawMode(true);           // raw mode(修飾を伴わない)で標準入力を受け付ける
process.stdin.resume();                   // keypress イベントをリッスン

// キー操作後に少しのあいだ入力を受け付けないようにする関数
function cooldown() {
  ACTIVE = false;       // いったん ACTIVE 状態でなくしておいて
  setTimeout( () => {   // 一定時間後に
    ACTIVE = true;      // ACTIVE に戻す
  }, STEPS * 12);       // この例では 24 ms
}

// キーボードからの入力による操作
process.stdin.on('keypress', (ch, key) => {   // keypress イベントが発生したら
  if (ACTIVE && key) {                        // ドローンが ACTIVE で key があれば
    // 緊急停止
    if (key.name === 'm') {             // m キー
      d.emergency();                      // 緊急停止(モーターを即時停止)
      setTimeout( () => {                 // 3秒後にプログラム終了
        process.exit();
      }, 3000);

    // 離陸(t; take-off)
    } else if (key.name === 't') {      // t キー
      console.log('takeoff');             // コンソール出力
      d.takeOff();                        // 離陸

    // w/s/a/d キー(前後左右の移動)
    } else if (key.name === 'w') {      // w キー
      d.forward({ steps: STEPS });        // 前進(前ピッチ)
      cooldown();                         // 少しの間キー操作を受け付けない
    } else if (key.name === 's') {      // s キー
      d.backward({ steps: STEPS });       // 後退(後ピッチ)
      cooldown();                         // 少しの間キー操作を受け付けない
    } else if (key.name === 'a') {      // a キー
      d.tiltLeft({ steps: STEPS });       // 左水平移動(左ロール)
      cooldown();                         // 少しの間キー操作を受け付けない
    } else if (key.name === 'd') {      // d キー
      d.tiltRight({ steps: STEPS });      // 右水平移動(右ロール)
      cooldown();                         // 少しの間キー操作を受け付けない

    // カーソルキー(上下と左右スピン)
    } else if (key.name === 'left') {   // 左カーソルキー
      d.turnLeft({ steps: STEPS });       // 左旋回(左スピン(ヨー))
      cooldown();                         // 少しの間キー操作を受け付けない
    } else if (key.name === 'right') {  // 右カーソルキー
      d.turnRight({ steps: STEPS });      // 右旋回(右スピン(ヨー))
      cooldown();                         // 少しの間キー操作を受け付けない
    } else if (key.name === 'up') {     // 上カーソルキー
      d.up({ steps: STEPS * 2.5 });       // 上昇
      cooldown();                         // 少しの間キー操作を受け付けない
    } else if (key.name === 'down') {   // 下カーソルキー
      d.down({ steps: STEPS * 2.5 });     // 下降
      cooldown();                         // 少しの間キー操作を受け付けない

    // i/j/k/l キー(アクロバット)
    } else if (key.name === 'i') {      // i キー
      d.frontFlip({ steps: STEPS });      // 前に宙返り
      cooldown();                         // 少しの間キー操作を受け付けない
    } else if (key.name === 'k') {      // k キー
      d.backFlip({ steps: STEPS });       // 後ろに宙返り
      cooldown();                         // 少しの間キー操作を受け付けない
    } else if (key.name === 'j') {      // j キー
      d.leftFlip({ steps: STEPS });       // 左に宙返り
      cooldown();                         // 少しの間キー操作を受け付けない
    } else if (key.name === 'l') {      // l キー
      d.rightFlip({ steps: STEPS });      // 右に宙返り
      cooldown();                         // 少しの間キー操作を受け付けない

    // 着陸(q; quit)
    } else if (key.name === 'q') {      // q キー
      console.log('landing...');          // コンソール出力
      d.land();                           // 着陸
    }
  }

  // プログラム終了(ctrl + c)
  if (key && key.ctrl && key.name === 'c') {  // ctrl + c なら
    process.stdin.pause();                    // 標準入力を一時停止
    process.exit();                           // プログラム終了
  }
});

動作確認

  • ミニドローンにバッテリーを装着し、電源を ON にします
    • Parrot MAMBO の目が緑色に点滅します(ペアリングモード?)
  • コマンドプロンプトで、上記の app.js があるディレクトリ(例 C:\(ユーザ名)\drone)に移動し、以下を実行します
node app.js

コンソールに以下のように表示され、MAMBO の目が緑色の「点灯」になったら準備OKです(xxxxxxは個体の番号)。

Mambo_xxxxxx is ready!

操縦方法(キーアサイン)

キー 動作 メソッド
m 緊急停止(即座にプロペラ停止) emergency()
t 離陸 takeOff()
w 前進(前ピッチ) forward()
s 後退(後ピッチ) backward()
a 左水平移動(左ロール) tiltLeft()
d 右水平移動(右ロール) tiltRight()
↑ カーソル 上昇 up()
↓ カーソル 下降 down()
← カーソル 左旋回(左スピン(ヨー)) turnLeft()
→ カーソル 右旋回(右スピン(ヨー)) turnRight()
i 前方宙返り frontFlip()
k 後方宙返り backFlip()
j 左に宙返り leftFlip()
l 右に宙返り rightFlip()
q 着陸 land()
ctrl + c プログラム終了 -

まとめ

node-rolling-spider モジュールを使って Windows でミニドローンを飛ばすポイントは以下です。

これらのうち1点目は今回の内容に限ったことではありませんが、これまで手動では設定しており、他のモジュールはうまくビルドされていたのに、今回つまずきました。windows-build-tools を再インストールしたらうまくいきました。手動設定で、なにか足りていなかったようです。

2点目と3点目については、node-rolling-spider の依存モジュールの readme までちゃんと読まずに試していて、つまずきました。また、一般的な Bluetooth アダプタのように「スタック」を使わずに(スタックをバイパスして)通信するというのが当初は理解できず、手間取りました。

このあたり、事前に Raspberry Pi で試した時は何も考えなくてもすんなり動いて、Windows でぜんっぜん動かなくて、心が折れかけでした。業務の都合上(?)、Windows マシンだけで済むと楽なので、少し手間取りはしましたが、動いてよかったです。

付録

Bluetooth アダプタのドライバの戻し方

  • デバイスマネージャーで WinUSB ドライバで書き換えていた Bluetooth アダプタを右クリックし、「ドライバーの更新」を選択

zadig4.PNG

  • 「コンピューターを参照してドライバーソフトウェアを検索」を選択
  • 「コンピューター上の利用可能なドライバーの一覧から選択します」を選択
  • 「Generic Bluetooth Radio」(当初に認識されていたデバイス名)を選択し、「次へ」でドライバをインストール
  • デバイスマネージャーで当該のアダプタが「Bluetooth」として認識されていれば OK です
  • 念のため zadig-2.3.exe を実行し、「List All Device」して、当該アダプタを選択して、もとのドライバになっていることを確認します
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away