2011年2月28日月曜日

GT5用のDirectXフィルターの作成(1)

グランツーリスモ5のキャプチャー画面から車速とギアを認識するDirectXフィルターをつくってました。

まだ習作レベルです。
こちら

やり方ですが、OpenCV1.0についてくるproxytransフィルターを改造してつくります。
(現在OpenCVのバージョンは2.2? バージョンアップしたほうがいいのかしらん。。。)
proxytransフィルターの組み込み方は、僕の以前のブログ(lostworksの方)に書きましたが、ちょっとしたコツが必要です。
僕は、KMPlayerに組み込んでます。

画像からの文字認識は、あらかじめ文字を切りだしておいて下のような画像を作っておき、これを使ってテンプレートマッチング(cvMatchTemplate())で特徴量を出します。


文字が赤いのは、R画像だけ使うためです。
cvMatchTemplate()にはいくつかメソッドを選択できるのですが、CV_TM_SQDIFF_NORMEDだとうまくいかなかったのでCV_TM_CCOEFF_NORMEDを使いました。

キャプチャー動画ですが、生の動画サイズは640X426なんですね><てっきり720X480だと思っていたので、テンプレート画像をもう一度つくらないといけないです><

特徴量を出した後の判定処理ですが、最初は、CvKNearest()を使ってk-近傍法で判定できるかなぁっと作ってみたんだけど、文字が存在しない場合(100, 10の桁の数字がない場合)の判定方法の対処方法がわからなかったので、現状ではcvMinMaxLoc()でテンプレートマッチングした値の最大値となる位置を割り出して判定してます。最大値が0.5以下だったらそこは路面って判定です。

かなり冗長に作ったのですが、フレームレートは1/60?1/30? PCにとっては長い処理時間間隔なので余裕で処理できますね^^
本当は、動画フレームから画像を切りだしてテンプレートマッチング以外の特徴量ベクトルをだし、判別処理にぶち込むのがよいのでしょうが、その特徴量ベクトルを出す手法としてなにが最適なのか、よくわからなかったので今後の研究対象です。

また、上のリンクの動画の最後の方、路面に文字が書いてあるところを通過するときに、認識が失敗しているのですよね><原因調査と改良が必要です。

まだまだ使えるレベルには程遠いなぁ。。。

2011年2月27日日曜日

インセプション

序盤は夢の設計?夢の階層?と理解できなかったです^^;;;

VFXが、いかにもって雰囲気で習作なチープな感じがしたのと(夢の中の出来事だし、話の流れの中での説明場面なので、こうなったのでしょうか)、話が長いのとが難ですが、夢の中に入り込む動機、集められたメンバーと役割、作戦内容と展開が新鮮で面白かったです。あー、でもやっぱり時間が長い><最後飽きてきちゃったもん。。。

で、DVD観終わった後でwikipediaでいつもの復習w

~ホルヘ・ルイス・ボルヘスの短篇集「伝奇集」から着想を得て~

これって文章量としては結構短い、薄い本じゃなかったですか???大学の頃、気に入って持っていたような気がする~♪懐かしいなぁ。多分実家の物置の奥に入っているはず。発掘したいw(これが、このネタで書きたかったことだったりするw)

2011年2月22日火曜日

やっと元のタイムに、そしてタイム更新。

速い人のライブビューをじっくり見ていまさらながら気づいた^^;;;;;
俺ってどアンダーな車に乗ってたんだなwww
となると、セッティングとしてはこうだろうってすぐにわかってタイムもポンポン縮めていけたんだけど、まだまだクラッシュ多し><

それと平行して、OpenCVでの画像解析の準備を。。。
速度の数字は画像切り出して特徴量出して、でいいのだろうけど、回転数をどうしたものか。
針の位置から?

2011年2月20日日曜日

だめだ、まだ鼻くそほじれねぇw

やっぱ、インターフェースを2箇所も変えるとだめっす><全然乗れてないっす。
とりあえず、ステアリングセンター、ステアリング、アクセル、ブレーキのMAX位置を設定できるようにして、若干非線形カーブ(x^y (0 < x < 1) でyを1.0近辺でいじれるようにした)にして、パッドスピードはデフォルトの1のまま試走したんだけど、高速域で自分の感覚より大きいステアリング操作が必要になるのでクラッシュしまくりました><レスポンスが悪い(ラグがある)のかもしれないな。今までアクセルふみっぱ~でいけてたところでアクセル抜いておかないと道を外れてしまいます。
中低速コーナーは、今までと違いハーフスロットルが使えるのですぐにパッド操作のタイムに追いつきそう。
高速域ではステアリングの非線形カーブをキツメになるように係数変更すればいいのかもしれないけど、スムーズに切り替えるのは難しいし、そんなことよりまず練習して体で覚えこまないといけません。

