ラベル フィジカルUI の投稿を表示しています。 すべての投稿を表示
ラベル フィジカルUI の投稿を表示しています。 すべての投稿を表示

2011年6月7日火曜日

[Kinect] カナダ医療チームが「Kinect」を現場採用


Kinect使ってMRIやCRTスキャンの画像を操作できるようです。
これからいろんな現場でジェスチャー操作ができるようになるんでしょうか。



元記事:Gigazin-カナダの医療チームが「Kinect」を外科手術の現場で採用、執刀医の作業負担を大幅に削減

2011年1月6日木曜日

[Kinectハック] SD_KinectIOをFlashで受ける

[Kinectハック] SD_KinectIO とりあえず公開でKinectからFlashへスケルトン情報を送るプログラムを公開していますが、それをFlashで受けるためのサンプルとライブラリも併せて作ってみました。



先のエントリでは人物のスケルトンモデルを表示するだけのデモを動かしていましたが、それと以前のオブジェクト移動のデモを合体させたサンプルを作成しています。

SD_KinectIOからは

"ユーザーID","部位ID","状態","x座標","y座標","z座標" (Null)

(例)
1,1,add,100.223,600.032,1534,302
1,1,update,140.344,632.323,1523,324
1,1,remove,0,0,0

ユーザーID:1~10
部位ID:1~24
状態:add,update,remove


というフォーマットで信号が送られてきます。

今回はスケルトン情報からFlash上にスケルトンモデルを表示させたいのですが、各ユーザーの各パーツがバラバラに送信されてくるので、それをFlashの方でユーザーごとにまとめてやる必要があります。
IDとパーツとの対応づけや、それを各ユーザーに割り振る処理は中々面倒そうだったので、今回もライブラリとしてまとめました。Google Code上のライブラリを更新していて、com.sdtech.kinect以下がそれに該当します。

・kinectパッケージ
・kinect.usersパッケージ

ひとまず、各オブジェクトをコンストラクタでインスタンス化していきます。


public function Main():void{
//
//ポインタ生成
mp=new MultiPointer(-1 , false);
mp.simulate=true;
mp.registerDefaultCursor(HandCursor);
addChild(mp);
//
//ボーン描画用
kmu=new KinectMultiUser();
kmu.mouseEnabled=kmu.mouseChildren=false;
addChild(kmu);
//
//FieldOfView設定
fov.addEventListener(Event.ENTER_FRAME , getFOV);
//
//接続開始
connect_btn.addEventListener(MouseEvent.CLICK , connectStart);
//
}


スケルトンモデルとボーン描画用のクラスのKinectMultiUserをインスタンス化し、以前も使用したPointerパッケージを組み合わせて使うため、同じようにインスタンス化します。また、3DのFieldOfViewを動的に変更出来るようにスライダーに、接続先を動的に変更出来るようにボタンにイベントを貼付けています。


private function connectStart(e:MouseEvent):void{
//
kc=new KinectConnector();
kc.addEventListener(Event.CONNECT , connectComplete);
kc.addEventListener(KinectEvent.RECEIVE , receiveKinectData);
kc.connect(ip.text , int(port.text));
//
}


接続ボタンが押されたら、SD_KinectIOに接続に行きます。受け取るデータは単なるプレーンテキストなのでXMLSocketで受け取っても問題ないのですが、今後特殊な仕様が追加される事を考えて、ここではKinectConnectorを使用しています。イベントリスナも専用の物を使用していますが、結局DataEvent.DATAと同じ処理になっているので、実はDataEvent.DATAを使用しても問題なかったりします。


