AWS RoboMakerのサンプルロボット「Navigation and Person Recognition」を試してみた

AWS RoboMakerにはHello Worldの他に追加のサンプルロボットが現時点で5個用意されています。今回はNavigation and Person Recognitionを試してみました。

概要

Navigation and Person Recognitionのサンプルロボットは室内に配置されている顔写真をロボットが認識するアプリケーションです。ロボットが室内を移動する中で下図のように顔写真を見つけます。そして写真を見つけたというログをターミナルツール上で参照することができます。

動作確認手順は公式ドキュメントのNavigation and Person Recognition - AWS RoboMakerに記載されています。またサンプルロボットの起動手順等はGitHubのaws-robotics/aws-robomaker-sample-application-persondetection: Use AWS RoboMaker and demonstrate the use of Amazon Rekognition to recognize people's faces and Amazon Polly to synthesize speech.に記載されています。

実行環境

  • オレゴンリージョン (us-west-2)
  • MacBook Pro (13-inch, 2017, Four Thunderbolt 3 Ports)
  • Google Chrome Version 71.0.3578.98 (Official Build) (64-bit)
  • aws-cli/1.16.68 Python/3.6.6 Darwin/17.7.0 botocore/1.12.58

基本的にマネジメントコンソールの操作のみですが、AWS CLIを1回だけ利用しているので実行環境に記述しています。

サンプルロボットの起動

まずはサンプルロボットを起動します。GitHubのResourcesに記載されている1.〜4.までの手順です。5.のCloudFormationのコンソールにアクセスする以降の手順は今回は利用しません。

  1. Sign in to the AWS RoboMaker console at https://us-west-2.console.aws.amazon.com/robomaker/home/.
  2. In the AWS RoboMaker console, expand Resources on the left and then select Sample applications.
  3. In the Try RoboMaker sample applications page, select Navigation and person recognition and then select Launch.
  4. There will be a banner at the top of the page listing the number of resources that have been created. When all of the resources have been created it will open the simulation job detail page. At this point cancel the simulation job. This will not delete any of the resources.

RoboMakerのマネジメントコンソールの左側のメニューから[Sample applications]を選択し、[Navigation and person recognition]を選択します。最後に[Launch simulation job]ボタンをクリックします。なお、Sample applicationsの画面において各アプリケーションの右側にある[Info]のリンクをクリックすると[Learn more]の箇所に公式ドキュメントの該当箇所のリンクとコードリポジトリ(GitHub)のリンクが表示されます。

シミュレートされたカメライメージを表示する

シュミレーター上のロボットのカメライメージを見てみましょう。公式ドキュメントのシミュレートされたカメライメージを表示するに記載されている手順です。

RoboMakerのマネジメントコンソールの左側のメニューから[Simulation jobs]を選択し、今回起動したジョブのリンクをクリックしてジョブの画面を開いてください。サンプルロボットの画面で[Launch simulation job]ボタンをクリックした場合は自動的に表示されているはずです。次にロボットのカメライメージを表示するためrqtを利用します。rqtの右上のチェックボックにチェックを入れて右側にある[Actions]の中から[Open]をクリックします。

するとrqtツールのウィンドウが別ウィンドウで開きます。開かない場合はブラウザのポップアップブロック機能を確認してください。rqtツールのウィンドウが開いたら[Plugins]メニューの[Visualization] - [Image View]をクリックします。

Image Viewプラグインが表示されるので[/camera/rgb/image_raw]を選択してください。そうするとロボットのカメライメージをrqtツール上で参照することができるようになります。

ロボットがシュミレーター上で移動している際のカメライメージであるためロボットの移動に合わせて表示される画像が変わります。イメージが湧きやすいように今回は2枚のスクリーンショットを貼っておきました。1枚目から室内であることが分かるかと思います。また左側に顔写真が貼ってあることが分かるかと思います。2枚目からは他にも顔写真が貼ってあることが分かるかと思います。これらの顔写真をロボットが認識します。

認識された人物を表示する

次にターミナルツール上でロボットが顔写真を認識しているログを確認してみましょう。公式ドキュメントの認識された人物を表示する に記載されている手順です。

