Translate

2019年3月14日木曜日

超音波センサHC-SR04をDonkey Carで使う


前方障害物との距離をはかりたくて、HC-SR04をRaspberry Piに接続し、Donkey Carのパーツクラス化してみた。

[GitHub] coolerking/donkeypart_hc_sr40
※インストール方法や使い方は上記リポジトリのREADME.mdを参照のこと。

このセンサ、5VとGND以外は、デジタルピンのままで使用できるのが良いのだけど、以下の仕様通りにGPIOを操作してやらないといけない。以下の記述は、購入元のスイッチサイエンス社サイトに掲載されていた。以下、引用する。

  • トリガ端子を10 us以上Highにしてください。
  • このセンサモジュールが40 kHzのパルスを8回送信して受信します。
  • 受信すると、出力端子がHighになります。
  • 出力端子がHighになっている時間がパルスを送信してから受信するまでの時間です。
  • 出力端子がHighになっている時間の半分を音速で割った数値が距離です。

トリガはTrigピン、出力端子というのがEchoピンです。

引用を要約すると、Trigピンに所定の値でOUTPUTして、Echo受信状態(HIGH)になったらLOWになるまでの時間を計測して、往復しているので2で割り、最後音速を使って自分で距離にしてね、ということ。

このような場合こそpigpioパッケージの指定ピンに対するコールバック関数機能が有効です。

最初に記述したリポジトリのコードは
pigpio python サンプルコードの「SonarRanger」をもとに書いたものです。

この超音波距離計即センサ、pigpioパッケージでコールバック関数をどう使うかを学習するのに最適。





この系統の処理なら、pigpioのほうがコード数が少ないと思いますよ。

2019年3月12日火曜日

DCモータをDonkey Carで使う

■DCモータを Donkey Carで使う