private function receiveKinectData(e:KinectEvent):void{
//
//受け取ったデータをパースし、ボーンを描画
kmu.parseData(e.data);
//
for(var i:int=0 ; i<kmu.users ; i++){
//
//ユーザーを取得
var ku:KinectUser=kmu.getUserById(i);
if(!ku)continue;
//
//両手を取得
for(var n:int=0 ; n<2 ; n++){
//
//一意のIDを作成
var pointer_id:int=int(String(i+1)+(n+1));
//
var parts:KinectUserParts;
if(n==0){
parts=ku.getPartsById(KinectUserParts.R_HAND);
}else{
parts=ku.getPartsById(KinectUserParts.L_HAND);
}
//
if(!parts){
mp.lost(0,0,pointer_id);
continue;
}
//
//グローバル座標に変換
var p:Point=ku.local3DToGlobal(new Vector3D(parts.x , parts.y , parts.z));
//
//ポインタ認識
mp.detect(p.x,p.y , pointer_id);
//ポインタを動かす
mp.move(p.x,p.y,pointer_id);
if(parts.z>1500){
mp.down(p.x,p.y,pointer_id);
}else{
mp.up(p.x,p.y,pointer_id);
}
//
}
//
}
//
//
}


データを受け取ったらボーンを描画しますが、KinectMultiUserにはサーバーから受け取ったデータを渡すと自動的にボーンを描画してくれるparseDataというメソッドが実装されているので、ボーン描画部分はその一行で済んでいます。
内部的には、受け取ったデータをパースしてユーザーIDを取得し、まだ存在しなかったらユーザー追加、存在したらデータ更新、という何の変哲も無い処理になっています。

肝心のボーン描画部分は一行で済んでしまっていますが(笑)、これでFlash上にスケルトンモデルを描画する事が出来ました。

さて、ここから以前のようにオブジェクトを動かせるようにしていきます。


for(var i:int=0 ; i<kmu.users ; i++){
//
//ユーザーを取得
var ku:KinectUser=kmu.getUserById(i);
if(!ku)continue;
//
//両手を取得
for(var n:int=0 ; n<2 ; n++){
//
//一意のIDを作成
var pointer_id:int=int(String(i+1)+(n+1));
//
var parts:KinectUserParts;
if(n==0){
parts=ku.getPartsById(KinectUserParts.R_HAND);
}else{
parts=ku.getPartsById(KinectUserParts.L_HAND);
}
//
if(!parts){
mp.lost(0,0,pointer_id);
continue;
}
//
//グローバル座標に変換
var p:Point=ku.local3DToGlobal(new Vector3D(parts.x , parts.y , parts.z));
//
//ポインタ認識
mp.detect(p.x,p.y , pointer_id);
//ポインタを動かす
mp.move(p.x,p.y,pointer_id);
if(parts.z<1500){
mp.down(p.x,p.y,pointer_id);
}else{
mp.up(p.x,p.y,pointer_id);
}
//
}
//
}


まず、KinectMultiUserのusersプロパティで現在認識されている最大のユーザー数を取得し、各KinectUserインスタンスへの参照を取得します。各ユーザーの両手を認識したいので、さらにfor文で両手分処理を繰り返します。繰り返し処理の中は以前のエントリとほぼ同じですが、x,y座標は各ユーザーの両手の座標を使用したいので、各パーツ(KinectUserParts)の座標を使用します。また、今回はx,yに加えてz座標も加味する必要があるので、座標変換の時にlocalToGlobalでなくlocal3DToGlobaを使用しています。

これで、スケルトンモデル+オブジェクト操作で、スケルトンがオブジェクトを操作しているようなデモができました。
オブジェクトの操作について、現在は奥行きでDOWN/UPを判断していますが、手のひらの開閉が取れればもっと自然で良いんですけどね。C++は専門外なので、解析するにしても時間がかかりそうです…

ソースをダウンロード

2010年12月25日土曜日

[Kinectハック] SD_KinectIO とりあえず公開



[Kinectハック]OpenNI+Flashでマルチユーザー・マルチタッチ」で使っていた
OpenNI解析アプリがexe化できたので、「SD_KinectIO」として公開します
手の情報だけじゃなく、すべてのスケルトン情報を送るように改良しました。
ソケットサーバーの知識さえあれば、これを使って誰でもKinectで遊べますよ。

