凹みTips

C++、JavaScript、Unity、ガジェット等の Tips について雑多に書いています。

筋電の Raw Data が取得できるようになった Myo SDK 0.8.0 を試してみた

はじめに

前回、Myo の記事について書きました。

この時点では筋電の生値が未だ取れない、と書いたのですが、公式ブログで生値が取れるようになったことが発表されましたので試してみました。

環境

サンプルを試してみる

最新の SDK は以下よりダウンロードします。

Connect App も同時に最新のものにしておきます。

ダウンロードすると以下のように bin ディレクトリの中に emg-data-sample が増えており、ここで筋電の生値がどんな感じで取れるか見ることが出来ます。

力を抜いている時

f:id:hecomi:20141221011905p:plain

力を入れている時

f:id:hecomi:20141221011931p:plain

Unity で使ってみる

リファレンス見てみます。

新しく onEmgData() が加わり、整数型で 8 個のデータ(8 個のセンサの値)が返ってくることが分かります。しかしながら SDK に同梱されている MyoUnity.unitypackage のサンプルには、これにアクセスするインターフェースがありません。

幸い構造自体は前回のエントリで書いたようにシンプルになっています。そこで適宜 libmyo.h などを参照しながら libmyo.cs を拡張したり、足りないイベントや API をポチポチ追加しながら自前でバインディングをしました。ついでに、対応されていなかった RSSI についても追加しておきました。

そのうち公式が対応すると思いますが、それまでのつなぎとしてご自由にお使い下さい。

デモ

Download

サンプルコード

サンプルコードは以下になります。

using UnityEngine;
using System;

public class RssiAndEmgGUI : MonoBehaviour 
{
    public GameObject myo;
    private ThalmicMyo thalmicMyo_;
    private ThalmicMyo thalmicMyo {
        get { return thalmicMyo_ ?? (thalmicMyo_ = myo.GetComponent<ThalmicMyo>()); }
    }

    void Start()
    {
        // Enabled にするとデータが降ってくる、Disabled だと降ってこない(デフォルト)
        thalmicMyo.SetStreamEmg(Thalmic.Myo.StreamEmgType.Enabled);
    }

    void Update()
    {
        // RSSI の値を更新する
        thalmicMyo.RequestRssi();
    }

    void OnGUI()
    {
        if (thalmicMyo != null) {
            // rssi は sbyte、emg は sbyte[8] で 8 個ある各センサの値を格納
            GUI.Label(new Rect (12, Screen.height - 100, Screen.width, 100),
                "RSSI: " + thalmicMyo.rssi + "\n" +
                "EMG:  " + string.Join(", ", Array.ConvertAll<sbyte, string>(thalmicMyo.emg, x => x.ToString()))
            );
        }
    }
}

おわりに

強く握っているかどうか位なら取れそうです。そのままだとノイズが大きいので適宜フィルタを掛けて見ながらいろいろ試してみようと思います。

Oculus Rift x Leap Motion を使った指で空間に魔法を描いて発動できる Magic VR を作ってみた

 

この記事は Oculus Rift Advent Calendar 2014 19日目の記事です。前日は KaleidPlayer の作者の @faifx さんによる「視差無し立体視のススメ - とあるOculusRift使いの備忘秘録」でした。2D の動画/静止画の単眼立体視を行うには、なるべく遠い場所に置くほうが有利で、それが何故かの考察、またそれに加えて平面感を消す手法・知見などがまとめられている素晴らしい内容です!

はじめに

以前、「Oculus Rift と Leap Motion で空中お絵描きアプリを作ってみた - 凹みTips」というエントリを投稿しました。ここでは Leap Motion VR(参照:VR の世界に手を持ち込める Leap Motion VR の仕組みを調べてみた - 凹みTips)を利用して空間に指で線を描けるという簡単なデモの紹介をしています。

指先の軌跡をダイレクトに空間に残せるのは結構面白くて、ゲームや UI への応用と色々なことが出来そうで、アイディアもたくさん思い浮かびます。そのアイディアの中の一つである、「この軌跡で魔法が描けたら良いな、魔方陣グルグルのククリみたいなことしたい」というアイディアを実現するべく、色々と試行錯誤してみました。しばらくウンウンとやってみた結果、何とか魔法が使えるようになり Magic VR というプロジェクト名で現在制作しています。本エントリでは、その内容を紹介させていただければと思います。

本当は年内公開の予定で作ろうと思っていたのですが、しばらく別のことをしていて開発が止まっていたので完成には至っていません...。取り敢えず、10/25 の DCExpo で行われた OcuFes 開発者会の懇親会に持って行ったバージョンよりは認識精度も良くなったので、α版も配布してみようと思います。

デモ

スクリーンショット

空間に線が描けて...、

f:id:hecomi:20141218012019p:plain

