Jetson Nanoで自律走行ラジコンを作る(3)
「Jetson Nanoで自律走行ラジコンを作る」シリーズのまとめ記事は以下。
モデルの訓練に必要なデータをGoogle Driveにアップロードできたので、ブラウザ上からPyhonが使える実行環境であるGoogle Colabを利用してモデルを作成しようと思う。
今回は前回機体から収集したデータの内容を確認しつつTensorFlow上でデータセットを作成する。
やり方はTensorFlowのチュートリアルを参考にした。
なお、今回紹介するコードはgithubに公開している。
Google Colabでは以下のようなコードを書くだけでGoogle Driveをマウントできる。便利。
from google.colab import drive drive.mount('/content/drive')
Google Drive上に格納したtarball(複数可)はKerasのAPIで展開できる。
import pathlib import os import glob for tarball_path in glob.glob("./*.tar.gz"): data_root_org = tf.keras.utils.get_file(fname='dataset', origin='file://' + os.path.abspath(tarball_path) , extract=True) data_root = pathlib.Path(pathlib.Path(data_root_org).parents[0]/'control') print(data_root)
上記を実行すると以下のように出力される。
Downloading data from file:///content/drive/MyDrive/lunchjet/train_2021_06_03_04_30_41.tar.gz 164814848/164812768 [==============================] - 4s 0us/step /root/.keras/datasets/control
ここまででtarballが所定の場所に展開されるので、あとはファイルを読めばよい。
データがきちんと展開されているか軽く確認してみる。
annot_paths = sorted(list(pathlib.Path(data_root/'annotations').glob("*"))) print(len(annot_paths)) print(annot_paths[-10:])
6469 [PosixPath('/root/.keras/datasets/control/annotations/2021_05_22_12_03_47_209.log'), PosixPath('/root/.keras/datasets/control/annotations/2021_05_22_12_03_47_273.log'), PosixPath('/root/.keras/datasets/control/annotations/2021_05_22_12_03_47_341.log'), ...]
データ点数は約6500個だった。
次に、これらのログファイルをパースする。
前々回に記載したように、ログファイルには、画像ファイル名・ステアリングモーターの制御値(-1が左旋回、0が直進、1が右旋回)、駆動モーターの制御値(0が停止、1が全速前進)がスペース区切りで記録されている。例えば以下。
2021_05_22_11_52_41_148.jpg 0.499992 0.108504
上記を以下のようなコードでパースする。
image_paths = [] labels = [] for annot_file in annot_paths: with open(annot_file) as annot: for line in annot: image, steer, speed = line.split(' ') image_paths.append(str(image_root/image)) labels.append([float(steer), float(speed)])
モーターの制御値がきちんと読めているかを確認するために、一旦可視化してみる。
いくつか方法があるが、今回は2Dのヒストグラム*1にした。
import numpy as np import matplotlib.cm as cm import matplotlib.colors steers = np.array(labels)[:, 0] throtles = np.array(labels)[:, 1] fig = plt.figure() hist = plt.hist2d(steers, throtles, bins=40, cmap=cm.jet, norm=matplotlib.colors.LogNorm()) fig.colorbar(hist[3]) plt.xlabel('steering') plt.ylabel('throtle') plt.show()
結果は以下。
各軸の値域に問題はなさそうで、値の分布としても想定と一致する。ちなみにこのデータは同じコースをぐるぐる右旋回して収集したものである。
どうでもよいが、あるドライバーがあるコースを走行した時のヒストグラム(ライン取りや加減速のクセ)はおおよそ決まっているような気がするので、このヒストグラムによりドライバーを特定したり操縦時の改善点を見つけられそうな気がする*2。
ここまで確認すれば、あとは tf.dataset
のAPIを少し使うだけでデータセットの準備ができる。
image_paths_ds = tf.data.Dataset.from_tensor_slices(image_paths)
image_ds = image_paths_ds.map(preprocess_image, num_parallel_calls=tf.data.AUTOTUNE)
label_ds = tf.data.Dataset.from_tensor_slices(labels)
ds = tf.data.Dataset.zip((image_ds, label_ds))
print(ds)
<ZipDataset shapes: ((224, 224, 3), (2,)), types: (tf.float32, tf.float32)>
あとはTFのチュートリアルと同じように shufle()
や batch()
prefetch()
などを設定すれば、モデルに流し込む準備ができたはずである。
最後に確認のためにデータセット内のデータを可視化してみる。自宅のリビングの画像なのでモザイクをかけているが、実際には画像のサイズは224x224である。
for batch in ds.take(1): images, labels = batch fig = plt.figure(figsize=(12.0, 12.0)) for i in range(9): image = images[i] label = labels[i] label = 'steer={}, throtle={}'.format(tf.strings.as_string(label[0], precision=2).numpy().decode('utf-8'), tf.strings.as_string(label[1], precision=2).numpy().decode('utf-8')) subplot = fig.add_subplot(3, 3, i+1) subplot.imshow(image) subplot.grid(False) subplot.set_title(label)
数枚サンプリングしてみる限りデータに問題はなさそうだ。
ということで、次回はモデルについて考える。