ラベル wiimote の投稿を表示しています。 すべての投稿を表示
ラベル wiimote の投稿を表示しています。 すべての投稿を表示

2010年12月12日日曜日

Wiiリモコンのジェスチャー認識

3軸加速度は大体分かったところで、次はジェスチャ認識を試してみましょう。
おそらくゲームのキモの部分になるけれど、じっくり調整は後にして当面は出来合いの流用してすましたいところ。

wiiremote gesture recognition で検索するといくつも出てきますね。

フリーソフトウェアとして整っているものは wiigee しか無いようです。
しかしこれは Java で書かれていて、C# XNA とは相性がよろしくない。

C で書かれた WiimoteGR というのもありました。ライセンスは不明。

論文は豊富にありますが、ソフトウェアが公開されているものは少なそう。
基本的には研究室内で使われているコードなんでしょうね。
いずれ書き直すとして、WiimoteGR 使いましょう。

論文は A latest compilation of links to the wiimote のリンク集が良さそうです。
ざっとサーベイしたところ、ニューラルネットはダメで、隠れマルコフモデル(HMM)が主流で、Slow Feature Analysis(SFA) が有望という感じ?


WiimoteGR

パターン認識のアルゴリズムはまだいいけれど、全体の設計は理解しておきます。

main() から処理が始まり、トレーニングをしてテストという流れになっています。
トレーニング時は パターン名を指定して、3軸加速度を何セットか記録。SQLLite で保存。

テスト時は SQLLite から学習データを読み込み、
リモコンのデータを記憶しながら、過去いくつかの入力信号をまとめてパターン認識にかける。

認識器は HMM クラス、隠れマルコフモデルでしょう。
const HMM& HMMLib::Recognize(const vector<HMM>& HMMVec, TimeSlot& seq) { vector<HMM>::const_iterator recognizedHMM = HMMVec.begin(); for(vector<HMM>::const_iterator curHMM = HMMVec.begin()+1; curHMM < HMMVec.end(); curHMM++){ if( SeqLogProb(*curHMM,seq,false) > SeqLogProb(*recognizedHMM,seq,false) ) recognizedHMM = curHMM; } return *recognizedHMM; }
HMMLib は個々のパターンを表す HMM オブジェクトを何組か持っていて、
Recognize() 関数は SeqLogProb() の値が一番大きい HMM を選んで返すようだ。

おそらくこれは、ユーザーがジェスチャで何らかのコマンドを入力するというシーンを想定しているのであろう。
未知の入力対して、一番近いジェスチャパターンを返す。
対して振りゲーの場合、正解となるジェスチャが分かっている。
そのジェスチャとのみ評価を行い、評価値が一定以上ならば正解とする使い方になる。