図形を描くと描いた軌跡に合わせて魔法陣発動!

f:id:hecomi:20141218012316p:plain

触ると魔法を発動!

f:id:hecomi:20141219234054p:plain

10月末バージョン 動画

12月現在バージョン 動画(作成途中)

魔法陣を自前で描いて描いた図形にフィットするようにしました。

α版

取り敢えず魔法が描けて撃てるだけのバージョン配布予定(2014/12/21)

ルール

現在のところ、以下のルールで作成しようと作っています。

  • 人差し指で空間に線を描ける
  • 円・三角、四角・星などの図形を描くと属性の異なる魔法陣を召喚
  • 魔法陣に触れると魔法陣に紐付いた魔法を発動
  • 魔法陣の重ねがけで発動する魔法を重ねがけする魔法陣に応じて強化

スケールしそうなアイディアがあれば是非教えてください!

図形認識の仕組み

どうやって検出しようか最初はうんうん考えました。Leap Motion SDK にも円の認識が入っており、小さく速くくるくるしてスクロールする、といったジェスチャ用には良いのですが、色々な大きさや形・速度で描く魔法陣用には使えないのでやめました。

カメラから平面に軌跡を射影して画像ベースの認識することも考えましたが、処理が重たそうなのと法線情報が失われるのでやめました。