SD_KinectIO:SD_KinectIO_v0_1.zip
ソケット通信でクライアントにスケルトン情報を送ります。
対応:Window XP 32bitのみ動作確認
[2011/1/6追記]
OpenNIのライセンスがLGPLなので、exeではなくソースを公開します。
OpenNIをインストールして、OpenNI/Samples/に下記のファイルを展開してください。

■ソケット通信仕様
クライアントからは下記に接続します。
IP: 127.0.0.1
TCP Port: 3000


■通信内容
内容はカンマ区切りで送っています。
デリミタはFlashの通信仕様に合わせてNull文字にしています。

"ユーザーID","部位ID","状態","x座標","y座標","z座標" (Null)

(例)
1,1,add,100.223,600.032,1534,302
1,1,update,140.344,632.323,1523,324
1,1,remove,0,0,0

ユーザーID:1~10
部位ID:1~24
状態:add,update,remove

部位の対応リストはこんな感じ。
HEAD   = 1
NECK   = 2
TORSO   = 3
WAIST   = 4
LEFT_COLLAR  = 5
LEFT_SHOULDER  = 6
LEFT_ELBOW  = 7
LEFT_WRIST  = 8
LEFT_HAND  = 9
LEFT_FINGERTIP  =10
RIGHT_COLLAR  =11
RIGHT_SHOULDER  =12
RIGHT_ELBOW  =13
RIGHT_WRIST  =14
RIGHT_HAND  =15
RIGHT_FINGERTIP  =16
LEFT_HIP  =17
LEFT_KNEE  =18
LEFT_ANKLE  =19
LEFT_FOOT  =20
RIGHT_HIP  =21
RIGHT_KNEE  =22
RIGHT_ANKLE  =23
RIGHT_FOOT  =24
 

ただ、OpenNIから値が来ていない部位もあり、
有効な部位だけをまとめるとこんな感じ。


■使い方
[注意] KinectドライバとOpenNIが必要です。
KinectドライバとOpenNIをインストールして下さい。

ダウンロードしたファイルから「SD_KinectIO_.exe」を起動します。

起動すると「クライアント接続」待機状態になります。

ファイアーウォールを有効にしている場合は、セキュリティ警告がでるので、
「ブロックを解除する」を選択します。

クライアント側からSocket通信で上記のIP/ポートに接続します。
接続テストだけならSocketDebuggerがオススメです。

クライアント接続後、しばらくするとカメラウィンドウが開きます。
Kinectの前に立って、手を90度に曲げながら上に上げるとキャリブレーションが始まります。

キャリブレーションが終わるとクライアントへスケルトン情報が送られます。


Flash側の解説はまた今度。

-> [Kinectハック] SD_KinectIOをFlashで受ける

2010年12月21日火曜日

[Kinectハック]OpenNI+Flashでマルチユーザー・マルチタッチ Flashで実装編



[Kinectハック]OpenNI+Flashでマルチユーザー・マルチタッチ OpenNI解析編 の続き。


fukusinさんがopenNIのサンプルを解析/改造してくれたおかげで、Kinectの信号をXMLSocketを利用してFlashでも受け取れるようになりました。現時点では各ユーザーの両手のみの検出になっていますが、信号的には頭、首、肩…と、ボーンを表示するのに必要な頂点は全て取れているようです。

さて、今回は「Flashで実装編」として、Kinectから送られてきた信号を解析してFlashに組み込んでいきます。今回のデモでは、各ユーザーの両手が識別できるためマルチタッチのような操作が可能になりますが、正確には「マルチ」ですが「タッチ」ではないですね。空中操作になりますから。「マルチポインタ」と言った所でしょうか。重箱の隅をつつくみたいですが、これ、重要なんです。



