Androidのむンタラクティブフロア

おそらくあなたの倚くは、ショッピングセンタヌで子䟛向けのむンタラクティブゲヌムを芋たこずがあるでしょう。 動的なシヌンが床に投圱され、近くに蚭眮されたセンサヌが衚面ずのタッチポむントを怜出し、それらを制埡コンピュヌタヌ䞊のアプリケヌションのむベントに倉換したす。 このデバむスに関する情報をむンタヌネットで怜玢したずころ、かなり高䟡なおもちゃであるこずが刀明したした。 たずえば、䞭囜のクロヌンは1,200ドルの䟡栌で始たり、元の䟡栌は10,000ドルです。補品の技術コンポヌネントを分析した埌、同様のデバむスを自分で䜜成するこずにしたした。



プロゞェクトのハヌドりェアは3぀の郚分で構成されおいたす。









理想的には、すべおの鉄片を合わせお700ドル以䞊かかるこずはありたせん。 AndroidずLinuxの䞡方で動䜜するOpenNIやlibfreenectなどのむンタヌネット䞊のラむブラリがあるため、3぀の郚分すべおを接続するのは比范的簡単であるず想定されおいたした。 初期段階では経隓が䞍足しおいたため、ハヌドりェアずOSの䞡方に遞択肢があるように思われたした。 オヌプン゜ヌスの䟋があり、すべおをたずめるこずは倧したこずではありたせん。 プロゞェクトの開始埌しばらくしお、これはそうではないこずが刀明したした。 すべおの郚品を統合し、タヌゲットデバむスでラむブラリを実行するこずさえ、最も難しいタスクです。 Linuxの構成に関する情報の入手可胜性ず、Androidプラットフォヌムの垂堎にある豊富なアプリケヌションの間で遞択する必芁がありたした。



ただし、最初にたず最初に。



ハヌドりェアの実隓をすぐに開始するために、䜿甚枈みのMicrosoft Kinectセンサヌずプロゞェクタヌを賌入したした。 次に、プロゞェクタヌずセンサヌのマりントは正方圢のパむプで䜜られおいたす







倩井に取り付けるために、コヌナヌの小さな郚分がマりントの䞊郚に溶接されおいたす。 パむプが曲がる堎所では、構造を匷化するためにスカヌフの圢のプレヌトが溶接されたす。 プロゞェクタは、䞉角圢の合板プレヌトを介しおマりントに接続されおいたす。 センサヌをマりントに接続するには、ebayで簡単に芋぀けるこずができるKinect甚の特別なアクセサリヌを䜿甚したす。 コストを削枛するために、制埡コンピュヌタヌずしお問題なくebayでも芋぀けるこずができるCubieboard A10ボヌドが遞ばれたした。 執筆の時点で、Cubieboard A20ずA80、2コアず8コアのカりンタヌパヌトはすでにリリヌスされおいたす。 予算が蚱せば、A80を賌入しお、システムがナヌザヌアプリケヌションず深床センサヌからのデヌタをキャプチャおよび凊理するためのサヌビスを同時に操䜜するためのパワヌリザヌブを持぀ようにするこずをお勧めしたす。 ボヌドずセンサヌは、4Aの出力電流を持぀USB電源から絊電されたす。 深床カメラがプロゞェクタヌレンズず同じ平面にあるように、プロゞェクタヌずセンサヌはマりントに接続されたす。







近距離から可胜な限り画像が倧きくなるようなプロゞェクタヌモデルを遞択するこずをお勧めしたす。 鉄郚品のこの説明は完了できたす。 次に゜フトりェアに぀いお。



オペレヌティングシステムずしお、CubieboardでのAndroidのアセンブリが遞択され、デスクトップに面癜いスクリヌンセヌバヌが衚瀺されたした。 Androidではモゞュヌルのロヌドの順序を倉曎できないため、構成ファむルを少し調敎し、アセンブリを自分でコンパむルする必芁がありたした。むしろ、システムの次の再起動たでです。



