Tech Sketch Bucket of Technical Chips by TIS Inc.

体を動かすロボット操作(ジェスチャ入力チーム:OpenNI + Xtion Pro Live )

Pocket

人の動きに合わせてモノが動いたら楽しい。

Kinectを利用して動作するものを考えたときに、チーム内で人の動きに合わせて動いたら楽しいよね。

やろうか

ということで、1泊2日で動くものを作るを目標にして2/24にチームの開発合宿を実施しました。
今回はその合宿で作った成果をここにまとめます。

つくってみます

本記事では冒頭の通り、「体の動きに合わせて物を動かす」ことを目標に2回の記事に分けて1つのアプリケーションを作っていきます。
まず、全体的なアーキテクチャを以下のように設計しました。

gesture-robot.png

画像内の左右に対応した以下の2チームに分かれて実装にかかります。

ジェスチャ入力チーム

  1. 人間の動きを検出して
  2. 画面表示しつつ、
  3. 条件を満たしたときに対応したRESTのリクエストを送付

ロボ制御チーム

  1. RESTのリクエストを元にロボット(LEGO MINDSTORMS NXT 2.0)を制御

まずはジェスチャ入力チームの記事です。


開発環境

開発に利用した端末、デバイス、ライブラリなどを記載します。
セットアップ方法などは省略気味です。

開発に利用したもの

  • OS
    • MacOSX Lion
  • 開発環境
    • Xcode4.2
    • C++

利用ライブラリなど

  • OpenNI&NITE&Sensor
    • Xtion(後述)からの入力取得、骨格認識のためのミドルウェア、Xtionのデバイスドライバなど
    • Mac版はunstableしかない。(2012/3/28時点)
  • OpenCV
    • 画面表示に利用、macportsでインストールした。
  • cURLpp
    • C++用のlibcURLのwrapper RESTのAPIを利用するために利用。
  • Boost
    • macportsでBoostをインストール

利用デバイス

ジェスチャ入力チームの記事では入力装置だけまず紹介します。

xtion.jpg

出展: www.asus.com

入力の種類

人の動きに合わせてロボットが動くようにするために以下のような仕様にしました。

人の動き ロボットの動き API Path
足を上げる 前方へ歩く /robot/legs/step
体を右へひねる 右旋回(時計周り)する /robot/legs/rightturn
体を左へひねる 左旋回(反時計周り)する /robot/legs/leftturn
右腕を上げる Readyと喋る /robot/ready
(右腕を上げた後)右腕を振り下ろす 腕を前後に振る /robot/arm/attack

今回のジェスチャ入力チームの記事の中では人の動きを検出してロボットを動かすためのAPIを叩くだけなので、人の動きを検出するところやそのAPIを叩くところがメインになります。

実装内容

いくつかの部分についてピックアップしてみます。

非同期HTTPアクセス

実際のロボットが動く際に、ひとつの動作を実行完了するのに、早い"前方へ歩く"でも1~2秒、時間がかかる"左右の旋廻"では完了までに8秒近くのロボット動作時間が発生します。
このとき、1つのリクエストのたびにその実行結果を動かない画面を見ながら待っていては非常にストレスのかかる入力になってしまいます。
そこで非同期に処理を実施するためにboost::threadを利用し、また、HTTPアクセスをするために cURLpp を利用しました。

スレッドを作成して、そのスレッドでHTTPアクセスします。アプリ終了時に後片付けをするためにthreadsというリストの中で作成したスレッドを管理します。
cURLppの使い方は下記やドキュメントを参考にしました。
参考: hidemonの日記:cURLpp

また、最後に入力が成功したコマンドを画面上に出すためにlastcmdに保持します。

ユーザフィードバック

上記の非同期対応のために、ロボットがひとつの動作完了にするまえに新しい入力が次々と実行可能になったため、ロボットの動作が大量に溜まってしまう問題が発生します。
そこで、今回の実装中では1つの入力ごとにロボットが動作するのに必要な時間コストを計測し設定することで、10秒以上分の未来の行動が設定された時点で画面をグレーにすることでユーザが過剰な入力をする前に通知するようにしました。

以下、ソースコード。

画面全体の描画時

whenemptyがロボットの動作完了予定時刻で、現在時刻とwhenemptyが10秒以上乖離したところで、
cvCvtColorで映像をグレースケール画像に変換します。

スケルトンの利用

最も重要な人の動きを取得して解析をする部分です。
基本的にOpenNIからあがってくるスケルトンの座標情報を元に「右手が肩より上に上がったら」とか「肩が首より前のほうにきたら」といったことをチェックして上記の非同期アクセスを実行します。
例として"右手を上げたときと振り下ろしたときにRESTAPIを叩く部分"を抜粋します。

割と単純ですが、スケルトンから右手と右肩の場所を取ってきてそのY座標の比較だけで判定を実施しています。
スケルトンからは、XYZと3次元で座標が取れるので各座標値の比較をするのは非常に簡単なものです。

動画もあるよ

と、言うわけで実装した結果入力側ではこんな感じになりました。


これだけだと、OpenNIのサンプルに毛が生えた程度にしか見えませんが、ちゃんとロボットを操作しています。
左上に検出されている人の数、最後に実行されたコマンドが表示されます。

余談ですが、動画の中で動いている人は僕です。みなさまよろしくお願いします。

おわり

と、いうわけで今回は体を動かすロボット操作の入力側についての記事でした。
ジェスチャ入力側からは、スケルトンを見てHTTPのアクセスを非同期に実施するというのが主たる内容になります。

次は Rubyからロボット(Lego Mindstorms NXT)を動かしていきますよ。

エンジニア採用中!私たちと一緒に働いてみませんか?