クラウドの力でロボットを遠隔から操作してみよう!

Part 2: ロボットのリアルタイム映像を送信

2023-09-01
日常で楽しむクラウドテクノロジー

Author : Muhammad Fikko Fadjrimiratno(ふぃっこ)

こんにちは。この記事は、「クラウドの力でロボットを遠隔から操作してみよう!」の続編(Part 2)です。Part 1 では、AWS IoT Core を活用し、シミュレーション上のロボットをクラウドから操作できるようになりました。今回は Amazon Kinesis Video Streams(KVS)で、ロボットが取得している映像をストリームし、遠隔から見れるようにしたいと思います。

それでは、Part 2 をはじめましょう!

ご注意

本記事で紹介する AWS サービスを起動する際には、料金がかかります。builders.flash メールメンバー特典の、クラウドレシピ向けクレジットコードプレゼントの入手をお勧めします。

*ハンズオン記事およびソースコードにおける免責事項 »

このクラウドレシピ (ハンズオン記事) を無料でお試しいただけます »

毎月提供されるクラウドレシピのアップデート情報とともに、クレジットコードを受け取ることができます。 


1. ROS のイメージトピックを RTSP に出力

ロボットが取得する映像は常に ROS のイメージトピックにパブリッシュされるので、それを活用しましょう。しかし、そのままの形では KVS にストリームできないので、KVS の WebRTC SDK と GStreamer 経由で連携できる RTSP(Real Time Streaming Protocol)に変換する仕組みが必要です。このステップでは、ROS イメージトピックを RTSP に変換してくれるライブラリをインストールし、ROS ローンチファイルに組み込んでみます。

1. Part 1 で使用した AWS Cloud9 の端末で、以下のコマンドで必要なパッケージをインストールします。

sudo apt-get install libgstreamer-plugins-base1.0-dev libgstreamer-plugins-good1.0-dev libgstreamer-plugins-bad1.0-dev libgstrtspserver-1.0-dev gstreamer1.0-plugins-ugly gstreamer1.0-plugins-bad

2. 以下のコマンドで catkin のワークスペースで ros_rtsp をクローンします。

cd ~/environment/catkin_ws/src
git clone https://github.com/CircusMonkey/ros_rtsp.git

3. ~/environment/catkin_ws/src/ros_rtsp/config/ にある stream_setup.yaml を以下に書き換えます。

port: "8554"
streams: # Cannot rename - must leave this as is.

  stream-ros:
    type: topic
    source: /camera/rgb/image_raw
    mountpoint: /front
    caps: video/x-raw,framerate=10/1,width=640,height=480
    bitrate: 500

4. ~/environment/catkin_ws/src/turtlebot3_simulations/turtlebot3_gazebo/launch/ にある turtlebot3_house.launch を開き、以下のコードを <launch> </launch> の間に追加します。

<include file="$(find ros_rtsp)/launch/rtsp_streams.launch" />

クリックすると拡大します

5. 以下のコマンドで前のステップにより変更された部分を反映し、ビルドします。

cd ~/environment/catkin_ws/ && catkin_make

6. シミュレーションを再実行し、Stream available at rtsp://xxx:8554/front が端末に表示されたら、ROS のイメージトピックが無事 RTSP に出力されています。

クリックすると拡大します


2. Amazon Kinesis Video Streams でリアルタイム映像をストリーム

このステップでは、KVS WebRTC の SDK を活用し、既に RTSP で利用可能なロボットからのリアルタイム映像をクラウドにストリームさせます。

2-1. KVS WebRTC SDK をインストール

1. 以下のコマンドで、KVS WebRTC SDK をクローンします。

cd ~/environment/
git clone --recursive https://github.com/awslabs/amazon-kinesis-video-streams-webrtc-sdk-c.git

2. ~/environment/amazon-kinesis-video-streams-webrtc-sdk-c/samples/ のディレクトリに
ある kvsWebRTCClientMasterGstreamerSample.c を Cloud9 の
エディターで開き、以下のコードを特定します。

クリックすると拡大します

3. 上記のコードが特定できたら、両方の部分を以下のコードに書き換えます。この変更によって、RTSP からの映像を GStreamer 経由で取得し、KVS WebRTC SDK でストリーミングできるようになります。

pipeline = gst_parse_launch(
                    "rtspsrc location=rtsp://0.0.0.0:8554/front short-header=TRUE ! rtph264depay ! "
                        "video/x-h264,stream-format=byte-stream,alignment=au,profile=baseline ! "
                        "appsink sync=TRUE emit-signals=TRUE name=appsink-video",
                        &error);