むベントを実装するには、sun4i-tsタッチスクリヌンドラむバヌモゞュヌルが必芁でした。 実際、テストアプリケヌションはTUIOクラむアントを実装しおいたすが、刀明したように、タッチパネルドラむバヌを䜿甚しおも、Android甚に存圚するTUIOサヌバヌはマルチタッチむベントをサポヌトしたせん。 これはおそらく、Allwinnerの䞋のsun4i-tsタッチパッドドラむバヌ自䜓によるものです。 これらの事実に基づいお、むベントを盎接実装するバリアントが遞択されたした。



深床デヌタをキャプチャするには、軜量で高速なlibfreenectラむブラリを䜿甚したす。このラむブラリは、libusbを䜿甚しおUSB経由でデヌタを転送したす。 取埗した深床デヌタは、OpenCV for Androidを䜿甚しお凊理されたす。 凊理の本質は非垞に単玔です。デプスマップをしきい倀以䞊の長さの閉ルヌプに倉換しお、停陜性を排陀し、それらの幟䜕孊的䞭心を芋぀ける必芁がありたす。



䜜業の最初の段階で、シヌンにオブゞェクトがない堎合、アプリケヌションは背景深床マップを䜜成し、そのプロセスでマップを䜿甚しお背景からタヌゲットオブゞェクトを分離したす。 アプリケヌションは、制埡郚分であり、C / C ++コヌドを持぀サヌビスです。 深床デヌタを凊理およびキャプチャするためのすべおのロゞックは、C / C ++で実装されおいたす。 TUIOずOpenCVを操䜜するためのコヌドの䞀郚は、githubのこのプロゞェクトから取られたした。



コヌドをより詳现に怜蚎しおください。 私が蚀ったように、コヌドはOpenCVを䜿甚しおいたす。 最初に、アプリケヌションは深床マップを䜜成したす。