再びジョブの画面に戻り、ターミナルの中央部分をクリックします。先程はツールの右上のチェックボックスにチェックを入れて接続していましたが、このようにツール毎にクリックするだけでウィンドウを開くこともできます。

ターミナルツールのウィンドウが別ウィンドウで開きます。

公式ドキュメントに沿ってまず以下のコマンドを入力します。コンテキストメニューにPasteがあるのでこちらを利用すると便利です。

BUNDLE_CURRENT_PREFIX=~/workspace/robot-application/bundle source ~/workspace/robot-application/bundle/setup.sh

次に以下のコマンドを入力します。なお、コンテキストメニューの表示がそのまま残ってしまう不具合があるようですが、動作自体は問題ありません。

rostopic echo /rekognized_people

しばらくするとロボットが顔写真を認識した際にdata: "I see XXXX"というメッセージが表示されるようになるのが分かるかと思います。以下のようにrqtツールとターミナルツールを並べて表示しているとかなり離れた距離からでも画像認識できていることが分かるかと思います。

以上が公式ドキュメントに記載されている手順の紹介でした。

アーキテクチャの解説

アーキテクチャとしては、室内を移動しているロボットのカメラ画像をKinesis Video Streams経由でAmazon Rekognition Videoに接続して、ストリーミングビデオ内の顔の認識を行います。そして分析結果をAmazon Kinesis Data Streams経由で読み取り、予め登録している顔写真が存在した場合は/rekognized_peopleというROSトピックに検出した画像名を配信します。GitHubにアーキテクチャの図が記載されていて分かりやすいです。

ArchitectureDiagram

図にもあるように顔の認識ができるよう予めRekognitionのIndexFacesオペレーションを実行しておく必要があります。しかし、aws-robomaker-sample-application-persondetectionリポジトリ内にIndexFacesに関するソースはなく、事前作業という位置づけのようです。そのため、サンプルロボットにおいても対応するCloudFormationによってIndexFacesを実施するLambda関数が作成されているようでした。実際にCloudFormationスタックが存在する状態で以下のコマンドを実行することでRekognitionのコレクションが作成されていることが確認できました。

$ aws rekognition list-collections --region us-west-2
{
    "CollectionIds": [
        "roboMakerSampleAppPersonDetectionCollectionXXXXXX"
    ],
    "FaceModelVersions": [
        "4.0"
    ]
}

ソースの解説

最後はソースファイルについての解説です。GitHub上には色々とソースファイルがありますが、今回は顔写真の認識に関係しているrobot_ws/src/person_detection_robot/nodes/rekognizeファイルについて解説します。なお、ROS自体はまだまだ学習中なので雰囲気で説明している点はご容赦くださいm(_ _)m

まずRoboMakerのロボットアプリケーションの起動の流れとしてはlaunchファイルが読み込まれて、そのlaunchファイルの中でROSノード(プログラム)を起動するという流れになっているようです。実際、マネジメントコンソール上で[Robot application]タブを開くと[Launch file]としてperson_detection.launchが指定されていることが分かります。

そしてこの設定に対応するperson_detection.launchの23行目のにおいてrekognizeファイルをROSノードとして起動している記述があります。

<node name="rekognize" pkg="person_detection_robot" type="rekognize" output="screen"/>

では実際にrekognizeファイルの中身を見ていきたいと思います。

import json

import rospy
import actionlib
from std_msgs.msg import String

class Rekognizer:

    def __init__(self, rekognition_topic="/rekognition/results"):

        #Listen for rekognition results
        self.rekognition_subscriber = rospy.Subscriber(rekognition_topic, String, self.rekognize_callback)

        #Publish names extraced from rekognition results
        self.output_publisher = rospy.Publisher("/rekognized_people", String)

    def rekognize_callback(self, msg):
        image_ids = []
        try:
            msg = json.loads(msg.data)
            if msg["FaceSearchResponse"]:
                rospy.logdebug("Rekognition result has FaceSeachResponse, looking for matched faces")
                for face in msg['FaceSearchResponse'][0]['MatchedFaces']:
                    rospy.logdebug("Matched face: %s", face)
                    # Default 'unnamed' is required for indexed images without ExternalImageId
                    name = face['Face'].get('ExternalImageId', 'unknown')
                    rospy.logdebug("Matched face has name: %s", name)
                    image_ids.append(name)
            else:
                return
        except ValueError as e:
            rospy.logerr("Error loading json message from rekognition:\n%s", e)
            return

        if not image_ids:
            rospy.loginfo("Rekognition result has no faces")
            return 

        names =" and ".join([image_id.replace("_"," ") for image_id in image_ids])
        text = "I see {}".format(names)
        self.output_publisher.publish(text)
        rospy.loginfo(text)


