読者です 読者をやめる 読者になる 読者になる

凹みTips

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

軽量な AR ライブラリの AruCo を Mac で使ってみた

はじめに

AruCoBSD ライセンスで配布されている OpenCV をベースにした軽量な AR ライブラリです。

以下の様な 2 次元マーカを 1024 個まで同時に認識可能です。

f:id:hecomi:20140405221655j:plain

例としては Oculus Rift 用のステレオカメラである Ovrvision の SDK が利用しています。

2 次元状にマーカを配置したボードを利用してロバスト性をあげる仕組みも入っています。

また他言語のバインディングや実装もあります。

本エントリでは、この AruCo を Mac で利用する方法について簡単にまとめてみました。

ビルド

AruCo のページの Download のところから「Download ArUco」および「Download ArUco Test Data」を開き、sorceforge からそれぞれ「aruco-1.2.5.tgz」と「aruco-1.2.2-testdata.zip」を落としてきて解凍します。

CMake を使ってビルドするのですが、Mac の標準の clang は OpenMP に対応していないのでそのままビルドするとエラーが出ます。

$ cmake .
...()...
$ make
Scanning dependencies of target aruco
[  3%] Building CXX object src/CMakeFiles/aruco.dir/ar_omp.cpp.o
[  7%] Building CXX object src/CMakeFiles/aruco.dir/arucofidmarkers.cpp.o
[ 11%] Building CXX object src/CMakeFiles/aruco.dir/board.cpp.o
[ 15%] Building CXX object src/CMakeFiles/aruco.dir/boarddetector.cpp.o
[ 19%] Building CXX object src/CMakeFiles/aruco.dir/cameraparameters.cpp.o
[ 23%] Building CXX object src/CMakeFiles/aruco.dir/chromaticmask.cpp.o
[ 26%] Building CXX object src/CMakeFiles/aruco.dir/cvdrawingutils.cpp.o
[ 30%] Building CXX object src/CMakeFiles/aruco.dir/highlyreliablemarkers.cpp.o
[ 34%] Building CXX object src/CMakeFiles/aruco.dir/marker.cpp.o
[ 38%] Building CXX object src/CMakeFiles/aruco.dir/markerdetector.cpp.o
In file included from /Users/hecomi/Downloads/aruco-1.2.5/src/markerdetector.cpp:36:
/Users/hecomi/Downloads/aruco-1.2.5/src/./ar_omp.h:31:10: fatal error: 'omp.h' file not found
#include <omp.h>
         ^
1 error generated.
...()...

CMakeList.txt を編集して OpenMP をオフにします。

CMakeList.txt:78-81
ELSE()
    # 以下の 2 行をコメントアウト
    # add_definitions(-DUSE_OMP -fopenmp)
    # set (REQUIRED_LIBRARIES ${REQUIRED_LIBRARIES} -lgomp)
ENDIF()

これでビルドが通るようになります。

$ rm CMakeCache.txt
$ cmake .
$ make

ビルドすると src の中に libaruco.1.2.5.dylib があります。utils にはマーカを生成する aruco_create_marker など様々なツールが入っています。例えば、aruco_create_marker は、

$ ./aruco_create_marker 123 123.png 256

で 123 番の 256px のマーカ画像が生成されます。

サンプルを動かす

では、試しにサンプルコードを動かしてみます。まず分かりやすいように作業ディレクトリにライブラリとヘッダをコピーしておきます。