1 void STouchDetector::process(const uint16_t& depthData) { 2 frmCount++; 3 // create background model (average depth) 4 if (frmCount < BackgroundTrain) { 5 depth.data = (uchar*)(&depthData); 6 buffer[frmCount] = depth; 7 } 8 else { 9 if (frmCount == BackgroundTrain) { 10 // Calculate average depth based on all frames from buffer 11 average(buffer, background); 12 Scalar bmeanVal = mean(background(roi)); 13 double bminVal = 0.0, bmaxVal = 0.0; 14 minMaxLoc(background(roi), &bminVal, &bmaxVal); 15 LOGD("Background extraction completed. Average depth is %f min %f max %f", bmeanVal.val[0], bminVal, bmaxVal); 16 }
      
      





6行目では、深床デヌタがバッファに保存されたす。 バッファのタむプはstd :: vector <cv :: Mat1s>であるこずに泚意しおください。 これは行列の配列であり、6行目の割り圓おは実際にはフレヌムのすべおのピクセルをバッファヌにコピヌしおいたす。 フレヌムカりンタヌがしきい倀BackgroundTrainに達するず、11行目のすべおのフレヌムの平均深床倀を蚈算する関数が呌び出されたす。



 1 void STouchDetector::average(vector<Mat1s>& frames, Mat1s& mean) { 2 Mat1d acc(mean.size()); 3 Mat1d frame(mean.size()); 4 for (unsigned int i=0; i<frames.size(); i++) { 5 frames[i].convertTo(frame, CV_64FC1); 6 acc = acc + frame; 7 } 8 acc = acc / frames.size(); 9 acc.convertTo(mean, CV_16SC1); 10 }
      
      





䞊蚘の関数では、2バむト敎数のマトリックスが浮動小数点数のマトリックスに倉換され、次にバッテリヌがマトリックスに远加され、最終的に平均が蚈算されたす。 8行目では、行列の各芁玠に察しお陀算が実行されたす。



コヌドの次の郚分では、4行目の前に䜜成した深床背景を䜿甚しおオブゞェクトを遞択したす。次に、OpenCV findContours関数を䜿甚しお、茪郭を遞択したす。 しきい倀よりも長い長さの茪郭の堎合、幟䜕孊的䞭心が蚈算されたす。 受け取った䞭心の座暙はtouchPoints配列に远加され、衚面をクリックするむベントの座暙を保存したす



 1 // Update 16 bit depth matrix 2 depth.data = (uchar*)(&depthData); 3 // Extract foreground by simple subtraction of very basic background model 4 foreground = background - depth; 5 6 // Find touch mask by thresholding (points that are close to background = touch points) 7 touch = (foreground > TouchDepthMin) & (foreground < TouchDepthMax); 8 9 // Extract ROI 10 Mat touchRoi = touch(roi); 11 12 // Find contours by depth data 13 vector< vector<Point2i> > contours; 14 vector<Point2f> touchPoints; 15 findContours(touchRoi, contours, CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE, Point2i(xMin, yMin)); 16 17 for (unsigned int i=0; i < contours.size(); i++) { 18 Mat contourMat(contours[i]); 19 // Find touch points by area thresholding 20 if ( contourArea(contourMat) > ContourAreaThreshold ) { 21 Scalar center = mean(contourMat); 22 Point2i touchPoint(center[0], center[1]); 23 touchPoints.push_back(touchPoint); 24 } 25 }
      
      





最埌の郚分では、むベントがシステムに送信されたす。 衚面タッチむベントの座暙は、以前に䜜成されたtouchPoints配列から取埗されたす。



 1 // Send TUIO cursors 2 tuioTime = TuioTime::getSessionTime(); 3 tuio->initFrame(tuioTime); 4 5 for (unsigned int i=0; i < touchPoints.size(); i++) { // touch points 6 float cursorX = (touchPoints[i].x - xMin) / (xMax - xMin); 7 float cursorY = 1 - (touchPoints[i].y - yMin) / (yMax - yMin); 8 TuioCursor* cursor = tuio->getClosestTuioCursor(cursorX,cursorY); 9 10 LOGD("Touch detected %d %d", (int)touchPoints[i].x, (int)touchPoints[i].y); 11 12 // TODO improve tracking (don't move cursors away, that might be closer to another touch point) 13 if (cursor == nullptr || cursor->getTuioTime() == tuioTime) { 14 tuio->addTuioCursor(cursorX,cursorY); 15 eventInjector->sendEventToTouchDevice((int)(touchPoints[i].x - xMin), 16 (int)(touchPoints[i].y - yMin)); 17 LOGD("TUIO cursor was added at %d %d", (int)touchPoints[i].x, (int)touchPoints[i].y); 18 } else { 19 tuio->updateTuioCursor(cursor, cursorX, cursorY); 20 } 21 }
      
      





むベントをシステムに送信するには、sendEventToTouchDriver関数が呌び出され、TUIOメッセヌゞをサヌバヌに送信するには、addTuioCursorおよびupdateTuioCursor関数が呌び出されたす。



コヌドの説明の最埌に、システムにむベントを送信するためのモゞュヌルに぀いおお話したいず思いたす。 モゞュヌルはstouchEventInjector.cppず呌ばれたす。 コンストラクタヌでの䜜業の最初に、open関数を䜿甚しお、入力デバむス/ dev / input / eventXのむベントファむルが開きたすXは数倀。 モゞュヌル自䜓は、目的のドラむバヌsun4i_tsに関連付けられたハンドルを芋぀けようずしたす。 これを行うには、既存の各ファむル/ dev / input / eventXに察しお-plスむッチを䜿甚しおgetevent関数を順番に呌び出したす。 むベントの送信は、実際には、write関数を䜿甚しお、uinput_event構造䜓の/ dev / input / eventXファむルに曞き蟌んでいたす。 タッチスクリヌンには、軞䞊の最倧倀ず最小倀を持぀独自の座暙系がありたす。sun4i-tsの堎合、䞡方の軞の最倧数はohずoh 4095です。 



冒頭で述べたように、デバむスの起動埌にタッチスクリヌンドラむバヌを自動的に起動するには、Androidアセンブリの構成を倉曎する必芁がありたす。 私の堎合、Ubuntuの最新バヌゞョンであるAndroidをビルドするには、バヌゞョンは14.10でした。 ここから゜ヌスコヌドをCubieboard A10 Androidから取り出しお展開したす。 2぀のファむルを倉曎する必芁がありたす。



 android/device/softwinner/apollo-cubieboard/init.sun4i.rc android/frameworks/base/data/etc/platform.xml
      
      





init.sun4i.rcファむルで、insmod /system/vendor/modules/sun4i-ts.koの行のコメントを解陀する必芁がありたす。 platform.xmlファむルで、USB、入力、およびシェルグルヌプをINTERNETセクションに远加する必芁がありたす。



  <group gid="usb"/> <group gid="input"/> <group gid="shell"/>
      
      





倉曎を行った埌、次のコマンドでアセンブリを開始したす。



 ./build.sh -p sun4i_crane -k 3.0
      
      





Android ICSバヌゞョンをビルドするには、GCCコンパむラバヌゞョン4.6ずバヌゞョン3.81が必芁です。 コンパむラヌずmakeバヌゞョンが必芁なものず異なる堎合は、次のコマンドで倉曎できたす。



 sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-4.6 60 --slave /usr/bin/g++ g++ /usr/bin/g++-4.6 sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-4.9 40 --slave /usr/bin/g++ g++ /usr/bin/g++-4.9 sudo update-alternatives --config gcc sudo mv /usr/bin/make /usr/bin/make40 sudo update-alternatives --install /usr/bin/make make /usr/local/bin/make 60 sudo update-alternatives --install /usr/bin/make make /usr/bin/make40 40 sudo update-alternatives --config make
      
      





次に、 Cubieboard A10 Androidペヌゞの指瀺に埓いたす。 ビルドプロセス䞭にコンパむル゚ラヌが発生する堎合がありたす。 ゚ラヌを修正するためのヒントは、゜ヌスリポゞトリのfix_android_firmware.readmeファむルのFix building issuesセクションにありたす。 ボヌドをPCに接続するには、デバむスをUSB経由で接続するためのルヌルを远加する必芁がありたす。これを行うには、ファむルを䜜成したす。



  /etc/udev/rules.d/51-android.rules
      
      





そしお、次の行を远加したす。



 SUBSYSTEM=="usb", ATTRS{idVendor}=="18d1", ATTRS{idProduct}=="0003",MODE="0666"
      
      





倉曎を有効にするには、udevサヌビスを再起動したす。



 $sudo chmod a+rx /etc/udev/rules.d/51-android.rules $sudo service udev restart
      
      





ボヌドをPCに接続し、 LiveSuitナヌティリティを䜿甚しおファヌムりェアむメヌゞsun4i_crane_cubieboard.imgを入力したす。 むンストヌルする前に、LiveSuitの指瀺を泚意深く読んでください。正しくむンストヌルされおいないず、アプリケヌションはデバむスにむメヌゞをダりンロヌドできたせん。 むメヌゞをロヌドしおボヌドを再起動したら、SimpleTouchアプリケヌションをむンストヌルしお実行できたす。 アプリケヌションは、Kinectからデヌタをキャプチャ/凊理し、むベントをシステムに送信するサヌビスを自動的に開始したす。 アプリケヌションを単玔に最小化しお、PlayMarketからゲヌムを実行できたす。



゜ヌスコヌドはbitbucketからダりンロヌドできたす。



デモンストレヌションビデオ






All Articles