masato-ka's diary

日々思ったこととか、やったことの備忘録。

TensorFlow Lite for MicrocontrollersとESP32を用いた安価なAI自動運転の検証

1. この記事について

 DonkeyCarなど行動模倣(Behavior Cloning)を行うAI RC CARではカメラ画像を入力とした推論と走行が一般的だ。1。この場合、エッジ側にもそれなりの計算力が必要になり、実機ハードウェアの構成が高額になる。この記事ではカメラを使用せずに単純なセンサ情報のみで同様の仕組みを実現できることをシミュレーション環境と実機での検証を通して確認した。検証では3つの距離センサの値を入力とする3層のニューラルネットワークを用いて自動走行を実現した。さらに実機の検証ではTensorFlow Lite for Microcontrollers2とESP32を使い、安価な構成でニューラルネットワークによるラジコンカーの自動運転が実現できることを示した。

検証で作成したコードは以下のGithubリポジトリで公開している。

github.com

2. シミュレーションによる予備検証

まず初めに、実現の目論見を確認するため、シミュレーションによる予備検証を実施した。

2.1 シミュレーション環境

 シミュレーション環境は詳解確率ロボティクス3に記載の差動2輪ロボットのシミュレーション環境を参考に実装した。別途、コースをあらわす壁と距離センサを実装している。Pytorch版のシミュレーション環境はGistで公開している。TensorFlow版は前述のリポジトリから参照できる。実装フレームワークが違うのみでそれぞれに違いはない。

gist.github.com

 ロボットには3つの距離センサを設置した。それぞれのセンサは前方方向に1つとそれを原点として左右に60度開いて設置している。

 シミュレーションはデータ収集・学習・推論走行の3つのパートに別れている。「データ収集」では比例制御によりコースを自動走行する。比例制御は左右のセンサ値の差分を入力として操舵の制御値を決定する。また学習データとして観測されるセンサの値と対になる制御値の記録を行う。「学習」では学習データを元にニューラルネットワークを訓練を実行する。「推論走行」では学習したニューラルネットワークを使い自動走行を実行し、その評価を行う。

2.2 ニューラルネットワークモデル

シミュレーションで取得した学習データは以下の形でCSV形式で保存した。速度の値は学習データとして記録されるが固定値であるため意味を持たない。各センサの値入力として操舵角と速度を推定する回帰問題として学習させる。

左センサ値,中央センサ値,右センサ値,操舵角,速度

 定義したニューラルネットワークモデルは3層の全結合層から構成される。アクティベーション関数は1層目と2層目にReluを使用し、最終段には設定しない。また、Loss関数はMSEを設定している。以下、TensorFlowでの実装例を示す。

import tensorflow as tf

from tensorflow import keras
from tensorflow.keras import layers

model = keras.Sequential([
    layers.Dense(8, activation='relu', input_shape=(3,)),
    layers.Dense(8, activation='relu'),
    layers.Dense(2),
  ])

optimizer = keras.optimizers.SGD(lr=0.01, decay=1e-6, momentum=0.9, nesterov=True)
model.compile(loss='mse',
              optimizer = optimizer,
              metrics = ['mae'])
model.summary()

2.3 シミュレーション検証の結果

シミュレーションでは実行の様子を動画で確認できる。ここでは結果の画像を掲載する。画像中の円が自動運転車を表している。自動運転車から3本出ている赤い線がセンサの計測方向となる。観測されるセンサの値は各線と壁の接触点と自動運転車との直線距離となる。3000ステップの試行を行い、3000レコードのデータを集めた。

f:id:masato-ka:20200708130329p:plain
比例制御による自動走行(学習走行)

収集したデータを使い訓練したニューラルネットワークを比例制御と置き換えて自動走行を実行した。初期位置をX軸方向にずらして実行している。以下の画像を確認すると比例制御の動きと同じ軌跡で自動運転が行われていることが確認できる。線形制御の出力データからニューラルネットワークがその関数を「近似’している。

f:id:masato-ka:20200708130302p:plain
ニューラルネットワークによる自動走行

いずれも人を介在しない自動走行だが、前者は人が走行できるようにルールをコードベースに落とし込んでいる。しかし、後者はデータから自動的にルールを発見させているという点で大きな違いがある。

3. 実機による評価

 シミュレーションにおいて距離センサ3つの入力のみで行動模倣を実現できることがわかった。実機へも適用できるか検証を実施した。

3.1 検証車両の紹介

 今回実機検証の車両としてRumiCarというオープンハードウェアの車両を用いて検証を行なった。RumiCarは安価なラジコンカーを改造し、ESP32と距離センサVL53L0Xを3つ搭載している。ベースとなるラジコンカーの制約としてスロットルは全身、後進、停止、ステアリングは直進、右、左という離散的な制御しかできない。

www.rumicar.com

3.3 実機に合わせたニューラルネットワークの変更とESP32へのデプロイ

ニューラルネットワークの構成をシミュレーションから実機に合わせて変更した。センサの値から「直進」「左」「右」の3つのクラスを推論するよう分類問題として学習させる。

import tensorflow as tf

from tensorflow import keras
from tensorflow.keras import layers