この場合、UI的にマルチタッチと全く同じ作法にするのは問題があるのです。タッチ操作であれば画面上のオブジェクトに触れる前にどこに触れるか分かりますが、空中操作、しかもKinectのように画面から離れて操作するものの場合は、実際に手を出してみないとどこにカーソルが表示されるか分かりにくいですよね。

普通タッチパネルでは触った=マウスダウンと同じ操作になり、ロールオーバーとダウン操作を共存させる事が出来ないのですが、前述の理由からKinectの場合はそれが必要になってきます。本家Microsoftは長押しの様な動作で決定(ダウン)操作としていますが、幸いにしてKinectは各ポイントのz値も取れるので、今回は各ポイントのz値がある閾値を下回ったら(手が前に突き出されたら)ダウン状態、閾値を越えたら(手が引っ込んだら)アップ状態として扱う事にします。

今回のデモでは、自前の汎用マルチポインタライブラリを使用しています。ポインタが「認識」「ダウン」「移動」「アップ」「消失」した状態をそれぞれ持つ事が出来、さらにポインタの下にある各DisplayObjectがイベントを発行するように設計したため、MouseEventと同じ様に使う事が出来ます。また、Kinectのような特殊なシステムでない、普通のマルチタッチにも対応できるようになっています。以前のエントリにある「自作マルチタッチシステムを作ってみた」でも、このクラスを使用してマルチタッチを実装しました。

まず、コンストラクタでマルチタッチの用意をし、XMLSocketコネクトのための準備をします。

//==========================
//コンストラクタ
//==========================
public function Main():void{
//
mp=new MultiPointer(-1 , false);
mp.simulate=true;
mp.registerDefaultCursor(HandCursor);
addChild(mp);
//
connect_btn.addEventListener(MouseEvent.CLICK , connectStart);
//
}


マルチタッチに用いるクラスは「MultiPointer」クラスです。汎用クラスとして色々出来るように作ってあるのですが、ここでは詳しい説明は割愛します。
詳細はGoogle Codeにアップしたドキュメントをご覧下さい。

//==========================
//接続開始
//==========================
private function connectStart(e:MouseEvent):void{
//
socket=new XMLSocket();
socket.addEventListener(Event.CONNECT , connectComplete);
socket.addEventListener(DataEvent.DATA , receiveData);
socket.connect(ip.text , int(port.text));
//
}
//
//
//==========================
//接続完了
//==========================
private function connectComplete(e:Event):void{
//接続のためダミーデータを送信(サーバー仕様による)
socket.send("test");
}


ステージ上にあるボタンを押したら接続を開始し、接続が完了したらサーバーにデータを送信します。これは今回作成したサーバー側の仕様で、一旦双方向に通信しないと接続が確立出来ないようになっているためです。

//==========================
//データ受信
//==========================
private function receiveData(e:DataEvent):void{
//
//データ解析
var data:Array=e.data.split(",");
var id:int=data[0];
var parts:int=int(data[1]);
var x:Number=Number(data[2]);
var y:Number=Number(data[3]);
var z:Number=Number(data[4]);
//
//一意のIDを作成
var pointer_id:int=int(parts.toString()+id.toString());
//
//ポインタ認識
mp.detect(x,y,pointer_id);
//ポインタを動かす
mp.move(x,y,pointer_id);
if(z<1500){
mp.down(x,y,pointer_id);
}else{
mp.up(x,y,pointer_id);
}
//
}


データを受信したら、受信したデータを解析してMultiPointerクラスに渡します。 今回、データは「,」区切りの文字列として送信されてくるので、splitで分割し、それぞれの変数に入れていきます。 サーバーから送られてくるデータの中身は

ユーザーID , パーツ名(左手9/右手15) , x座標 , y座標 , z座標

というフォーマットになっています。
このデータを受け、ポインタ生成部分は以下のようになっています。

//一意のIDを作成
var pointer_id:int=int(parts.toString()+id.toString());
//
//ポインタ認識
mp.detect(x,y,pointer_id);
//ポインタを動かす
mp.move(x,y,pointer_id);
if(z<1500){
mp.down(x,y,pointer_id);
}else{
mp.up(x,y,pointer_id);
}


