What's JINS MEME
以前まとめたものがありますので、そちらをご参考ください。
http://qiita.com/mito_log/items/020a87996ed2b9d793e6
Why
前回、JINS MEMEのデータをxx秒おきにGoogle SpreadSheetに流すをやりましたが、それはリアルタイムデータの取得&解析用といった感じなので今度はリアルタイムに且つ視覚的にデータを見てみようと思います。今回もRxSwiftで書きました。
ちなみに...
ios上でのグラフ化はAdventCalendar3日目がよくできています!今回は可視化という部分で重複しますが、tcp socket通信の部分も含みますので、とりあえず進めてみました。
What I created
- JINS MEMEのリアルタイムモードのデータをtcpソケット通信で特定のIP/Portに出力
- 受け側のアプリをProcessingで作成しグラフ化
というものを作りました。
※ 検討段階ではwebsocketやudpで~と思いましたが過去の遺産を漁ってみたらtcpが楽そうだったのでとりあえずそれでやっています
github
※ 今回のソースはProcessingのフォルダ配下
Screenshots
How to Use
- まずiOSデバイスとProcessingの走るマシンを同じローカルエリアネットワーク内に参加させます。
- ProcessingのJava applet(サーバ)から立ち上げ、次にiOSアプリ(クライアント)を起動します。
- 起動したら、iOS側でサーバのIPとポートを指定して、スタートボタンを押すと通信が開始されます。
Miscellaneous
- IPとポートのTextFieldにバリデーションを入れていて、IPは4つのセグメントがないといけません、ポートはtcpの自由割当ポート(49152から65535)の中に収まっていないといけません
- Processingでは、画面上をクリックすると、ソケット通信をON/OFFできます。
- Processingでは、fpsを左下にプロットしていて、JINS MEMEの通信速度20Hz(20fps)を下回るとデータ落ちしますので、遅延がひどくかんじられます
Recap
About RxSwift
Validation, Sync multiple inputs with combineLatest
combineLatest
は、複数のObservableを引数にとり、どちらか一方の引数で最新の値が来ると、それをトリガーにして最終引数にセットされたクロージャに値が引き渡されます。クロージャでそれらの値をよしなに処理してreturnすると、ObservableでラップされてsubscribeするObserverに値が渡されます。
画像: RxSwiftのPlaygroundより
example("combineLatest 1") {
let intOb1 = PublishSubject<String>()
let intOb2 = PublishSubject<Int>()
_ = combineLatest(intOb1, intOb2) {
"\($0) \($1)"
}
.subscribe {
print($0)
}
intOb1.on(.Next("A"))
intOb2.on(.Next(1))
intOb1.on(.Next("B"))
intOb2.on(.Next(2))
}
/*
Codes above will prints like below:
Next(A 1)
Next(B 1)
Next(B 2)
*/
今回は、このcombineLatest
の、**引数としてとる値が少なくとも1回以上publishされていないとクロージャに処理が回ってこない。**という性質を利用しました。具体的には、
- IPアドレスとポートをきちんと入力しないと通信開始ボタンが押せないようにする
- 通信開始ボタン、tcpソケット接続完了、JINS MEMEのデータ取得のタイミングが揃ったタイミングでcsvデータをProcessingに送信
という機能を実現するためにcombineLatest
を使っています。
About socket communication
Tcp socket communication using NSStream
今回、tcpソケット通信を行うクラスとして、TcpSocketManager
というクラスを作りました。そこでは、NSStreamとNSOperationを組み合わせて新規にスレッドを作成し、そこでrunloopを回して、socketデータのI/Oを管理しています。
基本的には、以下のサイトを参考しています。
NSStreamを利用してiRemoconと通信する
Setting Up Socket Streams
ソース中でautoreleasepoolディレクティブを使っている箇所は、データ更新頻度が多いため、ARCでなるべく早いタイミングでメモリ解放させるためにセットされているかと思います。
また、ちょっとトリッキーなところとしては、main関数の中で、
repeat{
autoreleasepool {
self.runloop.runUntilDate(NSDate(timeIntervalSinceNow:0.1))
}
} while (_executing)
とすることで、実行中0.1秒ごとにrunloopが延命され、その間はずっとstreamが維持される仕組みになっています。
About Processing
Server (Processing built-in socket communication library)
ProcessingにはServerというtcpソケット通信を可能にするクラスがネットワークライブラリの一部としてデフォルトで備わっています。使い方は簡単で、以下のように使うことができます。
import processing.net.*;
Server myServer;
int val = 0;
void setup() {
size(200, 200);
// Starts a myServer on port 5204
myServer = new Server(this, 5204);
}
void draw() {
val = (val + 1) % 255;
background(val);
myServer.write(val);
}
ServerがあるのでClientもありますが、今回は使っていません。
ライブラリのリファレンスはこちらからどうぞ。
About JINS MEME
Yaw value is...
グラフ化してみるとわかるのですが、頭のx,y,z軸方向の回転量を示すroll, pitch, yawの値のうち、yawがどうもおかしいようです。値が連続的ではなく急に変化しています。具体的には、0度から急に-300~くらいの値になってしまいます。
本来はこちらの記事と同じで、
roll: 0° -> 90° -> 0° -> -90° -> 0°
pitch: 0° -> 90° -> -180°/180° -> -90° -> 0°
yaw: 0° -> 90 -> 180 -> 270 -> 360°
となるはずですが、yawの値にマイナスがついてしまっています。310~320くらいでマイナスに転じているので、符号化bit云々の話かもしれません。そこはちょっと内部の処理になりますので組込な方がいれば教えて欲しいところです。もしくはSDKの改善に期待です。
EyeBlink :) , Eyemove :(
大体どちらの値もメガネをかけてしばらくすると安定してデータが取れるようになります。
グラフでみると、まばたきの検知は結構感度いいですが、目線の移動はそこまで正確にとれないようで、こちらもSDKの改善に期待したいと思います。
todo
yaw値がグラフからはみ出ていますが、上記のyaw値の関係で一旦スルーして、0を中心に+180, -180°の範囲にしています。ここはyaw値が修正され次第修正したいと思います。
References
IRMCommandOperation
Tcpソケット通信のクラスを作る上でベースにさせていただきました。ありがとうございます。