model = keras.Sequential([
    layers.Dense(8, activation='relu', input_shape=(3,)),
    layers.Dense(8, activation='relu'),
    layers.Dense(3, activation="softmax"),
  ])

optimizer = keras.optimizers.SGD(lr=0.01, decay=1e-6, momentum=0.9, nesterov=True)

model.compile(optimizer='adam', 
              loss='sparse_categorical_crossentropy',
              metrics=['accuracy'])
model.summary()

訓練後のモデルをTensorFlow Lite for microcontrollers向けに変換する。TensorFlow Lite形式で保存したモデルをxxdコマンドを使い、バイト配列として出力する。出力されたバイト配列をファームウェアの一部としてESP32といった低リソースのマイコンへデプロイする。TensorFlow Lite for microcontrollersの詳細な利用方法については公式を参照[^2]。

converter = tf.lite.TFLiteConverter.from_keras_model(model)
converter.optimizations = [tf.lite.Optimize.DEFAULT]
tflite_model = converter.convert() #量子化はせずFP32のまま処理していことに注意!
open("converted_model.tflite", "wb").write(tflite_model) #TFLite形式で保存


!apt-get install xxd
!xxd -i converted_model.tflite > airc_model.cpp  #xxdコマンドでモデルをC言語で参照可能なバイト配列として出力する。

TensorFlow Lite microcontrollersはCPUやフレームワークといった利用する組み込み環境に合わせて自分でビルドする必要がある。Arduino フレームワークとESP32の組み合わせはビルド済みのライブラリがリリースされており、Arduino IDE, PlatoformIOのライブラリ管理から「TensorFlowLite_ESP32」でインストールできる。今回はこちらのライブラリを利用して実装を行なった。

github.com

詳細な実装コードついては冒頭のリポジトリを参照してほしい。

3.4 実機検証結果

 実機の検証においても比例制御による自動走行で学習データを収集した。前述の制御の制約に合わせるため出力結果をルールベースで各命令に対応させている。ニューラルネットワークの訓練と形式の変換はGoogle Colaboraotryで行い、ESP32へプログラムとともに書き込む。

 以下が実機検証の動画である。コースは紙を山折りにした物を円形に配置し作成した。実機でもニューラルネットワークがセンサのデータから制御値を推論して走行できていることがわかる。

youtu.be

4. まとめ

この記事では距離センサ3つのみという構成で、ニューラルネットワークによる自動走行が可能であることをシミュレーションを通して検証した。また、TensorFlow Lite for microcontrollersを利用することで、ESP32を搭載した実機のラジコンカーで自動走行が可能であることを検証した。今回は単純な制御則でも十分に制御できるが、コースを逸脱しないように走行するという抽象的表現を人がプログラミングでルールとして落とし込むのではなく、データから演繹的に自動的にルールを作り出せること、さらにそれを「マイコンレベルで実現できる」という点が非常に面白い。今後マイコンを利用したセンサデータと複雑なルールの組み合わせが単純だが高度なことができるデバイスの登場を予感させる。

Jetson Nanoで動く深層強化学習を使ったラジコン向け自動運転ソフトウェアの紹介

この記事について

 深層強化学習を利用してAI RC Carの走行を学習させるソフトウェアをGithub上で公開しています。learnign_racerと命名しました。2020年4月29日の時点でのバージョンはv1.0.0です。この記事ではこのソフトウェアについて紹介していきます。

github.com

なお、NVIDIAのJetson Community Projectsのページでも紹介されています。

developer.nvidia.com

learning_racerの概要

 このソフトウェアは10分から15分程度の学習時間で、コースに沿って走行する方法を獲得することができます。従来のAI RC Carでは人間のお手本操作をDNNに覚えこませる手法が一般的です1。ですが、本手法は教師なし学習の手法である強化学習をベースとしているため、教師データとなる人間のお手本操作は必要ありません。さらにJetson Nanoを搭載したJetBot2やJetRacer3といったAI RC Carに対応しています。下の動画は実際にJetBotで学習させた様子です。


Deep reinforcement learning with Jetbot( on the Jetson nano) in real world.

さらに、実機だけでなく、AI RC CarのシミュレータであるDonkeySIMを使い、WindowsUbuntu, macOSといったプラットフォームで実行することが可能です。そのため実機がなくてもこのソフトウェアを利用することが可能です。


Deep reinforcement learning with DonkeySIM.

 深層強化学習をエッジで学習させる仕掛け

 今回の手法はイギリスのベンチャーWAYVE4の手法からAntonio Raffine5さんがさらに発展させたものです。私はこれをJetBotやJetRacerで動くように実装を行いました。この手法がエッジでの深層強化学習に適している工夫が行われています。

Learning to Drive Smoothly in Minutes - Towards Data Science

 DQNに代表される深層強化学習教師なし学習で様々なタスクを解くことができる手法として注目されています6強化学習に必要な関数を深層学習で近似したこの手法では都度得られるデータをもとにDNNをゼロから学習させます。そのため、学習にはそれなりのリソースが必要になり時間がかかると言えます。

 一方でJetson Nanoのような非力なエッッジ側でAIを学習するにはさらに多くの時間がかかります。そのため、あらかじめ学習したAIを少量のデータで学習し直し目的のタスクに適応させる、転移学習と呼ばれる手法を利用することが一般的です7。しかし、今回の深層強化学習では転移学習の元となるモデルを用意することができません。

 そこで今回は2つの仕掛けを導入して、エッジでの深層強化学習を実現します。一つは深層強化学習の手法としてSAC(Soft Actor Critic)8を利用しました。もう一つの仕掛けはVAE(Variational Auto Encoder)を利用した状態表現学習(State Representation Learning)9です。それぞれについて説明します。

 平易に書くため、専門的には誤解が多いところがあるかもしれませんが、その場合はコメント欄で優しく指摘してください。より詳細な情報を知りたい場合はRaffineさんのMidiamを参照ください。