ペダル操作は左足ブレーキを使うしかないです><ガスペダルとブレーキペダルとがちょっと近すぎるんですよね。同時に踏んでしまうことがあるのでやむなく。ただ、高速域でのアクセル踏んだままの左足ブレーキは使えるかもしれない。
実車では左足ブレーキなんてやったことないし、左足ブレーキだとシートバックに体をガッチリ固定しないとケツだけで体重を支えているような状態なので疲れることこの上ないです。背もたれのある椅子にすわってプレイしてるんじゃないんだもん^^;;;
ラジコン操作(トリガーでスロットル調整)も試してみたのですが、こっちの操作系は僕にはしっくりこないなぁ。

ハードウェアの用意はOK?

とりあえず、ハードウェアの準備までは到達しました。

僕の当初の構想では、DualShock3のスティックでステアリング操作、フットペダルでアクセル/ブレーキ操作、画像処理でドライブバイワイヤ化/AT制御、これでグランツーリスモ5を走る予定だったのですが、コントローラの重複使用は無理(できたとしてもコントローラのプロトコル的にいっておそらく不具合が出るかもしれないなぁ)なので、ラジコンのプロポでのステアリング操作に変更しました。。。要するにステアリングバイワイヤ化もやれっ、さらに自動運転もやれってことになってくると思うのですが、あまり制御内容が増えるととっちらかるので、ぼちぼち慣れながら遊んで行きましょうかね。

接続設定としては、
PS3 -> XFPS4.0 Force
XFP4.0 Force -> PC
PC -> Arduino
Arduino -> ラジコンプロポのコントローラ
Arduino -> フットペダル
って感じです。
Arduinoはラジコンプロポのステアリングとフットペダルの位置(アクセル/ブレーキ)をA/D変換して取得し、PCにUSB経由シリアル出力で渡す。
PCは、取得した値を整形してXFPS4.0 Forceのデバイスドライバ(HID)の用意するパイプに書き込む。























昔買った京商ミニッツレーサーのコントローラを引っ張り出してきました。
ステアリングと(今は使わないけど)スロットルのポテンショメータに配線。
左のトリムも活かそうかどうか迷ったけど、どうせステアリングバイワイア化するんだし必要ないかって省略しました。(回路を活かしつつ組み込むのがめんどくさかったともいうw)






フットペダルは、、、ハンコン買いましたよ><フットペダルのためだけに。
昔MadCatzのハンコン持ってたけど邪魔すぎて売っちゃったし。
買っても損しないようにPCでも使える汎用性の高いのをってことで、マルチレーシングコントローラSuperSports3Xってのを買いました。さすがにロジのハンコン(GTForceとか?)は、この遊びには高すぎて買えない、、、
ためしにこのハンコンでグランツーリスモ5やってみたけど、慣れの問題もあるのだろうけど走れるものじゃなかったですw
あぁ、ステアリングの方は捨てちゃおっかなぁ。でかくて邪魔だし。
これ、品質的に値段相応じゃないなぁって思いますね。上の京商のラジコンプロポですが、ミニッツレーサー用で安物ではあるんだけど、さすがに実走で遊ぶための品質は確保されていて、ポテンショメータの範囲調整がよく出来て出荷されてます。でもこのペダル、ポテンショメータの位置がいい加減でポテンショメータの測定範囲の半分しか使い切ってません。配線も2線のみ。リファレンス電圧に対して位置を求めてないで単純に抵抗値だけで位置を求めています。






















ポテンショメータの電圧のA/D変換は、Arduinoの端子につないでA/D変換し、PCにシリアル出力してるだけです。
簡単簡単♪

PC側のシリアル制御コードは、こちらを利用。これを使って、USBのCOM(115200bps)からRead()するごとにメッセージを飛ばすようにコーディング。なので、ちょいとラグが出ているかもしれませんね。メッセージ使う場合、即座に処理できるんだったっけ?1ms位のウェイト?まぁ、ラグが酷いようなら改善の余地はまだ少しあります。























画面はこんな感じ。右のダイアログは画像が潰れてしまってますが、各種ボタンを操作するパネルです。ゲーム画面はSD出力をキャプチャしてKMPlayerで表示させます。画質的には、一旦RGB24に変換しているためか、キャプチャをそのまま出力させるよりも見辛いかも。。。(例えばインターレース処理はキャプチャのダイレクト出力とは違和感があります)
まぁ、慣れの問題だと思うし、どうしても受け入れ難かったら普通にPS3から直接表示させればいいわけで。