クリックすると拡大します

4. 以下のコードで前回のステップで編集したものをビルドに反映します。

cd ~/environment/amazon-kinesis-video-streams-webrtc-sdk-c
mkdir build && cd build
cmake ..
make

5. ビルドしたものを以下のコマンドでメインのディレクトリにコピーします。

cp ~/environment/amazon-kinesis-video-streams-webrtc-sdk-c/build/samples/kvsWebrtcClientMasterGstSample ~/environment/

2-2. ロボットから KVS にアクセスする権限を追加

このステップでは、IoT Credentials Provider により、Part 1 で作成したデバイス証明書を使用することで、KVS にアクセスするための一時的な権限が与えられます。これにより、ロボットにアクセスキー ID やシークレットアクセスキーなどの認証情報を保存する必要がなく、決められた AWS リソースに安全にアクセスすることができます。

1. AWS マネジメントコンソールの検索欄に IAM と入力し、検索結果として表示された AWS IAM にマウスカーソルを移動し、「ロール」をクリックします。

クリックすると拡大します

2. 「ロールを作成」ボタンをクリックします。

クリックすると拡大します

3. 信頼されたエンティティタイプ で「カスタム信頼ポリシー」と選択し、以下の JSON コードを「カスタム信頼ポリシー」の欄に貼り付けます。最後に、パージの右下にある「次へ」ボタンをクリックします。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "Service": "credentials.iot.amazonaws.com"
            },
            "Action": "sts:AssumeRole"
        }
    ]
}

クリックすると拡大します

4. 「ポリシーを作成」をクリックします。その後、新しいタブが開きます。前回のタブを閉じないままにします。

クリックすると拡大します

5. 新しいタブで「JSON」をクリックし、以下の JSON コードを ポリシーエディタ に貼り付けます。「次へ」ボタンをクリックします。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Action": [
                "kinesisvideo:ConnectAsMaster",
                "kinesisvideo:GetSignalingChannelEndpoint",
                "kinesisvideo:CreateSignalingChannel",
                "kinesisvideo:GetIceServerConfig",
                "kinesisvideo:DescribeSignalingChannel"
            ],
            "Resource": "arn:aws:kinesisvideo:*:*:channel/${credentials-iot:ThingName}/*",
            "Effect": "Allow"
        }
    ]
}

クリックすると拡大します

6. 次のページで、ポリシー名に turtlebot3-kvs-access-policy と入力し、ページの右下にある 「ポリシーの作成」ボタンをクリックします。

7. 前のタブに戻り、検索欄に turtlebot3-kvs-access-policy と入力し、検索結果が出たら、turtlebot3-kvs-access-policy にチェックを付けます。その後、「次へ」ボタンをクリックします。

クリックすると拡大します

8. 次のページで ロール名 に turtlebot3-kvs-role と入力し、「ロールを作成」ボタンをクリックします。

9. AWS IoT Core のコンソール画面で左のナビゲーションペインから「セキュリティロールエイリアス」を選択し、「ロールエイリアスを作成」ボタンをクリックします。

クリックすると拡大します

10. 次のページで ロールエイリアス名 に turtlebot3-kvs-role-alias と入力し、ロールturtlebot3-kvs-role と選択し、最後に「作成」ボタンをクリックします。

11. 左のナビゲーションペインから「セキュリティポリシー」を選択し、ポリシー名 で「turtlebot3-sim」をクリックします。

12. 「アクティブなバージョンを編集」ボタンをクリックします。

クリックすると拡大します

13. ポリシードキュメント で「JSON」を選択し、以下のコードを ポリシードキュメント の内容に加えます。<アカウント ID> はご自身のアカウント ID に変更してください。

    {
      "Effect": "Allow",
      "Action": "iot:AssumeRoleWithCertificate",
      "Resource": "arn:aws:iot:ap-northeast-1:<アカウントID>:rolealias/turtlebot3-kvs-role-alias"
    }

クリックすると拡大します

14. 「編集したバージョンをこのポリシーのアクティブバージョンとして設定します」にチェックを付けて、「新しいバージョンとして保存」をクリックします。

クリックすると拡大します

2-3. KVS のシグナリングチャネルを作成