SAC(Soft Actor Critic)

強化学習は、多くの場合、シミュレーター上で試行を繰りすことで学習を進めていきます。エージェントと呼ばれるソフトウェアが、方策(Policy)と呼ばれるルールに従って、行動を行い、環境を変化させます。環境の変化から報酬を計算し、行動良し悪しを評価します。この結果得られたエージェントの行動、変化した環境の状態と報酬をデータとして集めて、行動全体の報酬の総和である価値が最大になるように方策を学習します。今回は実機のため、このエージェントのデータ集めは実時間で行わざる終えません。そのため、学習効率の良い、深層強化学習アルゴリズムであるSAC(Soft Actor Critic)を採用しました。

 SACはoff-policy型の深層強化学習のState of The Artな手法です。off-policyな方策型の深層強化学習は学習によって方策が変わっても過去のデータを活用することが可能です。そのため、新たな方策を学習しても少量の試行を行うだけで、過去に取得したデータと合わせて学習を行うことができます。そのため、効率的に方策の学習を進めることができます。

状態表現学習

 もう一つの仕掛けである状態表現学習(State Representation Learnig)は環境の状態をあらかじめ学習しておくことで、深層強化学習の学習効率を高める手法です。環境の状態は車両に搭載されたカメラで撮影した映像になります。状態表現学習を利用しない場合はこの画像を環境情報として深層強化学習の入力に与えます。画像という巨大な入力を学習する必要があるためマシンパワーが必要になります。しかし、状態表現学習を利用すると画像から圧縮した特徴を入力として利用することが可能です。今回は状態表現学習として千から1万程度の画像をVAEで学習しています。VAEの中間層から出力される潜在空間を32次元の特徴量としてSACへ入力にしています。下記画像の左側がカメラからの入力です。右側の画像は潜在空間からVAEが生成した画像です。潜在空間はカラーバーとして表示されています。

f:id:masato-ka:20200429143631p:plain
状態表現学習に使ったVAEの実行結果

 VAEの学習自体はGoogle Colaboratoryなどを利用して学習させています。集める画像の量が多いように感じますが、教師ありのAI RC CARよりもデータ量としては少なく、また教師データは必要ないため、収集は容易です10

 このようにエッッジでの学習が難しそうな手法でも、効率の良いアルゴリズムを採用することと、AIの学習全体を見直し、あらかじめクラウドで学習できる部分を切り出しておくことで、エッジ側で学習できるようになります。今回の場合は効率のよいSACの採用と深層強化学習の畳み込み層の計算をVAEとして切り出しクラウドであらかじめ学習しておくことがキモになります。 

ソフトウェアのセットアップ

続いて、learning_racerの利用方法を説明します。ソフトウェアはPythonで実装されpipコマンドでインストールすることができます。JetsonNanoでは環境固有の問題により、用意しているシェルスクリプトを利用してインストールします。

JetBot,JetRacer実機へのインストール

JetPack4.2以上のイメージではJetBotまたはJetRacerのソフトウェアを構築します。構築後、以下のコマンドでインストールします。

$ cd ~/ && git clone -b v1.0.0 https://github.com/masato-ka/airc-rl-agent.git
$ cd airc-rl-agent
$ sh install_jetpack.sh

DonkeySIMを用いたシミュレータ環境での実行

以下のサイトから環境に合わせてDonkeySIMをダウンロードします。Unityで作られた実行形式のバイナリです。

Releases · tawnkramer/gym-donkeycar · GitHub

以下のコマンドでインストールします。[choose platform]にはwindows, windows-gpu, osx, ubuntuのいずれか利用しているプラットフォームに合わせて実行します。windows-gpuを選択した場合、TensorFlow 1.15.0とPytorchが対応しているCUDA10.0がインストールされている必要があります。

$ cd ~/ && git clone -b v1.0.0 https://github.com/masato-ka/airc-rl-agent.git
$ cd airc-rl-agent
$ sudo pip3 install .\[choose platform\]

利用方法

詳細はこちらもREADMEを読んでいただければと思います。簡単に説明すると実機、シミュレータとも大きく3つのステップで学習します。

1. VAEモデルの作成

 まずはVAEモデルの学習を行います。実機やシミュレータを利用してコースの画像をくまなく撮影します。単純に1周するだけではなく、ジグザグ走行や、コースアウト、白線の上を走行など、様々なパターンを取得するのがコツです。学習の中でこのデータ集めが一番難易度が高い部分です。しかし、後段の深層強化学習の結果に大きく響くのでしっかりとデータを集めます。

 実機のデータに必要なツールはnotebooksフォルダに入っているためそちらを利用します。シミュレータの場合は画像を保存する機能があるため、そちらを利用します。学習用のJupyterノートブックもColaboratory向けに用意しています。

 VAEモデルの作成が面倒な場合は以下の学習ずみモデルも利用可能です。JetBotのデモでよく利用されるLEGO CITYの道路プレート用のモデルとDonkeySIMのGenerate-track-v1のモデルを用意しました。