これに自作のフィルターを組み込んで、画像処理をさせようって魂胆です。左の赤数字はサンプルとして昔作ったフィルターによるもの。次の目標は、フィルター内で得た画像データをOpenCVで画像解析して車速/回転数を認識することかな?

さて、とりあえず試走してみましたが、アクセル/ブレーキはまだしも、ステアリング操作の非線形さに慣れが必要です。もともとグランツーリスモ5のソフト側でスティックの値を操作してるはずで、その非線形曲線を把握しないと理想的な制御の非線形曲線は得られないのだろうなぁ><

でもまぁ、やっぱこれですよねぇ。グランツーリスモ5は、お気に入りの車に乗って楽な姿勢で鼻くそほじりながらw流して走りたいじゃないですかw

2011年2月19日土曜日

USBポート数たりね

僕のPC、WindowsXPで5年物なんだけど8ポートのUSBが全部うまってしまう。。。

キーボード、マウスで×2
USB音源で×1
外付けHDDで×2
BlueToothで×1
地デジ用がメイン用途のカードリーダで×1

残り1つしかないじゃん。。。キーボード/マウスで2つも使うのと、内蔵HDDの容量の無さから外付けHDDを使わざるをえないのがネック。

ArduinoとXFPSを使うには、、、BlueToothを外すしかないか、USBハブを買うか。

xfpsをアプリによってコントロールする

以前こちらでラフなコードを書きましたが、ちゃんとxfpsとパイプで通信できるように修正しました。

この関数で、xfpsのパイプのハンドルを取得します。


HANDLE xfpsInitialize()
{
  HANDLE hPipe=INVALID_HANDLE_VALUE;

  GUID hidGuid;
  HDEVINFO hardwareDeviceInfo;

  HidD_GetHidGuid(&hidGuid);
  hardwareDeviceInfo=SetupDiGetClassDevs(&hidGuid, NULL, NULL, 0x12);

  SP_DEVICE_INTERFACE_DATA sp_devinterfacedata;
  sp_devinterfacedata.cbSize=sizeof(SP_DEVICE_INTERFACE_DATA);

  for(DWORD MemberIndex=0; ; MemberIndex++){
    int ret=SetupDiEnumDeviceInterfaces(hardwareDeviceInfo,
                                        NULL,
                                        &hidGuid,
                                        MemberIndex,
                                        &sp_devinterfacedata);
    if(ret==0) break;
  
    DWORD requiredLength, predictedLength;
    SetupDiGetDeviceInterfaceDetail(hardwareDeviceInfo,
                                    &sp_devinterfacedata,
                                    NULL,
                                    0,
                                    &requiredLength,
                                    NULL);
  
    predictedLength=requiredLength;
    PSP_DEVICE_INTERFACE_DETAIL_DATA psp_devinterfacedetail=
      (PSP_DEVICE_INTERFACE_DETAIL_DATA)malloc(predictedLength);
    psp_devinterfacedetail->cbSize=sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);

    SetupDiGetDeviceInterfaceDetail(hardwareDeviceInfo,
                                    &sp_devinterfacedata,
                                    psp_devinterfacedetail,
                                    predictedLength,
                                    &requiredLength,
                                    NULL);
  

    HANDLE hHIDDevice=CreateFile(psp_devinterfacedetail->DevicePath,
                                 0x40000000, // GENERIC_WRITE
                                 2,
                                 NULL, // no SECURITY_ATTRIBUTES
                                 3, // OPEN_EXISTING
                                 0x40000000, // FILE_FLAG_OVERLAPPED
                                 NULL);
  
    if(hHIDDevice!=INVALID_HANDLE_VALUE){
    
      HIDD_ATTRIBUTES HIDAttributes;
      HIDAttributes.Size=sizeof(ULONG);
      HidD_GetAttributes(hHIDDevice, &HIDAttributes);

      PHIDP_PREPARSED_DATA HIDPreparsedData;
      ret=HidD_GetPreparsedData(hHIDDevice, &HIDPreparsedData);
      if(ret){
        HIDP_CAPS HIDCaps;
        HidP_GetCaps(HIDPreparsedData, &HIDCaps);
        //    ...
        HidD_FreePreparsedData(HIDPreparsedData);
      }
      CloseHandle(hHIDDevice);

      char pipeFileName[MAX_PATH];
      if((HIDAttributes.VendorID==0x04b4)&&(HIDAttributes.ProductID==0x0811)){
        wsprintf(pipeFileName, "%s\\PIPE1", psp_devinterfacedetail->DevicePath);
        hPipe=CreateFile(pipeFileName,
                         0xc0000000, // GENERIC_WRITE|GENERIC_READ
                         3, // FILE_SHARE_READ|FILE_SHARE_WRITE
                         NULL,
                         3, // OPEN_EXISTING
                         0,
                         NULL);
      }
    }
    free(psp_devinterfacedetail);
  }
  SetupDiDestroyDeviceInfoList(hardwareDeviceInfo);
  return hPipe;
}