ところで、テスト部では
while(wiimote.Button.A()){ wiimote.RefreshState(); // Acceleration: tempAcc.x=wiimote.Acceleration.X; tempAcc.y=wiimote.Acceleration.Y; tempAcc.z=wiimote.Acceleration.Z; tempSeq.AddObservableSymbol(defaultQuantizer.Quantize(tempAcc)); cout << trainer.Recognize(loadedHMMVec,tempSeq).gestureName << " "; gestureReceived = true; Sleep(10);//period (ms) } void AddObservableSymbol(size_t obsSymbol){ o.push_back(obsSymbol); }
3軸加速度そのものではなく、量子化(Quantize)した size_t 型の値を使っているようだ。

Quantize() 関数は
size_t DefaultQuantizer::Quantize(const Acceleration& acc) const{ double rho = sqrt(acc.x*acc.x+acc.y*acc.y+acc.z*acc.z); return (rho>rho_threshold? 1<<3 : 0) + (acc.x>acc.y? 1<<2 : 0) + (acc.y>acc.z? 1<<1 : 0) + (acc.z>acc.x? 1 : 0); }
となっていて、4bitのビットベクトルである。ビットの意味は
  • 加速度の大きさが、スレッショルドを超えているかどうか
  • 加速度の X方向成分が Y方向成分よりも大きいかどうか
  • 加速度の Y方向成分が Z方向成分よりも大きいかどうか
  • 加速度の Z方向成分が X方向成分よりも大きいかどうか
後段の 3つは加速度の向きを単純化したもので、最初の1つは大きさを単純化したものである。
3軸加速度そのものは使わずに、パターン認識に必要十分な特徴量へと変換したというわけであろう。
この量子化関数はパターン認識の精度に関わる重要なファクターになりそうだ。

例えば、このコードでは三軸加速度のみを用いているが、M+ のジャイロセンサーを活用できるであろう。
単純に、加速度と同様の手法で角速度を8通りに量子化すればよいのであろうか。
各速度を積分して姿勢を判別し、加速度に向きの補正をかければ良いのであろうか。
しっかり考えないと分からないな。

2010年11月28日日曜日

加速度から向きの算出

引き続き、XNAとWiimoteLibで3Dオブジェクトを操作の解析を行います。

過去50個の平均をとってならしている部分を省くと、
void wm_WiimoteChanged(object sender, WiimoteChangedEventArgs args) { WiimoteState ws = args.WiimoteState; //WiimoteStateの値を取得 x = ws.AccelState.Values.X } protected override void Draw(GameTime gameTime) { x = (-x * 90.0f); x = x / 180 * 3.14f; effect.World = Matrix.CreateFromYawPitchRoll(0, y, x); }
という感じです。y も同じコード。
AccesState.Values に角度に比例した値、具体的には -1..1 の範囲に丸めた値が入っているのかな?


イベントハンドラ

一番上の ws とは何かというと、
this.wm = new Wiimote(); this.wm.SetReportType(InputReport.IRExtensionAccel, true);//レポートタイプの設定 this.wm.WiimoteChanged += wm_WiimoteChanged; //イベント関数の登録
という手順で登録されたイベントハンドラの引数。

イベントハンドラ登録前の
Wiimote.SetReportType Method (InputReport, Boolean)
  Set Wiimote reporting mode (if using an IR report type, IR sensitivity is set to WiiLevel3)

  public void SetReportType(InputReport type, bool continuous)
    continuous: Continuous data
InputReport はおいといて、continuous は data が連続的な値になるみたいですね。
1byte の値そのままじゃなくて、実数に丸め込むのかな?
しかし AccelState には Values だけでなくて RawValues ってフィールドもあるから両方取れるのだが…。
ちょっと具体的にはよく分からないけれど、こういうパラメータがあることだけ覚えておきましょう。

続いて InputReport は、
InputReport Enumeration
  The report format in which the Wiimote should return data 

Member nameDescription
Status
Status report
ReadData
Read data from memory location
OutputReportAck
Register write complete
Buttons
Button data only
ButtonsAccel
Button and accelerometer data
IRAccel
IR sensor and accelerometer data
ButtonsExtension
Button and extension controller data
ExtensionAccel
Extension and accelerometer data
IRExtensionAccel
IR sensor, extension controller and accelerometer data
IRExtensionAccel は IR と Extension と Accel なイベントを有効にする、ということのようです。
内部的にはビットベクトルになってそうです。


イベント

Wiimote.WiimoteChanged Event Event raised when Wiimote state is changed Syntax public event EventHandler WiimoteChanged
とのことで、状態が変わったら呼ばれるようです。

このイベントオブジェクトには WiimoteState がメンバ変数にあって、WiimoteState は
WiimoteState Members
NameDescription
AccelCalibrationInfo
Current calibration information
AccelState
Current state of accelerometers
BalanceBoardState
Current state of the Wii Fit Balance Board
Battery
Calculated current battery level
BatteryRaw
Raw byte value of current battery level
ButtonState
Current state of buttons
ClassicControllerState
Current state of Classic Controller extension
DrumsState
Current state of Drums extension
Extension
Is an extension controller inserted?
ExtensionType
Extension controller currently inserted, if any
GuitarState
Current state of Guitar extension
IRState
Current state of IR sensors
LEDState
Current state of LEDs
MotionPlusState
Current state of the MotionPlus controller
NunchukState
Current state of Nunchuk extension
Rumble
Current state of rumble
TaikoDrumState
Current state of the Taiko TaTaCon drum controller
いろいろメンバがありますね。
おそらくはこのライブラリで取得できるリモコンの値はこれで全部なのでしょう。


AccelState

長々とイベントハンドラの仕組みを追いましたが、結局のところすぐ上の AccelState.Values を使っています。

AccelState.Values は、WiimoteLib のヘルプによると
Normalized accelerometer data. Values range between 0 - ?, but values > 3 and < -3 are inaccurate.

0からの範囲に正規化した値のようです。プラスマイナス3を超えた値は不正確であると。

正規化というのは具体的には何でしょう。
センサから取れる加速度は 1byte の値なので、これをプラスマイナス3の範囲になるように変換したと思われます。
ゼロはおそらく静止状態でしょう。

件のソースコードでは、この値が 1 のときに 90度であるとみなしていました。
力を加えていない時にリモコンを傾けた時に、重力のX方向成分はサインカーブを描きます。
ということは、正規化とは有効値を 3 に収めるのではなくて、リモコン回した時の重力加速度の変化が 1 になるようにしたものということかな。

しかし値はあくまで加速度の成分で、角度に変換されているとはちょっと考えにくい気がします。
おそらくは、サンプルプログラムだから簡単に、サインカーブと比例直線を同じとみなして扱っているのでしょうか。

と、大体予想がついたところで、次はライブラリのソースみたり実測して確認をしましょう。

2010年11月25日木曜日

加速度から向きの算出

前エントリのサンプルコードを貰ってきたサイトに、
という記事があって、加速度から位置を計算する時のポイントが色々かかれています。 しかしまだ位置の前段階、まずは角度です。 正直リモコンで測れる加速度とは何なのかイマイチ掴めていなかったのですが、上の記事を読んで分かりました。
F = ma
の a, 力に比例する値を計測しているのですね。 さらにサーベイしてみても、歪み検出から力を測定しているようです。 加速度を二階積分すれば位置になりますから、そうやって位置も測れるのでしょう。 向きを測る場合は、リモコンに力をかけない状態の加速度、つまり重力加速度を使えばいいわけです。 重力は鉛直方向ですから、静止状態の加速度の方向が「下向き」というわけです。 重力ベクトルとの角度を求めれば、リモコンの向きも分かるわけです。 #厳密に言えば遠心力やコリオリの力も考えないといけないかな? というところで今日はおしまい。

M+ 一体型リモコン

先日モーションプラス一体型のリモコンが発売されました。 スーパースマッシュボールプラス同梱のピンク色のリモコンを入手しまして、WiimoteLib に繋いでみました。

通常モードは普通に動きますが、モーションプラスは認識されていない?ようです。
アタッチメント式のモーションプラスは特殊な I/O アクセスをしていたらしいので、また少し変わったのでしょうね。
早めに対応してくれるといいなぁ。

2010年11月24日水曜日

サンプルを動かす

XNA で Wiiリモコン使ったサンプルが
KOSAKA Laboratory Tips にありましたので、早速ためしました。

コンパイルして動かすのは問題なし。ソースコードのお勉強です。

プログラムのキモは二つあって、XNA/DirectX 関連の部分と、リモコンから取得した値の意味の部分。

XNA


  public class Game1 : Microsoft.Xna.Framework.Game {
    protected override void Initialize() {
    protected override void LoadContent() {
    protected override void UnloadContent() {
    protected override void Update(GameTime gameTime) {
    protected override void Draw(GameTime gameTime) {
ゲームメインのクラスは Microsoft.Xna.Framework.Game のサブクラスとして作り、各種関数をオーバーロードするようです。

Update/Draw がフレーム毎に呼ばれ、それぞれゲーム内部処理と描画処理を記述。

3Dモデルや音楽などは Content という機能で扱うようです。
コンテントプロジェクトというものを作り(XNAプロジェクト作ると自動的にできる)、そこにコンテンツファイルを追加。
すると扱いやすいよう変換して実行バイナリに埋め込まれるそうです。
標準的なデータタイプはそのまま扱えるし、独自形式なら解釈する部分を書けば拡張できる、と。
なかなか良い仕組みですね。

コンテントのロードは

    protected override void LoadContent() {
      this.xfile = Content.Load("wiimodoki");  //Xファイルの読み込み
      foreach (ModelMesh mesh in this.xfile.Meshes)  //メッシュごと
      {
        foreach (BasicEffect effect in mesh.Effects) {
          effect.View = Matrix.CreateLookAt(...);
          effect.Projection = Matrix.CreatePerspectiveFieldOfView(...);
        }
      }
Content.Load でできる、と。Xファイルからモデルを読み込んでいるのでしょう。

コードによると、Xファイルには複数の Mesh があり、メッシュはさらに複数の Effect を持ち、Effect には方向などを指定できるらしい。
ここはちゃんと調べましょう。

メッシュ(ModelMesh)はひとかたまりの3Dオブジェクト、と言ってしまっていいのかな。
独立して動かすことの出来る単位だそうです。
人体で言えば頭とか上腕とか、そのくらいの分割単位になるんじゃないでしょうかたぶん。
結び付けられる親ボーンも一つ参照しています。

メッシュはさらにパーツ(ModelMeshPart)に分かれます。
これはマテリアル情位で分割するものらしい。
デバイスに送る頂点情報やジオメトリ情報を持っているそうで、ここが最小単位ですかね。
他にもいくつかの情報を持っていて、その一つにエフェクトがあります。

エフェクトが何なのかはちとすぐには見つからないので保留。
シェーダ関連の情報が入るみたいです。
描画処理の肝の部分でしょう。
そういうわけで、向きのデータなどはエフェクトに入れるわけですね。

また bluetooth

ところで上のテストやっている間、何度も何度もリモコンの接続やり直したわけですが、やっぱ耐え難いものがあります。

もうちょっと調べて見ると
kako.com
flatlib.jp
と、スタックを叩いて自動的に接続するような手法を採っているようです。

Broadcom スタック叩いた経験から、大体やることはわかりますので、ちゃんと作った方が後々効率よさそうです。
形態はアプリ内蔵か常駐プログラムですかね。XBOX化を考えるんなら内蔵形式がいいのかな。

エンジン/フレームワーク選び

さて Bluetooth はこの辺にしておいて、いよいよリモコン読みに入ります。

値を printf するだけなら簡単ですが、どうせならゲームを考えて DirectX なんぞを学びつつやりましょう。
しかし DirectX 直利用は大変そうだし、ゲームエンジンやフレームワークを使えたらよいな。

GUIツールキットなら WxWindows, Qt など多少わかりますが、あまりゲーム向きでないですかね。

いわゆるゲームエンジンでフリーで使えたかなというものを思い出すと、
  • UnrealEngine: 大げさすぎる気がする
  • Unity: いいエンジンだけどメリットはスマートフォンだよなぁ。そこは今は考えていない。
やっぱがっつりしたエンジンはちょっとためらいますね。
ロースペックで動くかも分からないし。

DirectX をラップしてゲームループ提供する程度のもの…そういえば XNA ってのがあったよな。
これで作っとけば XBOX でも動かせるんじゃなイカ?
Mac とか iPhone は別にいらないし。

というわけで、XNA を選びました。
最新版の XNA Game Studio 2010 は、Zune や WindowsPhone もサポートしてるらしい。
XNA は .NET 環境下のアプリ開発らしいので、言語は C#, ライブラリも WiimoteLib になります。

2010年11月22日月曜日

Wiiリモコンの自動再接続・2

以前 Broadcom から Bluetooth スタックの新しいのを拾ってきた時に、同じ場所に SDK が置いてありました。
「パスキー」の入力ウィジェットが問題なら、パスキーをプログラムに書いちゃえば解決するんじゃない?

というわけで SDK 使って、デバイスの検索~ペアリングを書いてみたんですよ。

BondReply() BondReply() allows the user application to send pairing response data. It should only be called in response to a tBond_CB notification for the Numeric Comparison or Passkey pairing methods.

Numeric Comparison or Passkey って書いてあるけれど、Numeric Comparison or PIN code の間違い?
パラメータも PIN コード用のものが定義されているし。


Prototype: void BondReply (eBOND_REPLY reply, UINT32 nPinLength=0, UCHAR *szPin=NULL); Parameters: reply Enumerated reply type as one of the following:
  • BOND_CONFIRM_ALLOW
    Confirm Numeric Comparison validated.
  • BOND_CONFIRM_DISALLOW
    Reject Numeric Comparison request.
  • BOND_PIN_ALLOW
    Legacy pairing allow, pin code sent.
  • BOND_PIN_DISALLOW
    Reject Legacy pin code request.

nPinLength For BOND_PIN_ALLOW reply, length in characters of pin code supplied in szPin. Valid range: 0-16

szPin For BOND_PIN_ALLOW reply, pin code to use for legacy pairing. This is an array of BT_CHAR, null terminated.

Returns: None


…NULL terminated string ですね。

これはひょっとして Bluetooth の仕様なんじゃないかな。
あまりここを追っかけても何なので、ここまでにしておきましょう。
とりあえず自由に PINコード渡せるプログラムは作れたので、進展あったらこれをベースに始められますし。

2010年11月16日火曜日

Wiiリモコンの自動再接続

少し調べて見たのですが、なかなか難しいようです。

まず、リモコン自体の初期化の方法は無いみたい。
Wii本体の方では、Syncボタンを10秒長押しすることで、登録されたリモコンを初期化することができました。

ついでに Wii への登録方法も整理しておくと、1+2 同時押しと、電池のところのSyncボタンを押す方法の二種類あります。
前者は別のWiiで一時的に遊ぶ時に使うもの(ゲスト登録と呼ぼう)で、接続が切れたらペアリングも解除されます。
後者はWiiのホーム登録と呼ばれている処理で、こちらは再接続してくれます。

さて、前回の記事にも書いたように、Broadcom のスタックを使って Alt+S でパスキーをスルーして登録しました。
確定的な情報はありませんでしたが、ここが再接続しない原因として怪しいようです。
接続アプリを見てみても、「セキュリティで保護された接続」が薄く表示されていて、保護されていないことを示しています。
おそらくはパスキーをセットすれば、セキュリティで保護されたことになり、自動的な再接続も行ってくれるんじゃないでしょうか。

となると、プロトコルスタックの実装に依存する問題のようです。
Broadcom のサイトに行って、最新版(5.5.0.4700だったかな)をダウンロードインストール。
すると…リモコンを登録できなくなりました\(^o^)/
登録処理は Alt+S して最後まで行くんですが、実際には登録されていない状態。

次に、評判の良い BlueSoleil のスタックを入れてみました。
$25 ほどする商品ですが、買わなくても評価用に機能された状態で使うことができます。
アプリでは、「登録」と「ペアリング」の二種類の登録方法がありました。
「登録」の方ではパスキー入力無しでできますが、再接続はされず。
「ペアリング」はパスキー入力を求められ、知らないので失敗します。
Alt+S のような隠し機能にされていないだけで、機能的には Broadcom のと同じですね。

高速なデータ転送をしているという触れ込みもあるけれど、これは後で必要になったら使ってみましょう。
買ってまで BlueSoleil にする利点も無いので、Broadcom に戻します。

マシンは EeePC-S101 なので ASUS からドライバ落としてきてインストール。
バージョンは 5.5.0.4100 で無事認識できました。
# しかし確か最初は 5.5.0.4100 だった気がするが…まぁいいや。

Wiimote Project に、Linuxでホスト側のBluetoothアドレスを 30:30:30:30:30:30 にして、ペアキーを "000000" にしたらペアリングできたという記事がありました。
0x30 は ASCII コードでの文字 '0' ですから、パスキーはアドレスを使えば良いと推測されます。
しかし…実際のアドレスは 00:22:43:... などとなっていて、ASCII では入力できません。
Windows での変え方は分からないな。
レジストリの USBベンダーID/デバイスID っぽいところにアドレスが入っているが、これ書き換えていいものか。

キーは
  • HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\BTHidBus\
  • HKEY_LOCAL_MACHINE\SOFTWARE\Widcomm\BTConfig\Devices\00:...\
に入りそうだが、これは入力するペアキーそのものではなく、ペアキーから生成された数値になるようで、生成アルゴリズムまで調べるのも面倒だ。

やるなら、ペアリングの時にキー入れるところで、アプリのメモリをクラックするなりして直接書き換えちゃった方が楽そうだ。
うさみみハリケーン…入力されているところはあっさりと見つかりました。
テキスト入力のメモリだから文字列なのは当然ですね。
bluetoothデバイスIDは 00:22:... って00終端だから入れられないじゃん!

おそらくこの文字列を受け取る側では固定長バッファへ格納しているだろうから、そこを探すか。
んー見つからないな。ちょっと腰をすえてやらないとか。

2010年11月15日月曜日

Wiimote認識

だらだらしちゃってますが、HDCC 始めないと。
まず wiimote からで。

ライブラリの選択

ライブラリをざっと調査(→調査メモ)
  • M+への対応
  • BSDライセンス
ということで WiiYourself! を選択。

wiimoteの認識

デモの実行プログラムがあるので、まずはこれを試す。

デモの前に、 Windows と Bluetooth レベルでのペアリングが必要。
リモコンの1+2ボタンを同時押しでペアリングモード…なのですが、既に登録してある Wii と繋いじゃってうまく再登録できません。確かクリアする方法もあったと思いますが、面倒なので Wii の電源を切っておきます。

試した PC は EeePC S101 で、付属の Bluetooth の管理ソフトは Broadcom だか WIDCOMM だかのマイBluetooth というソフトです。
コイツは登録時にパスキーの入力が必須にみえますが、Alt-S でスキップできます。分からんって。

で登録してデモを起動。
無事認識できているようです。

が、ここで問題。
どうもリモコンの電源を切って入れなおすとペアリングが解除されている模様。
Wii のゲストリモコン的な動作になっているのかな。
次はここちゃんと調べるとしましょう。