2. パラメータ設定

基本的に、学習パラメータを変更しなくてもデフォルトのセッティングで学習はうまくいきます。その代わり機体に合わせて以下のパラメータを変更します。変更はconfig.ymlファイルに記載します。

パラメータ名 説明 設定の目安
MIN_STEERING ステアリング命令の最小値 -1のまま固定
MAX_STEERING ステアリング値の最大値 1のまま固定
MIN_THROTTLE 最小スロットル値 停止やバックしないようにJetBotやJetRacerの司令値の下限に合わせる
MAX_THROTTLE 最大スロットル値 JetRacerの場合は速度が出過ぎないように司令値に注意する
MAX_STEERING_DIFF 1ステップで変更できる最大ステアリング値 値を大きくすると大きく蛇行する0.35-0.2がベター

3. 深層強化学習の実行

 以下のコマンドで学習を開始します。<robot> はjetbot, jetracer, simのいずれかを指定します。また<device>はcpuかgpuを使っているマシンに合わせて指定します。シミュレータの場合はコマンド実行前に、シミュレータを実行しておきます。

$ racer train -vae vae.path -robot <robot> -device <device> -steps 10000 

 コマンド実行後しばらくすると車両が動き始めます。実機の場合はnotebookに入っているuser_interface_without_gamepad.ipynbを使い、車両がコースアウトする直前で停止させ、車両の位置を手で戻して、もう一度走行させます。

 シミュレータの場合は自動的に学習が進んでいくので、シミュレータの画面を眺めているだけでOKです。

まとめ

 この記事では深層強化学習を用いたAI RC Car向けのソフトウェアを紹介しました。紹介した内容としてはSACとVAEを組み合わせた深層強化学習の手法、実装したソフトウェアの構築方法と簡単な利用方法となります。現在、AI RC Carの世界では実機のレースはもとより、シミュレータを利用したバーチャルレースが始まっています。アメリカのAI Robocarコミュニティーが中心となり開催しています11。初代チャンピオンはなんと日本人のnaisyさんです。また、日本のAI RC Carコミュニティーでもすでにレースが開催されています12。今後このソフトウェアをもとにこういったレースへ参加していきたいです[]。

Variational Auto Encoderを利用したAI RC Carの教師なし学習の検討

この記事について

この記事はAI RC Car アドベントカレンダー22日目の記事です。今回はAI RC Carの教師なし学習について考察します。

AI RC CarのDLモデル学習とそれぞれの課題

AI RC Carが走行に利用する学習モデルは基本的に教師あり学習で行われます。AI RC Carから撮影したコースの画像と、それぞれの画像に紐づけられた教師ラベルです。この教師ラベルの良し悪しによってAI RC Carの走行特性が大きく変わります。例えばDonkeyCarのような模倣学習を行なった場合、ドライバーがコース上を綺麗に走行することで、推論走行時にコースを綺麗に走行できます。また、JetRacerのようにマニュアルでアノテーションを付ける場合はアノテーションの付け方によって走行時の特性が決定します。

 これらの教師あり学習に共通して言えることは、教師ラベルの付け方が最終的な走行性能に影響を当たるという点です。これはAI RC Carを扱う人がラベルの付け方を訓練してコツをつかむ必要があることを意味します。

 しかし、画像のみからコースを走行するために必要な情報を学習できれば、人によるラベル付けに影響を受けません。そこで、今回はラベルを必要としない教師なし学習でAI RC Carの走行用モデルを学習させる方法を検討します。

VAEによる教師なし学習

DLの教師なし学習のモデルとして、Auto Encoderと呼ばれるモデルがあります。今回はAuto Encoderの発展系のモデルであるVariational Auto Encoder(VAE)を利用します。Variational Auto Encoderそのものについての詳細は他の詳しいサイトを参照ください。

qiita.com

簡単にいうとAutoEncoderは与えられた画像と同じ画像を出力するように学習させたCNNです。ネットワークの構造は大きく3つに別れています。まずはじめにEncoder層です。この層は一般的なCNNと同様に、画像を畳み込みフィルタで圧縮します。次に、中間層と呼ばれる層ではEncoderで画像から圧縮された 特徴を保持します。最後の層はDecoder層ですこの層はEncoderと逆向きのネットワーク構造を持っています。中間層から受け取った特徴から元の画像を再構成します。学習はEncoderに入力した画像を同じ画像がDecoderから出力されるように学習を行います。このように学習すると、中間層の特徴変数(潜在変数)は元の画像の情報を十分に表現した特徴を抽出していると言えます。このAEを拡張したVAEでは中間層の潜在変数が正規分布に従うように正則化して学習を行います。実はこうすることで、潜在変数の個々の変数に意味を持たせることができるようです。

VAEによるAI RC Carのステアリング制御