1. AWS マネジメントコンソールの検索欄に KVS と入力し、検索結果として出力された Kinesis Video Streams にマウスカーソルを移動し、「シグナリングチャネルを作成」をクリックします。

クリックすると拡大します

2. KVS のコンソールページで「シグナリングチャネルを作成」ボタンをクリックします。

3. シグナリングチャネル名 に turtlebot3-sim と入力します。有効期限 (TTL) 5 と入力します。最後に、「シグナリングチャネルを作成」ボタンをクリックします。

2-4. KVS 経由でロボットのリアルタイム映像をストリーム

1. AWS マネジメントコンソールの検索欄に cloudshell と入力し、検索結果として出力された「CloudShell」をクリックします。

クリックすると拡大します

2. CloudShell の端末で以下のコマンドを実行し、出力された IoT Credentials のエンドポイント (xxx.credentials.iot.ap-northeast-1.amazonaws.com) をメモします。

aws iot describe-endpoint --endpoint-type iot:CredentialProvider

クリックすると拡大します

3. Cloud9 に戻り、main.py を開きます (main.py と Ubuntu デスクトップ上の ROS シミュレーションが実行されている場合、先に実行を停止してください)。そして、以下マークされている部分(####追加分の始まり#### ####追加分の終わり#### が書いてあるところ) をスクリプトの中身に加えます。メモした IoT Credentials のエンドポイントを <IoT Credentials のエンドポイント> に貼り付けます。

import rospy
from rospy.exceptions import ROSInterruptException
from geometry_msgs.msg import Twist
from awsiot import mqtt_connection_builder
from awscrt import mqtt, http
import json
from math import pi
####追加分の始まり####
import ssl
import http.client
import subprocess
import sys
import os
import signal
####追加分の終わり####

host = '<ホスト>'
thingsName = 'turtlebot3-sim'
topic = thingsName + '/control'

certsPath = './certs/'
rootCAPath = certsPath + '<Amazon Root CA1>'
certificatePath = certsPath + '<デバイス証明書>'
privateKeyPath = certsPath + '<プライベートキーファイル>'
port = 8883

####追加分の始まり####
credentialsEndpoint = '<IoT Credentialsのエンドポイント>'
roleAlias = 'turtlebot3-kvs-role-alias'
region = '<使用されているAWS Region>'

context = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
context.load_cert_chain(certificatePath, privateKeyPath)
context.load_verify_locations(rootCAPath)
context.verify_mode = ssl.CERT_REQUIRED
context.check_hostname = True

headers={
    "x-amzn-iot-thingname": thingsName
}
conn = http.client.HTTPSConnection(credentialsEndpoint, port=443, context=context)
conn.request("GET", "/role-aliases/turtlebot3-kvs-role-alias/credentials", headers=headers)
print("Requesting credentials...")
response = conn.getresponse()
if response.status != 200:
    print("status:{} reason:{}".format(response.status, response.reason))
    sys.exit(1)
if response.status == 200:
    print("Got credentials!")
    
credential_data = json.loads(response.read())

environmentVars = {
    "IOT_GET_CREDENTIAL_ENDPOINT": credentialsEndpoint, 
    "ROLE_ALIAS": roleAlias, 
    "AWS_DEFAULT_REGION": region, 
    "DEFAULT_KVS_CACERT_PATH": rootCAPath, 
    "AWS_ACCESS_KEY_ID": credential_data['credentials']['accessKeyId'], 
    "AWS_SECRET_ACCESS_KEY": credential_data['credentials']['secretAccessKey'], 
    "AWS_SESSION_TOKEN": credential_data['credentials']['sessionToken']
}
    
command = ['./kvsWebrtcClientMasterGstSample', thingsName]
proc = subprocess.Popen(command, stdout=subprocess.PIPE, env=environmentVars)
####追加分の終わり####
        
def shutdown_turtlebot():
    disconnect_future = mqtt_connection.disconnect()
    disconnect_future.result()
    print("Disconnected from AWS IoT!")
    rospy.loginfo('Stop Turtlebot')
    cmd_vel_pub.publish(Twist())
    rospy.sleep(1)
####追加分の始まり####
    os.killpg(os.getpgid(proc.pid), signal.SIGTERM)
####追加分の終わり####
    
cmd_vel_pub = rospy.Publisher('cmd_vel', Twist, queue_size=1)
rospy.init_node('turtlebot3_sim_control_node')
rospy.on_shutdown(shutdown_turtlebot)
control = Twist()
rate = rospy.Rate(1)

def handleMessage(topic, payload):
    duration = 4
    print("Received a new message: ")
    print(payload)
    print("from topic: ")
    print(topic)
    print("--------------\n\n")
    payload = json.loads(payload)
    x = 0
    z = 0
    if payload["control"] == "forward":
        x = 0.1
    if payload["control"] == "stop":
        x = 0
        z = 0
    if payload["control"] == "backward":
        x = -0.1
    if payload["control"] == "left":
        z = pi*0.8/4/duration
    if payload["control"] == "right":
        z = -(pi*0.8/4/duration)
    control.linear.x = x
    control.angular.z = z

# AWS IoT MQTT Client connection configuration
mqtt_connection = mqtt_connection_builder.mtls_from_path(
        endpoint=host,
        port=port,
        cert_filepath=certificatePath,
        pri_key_filepath=privateKeyPath,
        ca_filepath=rootCAPath,
        client_id=thingsName,
        clean_session=False,
        keep_alive_secs=30)

# Connect to AWS IoT
connect_future = mqtt_connection.connect()

# Waits until a result is available
connect_future.result()
print("Connected to AWS IoT!")

# Subscribe to AWS IoT
subscribe_future, packet_id = mqtt_connection.subscribe(
        topic=topic,
        qos=mqtt.QoS.AT_LEAST_ONCE,
        callback=handleMessage)
subscribe_result = subscribe_future.result()
print("Subscribed with {}".format(str(subscribe_result['qos'])))

while not rospy.is_shutdown():
    try:
        cmd_vel_pub.publish(control)
        rate.sleep()
    except ROSInterruptException:
        rospy.loginfo('Finish')

4. main.py を以下のコマンドで実行します。

python3 main.py

5. Ubuntu のデスクトップにアクセスし、ROS のシミュレーションを実行します。

export TURTLEBOT3_MODEL=waffle_pi
roslaunch turtlebot3_gazebo turtlebot3_house.launch

2-5. ロボットのリアルタイム映像のストリームを確認

1. Amazon Kinesis Video Streams のコンソールページで、左のナビゲーションペインから シグナリングチャネル を選択し、「turtlebot3-sim」をクリックします。

2. 「メディア再生ビューワー」をクリックします。

クリックすると拡大します

3. プレイボタンをクリックすると、ロボットが取得している映像をリアルタイムで見れます。

4. 次回 (Part 3) でダッシュボードのウェブアプリでリアルタイム映像を表示するためにシグナリングチャネル ARN が必要なので、メディア再生ビューワー の 下にある「シグナリングチャネル情報」を選択し、表示されている シグナリングチャネル ARN をメモします。

クリックすると拡大します


3. まとめ

これで、クラウドを活用することで、ロボットが取得している映像を遠隔から見れるようになりましたので、Part 2 は終了です。Part3 では、これまで作ってきた Part 1 と Part 2 を利用し、ロボットの遠隔操作ができるダッシュボードウェブアプリを作りますので、Part 3 を楽しみにしてください!

注意事項:Cloud9 で使われる EC2 インスタンスを停止しない限り、EC2 の料金がずっと発生し続けます(c6i.2xlarge で東京リージョンの場合は $0.428 / 時間)。Cloud9 で作業しない場合など不必要な時に、関連する EC2 インスタンスを停止することがおすすめです。しかし、その後 EC2 インスタンスを再び開始するとパブリック IP アドレスが変わり、Cloud9 からアクセスできなくなる可能性があるため、以下のスクリーンショットのように、Cloud9 のホストを EC2 インスタンスの新しい IP アドレスに切り替える必要があります。

クリックすると拡大します

クリックすると拡大します


builders.flash メールメンバーへ登録することで
AWS のベストプラクティスを毎月無料でお試しいただけます

筆者プロフィール

Muhammad Fikko Fadjrimiratno(ふぃっこ)
アマゾン ウェブ サービス ジャパン合同会社
ソリューションアーキテクト

不動産・建設業界のお客様を中心に、AWS 利用をご支援しているソリューションアーキテクトです。ロボット、IoT、機械学習を得意領域としており、AWS に入社する前にも大学院の研究室や様々な業界でロボットと機械学習の研究開発に携わっていました。

AWS を無料でお試しいただけます

AWS 無料利用枠の詳細はこちら ≫
5 ステップでアカウント作成できます
無料サインアップ ≫
ご不明な点がおありですか?
日本担当チームへ相談する