$ cd YOUR_WORK_DIR
$ mkdir include lib
$ cp ARUCO_INSTALL_DIR/src/*.h include/.
$ cp ARUCO_INSTALL_DIR/src/libaruco1.2.5.dylib lib/.

また、「aruco-1.2.2-testdata.zip」に入っているカメラパラメタの YAML と、サンプルの画像もコピーしておきます。

$ cp ARUCO_TEST_DATA/board/intrinsics.yml .
$ cp ARUCO_TEST_DATA/board/image-test.png .

そして次のようなコードを書きます。

aruco.cpp

#include <opencv2/opencv.hpp>
#include <aruco.h>

int main(int argc, char* argv[])
{
    // マーカを含む画像
    cv::Mat inputImage = cv::imread("./image-test.png");

    // カメラパラメタのロード
    aruco::CameraParameters params;
    params.readFromXMLFile("./intrinsics.yml");
    params.resize(inputImage.size());

    // マーカを認識する
    aruco::MarkerDetector detector;
    std::vector<aruco::Marker> markers;
    const float markerSize = 0.04f;
    detector.detect(inputImage, markers, params, markerSize);

    // 結果を書き出す
    auto outputImage = inputImage.clone();
    for (auto&& marker : markers) {
        std::cout << marker << std::endl;
        marker.draw(outputImage, cv::Scalar(0, 0, 255), 2);
        aruco::CvDrawingUtils::draw3dCube(outputImage, marker, params);
    }

    // 結果を書き出す
    cv::namedWindow("input");
    cv::imshow("input", inputImage);
    cv::namedWindow("output");
    cv::imshow("output", outputImage);
    cv::waitKey(0);

    return 0;
}

そしてビルドして実行してみます。

$ clang++ main.cpp -Llib -Iinclude -laruco.1.2.5 -lopencv_core -lopencv_highgui -std=c++11 -o test
$ ./test

64=(292.438,281.347) (291.78,218.483) (344.908,218.539) (345.1,279.01) Txyz=0.0110084 0.00691255 0.421726 Rxyz=-3.05481 0.0152297 -0.695623 
76=(460.639,335.036) (461.067,283.905) (495.496,281.239) (494.672,329.868) Txyz=0.144801 0.054702 0.496637 Rxyz=-3.06084 0.0171299 -0.74177 
...(以下認識したマーカの結果が出力される)...

f:id:hecomi:20150617191135p:plain

無事出力されました。

おわりに

基本的なマーカの情報は勉強要らずで簡単に使えてとても便利です。ボードの認識や 1.2.5 で追加された Chromatic Mask などは追々試してみます。

現実の手を直接 VR 内に持ち込めるようになった Leap Motion Core Asset v2.3.0 を詳しく調べてみた

はじめに

VR 向け Leap Motion アセットに再び神アップデートがきました。

いくつかアップデートがある中で目玉は「Image Hand」という機能で、従来は 3D のモデルを認識した手の形状に合わせて動かしていたのに対し、カメラで取得した実際の手の領域を直接描画するモードが追加されました。従来同様当たり判定も効く上にオクルージョン(VR 内の 3D オブジェクトの後ろに回りこむような表現)も再現されています。

本エントリでは、Image Hand に焦点を当てながら、前回(VR の世界に手を持ち込める Leap Motion VR の仕組みを調べてみた - 凹みTips)との差分などについて解説したいと思います。

環境

現在、OVRSDK 0.6.0beta へ対応中とのことです(動くかどうかは試してないです)。

デモ

公式の 500 Blocks デモを動かしてみました。

デモで遊ぶ

公式のデモがアップデートされているのでダウンロードすれば直接遊ぶことが出来ます。各環境をアップデートしておきましょう。

Unity で触ってみる

以下のリンクから Unity Core Asset v2.3.0 がダウンロードできます。

LeapMotionCoreAssets_2_3_0.unitypackage をインポートすると以下の様な階層にサンプルシーンが含まれています。

f:id:hecomi:20150524155149p:plain

実行すると実際に遊ぶことが出来ます。

アップデート内容

詳細は後述しますが、アップデート内容についてざっと見てみます。

Enhanced passthrough experience

  • Unity でのパススルー画の表示を 2 msec 削減
    • Update() タイミングから OnPreRender() タイミングにしたこと?
  • パススルー画表示アセット利用時の Oculus Rift の IPD 補正の自動化
    • Leap Motion のカメラと Oculus の Configuration Utility で設定した IPD との補正を自動で行う

Image Hands

  • 前述のようにリアルの手を持ち込んでインタラクション出来るようになった

Other updates and bug fixes

  • 新しいパススルー画表示の仕組み
    • ↑の内容と併せて後述します
  • その他バグフィックスもろもろ

これらについてより詳しく見ていきます。

Image Hand の表示の仕組み

はじめに一番面白い「Image Hand はどうやって動いているか」について見ていきます。

オクルージョン表現

まずオクルージョンの表現についてです。はじめに勘の鋭い人はこれ一枚で仕組みが分かるかもしれません。

f:id:hecomi:20150525224808p:plain

どうでしょう?何か 3D モデルの腕のところにカメラで撮った指が映っているのが見て取れると思います。実は背景と腕にスクリーンスペースで Leap Motion のカメラ画を適用しています(モデルの UV 情報に関係なくスクリーン座標でテクスチャを貼り付けている)。これによってカメラから見ると手のモデルは完全に透けて見えるような形になります。しかしこの手の 3D モデルだけ他のオブジェクトとの前後関係を考慮するようにレンダリングすることで、他のオブジェクトの奥にある場合は他のオブジェクトを、前にある場合にはこのスクリーンスペースのテクスチャを貼り付けた手を表示することができ、オクルージョンが再現できるわけです。

この手のモデルは実際の手よりも少し厚めになっています。なので Leap Motion の認識がちょっと遅れて指がついてきたりずれていたりしても、大きな破綻なくそのままの手を透かして見ることが出来ます(後述しますが条件によってははみ出して見える時もあります)。

f:id:hecomi:20150526001005p:plain

手の縁が光る表現

ではこの手の縁が良い感じに光る表現はどうやっているのでしょうか。光っている画像と光っていない画像を並べてみます。

f:id:hecomi:20150526003204p:plain f:id:hecomi:20150526003215p:plain

...そうです、お察しの通りいい感じの灰色をいい感じの青色にスレッショルドをパラメタ調整して変えているだけです。なので赤外線をよく反射する物体が後ろにあり、それがスレッショルドとかぶる輝度をしていると、そこも青く光ってしまうわけですね。

基本はこれだけなのですが、もう一つ、インタラクションした物体との境界でグローする表現が加えられています。

f:id:hecomi:20150526010614p:plain

これはカメラの深度テクスチャを利用して表現しています。詳しくはコードを見てみましょう。

シェーダのコードで見てみる

簡易化したコードを簡単な説明付きで以下に示します。

Shader "LeapMotion/Passthrough/ImageHandHighlight" {
    ...
    CGINCLUDE

    #define USE_DEPTH_TEXTURE

    frag_in vert(appdata v) {
        frag_in o;
        o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
        float3 norm = mul ((float3x3)UNITY_MATRIX_IT_MV, v.normal);
        o.vertex.xy += TransformViewToProjection(norm.xy) * _Extrude;
        o.screenPos = ComputeScreenPos(o.vertex);
        #ifdef USE_DEPTH_TEXTURE
        o.projPos = o.screenPos;
        // COMPUTE_EYEDEPTH は頂点の視点空間デプスを計算して出力する
        COMPUTE_EYEDEPTH(o.projPos.z);
        #endif
        return o;
    }

    float4 trackingGlow(float4 screenPos) {
        // Leap で取得した画の輝度情報を取得
        float4 leapRawColor = LeapRawColorBrightness(screenPos);
        // スレッショルドでクリッピング
        clip(leapRawColor.a - _MinThreshold);
        // リニアにする
        float3 leapLinearColor = pow(leapRawColor.rgb, _LeapGammaCorrectionExponent);
        // いい感じのグレー部分(= 手の領域)だけ抽出
        float brightness = smoothstep(_MinThreshold, _MaxThreshold, leapRawColor.a) * _Fade;
        // いい感じのグレー部分(= 手の縁)だけ抽出
        float glow = smoothstep(_GlowThreshold, _MinThreshold, leapRawColor.a) * brightness;
        // 縁を指定した色で光らせる
        float4 linearColor = pow(_Color, _ColorSpaceGamma) * glow * _GlowPower;
        // 混ぜる
        return float4(leapLinearColor + linearColor, brightness);
    }

    #ifdef USE_DEPTH_TEXTURE
    float4 intersectionGlow(float4 handGlow, float4 projPos) {
        // カメラ深度テクスチャから該当ピクセルの z 座標を取得
        float sceneZ = LinearEyeDepth(SAMPLE_DEPTH_TEXTURE_PROJ(_CameraDepthTexture, UNITY_PROJ_COORD(projPos)));
        // COMPUTE_EYEDEPTH で取得した頂点の視点空間での z 座標を取得
        float partZ = projPos.z;
        // 差を良い感じにスレッショルドを設けてスムージング
        // _Intersection ~ 0 付近の細かい段差部分が光る
        float diff = smoothstep(_Intersection, 0, sceneZ - partZ);
        // リニアにすると同時に輝度の強さをパラメタ調整
        float4 linearColor = pow(_Color, _ColorSpaceGamma) * _IntersectionEffectBrightness;
        return float4(lerp(handGlow.rgb, linearColor.rgb, diff), handGlow.a * (1 - diff));
    }
    #endif

    // グロー部分を描く
    float4 frag(frag_in i) : COLOR {
        // 縁を光らせる
        float4 handGlow = trackingGlow(i.screenPos);
        #ifdef USE_DEPTH_TEXTURE
        // 3D オブジェクトとの境界を光らせる
        handGlow = intersectionGlow(handGlow, i.projPos);
        #endif
        return float4(handGlow.rgb, _Fade * handGlow.a);
    }

    // Leap Motion の画でクリッピングをする
    float4 alphaFrag(frag_in i) : COLOR {
        // 後で ColorMask 0 で切り抜く
        float4 leapRawColor = LeapRawColorBrightness(i.screenPos);
        clip(leapRawColor.a - _MinThreshold);
        return float4(0,0,0,0);
    }

    ENDCG

    SubShader {
        Tags {"Queue"="AlphaTest" "IgnoreProjector"="True" "RenderType"="Transparent"}
        Blend SrcAlpha OneMinusSrcAlpha

        // 1-pass 目はクリッピングを行う
        Pass {
            ZWrite On
            ColorMask 0

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment alphaFrag
            ENDCG
        }

        // 2-pass 目は手の画像や青く光るグロー部分を描く
        Pass{
            ZWrite Off

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            ENDCG
        }
    } 

    Fallback "Unlit/Texture"
}

うーん、とてもおもしろいですね。

追記(2015/05/27)

すみません、早とちりしていましたので上記説明を修正しました。具体的には、1-pass 目に通常の画を描いて 2-pass 目でグローを描いていると思っていたのですが、1-pass 目はクリッピングを行い、2-pass 目にグローを含む手全体の画像を描画していました。

パススルー画の高速化

概要

次にパススルー画の高速化について見てみます。基本的な戦略としては、以前は MonoBehaviour.Update() のタイミングで画を取得していたのに対し、今回から SyncModeLOW_LATENCY の場合は、描画する直前のタイミングで呼ばれる MonoBehaviour.OnPreRender() のタイミングで画を取ってくるようになった点だと考えています。LeapOVRCameraRigLeftEyeAnchorRightEyeAnchor にアタッチされた LeapImageRetriever.cs 内で次のようなコードが記載されています。

public enum SYNC_MODE {
    SYNC_WITH_HANDS,
    LOW_LATENCY
}
public SYNC_MODE syncMode = SYNC_MODE.LOW_LATENCY;

void Update() {
    ...
    if (syncMode == SYNC_MODE.SYNC_WITH_HANDS) {
        _imageList = frame.Images;
    }
}

void OnPreRender() {
    if (syncMode == SYNC_MODE.LOW_LATENCY) {
        _imageList = _controller.Images;
    }
    ...
}

詳細

これに伴い、若干描画方法が複雑になっています。

まず、カメラ画を表示するプレーンは CenterEyeAnchorQuad が担当します。

f:id:hecomi:20150526020712p:plain

ここには LeapImageBasedMaterial.cs というスクリプトがアタッチされており、どちらのカメラの画像を利用するかとシェーダのパラメタの設定、そして Leap Motion から画を取ってくる LeapImageRetriever.cs へ登録を行っています(static 経由)。

public class LeapImageBasedMaterial : MonoBehaviour 
{
    public enum ImageMode {
        STEREO,
        LEFT_ONLY,
        RIGHT_ONLY
    }

    public ImageMode imageMode = ImageMode.STEREO;

    void Awake() 
    {
        ...
    }

    void OnEnable() 
    {
        LeapImageRetriever.registerImageBasedMaterial(this);
        ... (シェーダの設定)
    }

    void OnDisable() 
    {
        LeapImageRetriever.unregisterImageBasedMaterial(this);
    }
}

LeapImageRetriever.cs は左右のカメラに取り付けられていて、この1枚の Quad を共有し、それぞれの OnPreRender() タイミングでテクスチャを書き換えレンダリングを行います。

public class LeapImageRetriever : MonoBehaviour 
{
    public enum EYE {
        LEFT = 0,
        RIGHT = 1
    }
    public EYE eye = (EYE)(-1);
    private static List<LeapImageBasedMaterial> _registeredImageBasedMaterials = new List<LeapImageBasedMaterial>();

    public static void registerImageBasedMaterial(LeapImageBasedMaterial imageBasedMaterial) 
    {
        _registeredImageBasedMaterials.Add(imageBasedMaterial);
        ...
    }

    public static void unregisterImageBasedMaterial(LeapImageBasedMaterial imageBasedMaterial) 
    {
        _registeredImageBasedMaterials.Remove(imageBasedMaterial);
    }

    void OnPreRender() 
    {
        if (syncMode == SYNC_MODE.LOW_LATENCY) {
            _imageList = _controller.Images;
        }

        // それぞれのカメラに応じたテクスチャを設定
        Image referenceImage = _imageList[(int)eye];
        ...
        loadMainTexture(referenceImage);
        ...
        foreach (LeapImageBasedMaterial material in _registeredImageBasedMaterials) {
            if (material.imageMode == LeapImageBasedMaterial.ImageMode.STEREO ||
               (material.imageMode == LeapImageBasedMaterial.ImageMode.LEFT_ONLY  && eye == EYE.LEFT) ||
               (material.imageMode == LeapImageBasedMaterial.ImageMode.RIGHT_ONLY && eye == EYE.RIGHT)) {
                updateImageBasedMaterial(material, ref referenceImage);
            }
        }
    }
    
    private void loadMainTexture(Image sourceImage) 
    {
        Marshal.Copy(sourceImage.DataPointer(), _mainTextureData, 0, _mainTextureData.Length);
        _mainTexture.LoadRawTextureData(_mainTextureData);
        _mainTexture.Apply();
    }
    
    private void updateImageBasedMaterial(LeapImageBasedMaterial imageBasedMaterial, ref Image image) 
    {
        imageBasedMaterial.GetComponent<Renderer>().material.SetTexture("_LeapTexture", _mainTexture);
        ...
    }
}

テクスチャのコピーも for 文を回していたのに対し LoadRawTextureData() を利用するようになってますね。ぱっと見コードは読みづらかったですがやってることはとても単純なので、仕組みがわかればコードは追いやすいと思います。

IPD 補正の自動化

IPD 補正に関しては、CenterEyeAnchor にアタッチされた LeapCameraAlignment.cs が担当しています。

f:id:hecomi:20150526022712p:plain

簡略化したコードを以下に示します。

public class LeapCameraAlignment : MonoBehaviour 
{
    void LateUpdate() 
    {
        LeapDeviceInfo device = handController.GetDeviceInfo();

        // 毎フレーム OVRSDK でセットされる Oculus Configuration Utility
        // で設定した IPD の値が Unity の世界でのカメラ間距離に設定される
        // (Execution Order に注意する必要がある)
        var oculusIPD = rightEye.position - leftEye.position;

        // Leap Motion のカメラ間距離と Oculus Rift の IPD の差の半分
        //   device.baseline: 2つのカメラ間距離
        //   tween: Quick Switch Demo の用に動的に Leap の世界と切り替わる時用
        Vector3 addIPD = 0.5f * oculusIPD.normalized * (device.baseline - oculusIPD.magnitude) * tween;

        // Leap Motion の撮像素子までの z 方向オフセット
        Vector3 toDevice = centerEye.forward * device.focalPlaneOffset * tween;

        // カメラ間距離を Leap に併せてかつ z 方向オフセットを足す
        leftEye.position = leftEye.position - addIPD + toDevice;
        rightEye.position = rightEye.position + addIPD + toDevice;
        centerEye.position = 0.5f * (leftEye.position + rightEye.position);
    }
}

自動で Leap の世界の IPD になってます。Leap のカメラ間距離はちょっと狭い気がするので、次期バージョンではもう少し広がるんじゃないかな、と思ってますがどうなのでしょう。

おわりに

また一段階 Leap Motion が進化した気がします。スクリプト / シェーダの随所に RGB 画へのアクセスについてのコードが仕込まれているので、おそらく公式では次期バージョンである Dragonfly の開発が既に始まっているのではないかと想像できます。今後の展開がとても楽しみですね。

ワイヤレスなブロック同士を簡単に連携できる MESH を手に入れたので現状で出来ることをまとめてみた

はじめに

MESHSONY によるプロジェクトで、タグと呼ばれるワイヤレスのブロックに備わった機能を Canvas というタブレット上のアプリでつなぎ合わせるだけで、タグ同士が連携する仕組みを簡単に作ることが出来るプラットフォームです。

今年はじめに Indiegogo 上で資金調達が開始され、目標の $50,000 に対し、$64,875 の出資が集まりました。私は $149 の Advanced Set(Early Bird)に Back していたので、GPIO タグを含む 4 つのタグが届きました。

f:id:hecomi:20150516125042j:plain

似たコンセプトのプラットフォームとして littleBits がありますが、littleBits はマグネットで接続することに対して、MESH はワイヤレスであるため、それぞれ異なる使い方が出来ると思います(両者を組み合わせてる方も既にいるようです)。

indiegogo で出資した人には届きましたが、販売はまだされておらず、現在準備中とのことです。

関連サイト

スペック

  • 大きさ
    • 24 mm x 12 mm x 48 mm
  • 通信
  • バッテリー
  • 動作温度
    • 0 ~ 35 ℃
  • 同時接続可能数
  • 動作環境

注意が必要なのは現在は iPad でしか動かない点です。Android 対応は検討中のようです。

Getting Started

詳細は省略しますが、セットアップは簡単で、手順に従ってタグ本体のアイコンを長押しするとペアリングが出来ます。レシピを作成し、サイドバーから好きなコンポーネントをドラッグドロップして配置、機能のソケットをつなぎ合わせると、つないだ直後からそれらが連携するようになります。また、発火したイベントはハイライト(本体 LED / Canvas 両方)されるので何が起きたかもリアルタイムに把握することが出来ます。

MESH タグはまだ 4 種類しかありませんが、タブレットの機能と組み合わせたりすれば、アイディア次第でいろいろな使い方ができると思います。例えば以下は Motion タグでなくて iPad のマイクを利用してタップを取得してみた例です。

Canvas

タグの種類

Canvas では MESH Tags、タブレット機能、ソフトウェアタグが利用でき、以下のものが現在用意されています。

  • MESH Tags

    • Button
      • Pressed / Hold / Double
    • Move
      • Shake / Flip / Tap / Orientation
      • Sensitivity や向きの設定が可能
    • LED
      • Light UP / Firefly(ゆっくり明滅)/ Blink / Off
      • 色や明るさ、長さ、明滅間隔が設定可能
    • GPIO
      • Vout Supply / Digital In / Digital Out / Analog In / PWM Out
      • ピンソケットの番号やトリガの種類、スレッショルドなどが設定可能
  • タブレット機能

    • Camera
      • Take Picture
      • フロントカメラか背面カメラ
    • Mic
      • Sound Trigger
      • Sensitivity が設定可能
    • Speaker
      • Play / Stop
      • プリセットから音を選んだり、別途録音した音を使用可能
  • ソフトウェアタグ

    • And
      • Simultaneous(2 つの入力が ON になっているかを調べる)
      • Window(同時と判断する時間の長さ)が設定可能
    • Timer
      • Wait(一定時間後) / Interval(一定間隔)
      • 時間を指定可能
    • Toggle
      • Alternate(順番に出力を切り分ける)

f:id:hecomi:20150517180116j:plain

今後の展開

公式サイトを見ると、カメラやアクションカムといったソニー製の製品との連携や WEB サービスとの連携も現在作成中のようです。

また将来的に自分でソフトウェアタグを追加できる仕組みも検討されているとのことです。

GPIO タグ

いろいろとハックしたいとなったときに面白いのが GPIO タグです。GPIO タグの仕様は以下に公開されています。

2.54 mm ピッチの 10 ピンソケット(Vout、GND、Digital Input x 3、Digital Output x 3、Analog Input x 1、PWMout x 1)が底部についていて、出力は Vo: 3.3 V / 100 mA(電源接続時は 250 mA)です。温度や気圧などのセンサや、サーボやバイブレータなどのアクチュエータを接続して好きな機能を作ることが出来ます。例えばシンプルな例として Vo と Digital Input 1 をリードスイッチで接続して見ると、以下のように接触センサが作れます。

他にも Vo と GND にモータをつないで回転させたり、PWMout を使ってサーボを制御したりも可能です。

PC との連携

Arduino やラズパイと連携したり、これらを経由して PC と連携することも出来ます。例えば Arduino の例として以下の様なスケッチを書き込んで、GPIO タグの Dout と Arduino の Din をつないでみます。

void setup()
{
  Serial.begin(9600);
  pinMode(5, INPUT);
  pinMode(6, INPUT);
  pinMode(7, INPUT);
}

void loop()
{
  Serial.print(digitalRead(2));
  Serial.print("\t");
  Serial.print(digitalRead(3));
  Serial.print("\t");
  Serial.print(digitalRead(4));
  Serial.println("");
}

この Arudino の出力を何かしらで取得してみます。以下は Node.js から取得した例です。

var serialport = require('serialport');
var SerialPort = serialport.SerialPort;
var parsers    = serialport.parsers;

var sp = new SerialPort('/dev/tty.usbserial-XXXXXXXX', {
    baudrate : 9600,
    parser   : parsers.readline('\r\n')
});

sp.on('data', function(data) {
    console.log(data);
});

ここまで来れば自前のシステムや IFTTT などの好きなサービスとの連携はし放題だと思います。試しに hue と連携させてみました。

ただし結構回りくどいので、はやく公式にウェブ連携やソフトウェアタグの追加をサポートして欲しいところです(HTTP アクセス出来るだけでも大分楽なのでなにとぞ...)。

個人的に欲しい機能

常時 ON

現状は Canvas アプリでレシピを立ち上げてないと機能が発火しませんが、常時 ON にしておける仕組みがほしいです。直接 PC と接続できるとか、レシピを書き込めるハブとなる親タグがいるとか、Android に対応してサーバとして裏で動かしておけるとか、そういった仕組みが出来るとプロトタイプにとどまらず日常生活を便利にする所まで延長できると思います。

状態取得

イベントは検知できるのですが、状態が欲しいなと思いました。例えば、傾き + ボタン押下で出す音を変えたいと思ってこんなものを作ってみました。

f:id:hecomi:20150517191615j:plain

が、これはうまく動かなくて Orientation が取れるのは傾けた直後なので、傾けた直後にボタンを押す、とやらないと所望の音が発火しません。ボタンも Pressing / Not Pressed みたいな状態が欲しかったりします。例えば傾きとボタン、後は音のピッチを変えるコンポーネントとか組み合わせて、オリジナルの楽器が作れる、とかそういうところまでカスタム出来るようになってくれると良いなと思いました。

(Pressing 後に Not Pressed が飛んでこなかったらステートが崩れたりする可能性があったり、状態を取得しようとするとアクセスが頻繁になり電池食うとか色々と悩ましいところではあるとは思いますが。。)

生値取得

上記のことを出来るように、ソフトウェアタグで生値を使えるようにして欲しいです。

おわりに

現状でもアイディア次第で色んな面白いものが作れますし、もっとたくさんの MESH タグやソフトウェアタグが増えると、複雑なシステムも10分くらいで簡単に作れるようになると思います。ワイヤレスで小さいのは本当に素晴らしいですね。今後のアップデートに大いに期待したいです。