このパイプに書き込むデータですが、リバースエンジニアリングとこちらの情報から、以下の50(0x32)バイトを書きこめばよいことがわかります。

#pragma pack(push, 1)
typedef struct
{
  unsigned char Reserved0;
  unsigned char ReportType;
  unsigned char Reserved1;
  unsigned char ButtonState1;
  unsigned char ButtonState2;
  unsigned char PSButtonState;
  unsigned char Reserved2;
  unsigned char LeftStickX;
  unsigned char LeftStickY;
  unsigned char RightStickX;
  unsigned char RightStickY;
  unsigned char Reserved3[4];
  unsigned char PressureUp;
  unsigned char PressureRight;
  unsigned char PressureDown;
  unsigned char PressureLeft;
  unsigned char PressureL2;
  unsigned char PressureR2;
  unsigned char PressureL1;
  unsigned char PressureR1;
  unsigned char PressureTriangle;
  unsigned char PressureCircule;
  unsigned char PressureCross;
  unsigned char PressureSquare;
  unsigned char Reserved4[3];
  unsigned char Charge;
  unsigned char Power;
  unsigned char Connection;
  unsigned char Reserved5[9];
  unsigned short AccelerometerX; // big endian
  unsigned short Accelerometer;  // big endian
  unsigned short AccelerometerZ; // big endian
  unsigned short GyrometerX;     // big endian
}PS3ControllerData;
#pragma pack(pop)


Reserved5の9バイトは、xfpsが書き込んでる内容をそのまま使いましょう。
  rawdata->Reserved5[0]  =0x00;
  rawdata->Reserved5[1]  =0x00;
  rawdata->Reserved5[2]  =0x00;
  rawdata->Reserved5[3]  =0x00;
  rawdata->Reserved5[4]  =0x33;
  rawdata->Reserved5[5]  =0xfe;
  rawdata->Reserved5[6]  =0x77;
  rawdata->Reserved5[7]  =0x01;
  rawdata->Reserved5[8]  =0xc0;

ということで、自作アプリからPSボタンをプッシュ/リリースできることを確認しました。

さて、次はアナログインターフェースの作成ですね。
世の中のアナログ操作系は大抵はポテンショメータの抵抗値変化によって値を出しているので、AD変換して1チップマイコンに取り込むだけですぐできそうです。

さて、ケーブルを買いに行ってきます♪

続・続・続・続ニュルブルクリンク

その後ラインもオフにして、ぼちぼちと練習してやっと以前の、スキッドリカバリーその他アシストありの時のタイムに追いつきました。
ただし、ABSは1で^^;;;ATもまだ使ってますよ~。

ですがやはり壁を感じますね。最近はSW20のGT-Sを400馬力にして走ってますが、7分30秒を切れない><
ここから6分台にいれるためには、12のセクターで3秒づつ縮めなければなりません。
例えば、最初の区間は、6分台で走れる人は43秒以内で普通に走ってますねぇ。。。
最初の区間(ニュルブルクリンクって、最初の区間が一番難しいのねん><)はタイムアタック時は(中断しないので)必ず通る区間で走りこんでるはずですが、ここから3秒縮めるのは無理じゃねって感じです><

6分台で走るってことは、ブレーキで姿勢作った後は全て「ふみっぱ~」ってことなんでしょうか。全てのコーナーを慣性ドリフトでまわる、とか???それをやろうとすると、セッティング的にはフロントの接地感がない、ゆるゆるの車で、低速じゃまともに走れなくなっちゃうし、攻めた走りだとワンミスで終わり、縁石に蹴られたりちょっとダートに入ったところで終わり、なので安定して周回できません><

ということで、ドライビングの技術向上は半ば諦めて^^;;;;;パッドのちょい上の操作感、だけどハンコンほど場所取らない、という操作環境をつくろうと画策しているところです。

2011年2月17日木曜日

XFPSとコントローラの併用

あるぇー、、、