VAEでAI RC Carのステアリングを制御する方法については以下の論文を参考にしました。この論文では実車の車載カメラから撮影した画像をVAEで学習を行い自動運転をするというものです。VAEの潜在変数のうち、道路の曲率を説明する変数を使いステアリングの制御に活用することを提案しています。実はこの論文の中ではステアリングの制御については詳細は説明されていません。ですので、ここから先はどのように実現するか検討する必要があります。

http://people.csail.mit.edu/rosman/papers/iros-2018-variational.pdf

本記事のゴールは、AI RC CarでもVAEを使いコース上のカーブの曲率を説明する特徴を抽出できることを検証し、それ以降は今後考えていきます。

VAEの実装と実験

VAEの実装はこちらのPytorchの実装を参考にしました。

github.com

ただし、このままの実装だとLoss関数が思わしくなかったため、Loss関数部分をこちらの実装に置き換えています。

www.sambaiz.net

ハイパーパラメータとして、バッチサイズは32、エポック数は300を与えています。画像サイズは64x64です。また、VAEでは潜在変数の数を決定します。潜在変数が少なければ表現力が失われ、変数が多くなれば変数を扱うときに不便です。今回は変数 5, 10,25で100エポックづつ学習させて再構成された画像を比較してみたところ、10程度で十分と考えました。かなり小さい画像ですが左が入力画像、右が再構成された(最終的に出力された)画像になります。

f:id:masato-ka:20191217000304p:plain
5変数
f:id:masato-ka:20191217000347p:plain
10変数
f:id:masato-ka:20191217000411p:plain
25変数

変数5では背景が多少ぼやけています。10, 25ではそんなに変化がないように見受けられます。

結果と考察

実際に1300毎程度の画像を300エポック学習させました。どのくらいが適正な画像枚数なのかわかりませんが、1300枚は学習データとしては少ない枚数だと思います。まずは学習後、実際に再構成された画像は以下です。これは学習ずみのVAEにコース画像を入力し、デコーダから出力された画像を並べたものです。かなりぼやけてはいますが、人目に見て、コースだということがわかるくらいには表現されています。まず学習としては成功と言えると考えられます。

f:id:masato-ka:20191217001223p:plain
再構成画像

続いて、各変数の値の意味を確認していきます。実は各変数がデコーダの出力を通ってどのように画像に影響を与えるかを解析的に求める方法はありません。(もし手法があるなら教えて欲しい)上記の参考文献でも各変数のうち1つのパラメータを変動させてデコーダ出力を観察すると記載されています。 そこで、上記再構成画像の1枚めの画像の潜在変数をサンプリングし、10個の潜在変数をそれぞれ独立に-20か20の間で1づつ変化させた画像を作成しました。つまり画像の生成を行なっています。

f:id:masato-ka:20191217001741p:plain

小さい画像で見にくいですが、パラメータを変えることで、画像が変化していっていることがわかります。このうち、上から7列めの画像では右から左にかけて白線の傾きが変わっているように見えます。拡大して、GIF画像にしたのが以下のものです。

f:id:masato-ka:20191217232040g:plain

このように、白線の傾きを1つの潜在変数で再現することが可能となりました。同様に、白線が傾いて見えるカーブの画像を入れると、この変数の値は白線の傾き具合、つまりカーブの曲率を出力されると考えられます。これで画像のみからコースに関する知識を抽出することが可能となりました。

課題

今回はVAEを使い、コース画像のみから、教師データなどの事前知識なしに、どういった画像がカーブなのかというコースに関する知識を抽出することができました。しかし、これで実際にAI RC Carを走らせるには課題があります。

  1. 潜在変数の取り扱い

VAEでは学習を行うたびに潜在変数が表す値が変わってきます。これを固定する方法は現在のところありません。また、今回はたまたま曲率を表すパラメータを抽出できましたが、必ずカーブの曲率を学習するという保証もないと思っています。潜在変数の評価方法と、最適な潜在変数のパラメータ数を追求する必要がありそうです。

  1. 実機での制御

カーブの曲率を表す潜在変数を見つけたとしてここから制御値にどのように落とし込むかはまだ課題です。値の変化がカーブの曲率に対して比例していれば、PID制御など単純な制御でうまくいきそうな気もします。ただし、必ずしも潜在変数がカーブの曲率を表現する保証がありません。先行事例では潜在変数を使った強化学習により、制御を獲得する方法もあります。

https://towardsdatascience.com/learning-to-drive-smoothly-in-minutes-450a7cdb35f4

まとめ

今回はVAEによる教師なし学習でコースに関する知識を学習させることができました。今後は潜在変数による制御方法の獲得を目指したいと思います。

JetBotのroad_followingを使いやすくする。

この記事について

この記事はAI RC Car アドベントカレンダー17日目の記事です。この記事ではJetBotのroad_followingのデモを実行しやすくするTipsを紹介します。

JetBotのroad_followingのデモ

JetBotの公式リポジトリに置いてあるJetBotのデモはゲームコントローラを使い、ブラウザ上に表示された画像上の座標を入力することでアノテーションを行なっていきます。このままでももちろんデモとして成功します。しかし、ゲームコントローラで画像上の狙った場所にカーソールを持っていくのは至難の業です。さらに、ゲームパッドで入力された座標を逐次Pythonインタプリタで処理しているため、ブラウザとJetBotが通信を行なっています。そのため、通信環境が著しく悪くなる走行会や展示会場では問題になります。