そこで色々と調べたり考えた結果、Leap Motion によって軌跡が綺麗に取れていることから、素直にこの時系列に並んだ点群の位置関係を元に計算する方式に決めて検討したところ、比較的すぐに出来て、高校数学程度で検出可能になりました。認識部分の全体のコード(Unity/C#)はこちらになります。

コード自体は Leap Motion に依存しない形で書いていますので、他でも流用できると思います。もう少し整理したらサンプルプロジェクトとともに github の方へ上げます。

それではコードを簡単に解説します。図形は主に円認識と多角形認識に分かれていますので分けて説明します。

円の認識

円の認識はとても簡単でそこそこロバストです。円は中心からの距離が一定となる図形なので、これを利用します。まず軌跡の平均点を中心と仮定し、そこから各軌跡上の点への距離の平均を半径と仮定、その半径と実際の距離の誤差の絶対値の総和が閾値以下だった場合に、現在の点が始点と近ければ円として認識しています。

f:id:hecomi:20141217225640p:plain

認識した後に魔法陣を描画する位置はこの平均点で、また隣接する3点からなる外積の平均を取れば法線がとれるので姿勢も分かります。コードで書くとこんな感じです。

アイディア自体は以下の stackoverflow の質問を参考にしました。

正多角形の認識

正多角形については、円よりも苦労したのですが、頂点をベースに認識する方式にしました。まず何らかの方法で頂点を認識し、その隣り合う頂点を結ぶ線を辺として、隣り合う辺のなす角度たちがその図形の角度の許容範囲内(正三角形なら 60 ± 15°など)であり、かつ最初の頂点と最後の頂点が近ければ正多角形と認識する、という方式です。

f:id:hecomi:20141218001555p:plain

で、どうやって頂点の認識をしているかというところですが、今のところ停止点の検出と鋭角の認識のハイブリッドにしています。

停止点は一定時間の間の移動距離が閾値以下だった場合に検出されたとします。主にジェスチャの開始点や終了点のために使いますが、認識しない時に各頂点でしばらく待っていることで、より高確率で魔法を出すことができます。

f:id:hecomi:20141218001547p:plain

本当は距離の絶対値を積分するべきですがサボったのでコードはこんな感じです。

鋭角認識については、実際の頂点部分は 3 点よりも多い点で形成されるため、過去の数点を使って認識してあげなければなりません。つまり加速度変化を見る方式では、スレッショルドを超えたことを認知は出来ますが、それが何度なのかは認識できません。そこで数点飛ばしの 3 点を前後に振って見て、最も鋭角なところが閾値以下だったら鋭角な頂点として認識する形にしています(下図左)。

f:id:hecomi:20141219014926p:plain

ただし、上図右のように早く図形を描いた時は鋭角の位置を誤認識してしまうこともあるので、ここらは要改良です。コードはこんな感じです。

エフェクトでの工夫

自分の行動に対して色々なフィードバックがあると、精度を上げるためのヒントになったり学習の加速につながると考え、いくつかエフェクトを入れてみようと試行錯誤しています。

軌跡に応じたサウンドフィードバック

Unity のオーディオの再生・エフェクト・解析周りについてまとめてみた - 凹みTips でも書いた Unity の OnAudioFilterRead() を利用して、指の速度に応じた音を生成して鳴らしています。まだあまり良いアルゴリズムは思いついていなくて、今は単純に周波数だけ変えて鳴らしています。

頂点認識の表現/効果音

アルゴリズムでは頂点認識は多角形認識に必須なので、このタイミングで認識した空間上の頂点から白いパーティクルを出すとともにシュッという SE を鳴らしています。ただ、これは現状問題点があり、多角形認識が頂点を描いてから数点後に認識するため、頂点を描いた瞬間に SE が鳴らせず、逆に遅延による違和感を感じてしまいます。加速度変化のタイミングで鳴らす、といった方式へ変更したほうが良いかもしれないと思っています。

このあたり認識精度や気持ちよさにより直結しそうに思われるので、もっと時間をかけて掘ってみようと思います。

おわりに

VR 内における汎用的な UI を考えるのはとても難しく、技術的な課題も多いです。しかしながら便利かは分からないけれど気持ちのよい UI、ゲームとして楽しい UI として考えると色々なアイディアが湧いてきます。魔法という題材は便利で、認識に練習が必要だとしても、練習次第で再現性があるものにすることが出来れば、楽しく遊べるものにすることが出来ると思います。

ゲームとしての完成はもう少し先になりそうですが、コツコツと進めていきたいと思いますので、何かフィードバックなどありましたら Twitter などでご連絡ください。

明日は @shigekzishihara 先生のVRで感性評価の記事です!楽しみです。

筋電によるジェスチャコントロールが可能なアームバンド Myo が届いたので遊んでみた

はじめに

先週、昨年の 6 月にプリオーダーしていた Myo の Developer Kit が届きました。

f:id:hecomi:20141206190843j:plain

f:id:hecomi:20141206190937j:plain

f:id:hecomi:20141206191053j:plain

Myo は Thalmic Labs 社によって開発されたワイヤレスなアームバンド型のジェスチャコントローラです。

価格はプリオーダー時が $149、現在は $199 です。

Buy Preorder Myo Gesture Armband Controller by Thalmic Labs

ハードウェアとしては 9 軸の IMU センサと医療レベルの EMG(表面筋電位)センサ、ハプティック用にバイブレータ、無線接続用に BLE (PC とペアリングするドングルも同梱)、そしてジェスチャの解析用に ARM Cortex M4 プロセッサを搭載しています。用途としては、PC の操作(プレゼン支援など)やスマホの操作(音楽再生・音量変更など)といったことが可能ですが、公開されている SDK に Unity 用のパッケージも含まれており、これを利用することで簡単に Myo を利用したゲームなども作成可能です。今年の 3 月あたりから Oculus Rift と連携した動画も公開されていました。

試作品を私は、Developer Kit と製品版両方注文していたのですが、先週 Developer Kit の方が届いたので遊んでみました。

デモ

取れるジェスチャの種類

Leap Motion のように自由自在、というわけには行かず、現状認識可能なジェスチャは、リラックス状態、ダブルタップ(親指と人差指 or 中指で2連続でタップ)、グー、パー、内スワイプ(内側に腕を曲げた状態 )、外スワイプ(外側に腕を曲げた状態)の 6 状態です。これに加えて、IMU センサによって回転が取れる形になります。

f:id:hecomi:20141210225838p:plain

SDK の外観

SDK のページは以下になります。

スタックを見てみると以下のようになっています。

f:id:hecomi:20141210014514p:plain

BLE からの情報を libmyo が受け取って各言語にバインディングしている形でシンプルです。

Unity のサンプルと仕組み

サンプル

SDK をダウンロードすると unitypackage が同梱されているので、これを展開することで Myo を Unity 上で扱えるようになります。

f:id:hecomi:20141210232240p:plain

サンプルとして Box On A Stick シーンが含まれており、腕の姿勢をオブジェクトに反映したり、ジェスチャに応じてオブジェクトの色を変更したりバイブレーションを Myo に指示したり出来るようになっています。

Unity 用 SDK の仕組み

Plugins 下に Mac/Win 用の libmyo が入っており、これを Myo/Scripts/Myo.NET/libmyo.cs から DllImportバインディングしています。それを Myo/Scripts/Myo.NET/Myo.cs でイベント(e.g. ジャイロセンサのデータが渡ってくる GyroscopeData やジェスチャが変化した際にジェスチャに種類が渡ってくる PoseChange)や関数(e.g. ジェスチャ認識を再開する Unlock や Myo 本体をバイブレーションさせる Vibrate)とユーザが利用する形でラップしています。この libmyo.cs から別スレッドでイベントをポーリングしたり、その結果を Myo.cs とよしなに結びつけてくれるのが Myo/Scripts/Myo.NET/Hub.cs で、Myo はマルチペアリングを想定しているので、その辺りの割り振りもここで行っています。そして、Myo/Scripts/ThalmicHub.csMyo/Scripts/ThalmicMyo.csMonoBehaviour を継承したこれらのクラスのラッパーになり、Unity の世界と結びつけてくれるスクリプトになります。

そしてスクリーンショットのように ThalmicHub.cs をアタッチした GameObject の子に使いたい Myo の数だけ ThalmicMyo.cs をアタッチした GameObject を配置しておきます。これにより、該当の ThalmicMyo から色々なデータを引っこ抜けるようになります。

自然言語だと分かりにくいと思うので、Myo Samples/Scripts に入っているサンプルコードを見てみます(サンプルコード自体はシンプルなのですが、分かりやすいように大幅に書き換えています)。まずはジェスチャに応じてマテリアルを差し替えるスクリプトです。

ColorBoxByPose.cs
using UnityEngine;
using Pose = Thalmic.Myo.Pose;

#pragma warning disable 108 
public class ColorBoxByPose : MonoBehaviour
{
    public GameObject myo;
    
    public Material waveInMaterial;
    public Material waveOutMaterial;
    public Material doubleTapMaterial;
    
    private Pose lastPose_ = Pose.Unknown;
    private ThalmicMyo myo_;

    private Renderer renderer_;
    private Renderer renderer 
    {
        get { return renderer_ ?? (renderer_ = GetComponent<Renderer>()); }
    }
    
    void Awake()
    {
        myo_ = myo.GetComponent<ThalmicMyo>();
    }
    
    void Update()
    {
        // 前回と比較して変化があればイベント発火
        if (myo_.pose != lastPose_) {
            lastPose_ = myo_.pose;
            
            // ジェスチャに応じて処理を分ける
            switch (myo_.pose) {
                case Pose.Fist: 
                    myo_.Vibrate(Thalmic.Myo.VibrationType.Medium);
                    break;
                case Pose.WaveIn:
                    renderer.material = waveInMaterial;
                    break;
                case Pose.WaveOut:
                    renderer.material = waveOutMaterial;
                    break;
                case Pose.DoubleTap:
                    renderer.material = doubleTapMaterial;
                    break;
            }
        }
    }
}

myo.cs でハンドルしているイベントは非同期のスレッドで走っているため、イベントのタイミングでマテリアルの設定は出来ません。最新の PoseMyo クラスのメンバの pose に保存されているので、それを前回と比較しながら変化があったらそれに応じた処理を行う、という形になっています。

次に Myo の角度にオブジェクトを合わせるサンプルです。サンプルでは JointOrientation.cs がそれにあたりますが、簡単に書きなおしたものが以下になります。

MyoSensorDataToTransform
using UnityEngine;
using System.Collections;

public class MyoSensorDataToTransform : MonoBehaviour
{
    public GameObject myoObject;
    public TextMesh accelText, gyroText;
    
    private Vector3 offsetRotation_ = Vector3.zero;
    private ThalmicMyo myo_;

    void Awake()
    {
        myo_ = myoObject.GetComponent<ThalmicMyo>();
    }

    void Update()
    {
        // 姿勢情報は ThalmicMyo がついた GameObject に入っている
        var rotation = myoObject.transform.rotation.eulerAngles;
        rotation = new Vector3(-rotation.x, rotation.y, -rotation.z);
        
        // 現在の位置を基準の角度にする
        if (Input.GetKeyDown(KeyCode.R)) {
            offsetRotation_ = rotation;
        }
        
        // 回転
        transform.rotation = Quaternion.Euler(rotation - offsetRotation_); 

        // 加速度やジャイロの生値も使える
        accelText.text = myo_.accelerometer.ToString();
        gyroText.text = myo_.gyroscope.ToString();
    }
}

これで腕の向きがオブジェクトに反映されます。その他のイベントやパラメタについては ThalmicMyo.cs やドキュメントをご参照下さい。

EMG 生値について

Myo は今のところ EMG の生値にアクセスすることが出来ません。しかしながら生値のストリームへのアクセスは、近日中(12月中)に可能になると公式ブログで述べられています。

未だ公開していない理由としては、バッテリーの問題や生値の取扱が難しいことなどが上げられています。異なる二人のデータを比較しながら説明したりと、面白い内容になっていますのでぜひご一読下さい。

なお、既にアクセスしている人もいますので先んじて扱いたい場合は以下を参考に可能かもしれません(私はまだ未確認です)。

Tips

使っているうちに認識が微妙になったのですが、Myo Armband Manager からキャリブすると精度が向上しました(代わりに他の人がつけるとあまり認識しない感じになっているかもしれません)。

おわりに

アームバンド型であることから手のひらには何も装着しなくてよく、他のデバイスとの相性も良いと思います。また生値が扱えるようになれば、手の形は同じだけれど力を入れている、といったようなビジョンベースでは取れない情報も取ることができるようになると思われるので、面白いインタラクションが可能になるのではないでしょうか。今後のアップデートに注目です。