指から文字がどんどん出てくる:2日目
指輪型で問題になるのはなんといっても電源確保でしょう。いわゆるエナジーハーベスティングです。
貯蔵には電気二重層キャパシタを使うことになるでしょうが、耐圧3Vなら1Fぐらいまで詰め込める可能性はあります。が、Vccに直接ぶらさげるとある電圧幅でしか使えないので、自由に使えるのはせいぜい1Jぐらいです。これは例えばAVRをフルスピードで1分ぐらい動かせるエネルギーです。通信にせよ・センシングにせよmsオーダでやって他はスリープさせるので、実際の使用で1日分ぐらいは持たせることができるということになります。(キャパシタの漏れ電流を考えなければ)
となると問題は1日あるいは1時間ぐらいの時間平均をとったときの、発電と消費の収支に移ります。前者が後者を上回るなら本当にどこからともなくエネルギーを取り出しているように見えます。もちろん、この指輪は給電されているデバイスに近づけて使うのだから、そこで無線給電して充電するのは問題なさそうなのですが、あまり他のデバイスに依存すると単なるRFIDのようになってしまいます。できればジェスチャーやその他のインタラクションの可能性も残しておきたいのです。
指輪型に加工するのは簡単ではないし測定も困難になるので、実装を探る上では発電と消費を切り離して評価して、どのぐらいの電流が継続的に使えるか調べることになります。
発電側
ソーラー
とりあえずソーラーだろう、ということで共立で買ったフレキシブル太陽電池を使ってます。ノーブランドと書いてありますが、多分元はPowerFilmの製品っぽい感じです。
MP3-25を広げて普段の環境(夜,蛍光灯)で測ったところ、開放で2.3V、短絡で13μAでした。悲観的に直線のIV特性を仮定して、2Vとりだすとすると、2μAぐらいということになります。実際にはもっと小さいものを指輪の外周に巻きつけることになるのでこれはかなり厳しい値です。日中は太陽光も入ってくるので、平均すればもっと取れるかもしれませんが、その場合漏れ電流との戦いになるでしょう。
圧電
これまで、指輪というと他のウェアラブルデバイスに比べて不利な点しか無いなーと思っていたのですが、物を持つ時にわりと強い力がかかることに気づきました。たとえば内側と外側を同心円上に分割して隙間を開けると、物を持つときにそのふたつのリング間に力が発生するのが分かります。
どうやって力を変換するかというのは難しいところなのですが、とりあえずLEDを付けた圧電素子を指に貼りつけてしばらく過ごしてみたところ、わりと頻繁に光るのでこれでいけるかもしれません。ただスパイク状の高電圧は取り扱いにくいので、LTC3588を発注して届くのを待ってるところです。(余談ですが、LEDは普通数mAかけて使うので気にしないかもしれませんが、今時の高輝度LEDなら数10μAでも光っているのがわかります。)
消費側
これはMCUと通信で間欠的に必要となるもの+漏れということになります。とりあえずAVRを使うことにしたのですが、これは外部割り込みでしか起きない深いスリープだと0.1μA以下に抑えられるので、稼動時間を短くすれば大丈夫です。むしろ問題なのは通信の方で、通信の開始を何らかの方法で検出する必要があって、これがかなり難しい。
今のところ、赤外線リモコンのモジュールを使って、たまにビーコンをチェックする方法で7μAぐらいです。これは全然許容できないのでなんとかする必要があります。それにこれは前回の分類の2が不完全(接触検出が固定デバイス側でしかできない)のも気になります。
指から文字がどんどん出てくる:1日目
ちょっと具体化してみます。
デバイスの形態
さて、とりあえず外せない条件を並べてみます。
- 指輪型デバイス
- 指先で触ることで文字列をやりとりする
そして触る先ですが、見せかけは指先を通じて文字列が移動しているが実際はwifiを使っているとか、そういうのは避けて、できるだけ操作の感覚と実装を近づけたい、という目標があります。
前回のエントリではアドレスの入力を例に挙げましたが、往々にしてそういう場面ではネットワークは使えなかったりもします。だから、文字入力はキーボードと同じレイヤー、つまりUSB HIDとして動作するのが好ましい。
そして、そもそも文字列がどこから来るかについてですが、とりあえずクリップボードか何かと同期するのがいいんじゃないかな?と思います。つまり、
- USBキーボードのフリをして、指輪から受け取った文字列を伝えるデバイス
- どこかのクリップボードの内容を表示し、触るとその文字列を指輪に伝えるデバイス
のふたつです。1と2はひとつの装置としてまとめることができますし、とりあえずUSBで繋げる板状のデバイスという事にしておきましょう。
操作モデル
さて、このあたりで気になるのが「触れる」という一種類の動作にどういう意味を与えるのか、ということです。いろいろ考えた結果、「スロット」という概念を導入することにしました。
- スロットは常にひとつの文字列を保存する
- 文字列は空でも良い
- スロットはある一定の空間を占有する
- 指輪はひとつのスロットを持ち、装着した指がそのスロットの占有する空間となる
- 板状デバイスは一般に複数のスロットを持ち、数cmほどのマークで示された領域を占有する
- ふたつのスロットが占有する空間が接触することで、互いの文字列を交換する
わりとわかりやすいモデルだと思います。各スロットは自分の持っている文字列を何らかの方法で示してもいいし、示さなくてもいい。が、少なくとも電力の豊富なUSB接続側のデバイスについては表示してほしいところです。本当は指輪の方もこんなかんじになって欲しいのですが、これは難しそうですし。
通信
指の中だけを通る信号を使った通信路というのが理想的なのですが、これは次の二つに分解することもできます。
- ある程度局所的な通信路
- ふたつのスロット間での共有情報
このふたつのバランスによって、いろいろな方法を分類できます。1が広い時空間を占めるほど、2で必要な情報が増えます。
例えば、ひっそりwifiを使って〜、というのは1として数十mの空間内の電波、2としてネットワークのパスワード+アカウントidのように分解できます。これはかなり2に依存する方法です。
逆に、何らかの方法で通信路を完全に一本の指で閉じることができれば、2は全く不要です。
そして、1がそれほど局所的でなく2の情報も不完全である場合、それは干渉や情報漏洩というモデルの綻びとして表れます。
通信の実装
まず理想的な1だけ使う方法を考えてみます。これには、指の振動を用いる、電界通信、近赤外窓を用いた光通信、などが考えられます。・・・どれも原理的には無理ではないような感じはします。が、そんなに簡単ではありませんでした。
これにはやはり指輪で使える電力の少なさがあります。
電界通信
ここ数年でわりとデモが出てきてますが(RedTactonとか)どう考えても実装が難しそうなので、ちょっと手抜きすることを考えました。つまり、よく静電容量式のタッチパネルがありますが、あんな感じで地面を介したカップリングが作れないかな、と。が、どうも指輪では面積が小さすぎるのか、信号がはっきりしないですし、そもそも計測がすごく難しい。何を測っているのかわからない、ということで没です。
振動通信
空気と人体の音響インピーダンスは4000倍ぐらい違うので、うまく整合してやれば閉じ込められるんじゃないか?と思い、成形の容易な圧電フィルムを使って見ましたが、もちろんキャパシタとしても動作するわけで、さっきと同じ理由で測定が困難。ということで没です。
近赤外窓を用いた光通信
実は人体は一部の赤外線をわりとよく透過し、さらに拡散もするので、それで数cmぐらいならぼやーっと伝わるんじゃないか、と思いかなり明るい赤外線LEDと前に作った赤外線カメラで拡散の様子を見てみたのですが、透過する分が煌煌としている一方、指先には光が見えず、こりゃだめだな〜という感じでした。
通信の実装(仕切り直し)
ちょっと妥協して、1を完全に局所化するのは諦めました。数十cmぐらい漏れるのはいいかな〜と。
1として
- 磁気結合(できれば共振)による通信
- 空気中光通信
2として
- タッチパネルによる接触時刻の検出 + 振動による接触検出
- 脈拍のタイミング情報共有
この辺はわりと良さそうだな〜という感じです。2はどちらも難しそうですが、別に完全でなくても干渉が起きる可能性があるだけで動くことは動きます。だから、とりあえずこの方針でいくことにします。
指から文字がどんどん出てくる:0日目
最近、音声・動画の使用はずいぶん増えましたが、テキスト形式のデータの重要性が低まったということはありません。
そもそも、人間にはいわゆる五感があって大量のデータを処理できるのに対して、出力は驚くほど貧弱で、基本的にどこかの筋肉を動かすしか無いわけです。ここに大きな非対称性があります。
自然言語などではわりと大量のまとまりを覚えておくことができ、視覚・聴覚による入力と、出力の速度差はせいぜい数倍に収まっています。これには表現の揺らぎの許容が大きく寄与しています。
が、URLやIPアドレスのような識別子ではそうはいきません。多くの場合、全ての可能な識別子を扱ってはおらず、その時考えているいくつかのうちの「あれ」「それ」と見て取れます。しかし、実際に文字列としては書き出せないということになります。
これは不自由です。クリップボードを使えば良いのですが、異なるデバイス間で運用できません。もちろんweb clipboardのように、デバイス間がデータをこっそりやりとりすることは可能ですが、どうも直感的ではありません。
あくまでも、「それ」を覚えているのは自分自身であって、外側に情報があるわけではないのです。電脳化できれば一挙に解決するのですが、そういうわけにもいかないので、ウェアラブルデバイスに覚えさせることを考えます。
昔からある装身具は日常的動作とうまく擦り合わされているはずなので、どれかのフォームファクタを真似るのが得策ということになります。
で、今回指輪型のデバイスでやってみようということにしました。もちろん腕につけるほうが大きくて楽なのですが、最終的なアクションは多くの場合指先で行われるわけで、そこに近いほうがいいかな〜などと思ってます。
現状、、、
↑のようなことを数日前に考えていて、それからエネルギーの確保をどうするか、指輪と対になる物はどうするか、とか考えてました。で、とりあえずできる可能性があるかな?と感じたので、hs2bf以来の連載形式でやってみようかと思います。
前に、
IPv6のアドレスは長いから、直接手で打ち込んだり覚えたりする必要が無くなるようにツール群が発展するんじゃないかなぁと期待しています。
https://twitter.com/#!/xanxys_/status/30920528998957056
と言ってたのを思い出したので、とりあえず一週間、毎日更新して、この目標を達成できればいいな〜。
一日のクリック回数
普段PCを使っていて一日に何回ぐらいクリックしてるのか気になったことはありませんか?ということで調べてみました。
調査方法
/dev/input/mouse0にマウスのイベントが流れてくるので、それを解釈してクリックのログを取ります。フォーマットはどこかのフォーラムの発言を参考にしました。
使ったコード
import Control.Concurrent import Control.Monad import Data.Time import Data.Bits import qualified Data.ByteString as BS import System.IO main=do withBinaryFile "/dev/input/mouse0" ReadMode $ record record hMouse=do hSetBuffering hMouse NoBuffering iter hMouse False False False iter h pL pR pM=do [button,horz,vert]<-liftM BS.unpack $ BS.hGet h 3 let leftPressed=(button .&. 0x01)/=0 rightPressed=(button .&. 0x02)/=0 middlePressed=(button .&. 0x04)/=0 -- not including scrolls tz<-getCurrentTimeZone date<-getCurrentTime let d=show $ utcToLocalTime tz date when (not pL && leftPressed) $ appendFile "buttonlog" $ d++" ,left\n" when (not pR && rightPressed) $ appendFile "buttonlog" $ d++" ,right\n" when (not pM && middlePressed) $ appendFile "buttonlog" $ d++" ,middle\n" print [leftPressed,rightPressed,middlePressed] iter h leftPressed rightPressed middlePressed
よく見ると分かるように、実際には押した回数を数えてます。
これを一日ほど動かしました。できたログからRでヒストグラムを描きます。
t<-read.table('log_under_analysis',sep=',') png('total.png',width=600,height=800,res=100) par(mfrow=c(3,1)) for(tag in c('left','right','middle')){ hist(as.POSIXlt(t[which(t$V2==tag),]$V1),'hours',freq=TRUE,main=sprintf("%s click",tag), xlab='',las=1,col='lightblue') t0<-as.POSIXlt('2012-03-06 00:00:00') lines(x=c(t0,t0),y=c(0,1000)) grid() } dev.off()
結果
2012/3/5 20:28〜2012/3/6 21:43における一時間ごとのクリック回数を種類別に分けたのが次の図です。
2012/3/5 20:28〜2012/3/6 20:28の合計回数は
- 左クリック: 5124回
- 右クリック: 57回
- 中クリック: 349回
でした。
ちなみに、このとき何をしてたかはtwilog(3/5 3/6)を見ると・・・残念ながらわからないので思い出してみると、適当な回路のデバッグ、調べ物、ニコ動閲覧等で、ゲームとかはしてません。
結果について
みっつのヒストグラムですが、だいたい同じ傾向を持っているように見えます。中クリックはほぼ「新しいタブでリンクを開く」専用ですが、わりと使っているようです。
ちょっと驚いてるのは右クリックの少なさで、コンテキストメニューはわりとどこでも出るのに、ほとんど使ってないな、という感じです。あまりUIを変えなくてもタッチパネルへ移行できるのはこういう理由もあるかもしれません。
π算法とIO
ここでは次の記法を使います。
- 並行: P Q
- 入力: channel>(arg) P
- 出力: channel<(arg) P
- 複製: replicate P
- 制約: new(arg) P
- 空のプロセス: 何も書かない
同じインデントで並んでるのがconcurrencyで、インデントを下げた部分は時間経過+新しいスコープを表しているというPython風表記です。
この記法を使うと(νy'(x(z).y
new(y') x>(z) y<(z) x<(y') replicate P
となります。
紹介
この記法の上で、π算法の簡約規則は次のように描けます。
◀が実行中のプロセスで、通信が起きると参照が張られるイメージです。これが本当にもとの公理を表しているのか調べていませんが、たぶんあっている気がします。
網羅的な説明・・・なんですかそれ(
種類
π算法は同期・非同期、単項・複項で四種類考えることができます。非同期というのは出力ch<(arg):PでPが0以外取れないつまり送りっぱなしのもので、複項というのはch<(arg0,arg1,...)という風に任意の個数の引数をとれるものです。オリジナルのπ計算は同期・単項にあたります。
さて、これらの間の相互変換を考えてみます。
単項→複項・非同期→同期
自明
複項同期→単項同期
FIFOみたいなチャネルを立ててやることで複数の値を渡せます。
# output(polyadic) c<(y1,...,yn) P # output(monadic) new(t) c<(t) t<(y1) ... t<(yn) P # input(polyadic) c>(y1,...,yn) P # input(monadic) c>(t) t>(y1) ... t>(yn) P
他は再帰的に書き換えるだけで問題ありません。
単項同期→単項非同期
送信先に自分の場所sを渡して新しいコネクションtを張ってもらい、それに送る感じでいけます。3-way handshakeに似てます。
# output(sync) c<(x) P # output(async) new(s) c<(s) s>(t) t<(x) P # input(sync) c>(x) P # input(async) c>(s) s<(t) t>(x) P
相互変換
さて、以上でこれら四種類のπ算法はどれも同じぐらい強力であることが分かります。以下では複項同期のものを使うことにします。
データとIOの導入
さて、Churchエンコーディングのようなものはπ算法でもできるのですが、どうせ実装では特別な最適化を施すので内部表現がどうなっているか考えずにリテラルやプリミティブを追加したい、ということがあります。かといって、チャネル以外も送れるようにするというのは美しくない。となると、チャネルで何でも表すしか無い、ということになります。
ここでは古典的なstdioと文字列がどう表現され得るか考えてみます。とりあえず、print,readline,str[A],...,str[AA],...みたいなチャネルがいま考えてる名前空間に存在するとして、それぞれのチャネルがどう振る舞えばよいか考えてみます。
もっともナイーブにprintはstr[なんとか]を受け取ると"なんとか"とどこかに印字する、としてみると、次のようになります。
print<(str[Hello world!])
さて、一見問題ないように見えますがそうでもありません。
print<(str[Hello]) print<(str[ world!])
は期待通りには動きません。(というより動くべきではない)
というのもprintがstr[Hello]を受け取るのと印字を終えるタイミングは当然異なり、もしprintが印字が終わるまで「受け取っていないかのようにふるまう」という仕様ならば、printを例えばprintCharのようなものでπ算法の枠組みで表すことは不可能になり、いきなり実装が露出してしまうからです。
printを非同期とするのは本質的な解決にはなりません。というのも入力に応じて違う振る舞いをするというのは本質的に重要なことで、いずれ同期が必要になるのです。
そこで、継続渡し風に書いてみましょう。例えば
new(s) print<(str[Hello],s) s>() print<(str[ world!],s)
printは印字が終わったらs<()することで、元のプロセスの実行フローが再開されます。継続渡しだとそのままプロセスを渡すのですが、簡単に並行実行ができると新しいプロセスを作って、前のコマンド終了のシグナルを待つだけでよいのです。
さて、この調子でありがちな例(名前を聞く)を書いてみます。
new(s) print<(str[name?],s) s>() readline<(s) s>(name) print<(str[hello, ],s) s>() print<(name,_)
わりとよさげに見えませんか。
静的型付けとeval
いわゆるweb系のものをHaskellで書いていて感じる窮屈さの理由はやはりevalが無いことに起因するんじゃないかと思うのでevalについて考えてみる。
命令型・動的型付け言語におけるeval
こういう言語で提供されているeval(の類)はかなり強力で、モジュールのインポート等を含めて基本的になんでもできる。とはいえ、こういう言語はもともと任意の場所でグローバルな変更をもたらすような文を実行できるので、evalの意味としてはソースコードレベルでの文字列置換とほぼ同じで特に混乱はなく、興味は専ら力を制限して安全性を確保する側にある。
関数型・静的型付け言語におけるeval
といってもHaskell以外知らないので、専らHaskellの話になる。
まず無難なものから考えてみよう。
eval ("1+2" :: String) :: Int
みたいなものはDSLを作れば良い。
実際に欲しいのは、任意の型の式が得られるようなevalであって、
eval "\x->x" :: Either Error (a -> a) eval "putStrLn \"hoge\"" :: Either Error (IO ())
こういうことができると便利そうだ。とはいえこういうevalがどのような型を持つのかよくわからない。とりあえず近似的に、Data.Dynamicなんかを使って
eval :: String -> Either Error Dynamic
みたいなものでも数行増えるだけでそれほど問題はないかもしれない。
さて、それでは型安全性を損なわずにどこまで強力なevalを考えることができるだろうか?例えばtop-level宣言はどうか?
実行中のコードをS1,evalするコードをS2として、S1でS2の定義を使ってるようなものはだめだろう。でも、S2からS1の定義を使うのは問題ない気がするし(さっきの例はそれが必須)、S2で何かを定義して、さらにevalするコードS3からS2の定義を使うのは問題ない気がする。というのも、この場合S2やS3は単体では問題があるけれど、evalのコンテキストでは充分な情報があるように思えるからだ。それに系全体としてはS1,S1+S2,S1+S2+S3という三つの状態を移動しているだけで、それぞれの状態はvalidだ。
意味の広がりと通信
さて、以前の記事(自己複製系と意味)では、はっきりとした区切りのない場に、ある共通の意味*1を運用できる個体群が含まれる*2場合がある、ということを、エントロピーの観点から見てました。今回は逆に、実際よく使われている通信の概念を、自己複製に寄せていくことができるか検討してみます。
さて、通信というとShannonのモデル*3が有名で、これはsource-transmitter-channel(+noise)-receiver-destination、という5つの部分からなる構成です。情報源(source)から出てくるのは、文字だったり、関数だったりするわけですが、別にもっと抽象的なものであってもかまわないわけです*4で、送信機(transmitter)がそれを何であれ予測可能な形で伝搬するものに変換します。ここでは電磁波のような物理現象*5が用いられることが多いです。この時点で制御を手放して何かの場に委ねます。これが伝送路(channel)になるわけですが、予測できなかった分は外乱(noise)として扱います。受信機(receiver)は、送信機と逆の手順を行い、そして目的地に着きます。このdestinationが何であるかは、"The destination is the person (or thing) for whom the message is intended."と曖昧なものです。
さて、何が伝送されたのでしょうか?実際、ここでいう意味というのは、sourceやdestinationがする行動によって暗黙に決まるものですが、そういうのは扱いが難しいので、ここではマルコフ連鎖その他の確率的モデルを考えることでお茶を濁しています。そうすれば同じ状態に対応するシンボルを復元できる、のような処理と無関係な定義ができ、情報源のエントロピーも計算できるようになります。これはあまりにも便利な考えなので、情報源符号化はシンボル-シンボル間の変換だけで扱えると勘違いしそうです。(そうでもない?)
その結果とりあえずbitを主体に据えて、いわゆるデジタルなんとか、という考えかたが広まった(ような気がします)。さまざまな形式で使われるにつれて、情報は実時間・実空間から切り離され、それ自身で実体とみなされるようになりました。*6また、これは万物はインターフェースを整えることでコピー可能であるという思想*7にも大きく寄与したと思います。
実際、この情報を実体として扱える状態を維持するために、いたるところでバッファを入れたり、とりあえず保存しておいたりすることで、(可能な限界に比べて)莫大なエネルギーを消費していると考えられるので、そこを解消することで思いもよらなかったことができるようになる、という可能性はあります。
広がりの制御
このようなわけで、一旦シンボルに落ちてしまうとなんであれ非常にアクセスしやすい形になるわけですが、それが好ましくない場合もあります。いわゆる機密性が欲しい場合です。で、これをシンボルの操作でなんとかしようとするのが暗号なわけですが、一番わかりやすいのは、情報論的なもので、例として送るメッセージと同じ長さの乱数を生成して、xorをとったものと乱数を別の経路で送って、両方ないと復元できない、というのが挙げられます。これはone-time padと呼ばれることのほうが多いかもしれません。ストリーム暗号は、ここでいう乱数を「長くする」操作を共有しておくことで、メッセージより短い鍵を送るだけで済むようにしたものです。
そして公開鍵暗号は・・・、と見ていくと、徐々に重点が鍵から操作に移っていることがわかります。
唐突ですが、何かを機密にするのは、その何かが(分かる|分からない)人(もの)に二分するという事でもあります。この観点から見ると、先ほどの例は次の二つの作用に分類できます。
- 「分かる」こと (encode/decode操作の共有)
- 「分かってない」相手の排除
2は書いてませんでしたが、操作がtotalでない場合定義域から外れるデータを送ってくる人は弾くべきという事ですし、totalでもうまく工夫すれば弾くことができます。ステガノグラフィーでは、そもそも2の作用がありません。
こうしてみると、これはまさに冒頭で述べた意味を共有する集団+α、という形式をとっていることがわかります。
これを再び具体化してみると、いろいろと面白いものが考えられます。例えば、ある単一の実行形式を複数のノードで実行してセキュアなネットワークを作るにはどうしましょう。秘密鍵を実行時に設定するというのがありがちな方法ですが、例えばその実行形式のコンパイル時に毎回プロトコルをランダムに変更する、ということが可能です。もっとドラスティックに、実行時に自分のプロセスの一部分を変更して、それを別のノードで実行する、といったことも考えられます。
こういう類の自律的な振る舞いを自然に許して、かつうまく抑えこむような環境がつくれるといいなぁ。
*1:フレーム・記号圏・解釈などと呼ぶ方がよいかもしれません
*2:そういう系が存在しているかは解釈する立場に依存するので、「発生する」という言葉を避けました。我々もそうです。気づいた時には自分が存在していた、そういう性質のものです。
*3:C. Shannon, A Mathematical Theory of Communication, 1948 (pdf)
*4:文字列で表せるとしても、ここで本当に扱いたいのはシニフィエなので、集合みたいな抽象化を運用するのはなかなか難しそうです。
*5:そもそも、法則というのは予測可能性から来るものです。
*6:ある程度使う人が多ければ、なんでも実体として扱われるようになります、多分。