JetRacerのinteractive_regressionを利用する

interactive_regressionはJetRacer用のPythonノートブックですが、学習させる内容は全くな同じため、JetBotにも利用することができます。interactive_regressionは画像をWEBブラウザ上でのクリックであつけることができるため、簡単に画像のアノテーションを行うことが可能です。さらに学習とバリデーションを同じノートブック上で行えるため、走行前に、コース上で学習が足りていな部分を簡単に見つけることができます。

github.com

JetBot向けinteractive_regression

JetBot向けにinteractive_regressionを修正するのはとても簡単にです。JetRacerとの差分はカメラモジュールの呼出部分のみです。カメラオブジェクトの呼びだし箇所を変更すれば大丈夫です。さらに3セル目で呼び出されているfrom jetcam.utils import bgr8_to_jpegは不要なため、コメントアウトしておきます。具体的にはオリジナルのinteracitivi_regressionの最初のセルを以下のように書き換えます。

from jetbot import Camera, bgr8_to_jpeg
camera = Camera(width=224, height=224)
camera.start()

jupyter_clickable_image_widgetをインストールする必要があります。JetBotにログイン後、以下のGitHubリポジトリのREADMEを参照し、インストールを行います。

github.com

インストール後はJetRacerと同様にデータ収集と学習を行えば大丈夫です。JetRacerの学習の練習にもなります。

まとめ

Jupyterノートブック上で画像クリックによるアノテーションから学習までできるのは非常に楽で良いです。jupyter_clickable_image_widgetは便利なので、自前のプロジェクトでも積極的に活用していきたいです。

AI RC Carは何を見て走行しているのか?

この記事について

この記事ではJetBotやJetRacerで学習させたAIモデルが推論結果を出力する際に、画像上のどこに着目したかを調べます。手法としてはResNet-18のモデルにGrad-CAMを使って推論時に重視した画像上の箇所を可視化させます。

AIの推論とその根拠について

ディープラーニングの推論結果に対して、なぜその出力が出たのか、入力データをどう解釈したかを調べることはAI研究のホットキーワドとなっているようです。詳細はXAIなどで検索されると多くの研究に当たります。AI RC Carにおいても、走行時にステアリング角や、画像座標を出力しますが、いったい画像上の何を見て出力しているかわかりません。もし推論の結果とともに、推論結果が画像上のどの部分に着目したのかを可視化できれば、学習後のモデルがどのくらい走行中の状況をしっかり見ているかがわかります。

Grad-CAM

Grad-CAMはCNNの推論根拠を可視化する手法の一つです。詳しくは詳細に書かれば他の記事を参考いただければと思います。端的に言うと、推論結果出力後、その誤差逆伝播を遡り、変化の激しい(勾配の大きい)パーセプトロンを見つけると言うものです。勾配の大きい箇所は推論結果の計算に大きく寄与した箇所なので、結果的に何を重要と考え、結果を出力できたかがわかるだろうという考え方です。

qiita.com

JetRacerやJetBotの転移学習に用いられるモデルはResNet-18で、PyTorchを利用して実装されています。ResNet-18でPyTorchを利用して可視化を行います。GradCAM自体の実装は以下のブログの内容を利用しました。こちらのブログではResNet-32を利用されていますが、Grad-CAMに渡すレイヤー名などは変わらないので、そのまま利用可能です。

tech.jxpress.net

モデルをGradCAMクラスでラップし、fowardを実行後の処理を変えます。参考にしているブログでは画像が出力となっていますが、AI RC Carの場合は2つの変数が出力になります。JetRacerの場合は出力のうち、推論された画像のx座標の値にのみ着目すればいいため、以下のように実行します。

# sample_image[None, ...]はモデルとロードした画像に合わせて変更、こちらはJetBotのサンプルでの実行方法
model_output = grad_cam.forward(sample_image[None, ...])
grad_cam.backward_on_target(model_output[0][1])

実際の可視化画像

実際に走行時の画像をGradCamで可視化した映像が以下になります。注意したいのは以下は実際にはJetBotで撮影した連続画像をオフラインで処理しています。また、このデータの場合は画像上の座標位置ではなく、ステアリング角を学習した場合のものです。ですが、JetRacerでも同様の方法で可視化が可能です。


Visualize Autopilot with Grad-CAM

この動画ではステアリング変更に、コースの白線が特徴として利用されていることがわかります。また、大きくカーブするシーンでは周囲の環境も特徴としてよく利用されているようです。JetRacerで画像座標を推論する場合もほぼ同じ結果が出ると考えています。

まとめ

GardCAMでの可視化は、直感的に走行に寄与しそうな画像上の特徴にモデルが着目しているかを確認することができます。これにより、学習したモデルがコース上を正確に走れるか検討ができそうです。

AI RC Carのプロポ信号をUSBゲームパッドに変換する

この記事について

この記事はAI RC Carアドベントカレンダー13日目の記事です。今回はRC Carのプロポの入力をUSBゲームパッドとして変換する内容を紹介します。

AI RC Carとプロポ

