工場長のブログ

日々思ったことを書いてます。

WebRTCでロボット操縦しようぜ!

SORACOM Advent Calendar 2019の12/19の記事です。一年に1回、この時期にだけブログを書いている気がします。

qiita.com

 

もともとは「ぼくのかんがえたさいきょうの再送ロジック」というタイトルで、IoTデバイスクラウドの間でやりとりされるメッセージの再送ストラテジの話を書こうと思っていました。

例えばデバイス -> クラウド方向に関して言えば、Raspberry Piのような比較的リッチなデバイスであれば、データ送信失敗時や通信がオフラインの時にはプログラム内のバッファやファイルシステム上にデータを保存しておいて、Availableになったらリトライすることが容易ですが、(基板として)ArduinoやWioLTE、M5Stackなどのマイクロコントローラでは、取りうる選択肢が非常に狭くなります。また、そもそもどうやってデータ送信失敗を検知するんだっけ?HTTPリクエストでやればいい?でもデータ通信量を最適化するためにはもっとプリミティブなプロトコルを使いたくなったりしますよね。じゃあどうするのがいい?みたいな話を書こうと思っていました。

 

が、今年のAWS Re:InventでKinesis Video StreamsがWebRTCをサポートしたので、これの「触ってみた」記事を書くことに心変わりしました。さーせん。(もし期待してくれていた方がいらっしゃったら)

Amazon Kinesis Video Streams で WebRTC による双方向リアルタイムメディアストリーミングのサポートを開始

あらためて

ということで今回は、ロボットにカメラを付けて、その映像をWebRTCでブラウザまで送信して、それをみながら遠隔操作する、というのをやってみたいと思います。もちろん、SORACOM使って。

WebRTCとは

ウェブブラウザやモバイルアプリケーション間でリアルタイムな通信(テキストなどのデータやり取りも可能ですし、音声や映像のやり取りも可能です。後者のイメージが強いかなとは思いますが)を実現するための仕組みです。モダンなウェブブラウザはWebRTCをサポートしていますので、追加のソフトウェアをインストールすることなくビデオ会議などが実装できたりします。

ざっくりとしたアーキテクチャは下記のようになっていて、Voice Engine、Video Engineというデータそのものの取り扱いの定義と、Transportというデータ転送についての定義 を持っています。更にSession Managementというピア同士のシグナリングを司るレイヤを持っていて、この部分はアプリケーションデベロッパに実装を任せる形になっています。(例えばWebSocketを使うなど)

(Architecture | WebRTCより抜粋)

Amazon Kinesis Video StreamがWebRTCをサポート?

実態は以下のようなコンポーネントです。

1. Session Managementサービス

  • WebSocketによって実装された、マネージドSession Managementサービスです。

2.  STUN/TURN

  • 実際のビデオや音声などのストリームのNAT越えを助けるためのサービス群です。
  • WebRTCのトラフィックUDP(上の図でいうSRTP部分で定義されています。RTP on DTLS on UDP)を利用していますので、インターネット越しに別々のNATルーター配下にいるクライアント同士が通信をするためにこういったヘルパサービスが必要になってきます。

つまり、ビデオや音声の転送そのものについてはクライアント同士で直接行われ、それを司る部分がサービスとして提供されています。

SFU(一度に多くの視聴者へ映像や音声を配信するための仕組み)も入っているのかなと思ったんですが、そうではないんですね。大規模配信はKinesis Video Streams(本体)でやろうぜって感じなのかな。

なぜWebRTCでロボットの遠隔操作?

で、やっと本題に近づいてくるのですが、なぜWebRTCでロボットの遠隔操作なんでしょうか。これまでのKinesis Video Streamsでもよくない?と思われる方もいらっしゃると思いますし、実際それでもOKです。私もやってみました。

例えばKinesis Video StreamsはHLSでの映像配信をサポートしていますので、RPiなどから送信された映像を簡単にブラウザにスケール感をもって配信することができます。

一方、パイプラインの詳細を見てみると、下記のようなマイクロバッチアーキテクチャになっていて、データソースからブラウザに届くまでの間に5〜10秒といった単位での遅延が挟まります。

もちろんこれでも、非常にゆっくり動くロボットや車であれば遠隔操作は可能ですが、ちょっとずつ、恐る恐る動かす必要があって、なかなかアレです。

f:id:imai-factory:20191219135226p:plain

WebRTCの場合、データソースのクライアントと閲覧者のクライアントが直接接続されてRTPベースのContinuousなデータ転送が実現できるので、より低遅延な配信ができるのではないか、というところを期待して試しています。(まあそういう意味ではRTSPでもRTPベースの通信はできるんですが。実はこれもSORACOM Napterで試しました。確かにレイテンシ小さかった。) 

やってみる

Turtlebot3(ロボット界のRaspberry Piのようなもの。たぶん。)を以下のような構成で動かしてみました。SORACOM Advent CalendarなのでもちろんSORACOM Air経由ですよ。

f:id:imai-factory:20191219171922p:plain

構成の詳細の前にまずはデモ。絵が小さくてすごいわかりづらいかんじになっちゃいましたが・・・汗、画面右側にいるのがTurtlebotです。ラップトップのブラウザに表示されている映像が、Turtlebotに搭載したカメラをWebRTC経由で受け取って表示しているものです。

どうですか?Turtlebotが回転するのに合わせてブラウザ上に表示される映像も回転していっているのが見て取れると思います。遅延は約1〜2秒というところでしょうか。(わかりづらいか。。)