MultiPointer内に以下のメソッドがあり、それぞれ

・ポインタ認識 - detect()
・ポインタ移動 - move()
・ポインタ押下 - down()
・ポインタ押上 - up()

そしてここでは出てきていないですが、

・ポインタ消失 - lost()

となっています。

各関数にはx , y , idの引数を渡す必要があり、ここではマルチユーザーに対応するためパーツのIDとユーザーIDを一旦String形式で結合し、intに変換する事で一意のIDを生成しています。

押下/押上の判定については、z値を使用しています。暫定的に閾値を1500と設定し、手の前後操作でdown/upをそれぞれ実行するようにしています。
肝心のマルチタッチを受けてオブジェクトを操作する部分ですが、これは良く使う操作ですので、基本クラスとして用意しました。 ステージ上のオブジェクトの基本クラスに「PointerHandlingObject」というクラスを設定しています。このクラスは、マルチポイントによる操作と言えば王道な移動、拡縮、回転をサポートしています。

これで、Kinectと接続してFlash上のオブジェクトを操作する簡単なデモが出来ました。
Flashのソース: KinectFlash_sdtech.zip

今回使用した「MultiPointer」クラスは「自作マルチタッチシステムを作ってみた」でも使用しているのですが、その解説はまた今度ということで。

2010年12月20日月曜日

[Kinectハック]OpenNI+Flashでマルチユーザー・マルチタッチ OpenNI解析編



少し乗り遅れ気味ですが、Kinectネタを。
OpenNIの登場でKinectのユーザーのスケルトン情報が一通り取れるようになったので、
それをFlashへの橋渡しをして、マルチユーザー・マルチタッチな環境を作りました。

これでFlashでもNaturalInterfaceコンテンツを作れるんダゼ!

今回は「OpenNI解析編」ということで、
Kinectを購入してから、OpenNIを解析、Flashへ信号を投げるまでの解説です。


■開発環境
kinect制御端末 : WinXP VisualC++ Express2010
コンテンツ端末:MacOSX FlashCS4
ソケット通信で Win→Macへユーザーの手の位置を送ります。


Kinectハックの手順は先人たちをお手本に。

WindowでKinectを繋ぐまでの手順:
OpenNI: WindowsでKinectを使う

OpenNIをビルドするまでの手順
OpenNIをVisual C++ 2010 Expressでビルド&デバックするまでの手順。


上記サイトを参考にOpenNIを改変できるようにしたら、
OpenNIと一緒にインストールされたサンプルから改変しやすそうなのを探します。
NiUserTracker.exeはスケルトン表示までできているので、
今回はこれにソケット通信を追加しました。

C++は初めてなので、ここから、かなり試行錯誤してます…。
【C++】ソケット通信」を参考にして、SocketServerクラスを作りました。
クラスファイル間のアクセス方法がわからなかったんですが、
ハッシュファイルというのを作るんですね。ちょっと面倒です…。
SocketServerクラスにconnectとsend関数を作り、
send関数に送りたい信号を投げるとソケットで通信するようになりました。 

#include 
#include 