前にコントローラとxfpsとで併用できた記憶があったんだけど、できねー111111

前はどういうふうに接続したんだろうか???

2011年2月12日土曜日

続・続・続ニュルブルクリンク

タイム的に、どの程度が標準的なのか、オープンロビーを覗いてみたりするのですが、
ATはまだしも(というか許して><)、スキッドリカバリーオンだと、

「きもーい、スキッドリカバリーが許されるのは小学生までよね!」(AA 略

と内なる声が聞こえてくるので、スキッドリカバリーオフで練習しました。
昨日は雪降ってたので、1日中^^;;;;

ホイールスピンやタイヤのスキッドを抑えるためにスキッドリカバリー機能がアシスト機能としてあるわけですが、そのセッティングに、僕のコントローラの操作が邪魔をしてくれます。
僕は、アクセル、ブレーキをボタンでデジタル的にやっているんですね^^;;;;
PWM的にやっているという。。。w
なので、アクセル全開、アクセル全閉、アクセル全閉+フルブレーキ、の3通りのパターンしかないw

アクセルオン=アクセル全開しかないため、パワーオーバーです。仕方ないので、リアを柔らかくしました。
タイヤのスキッドをねじ伏せるために、(僕の嫌いな)LSDをいじりました。アクセルオンとアクセルオフしか操作しないため、ベストなセッティングを見つけるのに苦労します。
フルブレーキ時にリアが流れるので、ブレーキの前後バランスを調整しました。

とりあえず、走れることは走れるけど、スキッドリカバリーオンのときの10秒落ち。。。

コントローラでアクセル/ブレーキのアナログ操作って難しいんだよ><ボタンの方に慣れてしまったので今さら変えれないし。
かといって、ドライビングコントローラはデカイし欲しくないんだなあ。
アクセル/ブレーキのフットスイッチだけ欲しいよ。

2011年2月8日火曜日

続・続ニュルブルクリンク

やっと、7分0秒台><
車的には650馬力級だから軽く6分台が出せるんだろうけど、だめだわ><
ATのスキッドリカバリフォースありだし><
ある程度乗れてくると、もっと攻めようとして、でも失敗してタイムが出ないという。。。意地悪なコースです。

実車だとGT-Rが7分20秒台かぁ。
どんどんこのコース専用セッティングになっていくなぁ。

2011年2月6日日曜日

続・ニュルブルクリンク

コース覚えて、乗れてきたら面白い面白い^^
このコースって、サーキットっていうより峠道なんだな。
昔、某峠道に通って熱くなってた懐かしさがぶり返してきます。

上のクラスのほうでニュルブルクリンクを選択できるレースは、ドイツ車限定と、DTMと、レース車両ばっかり出てくるレース(^^;)があります。
で、ドイツ車限定の方をM3クーペ、DTMをアルファロメオ155でやってます。
賞金の関係からアルファロメオ155ばっかですけどね。毎回優勝で約1000万ずつ貯金。

レース車両(LM仕様とか?)の入手は、お金が足りないのと、なにに乗るか決めてないのと、中古で出てくるタイミングとがあるので、当分先になりそうです。でも、僕に乗りこなせるのかねぇ、速い車って仕様的にはMRで大パワーでしょ><

あとで気づいたのですが、このニュルブルクリンクのチュートリアルがSPECIALに用意されていたんですね。知らんかった。。。で、初級やってみたらオール銀><このチュートリアルで金が取れる人って区間タイムで10秒速いってことであり、信じられません><

2011年2月2日水曜日

魔法少女まどか☆マギカ

録画が溜まっていたので一話からサラッと見てみた。
ふむー、魔女っ子モノかぁ、萌え系なのかな?大量生産品?あ、でも音楽は梶浦由記だよ?そうはいっても多分僕の守備範囲じゃないんだろうな(えっ^^;;;)って三話見たら(二話は、録り忘れか裏番組とかぶったのかなぁ、録ってなかった)、、、

早過ぎる退場にコーヒー吹いたw

なにこれw

監督が化物語の人かぁ、これ、見続けてみます。

2011年2月1日火曜日

ニュルブルクリンク

グランツーリスモ5も壁に当たって、1日にちょっとづつのペース。。。
ニュルブルクリンク北コース、長くて嫌いだ。しかも道からちょっとでも外れるとオシマイ><
やっとコース覚えて優勝したけど、なんだかなぁ。。。2位を保つのが難しいのでトップかリタイヤぎみかのどっちかだぁ。。。
逆にコースをすぐに覚えることが出来るのがモンツァ。モンツァサーキットってこんなに単純なコースだったのか。