def main():
    rospy.init_node("rekognize", log_level=rospy.INFO)

    rekognizer = Rekognizer()

    rospy.spin()


if __name__ == '__main__':
    main()

12行目のrospy.Subscriber(rekognition_topic, String, self.rekognize_callback)の箇所がKinesis Data Streamsのコンシューマーに対応しています。rospy.Subscriberのコンストラクタの第3引数に rekognize_callback 関数を指定しており、この関数内でRekognition Videoの分析情報のJSONを処理しています。21行目にif msg["FaceSearchResponse"]:と書かれていることからも分かるかと思います。このようにSubscriberのコンストラクタを作成するだけでKinesis Video Streams経由でAmazon Rekognition Videoを利用できるのはkinesisvideo-ros1パッケージを利用しているためです。kinesisvideo-ros1パッケージの詳細については別途紹介できればと考えています。

12行目の左辺でself.rekognition_subscriberインスタンス変数に代入しているのはSubscriberのインスタンスをメモリ上に保持することが目的であると予想されます。このように代入しておかないとコンストラクタを抜けたらSubscriberのインスタンスがなくなってしまうので。そしてコンストラクタの呼び出し元であるmainメソッド内では50行目のrospy.spin()でmainメソッドから抜けるのを止めているようです。rospy#spin()はROSノードが終了するまで処理をブロックするメソッドのようです。

46行目のrospy.init_nodeで現在実行されているPythonプログラムをROSノードとして登録し、48行目のRekognizerコンストラクタ内でSubscriberとPublisherを必要に応じて登録し、最後に50行目のrospy.spin()でmainから抜けないようにブロックしておくというのがROSノードの基本的なプログラムの作り方のようです。この辺りの考え方はja/ROS/Tutorials/WritingPublisherSubscriber(python) - ROS Wikiが参考になるかと思います。

最後に15行目のself.output_publisher = rospy.Publisher("/rekognized_people", String)の箇所ですが、ここでは/rekognized_peopleというROSトピックにメッセージを送信するPublisherを生成しています。そして、41行目のself.output_publisher.publish(text)の箇所で顔写真を認識した際は"I see XXXX"というメッセージを送信しています。そのためターミナルアプリケーションにおいてrostopic echo /rekognized_peopleというコマンドで/rekognized_peopleROSトピックのメッセージをechoすることで顔写真の認識結果を参照できるようになっていたわけです。

後片付け

Step 6: Clean up - AWS RoboMakerに沿って以下の順に作成したリソースを削除してください。

  1. シミュレーションジョブをキャンセルする
  2. シミュレーションジョブに対応するCloudFormationのスタックを削除する
  3. (必要なら)IAMロールを削除する
  4. (必要なら)シミュレーションジョブに対応するS3バケットを削除する

S3バケットに関する記載は公式ドキュメントにありませんが、画像認識に利用した顔写真画像やシュミレーションジョブのログが残っています。

最後に

いかがでしたでしょうか。rekognizeファイルも含めてaws-robomaker-sample-application-persondetectionリポジトリのソースコードはとても少なく、kinesisvideo-ros1などのパッケージを組み合わせるだけで簡単にカメライメージを処理するロボットが作れることが分かるので、GitHubのソースコードは一通り参照することをおすすめします。

ちなみに私自身はROSに関する予備知識がなかったので最初は読み解くのにかなり苦労しました。とりあえず今回のサンプルロボットを通じてROSのパッケージやノード、トピック、launchファイルなどがかなり理解できたので、今後その辺りの解説もできればと考えています。