SOCKET s, s1;         //ソケット
//
//connect
int connect(){
  int result;          //戻り値
  //接続を許可するクライアント端末の情報
  struct sockaddr_in source;
  char buffer[1024];  //受信データのバッファ領域
  char ans[] = "送信成功";

  memset(&buffer, '\0', sizeof(buffer));
  //送信元の端末情報を登録する
  memset(&source, 0, sizeof(source));
  source.sin_family = AF_INET;
  //ポート番号はクライアントプログラムと共通
  source.sin_port = htons(3000);
  source.sin_addr.s_addr = htonl(INADDR_ANY);

  //ソケット通信の開始準備
  WSADATA data;
  result = WSAStartup(MAKEWORD(2, 0), &data);
  if (result < 0){
    printf("%d\n", GetLastError());
    printf("ソケット通信準備エラー\n");
  }
  //ソケットの生成
  s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
  if (s < 0){
    printf("%d\n", GetLastError());
    printf("ソケット生成エラー\n");
  }
  //ソケットのバインド
  result = bind(s, (struct sockaddr *)&source, sizeof(source));
  if (result < 0){
    printf("%d\n", GetLastError());
    printf("バインドエラー\n");
  }
  //接続の許可
  result = listen(s, 1);
  if (result < 0){
    printf("接続許可エラー\n");
  }
  printf("接続開始\n");
  s1 = s;
  //クライアントから通信があるまで待機
 s1 = accept(s, NULL, NULL);
  if (s1 < 0){
    printf("待機エラー\n");
  }
  //クライアントから送信されたデータの受信
  result = recv(s1, buffer, 10, 0);
  if (result < 0){
    printf("受信エラー\n");
  }
  printf("%sを受信しました\n", buffer);
  return 0;
}
//
//send
int send(char id[],char part[] ,char px[],char py[],char pz[]){
 int result;          //戻り値
 char str[50]= "";
 //
 sprintf_s(str,"%s,%s,%s,%s,%s",id,part,px,py,pz);
 //
 result = send(s1, str, strlen(str)+1, 0);
 return result;
}


Flashには、ユーザーの手の情報を送りたいので、 スケルトンを描画しているクラスを解析します。
SceneDrawer.cppの中のDrawLimb()がそれです。

void DrawLimb(XnUserID player, XnSkeletonJoint eJoint1, XnSkeletonJoint eJoint2)
{
 if (!g_UserGenerator.GetSkeletonCap().IsTracking(player))
 {
  printf("not tracked!\n");
  return;
 }

 XnSkeletonJointPosition joint1, joint2;
 g_UserGenerator.GetSkeletonCap().GetSkeletonJointPosition(player, eJoint1, joint1);
 g_UserGenerator.GetSkeletonCap().GetSkeletonJointPosition(player, eJoint2, joint2);

 if (joint1.fConfidence < 0.5 || joint2.fConfidence < 0.5)
 {
  return;
 }

 XnPoint3D pt[2];
 pt[0] = joint1.position;
 pt[1] = joint2.position;

 g_DepthGenerator.ConvertRealWorldToProjective(2, pt, pt);
 glVertex3i(pt[0].X, pt[0].Y, 0);
 glVertex3i(pt[1].X, pt[1].Y, 0);
}

このコードからGetSkeletonJointPosition()がユーザーの部位の位置を返す関数だとわかります。
ユーザーIDと体の部位を指定するとx,y,z座標を返してくれます。
DrawLimb()を改変した関数SendPosition()を作り、SocketServerのsendに送ります。

void SendPosition(XnUserID player,int id, XnSkeletonJoint part,char _part[2])
{
 if (!g_UserGenerator.GetSkeletonCap().IsTracking(player))
 {
  printf("not tracked!\n");
  return;
 }
 XnSkeletonJointPosition joint1;
 g_UserGenerator.GetSkeletonCap().GetSkeletonJointPosition(player, part, joint1);
 XnPoint3D pt[1];
 pt[0] = joint1.position;
 g_DepthGenerator.ConvertRealWorldToProjective(1, pt, pt);
 //
 char _id[2] = "";
 sprintf( _id, "%d", id);
 //
 char _px[10] = "";
 sprintf( _px, "%.3f", pt[0].X);
 //
 char _py[10] = "";
 sprintf( _py, "%.3f", pt[0].Y);
 //
 char _pz[10] = "";
 sprintf( _pz, "%.3f", pt[0].Z);
 //
 send(_id,_part,_px,_py,_pz);
}

以上で、OpenNIにソケット通信を追加できました。
今回は手の位置だけですが、顔や胴、足の位置も同じ仕組みで取得できます。