模型などでよく使用される [マブチモータFC130RA-2270](https://product.mabuchi-motor.co.jp/detail.html?id=9) をDonkey Carの駆動輪用に使用する際のパーツクラスをつくってみました。

Donkey Carを知らない人でも、Raspberry PiにDCモータを接続し、動作をPythonプログラムで操作したい人も参考になると思います。


■TA7291P DCモータドライバ



DCモータをRaspberry Piに直接つないでも操作することはできません。3.3VとGNDにつないで一定速度で回す事はできますが、速度を変えたり、逆転したりすることはできません。

このような場合、モータドライバというICを間にはさむ方法が一般的です。
このICにもいろんな種類があって、用途に合わせて選定しなくてはなりません。

・モータの種類(DCモータ、ステッピングモータ、サーボモータ)
・モータの個数
・モータ以外にGPIOピンをどのくらい使用するか

GPIOピンをほかのセンサやデバイスに使用する場合、26本もあるとはいえ、できるだけ使用ピンをへらしたいと考えるはずです。なれていない回路設計も1ピンへるだけでバグ発生原因が減るわけですし..

モータ数が多数などGPIOが足りない場合は、I2CやSPI対応しているICやマルチチャネルをもつICを選択します。

今回ここで使用するDCモータドライバ TA7291P は、I2CやSPIを使用しない、PWMピンを併用して回転速度調整をおこなう仕様となっています。

以下の図は、単一のDCモータの場合の結線例です。

 
GPIO番号接続先備考
5VTA7291P 7pin(Vcc), 10kΩ経由でTA7291P 4pin(Vref) 
GNDTA7291P 1pin(GND), DCモータGND
GPIO19TA7291P 5pin(IN1)PWM OUTPUT
GPIO26TA7291P 6pin(IN2)PWM OUTPUT

実は、Raspberry PiのハードウェアPWMピンもGPIO15/GPIO12か、GPIO13を使用しなくてはなりませんが、上図のGPIO19とGPIO26は通常のGPIOピンです。ハードウェアPWMではないGPIOピンをPWM化させると疑似PWMという品質の低いPWMになってしまいます。

しかし、これをハードウェアPWM並みの性能に上げる方法があります。

■ pigpio


pigpioはPythonパッケージの一つで、GPIOを操作することのできるライブラリを提供します。このライブラリをつかうと、疑似PWMの性能を向上させてくれる機能をもっているそうです。


pigpioはPythonプログラムからGPIOを操作するライブラリとしては後発で、実行前提としてpigpiodというデーモンプロセスをあらかじめRaspberry Pi上であげておかなくてはなりません。

デーモンが常時存在している恩恵もあります。別のノードからコマンドでGPIOを操作することができますし、疎結合となっているためテスト時にモッククラスを比較的簡単に実現することができます。


pigpioを使用可能にするには、以下のコマンドをRaspberry Pi上で実行します。

   $ sudo apt install -y pigpio
   $ sudo pigpiod
パーツクラスは以下のGitHubリポジトリにあります。

coolerking/donkeypart_dcmotor
https://github.com/coolerking/donkeypart_dcmotor

   $ cd ~/
   $ git clone https://github.com/coolerking/donkeypart_dcmotor.git
   $ cd donkeypart_dcmotor
   $ pip install -e .

■Donkey Carアプリの修正

まず~/mycar/config.pyに以下の設定値を記述します。

変数名設定する変数値
MOTOR_IN1_GPIOIN1と接続されているGPIOの番号
MOTOR_IN2_GPIOIN2と接続されているGPIOの番号

~/mycar/manage.pyを編集して、DCMotorパーツを追加します。
以下修正サンプルです。

:
# ダミーデータ
V.mem['user/motor/value'] = 0.5
V.mem['user/motor/status'] = 'move'
:

:
from pigpio
pi = pigpio.pi()

from donkeypart_dcmotor import DCMotor
motor = DCMotor(pi, cfg.MONITOR_IN1_GPIO, cfg.MOTOR_IN2_GPIO)
V.add(motor, inputs=['user/motor/value', 'user/motor/status'])
:
上記の通り、出力値がvalue(float型)とstatus(string型)の2種類に増えているので、Tubデータフォーマットも変わってきます。

引数名範囲説明推奨キー名
motor_value[-1.0, 1.0]のfloat値正値:正転、負値:逆転となる。最大値は、モータにVsピンと同等の電圧が加わる。本リポジトリのコードではアナログパッドなどの遊びを鑑み、(-0.1, 0.1)の範囲はゼロとして扱っている。user/motor/value
motor_statusmove/free/brakeのいずれか'move':モータ駆動、 'free':モータ駆動なし、'brake':制動停止user/motor/brake

このためmanage.pyTubWriter引数も合わせて変更する必要があります。以下は一例です。

    :
    # recording ダミー入力
    V.mem['recording'] = True
    :
    # Tubデータ・フォーマットも変更しなくてはならない
    inputs = ['cam/image_array', 'user/motor/value', 'user/motor/status', 'timestamp']
    types = ['image_array', 'float', 'str', 'str']

    # single tub
    tub = TubWriter(path=cfg.TUB_PATH, inputs=inputs, types=types)
    V.add(tub, inputs=inputs, run_condition='recording')
    :
donkeycar パッケージのデフォルトmanage.pyではdonkeycar.partモジュールにデフォルトのオートパイロットパーツクラスKerasLinearが格納されていますが、Tubデータの項目を変更したので、デフォルトのオートパイロットでは動作しなくなってしまいました。

このため、以下のようにKerasLinearの代替となるクラスを作成する必要があります。

MOTOR_STATUS = ['move', 'free', 'brake']
class MyPilot(KerasPilot):
    def __init__(self, model=None, num_outputs=None, *args, **kwargs):
        super(KerasLinear, self).__init__(*args, **kwargs)
        if model:
            self.model = model
        elif num_outputs is not None:
            self.model = my_default_linear()
        else:
            self.model = my_default_linear()

    def run(self, img_arr):
        img_arr = img_arr.reshape((1,) + img_arr.shape)
        outputs = self.model.predict(img_arr)
        # モータ値:回帰
        left_value = outputs[0][0][0]
        # モータステータス:分類
        left_status = MOTOR_STATUS[np.argmax(outputs[1][0][0])]

        # 操作データを返却
        return motor_value, motor_status


def my_default_linear():
    img_in = Input(shape=(120, 160, 3), name='img_in')
    x = img_in

    # Convolution2D class name is an alias for Conv2D
    x = Convolution2D(filters=24, kernel_size=(5, 5), strides=(2, 2), activation='relu')(x)
    x = Convolution2D(filters=32, kernel_size=(5, 5), strides=(2, 2), activation='relu')(x)
    x = Convolution2D(filters=64, kernel_size=(5, 5), strides=(2, 2), activation='relu')(x)
    x = Convolution2D(filters=64, kernel_size=(3, 3), strides=(2, 2), activation='relu')(x)
    x = Convolution2D(filters=64, kernel_size=(3, 3), strides=(1, 1), activation='relu')(x)

    x = Flatten(name='flattened')(x)
    x = Dense(units=100, activation='linear')(x)
    x = Dropout(rate=.1)(x)
    x = Dense(units=50, activation='linear')(x)
    x = Dropout(rate=.1)(x)

    # 最終全結合層と活性化関数のみ差し替え
    # categorical output of the angle
    #angle_out = Dense(units=1, activation='linear', name='angle_out')(x)

    # continous output of throttle
    #throttle_out = Dense(units=1, activation='linear', name='throttle_out')(x)
   
    # モータ値(回帰)
    value_out  = Dense(units=1, activation='linear', name='value_out')(x)
    # モータ値(分類)
    status_out = Dense(units=len(MOTOR_STATUS), activation='softmax', name='left_status_out')(x)


    #model = Model(inputs=[img_in], outputs=[angle_out, #throttle_out])
    model = Model(inputs=[img_in], outputs=[value_out, status_out])

    #model.compile(optimizer='adam',
    #              loss={'angle_out': 'mean_squared_error',
    #                    'throttle_out': 'mean_squared_error'},
    #              loss_weights={'angle_out': 0.5, 'throttle_out': .5})
    # 回帰、分類別に最適化関数を使い分け
    model.compile(optimizer='adam',
                  loss={'value_out': 'mean_squared_error',
                        'status_out':  'categorical_crossentropy'},
                  loss_weights={'value_out': 0.5,
                        'status_out': 0.5})

    return model

上記モデルのアウトプット層は以下の通り。


変数名範囲説明
value_out[-1.0, 1.0]の範囲のfloat値DCモータの電圧値をあらしており、1.0/-1.0の場合、用意されたDCモータ用電圧の最大値が投入される。
status_out0,1,2のいずれかのint値配列MOTOR_STATUSのindex値に相当。

manage.pyのトレーニング関数train()も修正する必要があります。

まず先頭部分のTubデータのキーを修正します。

    :
    X_keys = ['cam/image_array']
    y_keys = ['user/motor/value', 'user/motor/status']
    :
そして、Tubデータのmotor_statusが文字列であるのに対し、モデルのアウトプット層の所定の変数は0,1,2のint値となっているため、このあたりの変更にも対応し無くてはならない。本来は大改修が必要なのだけど、donkeycarパッケージはその点うまいかんじにできている(これに気づいているユーザは少ないと思うけど..)。

TubGroup のトレーニングバッチと評価用バッチデータを取得するための関数TubGroup.get_traun_val_genの引数train_record_transformval_record_transformに修正する関数motor_status_transformを与えることで対応できてしまうのだ。

    :
    tubgroup = TubGroup(tub_names)
    train_gen, val_gen = tubgroup.get_train_val_gen(
        X_keys, y_keys,
        batch_size=cfg.BATCH_SIZE,
        train_frac=cfg.TRAIN_TEST_SPLIT,
        train_record_transform=motor_status_transform,
        val_record_transform=motor_status_transform)
    :
関数`motor_status_transform`の定義例は、以下の通り。

MOTOR_STATUS = ['move', 'free', 'brake']
:
def agent_record_transform(record_dict):
    """
    TubGroupクラスのメソッドget_train_val_genの引数として渡し、
    Agent用Tubデータ仕様のものでも学習データ化できるようにする
    マッピング関数。

    引数
        record_dict     もとのレコードデータ(Agent用Tubデータ、辞書型)
    戻り値
        record_dict     AIのinput/output層にあったマッピングが終わったレコードデータ(辞書型)
    例外
        ValueError      モータステータスキーが1件も存在しない場合
    """
    status_val = record_dict['user/motor/status']
    if status_val is not None:
        record_dict['user/motor/status'] = motor_status_to_list(status_val)
    else:
        raise ValueError('no value of key=\"user/motor/status\" in loaded record')
    return record_dict

def motor_status_to_list(val):
    """
    モータステータス値を数値リスト化する。

    引数
        モータステータス値
    戻り値
        数値リスト
    """
    classes = np.zeros(len(MOTOR_STATUS)).tolist()
    classes[MOTOR_STATUS.index(val)] = 1
    return classes

■まとめ

・pigpioパッケージで実装すれば疑似PWMはハードウェアPWMに近い性能になる
・I2C/SPIによる回路短縮をえらぶと、コードが増えるし、データシートが読めないと難しい
・PWMのみの場合はコードは簡単だが、ピン数は複雑になる


ちなみに、I2Cを使う場合はDRV8830があります。SPI化は更にADC/DACをつかうことで実現できます。






2019年2月25日月曜日

ブログ記事「Lingvo: A TensorFlow Framework for Sequence Modeling for Sequence Modeling」を勝手に翻訳してみた

今朝のタイムラインに流れていたブログ記事 Lingvo: A TensorFlow Framework for Sequence Modeling for Sequence Modeling を勝手に翻訳したものです。

--------

Lingvo: シーケンスモデリングのための TensorFlow フレームワーク

  • 2019年2月23日
  • Jonathan Shen

Lingvo とは、 国際語エスペラント語 で「言語」を表します。Lingvo と命名したことは、まさにLingvoフレームワークのルーツを暗示しています - Lingvoは、機械翻訳、音声認識、そして音声合成のような言語関連タスクのためのシーケンスモデルに焦点を合わせてTensorFlowを使った一般的なディープラーニングフレームワークとして開発されました。
内部的には、Lingvoフレームワークは勢いを増し、Lingvoを使用している研究者の数はバルーンのように膨らんでいます。その結果、Lingvoを使用して作成された最先端の結果を含む出版済みの 論文 が今では多数あります。サポートされているアーキテクチャは、従来のRNNシーケンスモデルからTransformerモデルおよびVAEコンポーネントを含むモデルまで多岐にわたります。私たちの研究コミュニティの支持を示し、再現可能な研究努力を奨励するために、私たちはフレームワークをオープンソース化し、私たちの論文で使われているモデルを公開し始めています。

図1:Lingvoフレームワークの概要。評価と提供のためにモデルをインスタンス化、トレーニング、およびエクスポートする方法を概説しています。

Lingvo は共同研究を念頭に置いて構築されており、さまざまなタスクで共通のレイヤの実装を共有することでコードの再利用を促進しています。さらに、すべてのレイヤが同じ共通のインタフェースを実装し、同じ方法でレイアウトされています。これにより、よりクリーンでわかりやすいコードが生成されるだけでなく、他の人が別のタスクに対して行った改善を自分のタスクに適用することが非常に簡単になります。この一貫性を強化するには、より多くの規律 (dicipline) と共通語 (boilerplate) が必要になりますが、 Lingvo では、これを最小限に抑えて研究中の反復時間を短縮することを試みています。

コラボレーションのもう1つの側面は、再現可能な結果を共有することです。Lingvoは、チェックインモデルのハイパーパラメータ設定を集中管理する場所を提供します。これは重要な実験を文書化するのに役立つだけでなく、同一のモデルを訓練することによってあなたの結果を簡単に再現する方法を他の人に与えることになります。

def Task(cls):
  p = model.AsrModel.Params()
  p.name = 'librispeech'

  # encoder パラメータの初期化
  ep = p.encoder
  # データは240次元フレーム(80x3フレーム)で構成されており、
  # それらを個々に80次元フレームとして再解釈します。
  # 詳細は LibrispeechCommonAsrInputParams も参照のこと。
  ep.input_shape = [None, None, 80, 1]
  ep.lstm_cell_size = 1024
  ep.num_lstm_layers = 4
  ep.conv_filter_shapes = [(3, 3, 1, 32), (3, 3, 32, 32)]
  ep.conv_filter_strides = [(2, 2), (2, 2)]
  ep.cnn_tpl.params_init = py_utils.WeightInit.Gaussian(0.001)
  # Conv LSTM レイヤを無効化
  ep.num_conv_lstm_layers = 0

  # decoder パラメータの初期化
  dp = p.decoder
  dp.rnn_cell_dim = 1024
  dp.rnn_layers = 2
  dp.source_dim = 2048
  # unrolling ベースに基づく間、機能を使用する。
  dp.use_while_loop_based_unrolling = False

  tp = p.train
  tp.learning_rate = 2.5e-4
  tp.lr_schedule = lr_schedule.ContinuousLearningRateSchedule.Params().Set(
      start_step=50000, half_life_steps=100000, min=0.01)

  # Setting p.eval.samples_per_summary を大きな値に設定すると、
  # dev, devother, test, testother は完全に評価され(これらの各セットの
  # num_samplesは5000未満なので)、トレーニングサマリは5000サンプルで計算される
  p.eval.samples_per_summary = 5000
  p.eval.decoder_samples_per_summary = 0

  # オーバーフィットを防ぐために、可変重量ノイズを使用
  p.vn.global_vn = True
  p.train.vn_std = 0.075
  p.train.vn_start_step = 20000

  return p


Lingvo は NLP に焦点を当てることから始めましたが、それは本質的に非常に柔軟性があり、画像セグメンテーションや点群分類などのタスクのためのモデルはフレームワークを使ってうまく実装されています。Distillation、GAN、およびマルチタスクモデルもサポートされています。同時に、このフレームワークはスピードを犠牲にすることはなく、最適化された入力パイプラインと高速分散トレーニングを特徴としています。最後に、Lingvoはプロダクションの容易さを目指してまとめられており、モバイル推論のためのモデルの移植への明確な道筋さえあります。

コードに直接ジャンプするには、GitHubページcodelab を調べてください。Lingvoまたはそれがサポートする高度な機能の詳細については、私たちの 論文 を参照してください。
-----

個々に乗ってるコードを読む限りでは、自然言語処理系DLは基本エンコーダデコーダで、それぞれのレイヤ群をあらわすオブジェクトにパラメータをセットするだけでモデル構築ができるフレームワークのようだ。

でもね..機械学習やディープラーニングで苦労するのはモデルよりもデータの口金(くちがね)にあわせることであって、特に日本語などの分かち書きから始めないとだめな言語はとっても面倒になるのよ..そのあたりをカバーするフレームワークが本当はほしいんだけどなあ..

 

2019年2月6日水曜日

pip install mpi4py したら mpi.h がないと怒られた件

Ubuntu 18.04 LTS 環境下のPython3(python3-dev)で


pip install mpi4py

したら、

    _configtest.c:2:10: fatal error: mpi.h: No such file or directory
     #include
              ^~~~~~~
    compilation terminated.
    failure.
    removing: _configtest.c _configtest.o
    error: Cannot compile MPI programs. Check your configuration!!!
   
    ----------------------------------------

Command "/home/tasuku_hori/env/bin/python3 -u -c "import setuptools, tokeni
ze;__file__='/tmp/pip-install-8php8m1j/mpi4py/setup.py';f=getattr(tokenize,
 'open', open)(__file__);code=f.read().replace('\r\n', '\n');f.close();exec
(compile(code, __file__, 'exec'))" install --record /tmp/pip-record-xt1ud26
w/install-record.txt --single-version-externally-managed --compile --instal
l-headers /home/tasuku_hori/env/include/site/python3.6/mpi4py" failed with
error code 1 in /tmp/pip-install-8php8m1j/mpi4py/


というエラーが出た。

いろいろ調べてみたら、

sudo apt install -y libopenmpi-dev

を実行後再度実行すると成功した。


調べている間、openmpi-binだとかmpichだとかをapt install しろ書いてあったが
私の環境では libopenmpi-dev だった。

ご参考まで。

2019年2月4日月曜日

Donkey Car走行会に参加した


AIでRCカーを走らせよう
ハンズオン&走行会&見学会
https://lab-kadokawa74.peatix.com/

というイベントが有り、2月2日の走行会のみ参加してきた。

手持ちのDonkey Carは 
Robocar Store で購入した標準車の rocinante号
と、
WARRIORシャーシベースで作った emperor 号
の2台。

安定しているのは rocinante なのだけど、
やっぱり走行経験がほとんどない emperor 号を持っていった。

emperor 号は 1/28 RCカーシャーシベースなので
Mac Book Air 11inchと一緒にお弁当小袋で持っていけるし..

着席と同時にセットアップをはじめたのだが..




イベントは最初の1時間だけトーク..
といってもスピーカの自己紹介と
海外事情についての話があり、作業途中だったけど
聴講モードへ..

じつはここでつまずいていた。
本体の電源を入れっぱなしにしたので、
バッテリが放電していて、
ESCの起動LEDがつかなくなる..

しょうがないので充電を開始..
1時間ほどで充電できたので
早速つなごうとしたら..

今度はターミナル接続が
とぎれとぎれでつながらない..

やっぱり◯ァーウェイ製ルータはまずかったか..

講師の方の助力で、ダイレクトに接続させてもらいことなきを得る。

が、キャリブレーション中にステアリングが右にまがったままもどらない..

PCA9536のチャネルを買えてもステアリングだけ動かない..

PCA9536より先ステアリングサーボまでの間に問題が発生..

でも、テスターは自宅だしボタン電池が切れてて使えない..
サーボの動作確認するにはオスオスのジャンパケーブル3本がいるけど
今日は持ってきていない..

ということで1センチも走行できずリタイヤ...


ずーっと他の人の走行を眺めてました..



くそー、rocinanteも持ってくりゃよかった..








電気系でリタイヤとは..
(ザクスピードの)鈴木亜久里かよっ

..っていっても、伝わらないだろうなあ..

2019年2月1日金曜日

GitHub:Learning to Drive Smoothly in Minuts 実装リポジトリのREADME.md を翻訳してみた

以下のドキュメントは、 GitHub 上の Learning to Drive Smoothly in Minuts の再現用リポジトリのREADME.md を翻訳したものです。

-----

Learning to Drive Smoothly in Minuts


Donkey Carシミュレータ、強化学習アルゴリズムである Soft Actor-Critic(SAC) と Variational AutoEncoder(VAE) を使用して、数分でスムーズな運転を学習します。

  • 媒体上のブログ記事:リンク
  • ビデオ



VAE レベル0VAE レベル1


VAEをダウンロードVAEをダウンロード
事前トレーニング済みエージェントをダウンロード事前トレーニング済みエージェントをダウンロード


注意 : 事前トレーニング済みのエージェントは logs/sac/ フォルダに保存する必要があります(事前トレーニング済みのエージェントを使用するには --exp-id 6 (フォルダのインデックス) を渡す必要があります)。

クイックスタート


0. シミュレータをここからダウンロード、もしくは ソースからビルド
1. 関連パッケージのインストール (cf requirements.txt)
2. (オプションだが推奨) 事前学習済み VAEのダウンロード:VAEレベル0 / VAEレベル1

3. Soft Actor-Critic (SAC) コントロールポリシを5000ステップトレーニング
python train.py --algo sac -vae path-to-vae.pkl -n 5000

4. 2000ステップのトレーニングを受けたエージェントをエンジョイ
python enjoy.py --algo sac -vae path-to-vae.pkl --exp-id 0 -n 2000

別のレベルでトレーニングするには、config.py内のLEVEL=0LEVEL=1に変更する必要があります。
 

Variational AutoEncoder(VAE) のトレーニング


1. 遠隔操作モードを使って画像を収集
python -m teleop.teleop_client --record-folder path-to-record/folder/

2. VAEをトレーニング
python -m vae.train --n-epochs 50 --verbose 0 --z-size 64 -f path-to-record/folder/

遠隔操作モードでのトレーニング


python train.py --algo sac -vae logs/vae.pkl -n 5000 --teleop

遠隔操作モードでのテスト


python -m teleop.teleop_client --algo sac -vae logs/vae.pkl --exp-id 0

潜在空間を探索


python -m vae.enjoy_latent -vae logs/level-0/vae-8.pkl

結果の再現


ビデオに表示されている結果を再現するには、 `config.py` でさまざまな値を確認する必要があります。
 

レベル0
 

config.py :
MAX_STEERING_DIFF  =  0.15  # 非常になめらかな操作の場合は0.1を指定、ただしより多くのステップが必要
MAX_THROTTLE  =  0.6  # MAX_THROTTLE = 0.5でもよいが高速走行のために
MAX_CTE_ERROR =  2.0  #通常モードのみで使用される、遠隔操作モード使用時は10.0に設定
LEVEL = 0

通常モード(なめらかな操作)でトレーニング、5~10分かかる
python train.py --algo sac -n 800.,0 -vae logs/vae-level-0-dim-32.pkl

通常モード(非常になめらかな操作、MAX_STEERING_DIFF = 0.1を指定)でトレーニング、~20分かかる
python train.py --algo sac -n 20000 -vae logs/vae-level-0-dim-32.pkl

遠隔モード(非常になめらかな操作、MAX_CTE_ERROR = 10.0を指定)でトレーニング、5~10分かかる
python train.py --algo sac -n 8000 -vae logs/vae-level-0-dim-32.pkl --teleop


レベル1

注意 : レベル1では、遠隔モードのみ有効

config.py :
MAX_STEERING_DIFF = 0.15
MAX_THROTTLE = 0.5 # MAX_THROTTLE = 0.6 でも動作するが最も急なターンであるためトレーニングが難しい
LEVEL = 1

遠隔操作モードでトレーニング、~10分かかる
python train.py --algo sac -n 15000 -vae logs/vae-level-1-dim-64.pkl --teleop

注意 : VAEのサイズはレベル0と1で異なりますが、これは重要な要素ではありません。

オンボードカメラでビデオ録画


トレーニングを受けたモデルが必要です。たとえば、最後のトレーニングを受けたSACエージェントで1000ステップを記録する場合は、次のようになります。

python -m utils.record_video --algo sac --vae-path logs/level-0/vae-32-2.pkl -n 1000

プロジェクトの引用


このリポジトリを引用するには:

@misc{drive-smoothly-in-minutes,
  author = {Raffin, Antonin and Sokolkov, Roma},
  title = {Learning to Drive Smoothly in Minutes},
  year = {2019},
  publisher = {GitHub},
  journal = {GitHub repository},
  howpublished = {\url{https://github.com/araffin/learning-to-drive-in-5-minutes/}},
}

クレジット


関連論文: Learn to Drive in a Day]() / 日本語訳


-----

最初てっきり Learn to Drive in a Dayの作者のリポジトリだと思っていたのだけど、読んでいるうちに別人と判明した。
こっちは in Minuts になってるのね...
関連論文に紹介されているってことは、あきらかにオマージュということかな..

でも Smoothlyが入ってるところが、お前よりぬるぬる動くし学習も速いぜ、とドヤ顔でいいたいのかもなあ..

2019年1月31日木曜日

ブログ記事 「Learning to Drive Smoothly in Minuts」 を翻訳してみた

以下の文章はブログ記事 Learning to Drive Smoothly in Minuts を勝手に翻訳したものです。

このブログ記事のタイトルから..前に読んだ論文「Learn to Drive in a Day」 (勝手日本語訳はこちらに喧嘩売ってを意識してるんだろうなあ..きっと..

-----

Learning to Drive Smoothly in Minuts

小さなレーシングカー上で強化学習




Unityシミュレータのてれオペレーションコントロールパネルで動作中のDonkeycar

この記事では、自動運転レーシングカーを数分でトレーニングする方法と、その制御を円滑にする方法について説明します。この方法は、強化学習(RL)に基づいており、ここではシミュレーション(Donkey Car simulator)で提示されていますが、現実の世界に適用できるように設計されています。それは自動運転にフォーカスしている Wayve.ai というスタートアップの作業上でビルドしています。

この記事で使用されているコードとシミュレータはオープンソースで公開されています。詳細については、関連するGitHubレポジトリを確認してください。 :)(トレーニング済みのコントローラもダウンロード可能)


動画




GitHub リポジトリ: 結果の再現

[GitHub] Learning to Drive Smoothly in Minuts
https://github.com/araffin/learning-to-drive-in-5-minutes


はじめに:レージングカー競技


数年前の DIY Robocars を立ち上げ以来、数多くの自動運転レーシングカーの競技会が開催されています(例:Toulouse Robot Race、Iron Carなど  )。それらの目標はシンプルです:あなたはレーシングカーを持っていて、入力としてその搭載カメラからの画像だけを与えられて、トラックにとどまっている間できるだけ速く走行する。




DIY Robocarに触発されたDonkeyシミュレータの倉庫シーン

自動運転への挑戦は、ロボット工学に入門するための良い方法です。学習を容易にするために、オープンソースの自動運転プラットフォームである Donkey Car が開発されました。そのエコシステムでは、その小型ロボットを特色とする Unityシミュレータ があります。このDonkey Carベースで提案されているアプローチをテストします。


アウトライン


小規模な自動運転車の競技で使用されているさまざまな方法を簡単に検討した後、強化学習とは何かを提示してから、私たちのアプローチの詳細に入ります。



Arduino、Raspberry Pi、PiCamera を搭載した自動運転レーシングロボット


自動運転競技で使用される方法:ライン追跡と行動クローニング


強化学習を紹介する前に、まずRCカーの競技会で現在使用されているさまざまなソリューションについて簡単に説明します。

以前のブログ記事では、私は組み合わせて自律的に駆動するための最初のアプローチである、コンピュータビジョンとPIDコントローラについて説明しました。このアイデアはシンプルで多くの設定に適用できますが、それは(トラックの中心がどこにあるかを車に伝えるために)データの手動ラベリングを必要とします(手間のかかるラベリングは楽しいことではありません)。

他のアプローチとして、競合他社の多くは人間の運転手の行動を再現するために教師つき学習を利用しています。そのためには、人間は数周ほど車を手動で運転し、カメラ画像とそれに関連するジョイスティックからの制御入力を記録する必要があります。それから、モデルは人間の運転を再現するためにトレーニングされます。しかし、このテクニックは一般的に極めて悪いものであるため、実際には堅牢ではなく、均一な運転とトラックごとに再学習を要求されます。


強化学習(RL)とは?なぜ使用すべきなのか?


上記の問題を考慮すると、強化学習(RL:Reinforcement Learning)は興味深い代替方法のです。

強化学習の設定では、エージェント(またはロボット)はその環境に基づいて行動し、フィードバックとして報酬を受け取ります。それは肯定的な報酬(ロボットが何か良いことをした)または否定的な報酬(ロボットがペナルティを科されるべきである)であるかもしれません。

ロボットの目標は累積報酬を最大化することです。そうするために、それは世界との相互作用を通して、感覚による入力を行動にマッピングするいわゆるポリシー(あるいは振る舞い/コントローラ)を学びます。

私たちの場合、入力はカメラ画像で、アクションはスロットルとステアリングアングルです。そのため、車が軌道に乗ってその速度を最大にするように報酬をモデル化すれば、完了です。



stable-baselines:便利な強化学習ライブラリ

これが強化学習の美しさです、あなたはほんの少しの仮定(ここでは報酬関数を設計するだけ)を必要とします、そしてそれはあなたが望むものを直接最適化します(レースに勝つために軌道に早く行きます!)


注意 :これは、小さな自動運転車に強化学習についての最初のブログ記事はありません。しかし既存のアプローチと比較して、現在のテクニックは良質でなめらかなコントロールポリシを数分(数時間ではない)で(なめらかなコントローラのために5~10分、20分以内にとてもなめらかなコントローラを)学習します。

次に、私たちの方法の基礎である Wayve.ai アプローチを詳細に検討することから始めましょう。


1日で運転を学ぶーWayve.ai アプローチの重要な要素


Wayve.ai は、物理世界で自動運転車を単純な道路でトレーニングする方法を説明しています。このメソッドはいくつかの重要な要素で構成されています。



Wayve.ai アプローチ:1日で運転を学習する

最初に、彼らは画像をより低い次元の空間に圧縮するために特徴抽出器(ここではVariational Auto-EncoderまたはVAE)をトレーニングします。モデルは入力画像を再構築するようにトレーニングされていますが、情報を圧縮することを強制するボトルネックを含んでいます。

生データから関連情報を抽出するこのステップは、状態表現学習(SRL)と呼ばれ、私の主な研究テーマでした。それは特に検索スペースを減らすことを可能にし、それ故にトレーニングを加速させます。以下は、SRLとエンドツーエンドの強化学習、つまりピクセルから直接制御ポリシーを学習する方法の関係を示す図です。


注意:Auto Encoder をトレーニングすることは、有用な特徴を抽出するための唯一の解決策ではありません。例えば、インバースダイナミクスモデルをトレーニングすることもできます。



ポリシ学習からの特徴抽出の分離

2つ目の重要な要素は、入力としてVAE機能を使用してコントロールポリシを学習する、ディープ決定論的ポリシ勾配(DDPG)という名前のRLアルゴリズムの使用です。このポリシは各エピソードの後に更新されます。アルゴリズムの重要な側面のひとつとして、再生バッファと呼ばれるメモリを持つことです。記録され後で"再生"可能な環境で相互に再生バッファでやりとりを行います。そのため、車が世界とやり取りしない場合でも、このバッファから経験をサンプリングしてポリシを更新できます。


車は人間の介入前に走行したメータの数を最大にするようにトレーニングされています。そしてそれが最後の重要な要素です。自動車が道路から外れ始めるとすぐに、オペレータはエピソードを終了します。この早期終了は(Deep Mimicによって示されているように)本当に重要であり、車がタスクを解決するのに面白くない領域を探索するのを防ぎます。




ここまで、何も新しいものは提示していません。Wayve.ai アプローチを要約しただけです。以下は、基本テクニックに対して行った変更のすべてです。


数分で運転を学習ー最新のアプローチ


Wayve.ai のテクニックは原理的にはうまくいくかもしれませんが、それを自動運転RCカーに適用するために取り組む場合にはいくつかの問題があります。

まず、特徴抽出器(VAE)は各エピソードの後にトレーニングされるので、特徴の分布は定常的ではないことです。つまり、特徴量は時間の経過とともに変化し、ポリシトレーニングが不安定になる可能性があります。また、ラップトップで(GPUなしで)VAEをトレーニングするのは非常に遅いので、各エピソードの後にVAEを再トレーニングすることは避けたいと思います。

これら2つの問題に対処するために、私は事前にVAEをトレーニングし、私のコンピュータを保護するためにGoogle Colabノートブックを使用しました。このようにして、ポリシは固定機能抽出プログラムでトレーニングされます。

下の画像では、VAEが学んだことを探索します。私たちはその潜在空間を(スライダーを使って)ナビゲートし、再構成された画像を観察します。





VAEが学習した潜在空間を探る

その場合、DDPGは不安定であることが知られており(トレーニング中にそのパフォーマンスが壊滅的に低下する可能性があるという意味で)、調整するのは非常に困難です。幸いなことに、最近の Soft Actor-Critic(SAC)というアルゴリズムは同等の性能を持ち、調整がはるかに簡単です。


実験中に、PPO 、SAC、DDPGを試しました。DDPGとSACはいくつかのエピソードで最高の結果を出していましたが、SACは調整が簡単でした。

私がstable-baselinesのために記述し、stable-baselines内で最後に実装した、Soft Actor-Critic(SAC)実装を使用しました。(RLを使っている方は、ちょっと覗いてみることをおすすめします😋 )


[GitHub] hill-a/stable-baselines
https://github.com/hill-a/stable-baselines
Open AI baselines をフォークした、強化学習アルゴリズム群の実装

最後に、コントロールを滑らかにしてスピードを最大にするために、報酬機能とアクションスペースを更新しました。


報酬関数:早く走行しなさい、ただしトラック内にとどまりなさい!


ロボットカーには走行距離も(速度センサも)含まれていないため、走行したメータ数(速度も)を報酬として使用することはできません。

したがって、私は各タイムステップで「ライフボーナス」(すなわち、トラックにとどまることに対して+1の報酬)を与え、トラックを離れた場合「クラッシュペナルティ」(-10の報酬)を使ってロボットにペナルティを科すことにしました。さらに、あまりにも速く道路を外れることを罰することも有益であると思いました:スロットルに比例して追加の負の報酬がクラッシュペナルティに追加されます。

最後に、レーシングカーとしてできるだけ速く走りたいので、現在のスロットルに比例した「スロットルボーナス」を追加しました。 そのようにして、ロボットは軌道上にとどまり、同時にその速度を最大化しようとします。

まとめると、

ここで、w1 と w2 は目的のバランスをとることを可能にする単なる定数( w1 << 10 と w2 << 1 は2次目的であるため)。


不安定な制御を回避:なめらかに運転するための学習


世界は実際には確率的ではありません。
お気づきのとおり、ロボットが自発的に揺れ始めるのではありません。
RLアルゴリズムを接続しない限り。
- エモトドロフ



左:振動コントロール、右:提案手法によるなめらかなコントロール

あなたがこれまでに提示されたアプローチを適用するならば、それはうまくいくでしょう:車は軌道上にとどまり、速く走ろうとします。しかし、あなたはおそらく不安定なコントロールになってしまうでしょう。車は上の画像に示すように振動します。そうしないのはインセンティブがないからです。報酬を最大化しようとするだけです。

コントロールを円滑にするための解決策は、 *前のコマンド(ステアリングとスロットル)の履歴で入力を増やしながら、ステアリング角度の変化を制限する* ことです。そのように、あなたはステアリングに連続性を課します。

例として、現在の自動車の操舵角が0°で、突然90°に操縦しようとすると、連続性制約では、たとえば40°に操縦できるようになります。従って、2つの連続する操舵コマンド間の差は所与の範囲内に留まることになります。この追加の制約には、もう少しトレーニングが必要です。

私は満足のいく解決策を見つける前に、その問題を解決しようと数日を過ごしました。ここで私は試してみましたがうまくいきませんでした


  • 絶対ステアリングではなく出力相対ステアリング:低周波数の振動を生成
  • 連続性のペナルティを追加(ステアリングの大きな変更に対してロボットにペナルティを課す):ロボットは正しいことを最適化しません。ときどき動作しますが、軌道に乗らないようにします。そのペナルティのコストが低すぎる場合は、無視します。
  • 最大ステアリングを制限:最も急な方向転換では、車はこれ以上とどまることができなくなります。
  • いくつかの速度情報を与えるために複数のフレームを積み重ねる:より低い周波数の振動を生み出しました。


注意 :最近、ETHチューリッヒの研究者は、継続的でエネルギー効率の高い管理をするためにカリキュラム学習を使用することを提案しました。これは2番目の解決策になる可能性があります(ただし調整が少し困難です)。



アプローチのまとめ


私たちのアプローチでは、ポリシ学習を特徴抽出から切り離し、制御を円滑にするために制約を追加します。

まず、人間は車を手動で運転してデータを収集します(手動運転で約5分で10k枚の画像)。これらの画像はVAEをトレーニングするために使用されます。

それから、私達は探索エピソード(確率論的ポリシが使用される)とポリシトレーニング(費やされた時間を最適化するために、人間が車を軌道に戻す際に行われる)の間で交替します。

ポリシをトレーニングするために、画像はまずVAE(ここでは64次元の潜在空間)を使用して符号化され、行われた最後の10の動作(スロットルおよびステアリング)の履歴と連結され84次元の特徴ベクトルを生成する。

コントロールポリシは、ニューラルネットワーク(ReLUまたはELU活性化機能を有する32および16ユニットの2つの完全に接続された層)によって表されます。

このコントローラはステアリングアングルとスロットルを出力します。スロットルを一定の範囲内に収めるように制限し、現在と前のステアリング角の差も制限します。



結論


この記事では、カメラだけを使用して、数分でDonkeycarのためのなめらかなコントロールポリシを学習するためのアプローチを紹介しました。

この方法は現実の世界に適用されるように設計されおり、このプロジェクトにおける私の次のステップでもあります。実際のRCカーでアプローチをテストしました(下記参照)。これは Raspberry Pi で実行するためにVAEモデルを縮小することを必要とします(ポリシーネットワークは既にかなり小さい)。


wayve.ai アプローチは本物のRCカーでRoma Sokolov によって再現されました。しかしこの再現には、なめらかなコントロールのための最新の改良を含んでいませんでした。

これですべてです。コードをテストしたり、コメントしたり、質問したりすることを躊躇しないでください。共有はとても気にしています。


------

Donkey Carのデフォルトモデルは、教師あり学習ベースなのだけど、すでに強化学習の既存のアルゴリズムを複数用いて挑戦している人がおり、海外の研究速度の速さにあらためて驚かされた。

実際の自動運転車は、(運転しない)人や荷物を載せないと意味がないんだよなあ..

今まで振動(左右に振りながらの運転)には目をつぶってたけど、実現させるにはそれもクリアし無くてはならない要件の一つであることを思い出させてくれたよ..


超音波センサHC-SR04をDonkey Carで使う

前方障害物との距離をはかりたくて、 HC-SR04 をRaspberry Piに接続し、Donkey Carのパーツクラス化してみた。 [GitHub] coolerking/donkeypart_hc_sr40 ※インストール方法や使い方は上記リポジトリの README...