AI RC Carでは人がプロポを使って操作した内容をJetsonNanoなどのSBC(シングルボードコンピュータ)に記録する場合が多々あります。通常、スピコンやサーボへ接続するプロポ受信機のPWM信号をなんらかの方法でSBCへ渡すことで実現します。一般的なラジコンのPWM信号は1msから2msのパルス幅となっています。これをSBCで直接読み込む方法はありません。PWMの値を読む前にOSの割り込みなり入るため、専用ハードを用意する必要があります。RTOSなどを利用すればできるかもしれませんが、基本的にはSBCで実施するのは厳し目です。

マイコンを利用したPWM信号のデコード

こういった用途に非常に適しているのがマイコンです。一般的に多くのマイコンではこういったPWM信号のパルス幅を計測するための機能が用意されています。Arduinoなどにもようしてありますが、今回は格安マイコンのBluepill(STM32F103)を利用します。理由としては以前購入してそのまま放置していた。Amazonで格安で購入できる点です。

BluepillとSBCの接続

Bluepillはmicro USBを搭載しており、HIDにも対応しています。HIDはいわゆる、マウスやキーボード、ゲームパッドなどのインタフェースとしてホストPCへ接続できる機能です。今回はBuluepillをゲームパッドとしてSBCへ接続します。ゲームパッドジョイスティックとして認識されるためプログラムから制御可能です。Pythonであれば、PyGamesなどを使って制御できます。また、HTML5のGamePad APIも利用できます。簡単に試すにはPCへUSBで接続、以下のサイトを最新のChromeブラウザから開くとGamePadとして認識していることがわかります。

HTML5 Gamepad Tester

プログラム

Bluepillの詳しい開発方法は掲載していません。Arduino IDEで開発できるように環境を整えてください。実際に書き込みを行ったソースコードは以下のようになっています。HIDのライブラリであるUSBCompositeをあらかじめインストールしておきます。PB5とPB6にプロポの受信機のCH1, CH2をそれぞれ接続します。USBでホストに接続するとXBOX360のコントローラとして認識され、CH1が左ジョイスティックの縦操作、CH2が右ジョイスティックの横操作に対応します。

qiita.com

#include <USBComposite.h>

#define CHANNEL_NUM 2
#define DEBUG 1

const int WIDTH = 410;
byte PWM_PIN[CHANNEL_NUM] = {PB5,PB6};
int base[CHANNEL_NUM] = {0,0};
int value[CHANNEL_NUM] = {0,0};
float joystick[CHANNEL_NUM] = {0,0};
  
USBXBox360 XBox360;


void setup() {
  // put your setup code here, to run once:
  pinMode(PC13, OUTPUT);
  digitalWrite(PC13, HIGH);
  pinMode(PWM_PIN[0], INPUT);
  pinMode(PWM_PIN[1], INPUT);

  delay(100);

  base[0] = pulseIn(PWM_PIN[0], HIGH, 29412);
  base[1] = pulseIn(PWM_PIN[1], HIGH, 29412);

  XBox360.begin();
  digitalWrite(PC13, LOW);
  delay(1000);
  
}

void loop() {
  for(int i=0; i < CHANNEL_NUM; i++){
     value[i] = pulseIn(PWM_PIN[i], HIGH, 29412);
     joystick[i] = map(value[i], long(base[i] - WIDTH), long(base[i] + WIDTH), -30000, 30000);
  }
  XBox360.X(joystick[0]);
  XBox360.XRight(joystick[1]);
  
}

init内では入出力ピンの初期化処理とHIDデバイスの初期化を行っています。メインループの中ですが、逐次PWMピンの値を読み取り、-30000から30000の間に値をマッピングし直しているのみです。その後はXBox360のコントローラに対応した値をセットしてホスト側PCに入力を送っています。

まとめ

ラジコンプロポの信号をRaspberryPiやJetson Nanoで読み取る方法は以外にめんどくさく情報がありません。Arduinoで一度受け取ってからHIDで送る方法は大変お手軽で思ったよりも簡単に作れるので、AI RC Car作成の際に一つ用意しておくと良いでしょう。

AWS RoboMakerのJetbotチュートリアルをやってみる(シミュレーター編)

この記事について

この記事はAI RC Car アドベントカレンダー11日目の記事です。今日はAWS RoboMakerのJetbotチュートリアルをやってみます。このチュートリアルは2019年12月に開催された AWS re:Invent 2019でワークショップとして開催された内容です。実際のワークショップではAWSで用意されたアカウントを使いチュートリアルを進めていたようです。個人のアカウントで進めることもできましたが、実施には一手間必要だったのでその部分について触れていきます。AI RC Carと言いつつも、全然RCじゃないですし、むしろロボットっぽいですが、この分野はいずれ融合します。多分。。。

AWS RoboMakerとは

AWS RoboMakerはAWSのサービスの一つです。クラウド上でROSアプリケーションの開発とシミュレーションができます。ROSアプリケーションの開発はCloud9と呼ばれるWEBブラウザで動くIDEで開発を行います。AWS のコグニティブサービスやKinesisによるビデオストリーミングをサポートしています。また、開発後はGazeboでのシミュレーションをWEBブラウザ上で確認可能です。さらに、AWS Greengrass IoTと連携したロボットのフリート管理機能ではロボットへアプリケーションを直接デプロイすることが可能です。

