指から文字がどんどん出てくる:3日目

1日目にはスロットという概念を導入し、接触による交換を基本とした(というかそれしかできない)操作モデルをつくりました。が、同じ文字列を何回も入力するのはどうするか?とか、使わずに破棄するにはどうするか?といった問題が発生します。そこで、操作は変更せずにスロットを拡張することで解決を試みます。

形式変換・複製・破棄

さて、実は前回にひっそりと「各スロットは自分の持っている文字列を何らかの方法で示してもいいし、示さなくてもいい」と書いていました。これはスロットが内部に保持しているデータを自動でコピーして、それを人間が読める画像に変換していることに他なりません。この見方をすると、例えば文字列をQRコードとして表示するとか、読み上げるとか、いろいろなバリアントが考えられます。

そして形式を維持した複製には、主スロット+副スロットがあって、副スロットの内容を頻繁に主スロットの内容で上書きする、というような表現が考えられます。破棄というのは単に空の文字列で定期的に上書きされるスロットを用意すれば良いのです。

表現

スロットの振る舞いは図形的に表すことも考えられて、例えば↓のようなものがありえます。

形式の表現にはᚢᚹ(U8,UTF-8表現)、ᛗ(人間、触れる場所)のようにルーン文字を使ってます。私はこういうのが大好きなので、たぶんこの系列のデザインはどこかに取り入れるでしょう。

さて

スロットの振る舞いを変えることの利点は、操作を単純に保ったまま機能をいくらでも増やせることと、その説明と機能を同じように表現できることに有ります。例えば、二回タップすると〇〇、長く押すと△△、みたいなUIは際限なく覚えないといけないシーケンスが増える可能性がありますし、そのような時間的系列の説明はどうしても実際の動作とは分離して表現されることになります。

ところが操作をひとつに限定し、スロットの種類と数を増やすことで機能を空間に割り当てることができるようになります。そうすると説明であれ何であれ、その空間内であれば自由に表現できるようになります。

指から文字がどんどん出てくる: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日目

ちょっと具体化してみます。

デバイスの形態

さて、とりあえず外せない条件を並べてみます。

  1. 指輪型デバイス
  2. 指先で触ることで文字列をやりとりする

そして触る先ですが、見せかけは指先を通じて文字列が移動しているが実際はwifiを使っているとか、そういうのは避けて、できるだけ操作の感覚と実装を近づけたい、という目標があります。

前回のエントリではアドレスの入力を例に挙げましたが、往々にしてそういう場面ではネットワークは使えなかったりもします。だから、文字入力はキーボードと同じレイヤー、つまりUSB HIDとして動作するのが好ましい。

そして、そもそも文字列がどこから来るかについてですが、とりあえずクリップボードか何かと同期するのがいいんじゃないかな?と思います。つまり、

  1. USBキーボードのフリをして、指輪から受け取った文字列を伝えるデバイス
  2. どこかのクリップボードの内容を表示し、触るとその文字列を指輪に伝えるデバイス

のふたつです。1と2はひとつの装置としてまとめることができますし、とりあえずUSBで繋げる板状のデバイスという事にしておきましょう。

操作モデル

さて、このあたりで気になるのが「触れる」という一種類の動作にどういう意味を与えるのか、ということです。いろいろ考えた結果、「スロット」という概念を導入することにしました。

  • スロットは常にひとつの文字列を保存する
    • 文字列は空でも良い
  • スロットはある一定の空間を占有する
  • 指輪はひとつのスロットを持ち、装着した指がそのスロットの占有する空間となる
  • 板状デバイスは一般に複数のスロットを持ち、数cmほどのマークで示された領域を占有する
  • ふたつのスロットが占有する空間が接触することで、互いの文字列を交換する

わりとわかりやすいモデルだと思います。各スロットは自分の持っている文字列を何らかの方法で示してもいいし、示さなくてもいい。が、少なくとも電力の豊富なUSB接続側のデバイスについては表示してほしいところです。本当は指輪の方もこんなかんじになって欲しいのですが、これは難しそうですし。

通信

指の中だけを通る信号を使った通信路というのが理想的なのですが、これは次の二つに分解することもできます。

  1. ある程度局所的な通信路
  2. ふたつのスロット間での共有情報

このふたつのバランスによって、いろいろな方法を分類できます。1が広い時空間を占めるほど、2で必要な情報が増えます。

例えば、ひっそりwifiを使って〜、というのは1として数十mの空間内の電波、2としてネットワークのパスワード+アカウントidのように分解できます。これはかなり2に依存する方法です。

逆に、何らかの方法で通信路を完全に一本の指で閉じることができれば、2は全く不要です。

そして、1がそれほど局所的でなく2の情報も不完全である場合、それは干渉や情報漏洩というモデルの綻びとして表れます。

通信の実装

まず理想的な1だけ使う方法を考えてみます。これには、指の振動を用いる、電界通信、近赤外窓を用いた光通信、などが考えられます。・・・どれも原理的には無理ではないような感じはします。が、そんなに簡単ではありませんでした。

これにはやはり指輪で使える電力の少なさがあります。

電界通信

ここ数年でわりとデモが出てきてますが(RedTactonとか)どう考えても実装が難しそうなので、ちょっと手抜きすることを考えました。つまり、よく静電容量式のタッチパネルがありますが、あんな感じで地面を介したカップリングが作れないかな、と。が、どうも指輪では面積が小さすぎるのか、信号がはっきりしないですし、そもそも計測がすごく難しい。何を測っているのかわからない、ということで没です。

振動通信

空気と人体の音響インピーダンスは4000倍ぐらい違うので、うまく整合してやれば閉じ込められるんじゃないか?と思い、成形の容易な圧電フィルムを使って見ましたが、もちろんキャパシタとしても動作するわけで、さっきと同じ理由で測定が困難。ということで没です。

近赤外窓を用いた光通信

実は人体は一部の赤外線をわりとよく透過し、さらに拡散もするので、それで数cmぐらいならぼやーっと伝わるんじゃないか、と思いかなり明るい赤外線LEDと前に作った赤外線カメラで拡散の様子を見てみたのですが、透過する分が煌煌としている一方、指先には光が見えず、こりゃだめだな〜という感じでした。

通信の実装(仕切り直し)

ちょっと妥協して、1を完全に局所化するのは諦めました。数十cmぐらい漏れるのはいいかな〜と。

1として

  1. 磁気結合(できれば共振)による通信
  2. 空気中光通信

2として

  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|x))|!Pは

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,_)

わりとよさげに見えませんか。

まとめ

π算法その他は明らかにopen-endedな計算環境をどうにか記述するためにつくられたもので、古典的なプログラム(有限時間で終了することで有用な結果を得るもの)が綺麗に書ける必要はありません。ずっと動いてるエージェントをつくるには、実行時の部分的書き換えは不可欠(squeak,self的な環境をイメージしている)なので、シーケンシャルなものが欲しい時にはそこだけシーケンシャルな動作を生成する言語でもツールでも使いわけるのが良いかと思われます。

また、すべてがプロセスとチャネルで表されていること、ここからはGCの計算への拡張が考えられます。

静的型付けと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だ。