Leap Motion + Kinect
Leap Motionが届いたので、前からやってみたかったkinectのスケルトンデータとの統合をやってみた。
Leap Motion?
ちょっと早めに届いてたひとのレビュー
http://blog.livedoor.jp/k_yon/archives/52452827.html
http://blog.impartech-lab.org/?p=114
を見るといいと思います。
LM + kinect
で、kinectからのデータと、leapからのデータを適当に変換してやればいいのですが、どちらも右手系で、mとmmが違うだけなので簡単です。とりあえず適当にメジャーで測った精度の悪い定数をハードコードしてます。
すると・・・
世の中に魔法はない!
とりとめのない感想
ポジティブなレビューは他の人に任せておくとして、実際何には使えなさそうかという点から書いてみる。
これを書いた時のSDKがv0.7.3で、ファームウェアもアップデート出来るみたいなので時間が経つと良くなるかもしれない。
まず、LMの動作原理ですが、どうも(広角の)ステレオカメラになってるようです。光源としては肉眼でも見えるぐらい*1明るい赤外線LEDが3つついてます。赤外線カメラで見ても特にパターンを照射してるようには見えないですが、フリッカーが見えるので(部屋を暗くしてゆっくり振ってみると分かります)変調してSN比を上げたりはしてるようです。
で、手というとあまりテクスチャがなくてステレオカメラからの深度の抽出はわりと難しいので、シルエットの方を主に使ってるか、すごく解像度を高く露出時間を短くしてがんばってマッチングしてるかのどっちか(あるいは両方?)だろう。
それはともかく、LMと垂直に手のひらを向けるのがワーストケースで、指がほとんど遮蔽されてしまうわけです。このとき、LMは手に特化したデバイスなのでわりとインテリジェントなpriorがあっていい感じに補完してくれるのかと期待してたがそんなことはなかった。見えないだろうな〜という場面では指はトラッキング結果から消えます。もっとも今後のアップデートですごく良くなる可能性は無くはない。
あと、さっきの例のようにkinectと組み合わせる方法ですが、身体の動きと比較するとLMが動く範囲はかなり狭いので、どっちも固定して使うのはちょっと微妙っぽい。
ただ、動いてる時にはかなり精度がいいので、自然な動作とかモーションキャプチャーとかあんまり考えずに、新しい入力方法を考えるのが良さそうだ。(あるいはシンプルに空中の仮想タッチパネル的な)
*1:可視光に近い赤外LEDは波長の広がりがちょっと可視光とかぶる
手動エスケープは有害である
多くのプログラミング言語は文脈自由文法を使って文法を定義している。その結果、ソースコードは綺麗な階層構造を持つことになる。
そして意味が抽象構文木に対して定義される・・・・本当にそれだけだろうか?
def add(x,y): """ 2数を足す """ return x+y
と書いてあるとき、「2数を足す」の部分が意味をなすのはそれが日本語だからだ。このとき"""..."""の記号は単にある文字列を決めているだけではなく、Python-日本語という違う体系の境界になっている。そして、内側で"を使おうと思ったら、エスケープ文字を使って\"と書いたりするわけだ。
ここまではいい。うまい規則で文字を組み合わせると、文字の種類が有限しかなくてもいくらでも深く階層構造を表せるテクニックがあるということだ。問題は、人間がそれをやってるという現状にある。
言語とファイルの乱立
さっきの例は文字列が短いから特に問題は感じなかったかもしれない。では、次の例はどうだろうか。
var content = '<div class="foo">' + '<h1>Lorem Ipsum</h1>' + '<div>'+x+':'+y+' : <a href="http://www.example.com/get?q='+x+'">xxx</a>' + '</div>';
かなり見にくくて、一見正しいかどうかわからない。実際この例ではdivが閉じていない。普通にHTMLを書いていて、シンタックス・タグ対応ハイライトがあればすぐに分かるにもかかわらず。
で、ここで例えばjQueryを使って、
var content = $('<div/>').addClass('foo'); cotent.append($('<h1/>').text('Lorem Ipsum'); ...
と書けばいいという問題ではない。
それぞれの言語は、それぞれが扱うものが書きやすいように作られている(はず)で、なんでも1つの言語で書けばいいというものではない。*1
こういう問題が今どう対処されているかというと:
- ファイルを分割しまくる
- 言語を作りまくる
ファイルを分割しまくる
1はわりと単純で、違う言語で書きたい部分を別のファイルに分けてロードしたりする。しかしこれには問題がいくつもあって、
- 意味が近いものの場所が離れてしまう
- → ファイル名をつけて管理しないといけない
- 余計なIOが必要になる
- ちょっとだけ混ぜるのが難しい (さっきのxとかyみたいな部分)
特に最後の問題はこの方法だけでは解決できない。
言語を作りまくる
汎用的ながらすごく筋の悪い解決策として、2つ以上の言語の特性をほどほどに併せ持った言語を作るという方法がある。
たとえばweb系でテンプレート言語と呼ばれているものは、django,haml,jinja,mustache...と(言語の数)*(配合の度合い)で無数に存在する。
もうひとつ乱立が目立つのが軽量マークアップ言語と呼ばれているもので、wiki記法に始まりmarkdown,reST,textileと盛り込む機能によって無数に存在し、これらの汎用の構造化テキストとは別に各プログラミング言語のドキュメント記述用言語というのも大量にある。
言語に盛り込む機能と記述の簡潔さにはトレードオフがあり、簡潔な記述は皆の好物なので、用途ごとの特徴セットに対して言語が出来、指数関数的とまではいかなくとも何乗かで増加することになってしまう。そして、Internet of Thingsなどと言われる中、プログラムしたい対象の多様性は増える一方だ。
このままどんどん増加する言語たちに押し潰されるしか無いのだろうか?
エディタの役割
ここで基本に立ち返ってみる。これらの言語をどうやって見て・どうやって書いているか?―エディタだ。*2
ここでもう一度普通のハイライトを見てみよう。
var hoge = "<div>ほげ</div>";
ここでエディタが持っている知識を考えてみる。文字列リテラルのハイライトにはエスケープの仕方のかなりの部分が必要だ。つまりちょっとの手間で境界の中と外を明確に区別できる。そして、このようなエディタはjavascriptもHTMLも知っている。
・・・なぜ両方の文法を同時に適用できないのか?
深いシンタックスハイライト+自動エスケープ
つまりこういうことだ。javascriptとHTMLの両方で同時にハイライトされている。そしてエスケープは暗黙に処理されて、より本来の境界の意味に近い枠で表されている。
動作デモ
http://xanxys.github.com/deep_syntax/
とりあえずplaintext/HTML/javascriptのごく一部をサポートしている。"..."と書くと領域ができるので、そこをクリックすると編集対象が切り替わる。
ちゃんとカーソルを出せてないというところはいいとして(よくない)、表示の仕方は折り畳みできるようにするとか、半透明に重ねるようにするとかも考えられる。とはいえ、デフォルトでは展開している方が読みやすいコードを書くインセンティブが生じていいと思う。
モノリシック文法・マイクロ文法
こうして細かい粒度で言語を切り変えられるのを前提に考えると、1つの言語はどこまでシンプルになれるだろうか?たとえば文字列もコメントも、ちゃんとエスケープできるものが1種類あればそれでいい。文の並びはエスケープ可能な配列の記法があれば言語のコアに含める必要はないのではないか・・・?
今までの文法を多くの機能を摺りあわせたモノリシック文法と呼ぶなら、このように単機能に特化した、部品としての文法はマイクロ文法と呼べるだろう。
今回は異なる系の組み合わせの記述だけを見た。
これを実行時に拡張するとどうなるだろうか。・・・それについては見えてないけど、まだまだ面白いことはありそうだ。
スケールと記述体系の階層構造
ここ半年ほど、わりと数字の計算をもりもりする用途でPythonを使っていまして、思ったことをつらつら書きます。
numpy
Pythonにはnumpyというライブラリがありまして、これは裏でLAPACKみたいな黒魔術産物を呼んで高速に計算しつつ、いわゆるPython風な書き方ができるという便利なものです。
で、その抽象化の中心にあるのが以下の多次元配列です。
typedef struct PyArrayObject { PyObject_HEAD // 本体 char *data; // .shape int nd; npy_intp *dimensions; // 順序 npy_intp *strides; // その他 PyObject *base; PyArray_Descr *descr; int flags; PyObject *weakreflist; } PyArrayObject;
http://docs.scipy.org/doc/numpy/reference/c-api.types-and-structures.html
dataはまあいいとして、ndとdimensionsの対で配列の形(shape)を表します。形というのはnd次元の直方体になります。で、おもしろいのがstridesで、これは何byte進めばひとつの軸だけが1つ進むかというのを各軸に対して持っています。
たとえば以下のようなuint8の配列があったとします。
data = [0,1,2,3,4,5]
shape = [2,3]
strides=[3,1]だと[[0,1,2],[3,4,5]]と解釈され、strides=[2,1]だと[[0,2,4],[1,3,5]]となります。
この表現を使うと、軸の入れ替え・1つ飛ばし・逆順・形の変更などがメタデータを書き換えるだけのO(1)でできます。*1
そんなのいつ使うんだというと、たとえばW×HでRGBAの画像がimg(img.shape=[H,W,4])に入ってる時、ABGRに変換したければimg[:,:,::-1]と書けます。
この辺の話は「ビューティフルコード」
にも1章分使って書かれています。
数値計算?
よくある(?)勘違いが、「数値計算ってスパコンでFortranとか使って流体計算みたいなことをするやつでしょ?そんなのと縁ないよ。」というものです。少なくとも私はそう思ってました。
が、実は数字が並んでるものならなんでもいいのです。画像・音声はもちろん、たくさんの三角系なども楽に表せます。あるいは単にネットワークから持ってきたデータのエンディアンを変換したいというような用途でも使えます。特化したライブラリに比べて数倍遅くなってしまうとはいえ、同じように扱えるのは大きな強みです。
もちろん、N×Mの行列に特化した表現としてnumpy.matrixというクラスがありますが、実はこれはndarrayを継承したクラスで中心となる構造ではありません。(http://projects.scipy.org/numpy/browser/trunk/numpy/matrixlib/defmatrix.py)インタラクティブなシェルで試行錯誤してる時にはそれなりに便利かもしれません。
しかし、本当に「ふつうの」コードでもPythonではnumpyを使わざるを得ない、というよりも、使うことで新しい可能性が見えてくる場合があるのです。
Python
Pythonの標準の実装であるCPythonはみなさんご存知の通り、特にJITコンパイルなども行わない、モダンなV8などに比べると遅い処理系です。
で、Cだとループ回してゴリ押しできるようなことができないのです。基本的に100万回ぐらいになると遅すぎる場合が多いです。100万回というと多いような気がしますが、一要素が数byteならたった数MBを一回なぞるだけです。このぐらいの容量のデータは今時どこでも発生しうるものです。
で、numpyが提供するもっとも基本的な機能は同じ計算を配列全体に適用するというもので、いわゆるデータ並列な処理です。で、この処理は「調整したCの何倍か遅い」ぐらいで動くことが期待できます。基本的にはこれを使って回数の多いループをできるだけ回避することになります。
そして、numpyで小さい配列を作るのはPythonの組込み型と比べるとすごく遅いので、さらにこの傾向は強くなります。
たとえばvs,dvsに[N,3]で点群が入ってるとき(Nはそれなりに大きい)、
for (v,dv) in zip(vs,dvs): do_something(v+dv)
よりも
for v in vs+dvs: do_something(v)
のほうがvs+dvsでvsと同じ容量のメモリが確保されるのにも関わらず、高速に動作します。
縦と横の転換
このように、明らかに同じ事を繰り返しているループを大きい配列に対するひとつの操作で置き換えるのが第一段階です。次の段階は、上の例で言うとdo_somethingの部分、つまりifを使ってたりdictを含んでたりするような処理をnumpyで記述することです。
基本的には「無駄」を許容することにあります。
vs = [...] # 100万要素ぐらい cycle_table = [3.1,2.5,1.3] accum = 0 for (i,v) in enumerate(vs): if v>0: accum += v * cycle_table[i%3]
というコードがある時
import numpy as np vs = np.array([...]) # 100万要素ぐらい accum = (cycle_table[np.indices(vs.shape) % 3] * (vs>0)).sum()
で同じ値が計算できます。もちろんvs<=0のときのcycle_tableを参照した値などは完全に捨てられます。
が、それに問題があるでしょうか?もちろん実際速いのは正義という見方もありますが、もっと根本的な擁護を行うこともできます。
たとえば、何か整数を扱う時intを使うとそれは64bit機では8byteになりますが、実際に使うのはほとんど0に近いような値ばかりで、CPUの中で高い桁の計算をする部分の回路はリーク電流を増やしてるだけになります。が、その無駄は「だいたいどんな整数・アドレスでも表せる」という抽象化のために許容されているのです。スタックを用いることでレジスタの数を考えないのも同様です。
このような計算機をとりまく環境の進歩の延長として、「直感的に把握できない大きさだけど計算機では数msで扱える配列」というのをおいてみてもいいのではないか?ということです。もともとが遅いPythonのような言語では、これは非常にしっくりきます。
この計算はGPUでやることもできて、PyOpenCL(http://documen.tician.de/pyopencl/)などはごく部分的にではありますが、numpy互換のインターフェースを提供することができています。
ヘテロジニアスな階層構造
どうも単一の体系でなんでも表したいという欲求は普遍的に思えます。実際、計算という概念そのものが極めて広範な処理をチューリングマシンで実行できるという証明によって成り立っているわけです。
しかし、悲しいかな、人間の短期記憶は同時にせいぜい数個のものしか覚えることができず、証明はできても、実際に記述することは人間にはできないということはあるのです。
多くの人の長期記憶に入っているであろう自然言語の構造を借りることで、この数の多さに対処しようというのが識別子による部品の管理なわけですが、これにも限界はあります。なぜなら、文章は長い記述により文化や社会といった、これまた共有されている構造を記述できる一方*2、てきとうなフレーズの寄せ集めがつくる上位構造は安定に運用するにはあまりにも人数が少ないのです*3。
この問題に対処する方法は、とにかく上位構造を共有可能な形で記述すること、そのためには異なる複数の記述体系をうまくすり合わせることが必要となります。コメントとFFIは自然言語を緩衝材とする接合ですが、これをもうちょっと計算機側に寄せるとどうなるか?
なかなかおもしろい問題ですし、今回の経験はそれになんかしらの示唆を与えてくれたように感じます。
グラフィカルCUIの可能性
こういう感じのシェルを書いてました。格子の1マスを占めるタイルが単一の機能に相当し、角にある白(出力)と黒(入力)の三角を結んでデータをやりとりします。
左上にあるのが新規プロセス作成タイルで、残りは左下から流れに沿って、
- ls
- 文字列バッファ
- grep
- 文字列バッファ
という風になっています。lsとかgrepのような普通のコマンドはファイルデスクリプタ0,1,2が露出するごく標準的なものですが、文字列バッファというのがちょっと面白いかと思います。
文字列バッファというのは、入力と出力がひとつずつあって、入力されたデータは全て保存しておいて、出力側に何かつながると順次FIFOで出していく、というものなのですが、おもしろいのは保存されてる分が可視化されることです。
無名でデータをおいて置けるので、普通のshellを使っていてありがちな、なにか実行してあとからデータ欲しいが流れてしまって再現もできない、みたいなことが起きません。
方向性
タイルUI
今はタイルの大きさが固定ですが、タイルの大きさを可変にして、複数のマスを使うようにするのは良いかもしれません。
ウィンドウだと自由度はあるもののレイアウトが面倒、完全なタイリングでは位置に意味を持たせにくい、ということがあるので、その間をとって、かなり荒いグリッドにスナップするウィンドウというのはおもしろいかもしれません。
マルチメディア
CUIでかなり不満なのは、画像や音声の扱いです。CUIの便利さは、流れる表示にある程度可読性があることに依存していて、これらの連続メディア、あるいはテキストでもXMLのようなものはあまりうまく扱えず、目grep的な熟練が必要、多くの場合単なるblobとして扱うことになります。
結局のところデータ型が文字のストリームだけでは不足、という話なのですが、際限ない複雑化をどう抑制するか?ということに関して↓に続きます。
π計算
標準的な文字列操作プログラム群は一種のhomoiconicityを実現していて、これが強力さを支えています。一方、ここで書いたのは(高階でない)データフロー記述であって、forのような制御構造ですら扱いに困る、という貧弱なものです。
そこで、プロセス計算、特にπ計算に目を向けてみると、チャネルを流れるのはチャネルそのもののみながら、非常に強力であることがわかります。
ここで、前の記事π算法とIOの後ろの方で使ったリテラルの扱いをふまえて他のメディアに一般化してみます。
つまり、文字は見えないチャネルの先に「実体」があり、文字のストリームはそのリンクを連続で送っている、と見ると、その他のメディアでも、どこかに実体を用意して、その一側面を表すリンクを送ればよい、ということになります。
だいたいどのような記号系でも、意味は構造にのみ存在するのですが、π等のよいところは、その一部分を簡単に現実に接地できることです。
指から文字がどんどん出てくる:5日目
さて、しばらく日が空いてしまいましたが、ようやく光通信でプログラムを更新できるようになりました。
クレードル
こんな感じで、書き込みはarduinoに制御させてます。ちなみに二枚目の写真に写ってる指輪は、デバッグ用のために同じ回路でISPコネクタを付けて作ったものです。発電用のフォトダイオードの数に余裕が無いので電源はケーブルから供給してます。
こんな感じでクレードル(XFR1P)と指輪(XFR1)の間は
- 外側:青色光: 給電
- 内側:赤色光: リセット
- 側面:赤外線: 通信(half-duplex)
という三種類の光でリンクが張られます。青と赤の選択には特に意味はありません
こんな感じで内側が赤く光ると、リセットピンにぶら下げてあるフォトトランジスタによってassertされます。XFR1のブートローダは、リセット後に赤外線が出ていればデバッグモードに入り、そうでなければ通常モードでユーザプログラムを実行します。
そして、デバッグモードに入ったXFR1は適当に通信してflashを書き換えたり・電圧を取得したりできるようになります。avr/boot.hを使うと便利です。
一方、PC側ではpythonのコードが走ってintel hex形式のバイナリをページ単位に区切って転送したり、ベリファイしたりします。まぁ普通のライタと同じですね。
ちなみにソースコードはgithubに移した( https://github.com/xanxys/XFR1 )ので、気になる人は見てみると良いかと思います。
組立
さて、これまではケミカルウッドを削り出したものに部品を接着して配線していたのですが、後から配線を傷つけずに見た目を整えるのが難しいという問題がありました。また、内側がアクセスしにくいというのも問題です。
何らかのモジュールに分解して、電気的・構造的接続は簡単にできる、というのが理想的です。普通に設計するなら、電気的な部分と構造的な部分を分離して、フレキシブル基板をリング状の構造材で挟むような構成になるでしょうが、なかなか大変です(時間と金的な意味で)。
ということで、1cm角ぐらいのモジュールいくつかにそれぞれ部品を封入してしまって、端子をはんだ付けすることで全体の構造をつくり、問題なければ接合部をさらに封入、という手順でいこうかと思います。
指から文字がどんどん出てくる:4日目
色々と要素を検証してきた結果、ようやく指輪のスペックが固まって来ました。
左の部品群をなんとかリング状にまとめれば(たぶん)完成です。右はデバッグ用。
電力
2日目ではフレキシブルなパネルを試してましたが、切断すると断面でショートしてしまうのか、効率がかなり低下するようです。そこで、わりと大きめのフォトダイオードをアレイにする方針に切り替えました。写真に写ってるS6775を8個直列にすると、室内でも2V以上で数μAが確保できるようで、これならなんとかなる気がします。
通信
ちょっと不本意ですが、とりあえず動くのが分かっている赤外線通信にします。ただ、通信速度はプロトコルを工夫すれば改善する可能性が高いので↓の話に続きます。
指から文字がどんどん出てくる:3日目
1日目にはスロットという概念を導入し、接触による交換を基本とした(というかそれしかできない)操作モデルをつくりました。が、同じ文字列を何回も入力するのはどうするか?とか、使わずに破棄するにはどうするか?といった問題が発生します。そこで、操作は変更せずにスロットを拡張することで解決を試みます。
形式変換・複製・破棄
さて、実は前回にひっそりと「各スロットは自分の持っている文字列を何らかの方法で示してもいいし、示さなくてもいい」と書いていました。これはスロットが内部に保持しているデータを自動でコピーして、それを人間が読める画像に変換していることに他なりません。この見方をすると、例えば文字列をQRコードとして表示するとか、読み上げるとか、いろいろなバリアントが考えられます。
そして形式を維持した複製には、主スロット+副スロットがあって、副スロットの内容を頻繁に主スロットの内容で上書きする、というような表現が考えられます。破棄というのは単に空の文字列で定期的に上書きされるスロットを用意すれば良いのです。
表現
スロットの振る舞いは図形的に表すことも考えられて、例えば↓のようなものがありえます。
形式の表現にはᚢᚹ(U8,UTF-8表現)、ᛗ(人間、触れる場所)のようにルーン文字を使ってます。私はこういうのが大好きなので、たぶんこの系列のデザインはどこかに取り入れるでしょう。
さて
スロットの振る舞いを変えることの利点は、操作を単純に保ったまま機能をいくらでも増やせることと、その説明と機能を同じように表現できることに有ります。例えば、二回タップすると〇〇、長く押すと△△、みたいなUIは際限なく覚えないといけないシーケンスが増える可能性がありますし、そのような時間的系列の説明はどうしても実際の動作とは分離して表現されることになります。
ところが操作をひとつに限定し、スロットの種類と数を増やすことで機能を空間に割り当てることができるようになります。そうすると説明であれ何であれ、その空間内であれば自由に表現できるようになります。