aws.amazon.com

AWS RoboMakerとJetBot

AWS RoboMakerのチュートリアルではJetBotが題材となっています。チュートリアルのマテリアルにはJetBotのROSアプリケーションのサンプルとGazeboシミュレータで利用可能なJetBotの3Dモデルが含まれています。もちろん開発した内容は実機のJetBotへインターネット越しにデプロイできます。JetBotでROS開発を行う場合は強力なツールになりえます。

AWS RoboMakerのチュートリアル

対象とするAWS RoboMakerのチュートリアルJetBot Teleopチュートリアルです。

robomakerworkshops.com

このチュートリアルは3つの構成に分かれてます。一つはWorkshop setupチュートリアルを進めるのに必要なリソースをAWS Cloud Formationを使い自動構築します。その次はActivity #1: An Introduction to ROS Developmentで、AWS RoboMaker上でのROSアプリケーションのビルドとシミュレーションの実行を学習します。最後はActivity #2: Deploying ROS Applicationsです。実機へのROSアプリケーションのデプロイ方法を学習します。この記事では2つ目までを対象とします。また、チュートリアルの中ではROSのプログラミングやアプリケーションの基本構成については深く語られていませんので、そのあたりの知識は別途学習する必要があります。

AWS RoboMaker環境の構築

まずはチュートリアルのWorkshop setupを進めていきます。このチュートリアルではAWSがイベント向けに用意した環境を利用するか、自分のAWS環境で進めるかで手順が異なります。今回は自分のAWS環境を利用するので、No. I will use own AWS account.を選択します。

下の方に進んでいくとLaunch stack ボタンを押下すると、AWS CloudFormationの画面がブラウザ上に表示されます。AWSコンソールにログインしていない場合は認証画面が開くので、ログインしましょう。

右上でリージョンがオレゴンになっているので、東京に切り替えます。この後のCloud9IDEを立ち上げる際に、オレゴンだとm4.largeのインスタンスが利用できない旨エラーが出ました。

デザイナーでCloudFormationで作成されるリソースを確認しておきましょう。VPCと2つのサブネット、S3バケットAWS Robomaker用のロールが2つ作成されます。確認したら右上の「閉じる」を押して下の画面に戻り、右下の「次へ」を押下します。

画面が切り替わったら「スタックの名前」と「S3BucketName」に名前を入れます。いずれも任意の名前を入れれば大丈夫ですが。S3BucketNameだけはグローバルに一意な名前をつける必要があります。「次へ」を押して、次の画面に進みます。次の画面の下の方に、AWS Roleを自動で作成する旨の警告が表示されていますので、チェックボックを入れて確認しましょう。そのまま「作成」を押せばリソースの自動作成が始まります。しばらくすると作成が完了します。

RoboMakerによる開発

続いて、Activity #1: An Introduction to ROS Developmentです。まずは手順にしたがい、AWS RoboMakerを使って開発環境を立ち上げます。この時、選択するVPCはdefaultと表示されているものと、先ほどのCloudFormationで作成されたものの2つが表示されます。一旦私はCloudFormationで作成されたVPCを選択しました。subnetは適当に。

その後、CloudFormationでmod-xxxxxぽい名前を控えるとありますが、実際には環境構築で作成したS3が含まれるCloudFormationでつけた環境の名前で大丈夫です。Cloud9立ち上げ時にもう一つCloudFormationの環境が作られますが、そちらではありません。引き続き、手順にしたがってGitからサンプルコードをダウンロードとinstall_deps.shの実行まで進めます。

この後、ビルドを実行する手順になりますが、すぐに実行すると他のaptのプロセスがロックした状態で正常にビルドできません。しばらく待って実行するが、psコマンドでaptのプロセス番号を特定し、killコマンドで停止させるなどで回避可能です。

その後のsimulationの実行では実行時に権限がないと言われ、実行ができません。設定からIAMRoleを追加します。Add or Edit Configurationsを選択して設定画面を開きます。

f:id:masato-ka:20191208122132p:plain

設定画面で、SIMULATION=>JetBotCircle Simulation を選択して、IAM roleでrobomaker-simulation-roleを選択します。同様にteleopの方にも設定しておきましょう。

f:id:masato-ka:20191208122433p:plain

ここまでくれば後は手順通りに進めます。シミュレーション実行後、Gazeboの画面を開くと以下のようにJetBotが回転している状態が表示されます。Teleopの方もクライアントをダウンロードして、認証情報を設定すればローカルマシンからシミュレーション上のJetBotを操作することが可能です。

f:id:masato-ka:20191208123648p:plain

まとめ

JetBotの開発にROSを利用しようとするとAWS RoboMakerは選択肢になりえそうです。開発からシミュレーションまで環境が最初から準備されているのがマネージドサービスの強みですね。一方でちょっと遊んだだけで2ドルくらい取られるので、本当に使い始めたらいくらかかるのかちょっと心配です。また、AWSとROS両方の知識を求められるので、学習コストは非常に高い気がします。もし、ROS(Gazeboを)動かせるPCを運良く持っていたのであれば、大人くしROSの環境構築をしてしまった方が良いかもしれません。実機へのデプロイができたら続きを書きます。