前述のKinesis Video Streamsだと5〜10秒のレイテンシがあったのでだいぶ改善していると言えるでしょう。時速数km/hでうごくロボットであればまあ行けるんじゃないですかね。

ラズパイ上でH264へのエンコードも行っているので、そのあたりの遅延(おそらく1秒前後)も入っているので、この辺、まだ改善の余地もあると思います。

 

ということでWebRTC試してみました!絵が出た!ロボット動いた!!!めでたい!

How it works

では中身の話をしましょう。ロボットの操作は、上記の絵でいうUbuntuにログインしたターミナル上で(みんな大好き?)WASDキーを押すと、AWS IoT Core経由でROSのcmd_velメッセージ(Command Velocityの略称)がTurtlebotに対して発行されます。より厳密には、WASDキーを押すとごとに、前後方向への速度、回転方向の速度のあるべき状態(下記のようなイメージ)が更新され、それが約10Hzで定常的にメッセージ発行され続けます。(ジョイコンとかもきっとこんな実装ですよね)

 

currently: linear vel 0.0 angular vel -1.7
currently: linear vel 0.0 angular vel -1.8
currently: linear vel 0.0 angular vel -1.9
currently: linear vel 0.0 angular vel -2.0
currently: linear vel 0.0 angular vel -2.1
currently: linear vel 0.0 angular vel -2.2

ROSメッセージをMQTTにトンネルしてAWS IoT Coreを経由させるための仕組みとして、GROOVE Xのエンジニアさんが作っているmqtt_bridgeを使っています。

github.com

このbridgeを使うとROS->MQTT、MQTT->ROSの変換を行ってくれるので、ロボットとEC2、それぞれの出入り口にデプロイして上げるイメージです。それぞれ、ロボットもEC2も内部的には普通にROSプログラミングすればいい、というわけです。

で、これをAWS IoT Coreに接続するためのサンプルが下記に紹介されてるので、それを参考に行いました。ここでは詳細な設定等の説明は(長くなって力尽きそうなので省きます。それなりにハマりました。そのうち書くかもしれません。)

AWSOfficialなサンプルから参照されてるってすごくかっこいいすね、GROOVE Xさん!

github.com

 

あとはロボット側ですが、下記が素のTurtlebot3です。これに・・・・

f:id:imai-factory:20191219173906p:plain

 

こんなかんじにゴテゴテにいろいろ取り付けます。なんかかっこいい!!!!

f:id:imai-factory:20191219145101p:plain

 

Turtlebot3は制御用にRaspberry Pi3B+が載っているのですが、ここに直接手を加えるのは若干はばかられた(依存関係壊したりしてロボット動かなくなったらつらいし、リストアするためにイメージ焼き直して設定作り直してとかめんどうなので)ので、上記の構成図にあったように、もう一台別のラズパイを用意して、そこにカメラ、LTEモデム、前述のmqtt_bridgeとROS Master、Kinesis Video StreamsのWebRTCクライアントをインストールして、制御用ラズパイとEthernetで接続しました。このラズパイはクラウドゲートウェイみたいな位置づけですね。

Turtlebot3の制御用ラズパイは、もともと外部にROS Masterを持つ構成になっているので、この辺は簡単でした。クラウドゲートウェイのラズパイにDHCPサービスを動かしているので、接続して双方電源入れたらすぐにロボットがクラウドにつながるよ!(一点だけ、制御用ラズパイが参照するROS MasterのIPアドレスだけ設定の必要がありました。)

Kinesis Video StreamsのWebRTCクライアントについては下記のC SDKに同梱されているサンプルプログラムをそのまま利用しました。GStreamerからそのままWebRTCに流してくれるので、no configで動きました。サンプルコードの中を覗いてみると、以下のような感じでパイプラインが構築されていることがわかります。

gst-launch-1.0 autovideosrc num-buffers=1000 ! queue ! videoconvert ! video/x-raw,width=1280,height=720,framerate=30/1 ! x264enc bframes=0 speed-preset=veryfast key-int-max=30 bitrate=512 ! video/x-h264,stream-format=byte-stream,alignment=au,profile=baseline ! appsink sync=TRUE emit-signals=TRUE name=appsink-video

github.com

通信量/通信料はどうなの?

残念ながら今回はデモのために数分しか動かしていないので正確な通信量は測れていません。少なくともs1.fastで問題なく動いてくれることは確認しました。

映像を転送してるわけですし、MQTTも10mesasge/sec(操作だけで)飛ばしてるのでそれなりのデータ転送量は発生すると思います。ただ、遠隔操作っていつでも必要なものではなく、どちらかというと緊急時やイレギュラーなこと(ナビゲーションスタックがスタックしちゃったときとか、SLAMするときとか)をやるときに必要なものなのでぜんぜんありなのではないかなと思ってます。

あと、MQTTの部分はSORACOM Beamを使ってというのもありかなと思うんですが、若干メッセージの頻度が高いきがするので(cmd_velだけならまだしも、他のROSメッセージも転送しようと思うと50msg/secとか100msg/sec行っちゃう)、SORACOM Beamを入れるのがベストかどうか迷ったので今回は使ってません(いちおう、SORACOM Kryptonで証明書を払い出しはした)。ただ、やっぱり証明書を払い出したりインストールするのめんどくさいので、SORACOM Beamつかいたいすね。

まとめ

Amazon Kinesis Video StreamのWebRTCの機能を使うと、こんな感じのリアルタイムな遠隔操作なども現実的に、比較的簡単に実装できるようになってきますね!めでたい!マネージドTURN爆誕

 

(操縦しようぜ!って言ったわりには、ロボットを首振りさせただけですね・・・さーせんw