>   >  Unityでつくるインタラクティブコンテンツ:第5回:測域センサを使ってみる
第5回:測域センサを使ってみる

第5回:測域センサを使ってみる

<3>測域センサのデータをプログラムで取得する

センサのデータを取得するプログラムを準備します。北陽電機製センサのプログラム情報は下記のサイトにまとめられています。

●URG Network
https://sourceforge.net/p/urgnetwork/wiki/top_jp/


C/C++、C#、Java用のライブラリが公開されていますが、今回はUnityで直接使用したいのでC#用のサンプルを利用します。[ダウンロード]以下のリンクからUrg_cs_sample.zipをダウンロードして展開します。


Urg_cs_sampleを展開すると以下のようなファイル構成になっています。


Urg_cs_sample.slnをVisual Stuidio 2017で開き、get_distance_ethernetプロジェクトを実行します。IPアドレスとポート番号を聞いてきますので、[Enter]キーを押して進めると深度情報が取得されます。


これをUnityで直接使えるようにしていきます。Urg_cs_sample\get_distance_ethernetフォルダのget_distance_ethernet.csをベースにします。get_distance_ethernet.csはTCPで測域センサと接続し、流れてくる情報を解析して100番目の距離データを10回表示するように実装されています。しかしコンテンツで使うためには270度全ての距離データを取得する必要があります。

プログラムを見ていくと、距離データの取得を行なっているのは以下の2行です(39、40行目)。

string receive_data = read_line(stream);
if (!SCIP_Reader.MD(receive_date, ref time_stamp, ref distances)) {

long型のListとして宣言されているdistancesにミリの単位の距離が返ってきます。ここを毎フレーム呼び出すことができれば各ステップの距離を取得することができます。ただ、距離データ取得のタイミングには注意が必要です。開発情報に書いてある通り物体の位置を検出するためには各ステップの距離の差を基にクラスタリングを行います。


URG Network より

このセンサは1回の距離データを取得するのに25msかかるため、60fpsのコンテンツでデータ取得処理をUpdate関数内で行うと何度も同じ距離データを取得することになります。

これを図式化してみました。上段がUnityのUpdate、下段がセンサのデータ取得の時間のながれです。Updateごとにデータを取得し、緑の時間で物体検出を行うと何度も同じデータを処理することになるため無駄が増えます。


Update関数から距離データ取得と物体検出する場合(緑が物体検出処理)

そこでUpdate内では位置検出の処理を行わず、距離データ更新と位置検出処理をまとめて別スレッドで行うようにしてみます。


距離データの取得と位置検出を並列化した場合

こうすることで同じデータを処理することがなくなり、処理を並列化できるためfpsを稼ぐことができます。get_distance_ethernet.cs はMain()、get_connect_infomation()、read_line()、write()の4つのメソッドが定義されていますが、使いたいのはread_line()とwrite()だけです。

これを残して、URGSensor.csとして書き直したプログラムをGitHubに置きました。あわせて距離データ取得と物体検出処理を別スレッド化し、検出時の演出も少し入れてあります。Unityはバージョン2018.1から.NET4.xを正式にサポートするようになりました。今後並列処理を新しく書く場合はできるだけTaskを使うべきと思います。しかし.NET4.xランタイムを指定するとデータが遅延する現象が確認されたため、.NET3.xを使い従来のスレッドを使い実装しています。

●今回のプログラム
https://github.com/toshinoritakata/URG

<4>測域センサで物体を検出する


もう一度この図を見てみましょう。基本的にはステップごとの距離の差が大きくなった部分をまとめることで物体があることを検出します。


黄の線:1つ前のステップとの距離の差が指定した値より大きい
緑の線:1つ前のステップとの距離の差が指定した値より小さい

距離の数が8個の場合で単純なクラスタリングのアルゴリズムを考えてみます。s0から順にs7まで順に距離の差を見ていきます。物体を検出させるための距離の差を「閾値」として適当な値を指定します。s1とs2の距離の差が閾値よりも大きいので、物体があるとみなしてp2の位置をPとして代入します(P = p2)。

次にs3とs2の距離の差を調べます、閾値よりも小さいのでPに加算(P = P + p3)、同様にs4とs3の距離の差を調べ、閾値よりも小さいのでPに加算(P = P + p4)。s5とs4の距離の差を調べ、閾値よりも大きいので物体がなくなったとみなし、Pを加算した回数3で割ります。s6,s7は距離に差がないので何もしません。これで1つの物体の中心が求まりました。この手法で検出された様子を見てみましょう。

黄色の線の先に球が見えると思います。そこが認識された位置です。これを基に、検出した位置にパーティクルを表示させるサンプルをつくりました。以下のようにセンサを設置し、壁にUnityの画面を投影します。


壁に対してお手玉を投げるとそれを認識してパーティクルが発生します。 タイムラグが結構ありますが、調整できると思います。ソースは前述のGitHubに置いてあります。次回ももう少しこれを基に進めていきたいと思います。

●今回のプログラム
https://github.com/toshinoritakata/URG

Profile.

高田稔則/Toshinori Takata(Codelight)
Codelight株式会社 代表取締役・インタラクションエンジニア
フリーランス、株式会社TBSテレビ等で映画CG制作、株式会社ソニー・コンピュータエンタテインメント(現 ソニー・インタラクティブエンタテインメント)でPS4のOSD開発などを経て2006年にCodelight株式会社を設立。インタラクティブコンテンツの制作を中核として、製造業向けのプロトタイプ開発なども行う
www.codelight.co.jp




その他の連載