ここまでのソース: NiUserTracker_sdtech.zip

※VS上のデバックビルドでは動きますが、exeだとソケット通信開始時に落ちてしまいます。
いいところまで出来ているのに、なぜ…。
[追記 2010.12.22]
exeでも動くようになりました。
OpenNI/Data内の「SamplesConfig.xml」を読み込んでいたのですが、
開発環境とexeではパスが変わってしまうので、読めなくなっていたようです。


main.cppの
#define SAMPLE_XML_PATH "../../Data/SamplesConfig.xml"

#define SAMPLE_XML_PATH "SamplesConfig.xml"として、
exeと同じディレクトリにSamplesConfig.xmlをコピーすれば、
落ちなくになります。


 続きはFlash編へ。


Xbox 360 Kinect センサー
定価:¥ 14,800
新品最安価格:¥ 17,300 (2店出品)
売上ランク:62位
レビュー平均:4.54.5点 (127人がレビュー投稿)
by amazon通販最速検索 at 2010/12/21

2010年12月16日木曜日

自作マルチタッチシステムを作ってみた

いまさら感がありますが、「5分で作れるマルチタッチパネルシステム」を参考に
マルチタッチシステムを作ってみました。
上記のシステムは「Touchlib」を使っていますが、
今回は「Community Core Vision v1.4 (CCV)」を使って、Flashと連携させました。
少し前まで「tbata」という名前だったんですが、いつの間にか「CCV」に変わってますね。


この記事を書いた時点でのCCV最新版は1.3ですが、
プロジェクトページではv1.4βが配布されています。
v1.4βではReacTable等で使われるFiducialsマーカーを認識できるようになっています。


作る際に用意したものは、
-----------------------------------
・スチレンボードで作ったテーブル
・トレーシングペーパーで作ったテーブル天板
・赤外線カメラ
・赤外線ライト
-----------------------------------

赤外線カメラはフレームレートの高いものを用意すると認識精度が上がります。
100fps出るモーションキャプチャ用のOptiTrack FLEX:V100R2カメラを用意できるのが理想ですが、
PlayStationEyeでも十分精度が高いです。
実はPSeye値段に似つかわしくない程ハイスペックで、なんと640x480の解像度で75fps出ます。
数万円してもおかしくないのに3000円でお釣りがきちゃいます。
このコストパフォーマンスの高さからなのか画像認識まわりではPSeyeを使うのが常套手段になっていますねー。

今なら、PSeyeとMOVEコントローラがセットになった「PlayStationMOVE スターターパック」を
買うのが賢いかも。
MOVEコントローラみたいな赤外線ペンライトがあるとキャリブレーションがすごく楽です。


PSeyeの赤外線カメラ化はここここを参考にしました。
可視光をカットするIRフィルターは富士フイルムの「IR 86」を使いました。
ただ、このフィルターだと蛍光管の光も少し拾ってしまうので、
どなたか最適なIRフィルターがあれば教えて下さい。


PSeyeのドライバはCode Laboratoriesの「CL Eye Platform DriverCL Eye Platform Driver」を使います。
このドライバにはノーマルモードとアドバンスモードがあって、
「640x480/75fps」で使用するにはまずアドバンスモードにする必要があります。

アドバンスモードにするには、下のコードをメモ帳にコピペして、「cleye.config」として保存します。

    
    

このファイルを「Community Core Vision.exe」と同じディレクトリに配置すると「640x480/75fps」が選べるようになります。


立ち上げる度に設定するのは面倒なので、
CCVの「data/config.xml」の一部を下のように書き換えて、自動的で「640x480/75fps」にします。


        1
        0
        640
        480
        75


これで運用時に楽になります。


赤外線ライトはなんでもいいのですが、ケイヨーの「K-Light」あたりがオススメです。

あとは、CCVを起動してFlash XMLにチェックを入れておくとCCV-Flash間でソケット通信してくれます。