Pepperをゲームパッドで操縦する


Pepperを自由に操縦したかったので、ゲームパッド(xboxコントローラー)を使って操作する仕組みを作りました。
PCやスマホで操作するのもいいけれど、ロボットはやっぱり「操縦」ですよね。
(KinectとかVRとかJoy-Conがいいとか言わないでね!)

しくみとできること


ざっくりとした仕組みは上図参照。
ゲームパッドのボタンを押すと、Pepperが歩いたり、首を向けたり、回転したりします。

事前に用意するもの

  • XINPUT規格対応 USBゲームパッド
    • 本書ではLogicool Gamepad F310に基づき説明します。
  • Windows PC
    • Macではゲームパッドを正しく認識しません。
  • Google Chrome
    • HTML5 Gamepad APIに対応してることが必須。
    • 他のWebブラウザでも対応している可能性がありますが、未検証です。
  • Pepper
  • LAN環境
    • PCとPepperが同じLANに接続されている必要があります。
    • できればインターネットに接続できる環境を用意しておくといいです。

htmlを用意する

以下のページ、Pepper controller を参考にさせていただきました。
ゲームパッドの操作で補えない部分や、使用頻度の低い機能はHTML上にボタンを設置しました。
http://kuetsuhara.github.io/pepperConnect.html

jsを用意する

上記2つは必須です。

Pepperへの接続

先述したページを流用して作ったので割愛。

ゲームパッドのポーリング

自分で書いておきながら最も不可解なところだけれど、以下のページなどを参考にして書きました。
https://developer.mozilla.org/ja/docs/Web/Guide/API/Gamepad

control.js
var interval;

if (!('ongamepadconnected' in window)) {
  // ゲームパッドが接続されていたら0.5秒ごとに操作をポーリングするように設定
  interval = setInterval(pollGamepads, 500);
}

function pollGamepads() {
    var gamepads = navigator.getGamepads ? navigator.getGamepads() : (navigator.webkitGetGamepads ? navigator.webkitGetGamepads : []);
    for (var i = 0; i < gamepads.length; i++) {
        var gp = gamepads[0];//ゲームパッドはとりあえず1台まで
        if (gp) {
            loop();
            clearInterval(interval);
        }
    }
}

移動・回転・首の向きを操縦

接続されたゲームパッド1台のジョイスティックaxes[n]やボタンbuttons[n]の値が0または1になった時の挙動を
if文でゴリゴリと定義しています。(ポーリングしているので定期的に変化が取れる)

お手元のゲームパッドのボタン割り当ては、以下のサイトなどで確認してください。
http://html5gamepad.com/

control.js

//変数宣言:頭部の角度を記憶するのに使う
var x = 0;
var y = 0;

function loop() {
    if (gp.axes[0] == -1.0) {
        //左移動
        self.alMotion.moveTo(0 ,0.5 , 0).fail(function(err){console.log(err);});
    } else if (gp.axes[0] == 1.0) {
        //右移動
        self.alMotion.moveTo(0, -0.5 , 0).fail(function(err){console.log(err);});
    }

    if (gp.axes[1] == 1.0) {
        //後移動
        self.alMotion.moveTo(-0.5 , 0 ,0).fail(function(err){console.log(err);});
    } else if (gp.axes[1] == -1.0) {
        //前移動
        self.alMotion.moveTo(0.5, 0 ,0).fail(function(err){console.log(err);});
    }

    if (gp.buttons[4].pressed == 1.0) {
        //左回転
        self.alMotion.moveTo(0 , 0 , 0.52)
    }

    if (gp.buttons[5].pressed == 1.0) {
        //右回転
        self.alMotion.moveTo(0 , 0 , -0.52)
    }

    //頭部の回転
    if (gp.axes[2] == 1.0) {
        if(x>-1.0){
            x-=0.02; //左向き
            self.alMotion.setAngles("HeadYaw", x, 0.1);
        }
    } else if (gp.axes[2] == -1.0) {
        if(x<1.0){
            x+=0.02; //右
            self.alMotion.setAngles("HeadYaw", x, 0.1);
        }
    }

    if (gp.axes[3] == -1.0) {
        if(y>-1.0){
            y-=0.02; //上
            self.alMotion.setAngles("HeadPitch", y, 0.1);
        }
    } else if (gp.axes[3] == 1.0) {
        if(y<1.0){
            y+=0.02; //下
            self.alMotion.setAngles("HeadPitch", y, 0.1);
        }
    }
}
//本当はもっと長いけれど省略

おまけ

この方法を応用して、「Aボタンを押した時に、事前に覚えさせたセリフをしゃべらせる」なども実装できますが、
いろいろと設定がややこしいので割愛します。興味がある方はご一報ください。

まとめ

この方法で、Pepperをイベントに登壇させて会場中を移動させたり、
まるで意思を持って喋っているかのような掛け合いを実現したりすることもできます。
人間が裏で操縦することで、プログラムで決まった動作以外にもできるようになり、
リッチなロボット体験に繋がるかもしれません。

本当はデモサイトを載せたり、コードを丸々公開したりして、有意義な記事にしたかったのですが、
なんといってもよく意味を理解しないまま、オレオレコードで改造しながら書いたので…すみません。

Advent Calendar用に急いでアップしたので、あとからこっそり修正を加えると思いますが、
明らかな間違いがあったらご指摘いただけると嬉しいです。

あなたの近くで眠っているPepperが、少しでもいきいきと動くきっかけとなりますように!