Translate

2020年11月24日火曜日

OpenAI Gym 環境クラスを独自実装してみる

強化学習を行う場合、Stable BaselinesやTensorFlowなどのAPIではOpenAI Gym準拠の環境クラスを使用することができる。

ので、個別案件に対応する独自環境クラスを作りたい..

ということで実際に作ってみた。

独自環境クラスを実装するには、まずOpenAI Gymが提供する gym.Env という基底クラスを継承し、以下の表にあるプロパティ(インスタンス変数、つまりself.~)やメソッドをオーバライドすればよい。



 

 

サンプルとして強化学習AIでじゃんけん対戦を実現する。
以下、作ったサンプルコードのリポジトリリンク

[GitHub] coolerking/rock-paper-scissors
https://github.com/coolerking/rock-paper-scissors


実装してみて思ったのが、報酬関数の仕様は機能要件上には登場しないので、(業務用件によっては)設計者が発明しなくてはならないこと。

そして、状態と観測の違いが理解できない人(意外と多い)は、もう「状態=観測」で割り切って設計すること。

軽く触った程度なので、もうすこしほってみないとアレだけど..とりあえず現時点での感想です。


2020年9月14日月曜日

Stable Baselinesのコールバックの書き方を調べてみる

Donkeycar の OpenAI Gym 環境 gym-donkeycar を理解するためには OpenAI Gymのフレームワークを手っ取り早く把握する必要がある。

 ということで書店でみつくろい、比較的カラフルで分厚い「OpenAI Gym/Baselines 深層学習 強化学習 人工知能プログラミング 実践入門」という書籍を買った。

 

過去に一度さらっとTensorflo(v1の頃だけど)強化学習のチュートリアルをやって、だいぶ時間がたっていたが、必要な箇所しか説明していなかったので、すんなりすすめることができた。

 本に書いているコードに多少誤り(breakぬけとか)があり、治す必要はあるが Python を知っている人だったら問題ない程度..

 

..だったのだけど、Stable Baselinesのコールバックを使うところで、サンプルコードが動かな書くなった..

..動かなくなった時点でピンときたのが、後付で読んだアマゾンのレコメンド、情報が古い云々..ああ、Stable Baselinesのコールバック実装が変わったんじゃないの、とピンときた。

 

で公式ドキュメントの Callbacks を読んでみたら、案の定"レガシ"になってた..

 

以下、勝手翻訳した内容です。

(ただしレガシとかかれた旧呼び出しに関する箇所はomitしました)

参照はat your own riskでお願いします。

 ------

コールバック


コールバックは、トレーニング手順の特定の段階で呼び出される関数のセットです。トレーニング中にコールバックを使用して強化学習(RL)モデルの内部状態にアクセスできます。監視、自動保存、モデル操作、進行状況バーなどの機能を提供することができます。

独自 Callback


独自の Callback クラスを構築するには、BaseCallback を継承したクラスを作成する必要があります。これにより、イベント( _on_training_start _on_step )や便利な変数(RLモデルの self.model など)にアクセスできます。

ここではカスタムコールバックの2つのサンプルを紹介します。1つはトレーニングの報酬に応じて最適なモデルを保存するためのもの(セクション「サンプル」を参照)と、Tensorboardを使用して追加の値をログに記録するためのもの(「Tensorboard」セクションを参照)です。

from stable_baselines.common.callbacks import BaseCallback


class CustomCallback(BaseCallback):
    """
    BaseCallback クラスを継承したカスタムコールバック

    :param verbose: (int) Verboseレベル 0:出力なし 1: infoのみ 2: debugレベルまで
    """
    def __init__(self, verbose=0):
        super(CustomCallback, self).__init__(verbose)
        # 以下の変数にはコールバック内でアクセス可能
        # (ベースクラス上で定義済み)
        # 強化学習モデル
        # self.model = None         # タイプ: BaseRLModel
        # トレーニングに使用される環境のエイリアス
        # self.training_env = None  # タイプ: Union[gym.Env, VecEnv, None]
        # コールバックが呼び出された回数
        # self.n_calls = 0          # タイプ: int
        # self.num_timesteps = 0    # タイプ: int
        # ローカル変数辞書、グローバル変数辞書
        # self.locals = None        # タイプ: 辞書[文字列, Any]
        # self.globals = None       # タイプ: 辞書[文字列, Any]
        # レポートに使用されるロガーオブジェクト
        # self.logger = None        # タイプ: logger.Logger
        # # イベントコールバックでは、親オブジェクトにアクセスできると
        # # 便利な場合がある
        # self.parent = None        # タイプ: オプション[BaseCallback]

    def _on_training_start(self) -> None:
        """
        このメソッドは最初のロールアウトが始まる前に呼び出される。
        """
        pass

    def _on_rollout_start(self) -> None:
        """
        ロールアウトは、現在のポリシを使用した環境対話の集合である。
        このイベントは、新しいサンプルを収集する前にトリガされる。
        """
        pass

    def _on_step(self) -> bool:
        """
        このメソッドは env.step() が呼び出されるたびに
        モデルによって呼び出される。

        (EventCallback の) 子コールバックの場合、イベントがトリガされた
        ときに呼び出される。

         :return:(真偽値) コールバックが False を返した場合、トレーニングは早期中止される.
        """
        return True

    def _on_rollout_end(self) -> None:
        """
        このイベントは、ポリシを更新する前にトリガされる。
        """
        pass

    def _on_training_end(self) -> None:
        """
        このイベントは learn() メソッド終了前にトリガされる。
        """
        pass


注意
self.num_timesteps は、環境で実行されたステップの総数に対応する。つまり環境の数に env.step() が呼び出された時間の数を掛けたものである。
PPO1および TRPO は、 MPI に依存しているため、(各ステップではなく)各ロールアウト後に self.num_timesteps を知って更新する。
その他のアルゴリズムの場合、self.num_timestepsn_envsenv.step() を呼び出すたびに、(環境数)ずつ増加する。


注意
SAC、DDPG、TD3、DQNなどのオフポリシアルゴリズムの場合、rollout の概念は、2つの更新の間の環境で実行されるステップに対応する。



EventCallback


Kerasと比較すると、Stable Baselinesは EventCallback というイベントをトリガするための BaseCallback のセカンドタイプを提供します。イベントがトリガされると、子コールバックが呼び出されます。

例として、EventCallbackのサブクラスのEvalCallback (後述セクション参照)は、新しい最適なモデルがある場合、その子のコールバックをトリガします。子コールバック StopTrainingOnRewardThreshold の場合、強化学習モデルによって達成された平均報酬がしきい値を超えた場合にトレーニングを停止します。

注意
EvalCallbackStopTrainingOnRewardThreshold のソースコードを確認して、この種のコールバックで実現できることの概要を理解することを推奨。


class EventCallback(BaseCallback):
    """
    イベント発生時にコールバックをトリガするためのベースクラス。

    :param callback: (オプション[BaseCallback]) イベントがトリガーされた時に呼び出されるコールバック
    :param verbose: (int)
    """
    def __init__(self, callback: Optional[BaseCallback] = None, verbose: int = 0):
        super(EventCallback, self).__init__(verbose=verbose)
        self.callback = callback
        # 親にアクセスする権限を与える
        if callback is not None:
            self.callback.parent = self
    ...

    def _on_event(self) -> bool:
        if self.callback is not None:
            return self.callback()
        return True


Callbackコレクション


安定したベースラインは、以下のような共通コールバックセットを提供します。
 

  • モデルを定期的に保存する (CheckpointCallback)
  • モデルを定期的に評価し最適なモデルを保存する (EvalCallback)
  • コールバックの連鎖 (CallbackList)
  • イベントのコールバックのトリガ(イベントコールバック、EveryNTimesteps)
  • 報酬のしきい値に基づいてトレーニングを早期に停止する (StopTrainingOnRewardThreshold)


CheckpointCallback


save_freq ステップごとにモデルを保存する Callback クラスです。ログフォルダ( save_path ) を引数に指定します。またオプションとしてチェックポイントファイルの接頭文字列を指定できます( デフォルトは rl_model )。

from stable_baselines import SAC
from stable_baselines.common.callbacks import CheckpointCallback
# 1000ステップごとにチェックポイントを保存する
checkpoint_callback = CheckpointCallback(save_freq=1000, save_path='./logs/',
                                         name_prefix='rl_model')

model = SAC('MlpPolicy', 'Pendulum-v0')
model.learn(2000, callback=checkpoint_callback)



StopTrainingOnRewardThreshold


一時的な報酬(評価に対する平均エピソード報酬)のしきい値に達したら(つまりモデルが十分に良いときに)、トレーニングを停止します。EvalCallback と一緒に使用し、新しい最良のモデルによってトリガされるイベントを使用する必要があります。

import gym

from stable_baselines import SAC
from stable_baselines.common.callbacks import EvalCallback, StopTrainingOnRewardThreshold

# 評価用環境
eval_env = gym.make('Pendulum-v0')
# モデルが報酬のしきい値に達したときにトレーニングを停止する
callback_on_best = StopTrainingOnRewardThreshold(reward_threshold=-200, verbose=1)
eval_callback = EvalCallback(eval_env, callback_on_new_best=callback_on_best, verbose=1)

model = SAC('MlpPolicy', 'Pendulum-v0', verbose=1)
# タイムステップの数はほぼ無限(1e10)だが、
# 報酬のしきい値に達するとすぐにトレーニングを停止する
model.learn(int(1e10), callback=eval_callback)



EveryNTimesteps


n_steps ステップごとに子コールバックをトリガするイベントコールバック。

注意
PPO1やTRPOはMPIに依存するため、s_stepは2つのイベント感の下限となる。


import gym

from stable_baselines import PPO2
from stable_baselines.common.callbacks import CheckpointCallback, EveryNTimesteps

# これは CheckpointCallback (save_freq = 500) を定義することと同等
# checkpoint_callback は 500 ステップごとにトリガされる
checkpoint_on_event = CheckpointCallback(save_freq=1, save_path='./logs/')
event_callback = EveryNTimesteps(n_steps=500, callback=checkpoint_on_event)

model = PPO2('MlpPolicy', 'Pendulum-v0', verbose=1)

model.learn(int(2e4), callback=event_callback)



レガシ:機能的アプローチ


警告
以下書かれているコールバックの実装方法は、オブジェクト指向アプローチを支持するため
廃止となった。
(以下略)


----


本読んだときのコールバック、たしかに実装がダサかった。

OpenAI Gymの環境クラスって、ビジネスアプリケーションの設計アプローチで書かれている。理由はシミュレータが業務アプリと科学技術計算のファサードだから。

そこが理解できるかできないかで、強化学習をさくっと一般業務アプリケーションへ適用できるかどうかのスピードが変わる。

 とはいえトレーニング実装は、いわゆる機械学習ライブラリ側の責務なので、無理にオブジェクト指向にシなくてもいいと思っている。..けど、実装しやすいPyTorchにTensorFlowが押されているのって、科学技術計算指向脳で設計されいたライブラリだからなので、今の風潮はこっちが正解なのかもしれない..

 

..まあ、使わせて頂く見としては、どうでもいいけど..

2020年8月11日火曜日

SONY VAIO VPCZ1 (SSD)にUbuntu 20.04 をインストールする



..簡単に読めるが、かなりてこずった...

ぶっちゃけ、1週間弱かかった


DVD/CD-ROMドライブが付属しているので
ISOイメージをDVDに焼いて刺して起動すれば
インストールできる..と思っていたのだけど

Ubuntu Server 20.04 LTS ISO イメージを使うと
インストール中にキーボードが反応せず
タイムアウトになり先に進まない。

Ubuntu Desktop 日本語Remix 20.04 ISOイメージを使うと
なぜかインストール作業中に電源オフする。


VAIO VPCZ1 のSSD構成は
ハードRAID構成で提供されている。

ので上記インストールを

  • RAID-0
  • 1パーティション遺し残りをRAID-0
  • RAIDなし(つまり複数のSCSIディスク)

でインストールを試したがどれもだめ。

で、結局やったのが
18.04LTSをインストールしてマイグレーションする方法。


  • Ctrl+IでRAIDなし構成にした状態で
  • Ubuntu Server 18.04 LTS ISOイメージで焼いたDVDをさし
  • Live Ubuntu OS を起動
  • 18.04インストールアイコンをダブルクリックしてインストール
  • 再起動
  • sudo apt update && sudo apt upgrade -y
  • 再起動
  • sudo do-release-upgrade -d
  • 再起動


これでようやく最初のディスクに20.04がインストールできた。

でも..理由がわからない..本当にRAIDだけの問題なのだろうか..

上記の問題やうまくいった手順は自分のVAIOだけなのだろうか..

他に持ってる人がいないので、調べようがない

syslogや/var/logも見たけど、よくわからなかった。

なので、上記はすべてたまたまうまくいった手順を書き留めただけなのかもしれない..


 

p.s

2020/8/18 追記:

GNOMEをインストールしたら矢印アイコンだけ表示され

それ以外一切何も出てこない..


なのでKDEをインストール(sudo apt kubuntu-desktop -y)しました..

2020年6月17日水曜日

aws-iot-device-sdk-js を使っている人へ



こんなトラップに引っかかるのは
私だけかもしれませんが..



AWS IoT Core をJavaScriptから使いたくて

(2020/6/17 本記事執筆段階の)公式ドキュメント

AWS IoT Device SDK for JavaScript をインストールする
https://docs.aws.amazon.com/ja_jp/iot/latest/developerguide/iot-device-sdk-node.html


にかかれている

git clone https://github.com/aws/aws-iot-device-sdk-js.git

を実行して JavaScript SDK をつかっていた。


実は
この記事を書く1年前ほどから
この JavaScript SDK を使っていたのだけど、

なんとなく
機能がしょぼいなあ..
とか
デバイスシャドウを使うとき
pub/subするためのデバイスのコネクションが一旦切れる
(デバイスシャドウを使い終わるとreconnectする)
なあ..
とか
なんか洗練されていないなあ
ださいなあ..


とおもいながら
使っていた。

で、
昨日重大なことに気づいた。

先程書いた GitHub サイトを
ブラウザで開くと
README.mdの先頭に


New Version Available
A new AWS IoT Device SDK is now available. It is a complete rework, built to improve reliability, performance, and security. We invite your feedback!

This SDK will no longer receive feature updates, but will receive security updates.



..


....


.....最新バージョンじゃないじゃん....


しかも
最新バージョンは
別のリポジトリだと...


公式ドキュメント(これを書いている時点)も
ふるいほうのリポジトリじゃん..

公式ドキュメントの手順だと
このサイトのREADME.mdひらかず
いきなり git clone しろって
書いてあるじゃん..




v2のリポジトリはこちら

[GitHub] aws/aws-iot-device-sdk-js-v2
https://github.com/aws/aws-iot-device-sdk-js-v2




p.s.

v2のサンプル、読みづらい。

jsってかいてるけど
TypeScriptでサンプル書かれてるし..
しかも非同期 async や Promise つかってるし..
yield とかつかってるし..
リファレンスマニュアル全然読みにくいし..


つかってもらおうっていう
おもてなしの心が
まったく感じられん...


ルンバ980の充電がなかなか終わらない件

ITではない記事を書きます。
まあ..IoTの記事..なのか..な..

今回の記事は、自分の推測がかなりはいっています。
それを踏まえて参考にしてください。


ルンバ980を使って4年になるが、
先日ファイアウォール
(此処から先は行くなと指定できる黒い直方体)の電池が切れ、
論場が自力では復活できない場所にはいってしまい、
結果バッテリのほぼすべての電力を使い果たした状態で
とまっていた。

クリーンボタンを押すと、電池マークが赤く点灯するので、
ホームベースに本体を戻して、
翌日の定期清掃開始時間まで充電させることにした。

ホームベースにもどすと
電池マークは橙色の長点滅状態になっており、
充電状態にはいったことは確認している。
#1分ほどで消灯する

そして翌日、あらかじめ指定した時刻にルンバは起動するが、
走行開始時点から
電池マークは赤点灯状態で、
しばらく走行するも
すぐにホームベースにもどってしまった。

そして1時間ほどした頃、
突然また掃除を開始するのだ。

でも電池マークは赤のまま
充電不足でホームベースへ戻る。

これの繰り返し。


これは購入後4年もたつので
バッテリがへたったのだとおもい、
某量販店でバッテリを購入した。
値段、約1万2千円


裏蓋を+バッテリーで開いて
バッテリーを交換、
ホームベースに乗せると、
電池マークが橙色の長点滅に。

このとき
なんか嫌な予感はしていたが、
購入したバッテリの充電は
できていなかったのだろう
まあ翌日の掃除までには
充電できているだろう
と思っていた。

..が
現象は交換前と全く同じ

ホームベースとルンバ本体の接点の接触が悪いのかと
掃除してもだめ。

FAQにあるように前輪をはずして
ホームベースに乗せてもだめだった。


バッテリ交換しても
ルンバが復帰しない..

これは買い替えか..
と思い始めた。


実はルンバを購入したのは
これで2度め。

前回もバッテリがヘタってしまい
交換しても動作しなかったのが
原因だったのだ。


そんなことが2度も起こるか?!


ルンバって、欠陥品じゃないの?



思い
サイトを真剣に検索し始めた。


そしてひとつの未実施の方法を見つけた。

それは

クリアボタンを長押ししてルンバをリセットしてから
ホームベースに乗せて充電する

という方法。

リセットする方法は
なんとなくしっていたが
この減少が起こったのは
バッテリが切れたためなんだから
リセットされているはず

思い込んでいた。


..が、
結局この方法を試すと
翌朝からルンバは正常動作に戻った..



考えてみると
ルンバがとまっていたにもかかわらず
クリアボタンを押すと
電池マークが赤く光った
つまり
バッテリはゼロではなく
ほとんどゼロに近かったのだ。

ルンバには
2種類の充電状態がある。

それは通常の充電、
掃除しおえたルンバがホームベースで行うやつ。

そしてもう一つの充電が
ルンバ購入時にやる初期充電。

ルンバ980は
バッテリ交換したことが判断できず、
リセットをおこなわないと
通常充電ではなく初期充電がはじまらない仕様
なんじゃないか
と。


..そうすると
バッテリ自体も
交換する必要がなかったのではないか..


リセットを実行して
初期充電をしなおしてやれば
復帰できたんじゃないか..

そして先代ルンバも...


..でも
そんな現象
アイロボット社は承知しているはず..

..なのに
そんな注意を明記していないのは..

定期的に新製品を買わせるため..





..という憶測まみれの記事を書いてみました..


p.s.

もしそうなら

さすが

外資系

えげつない商売しやがって..





いいたいが


正しいかどうかは


判断できない...

2020年5月13日水曜日

Donkey Simulator(v3.1.2/)上でlinear/categorical/rnn/3d各モデルを同時に自動走行させてみた

Donkeycar v3.1.2 がリリースされて、Donkey Simulator自体も更新されシミュレータ上で自律走行レースができるようになった。

Donkey Simulator 上のコースで学習データを作成し、各モデルタイプでトレーニングを行いモデルファイル化し、myconfig.py

DEFAULT_MODEL_TYPE = 'categorical' # 'linear', 'rnn', '3d'
DONKEY_GYM = True
DONKEY_SIM_PATH = 'remote'
SIM_HOST = "127.0.0.1" # Donkey Simulatorが動いているマシンのIPアドレス

とかいてそれぞれ python manage.py drive --model <モデルファイルへのパス> で実行すると、以下の動画のように実行することができる。





..結果は、1つのPCで4つのDonkeycarプロセス+シミュレータなのでおそいおそい..
しかも、団子状になって3台自滅して終わり..


これじゃあ評価できないので、各モデルで1周自律走行させて、動画編集して横並びにしてみた。






3dやrnnモデルは複数件データを入力データとして扱うため、初期起動がどうしても遅くなる。

3dモデル以外はほぼほかのモデルと同じラップタイムで1周したが、3dモデルは1周するまで待ってられずに止めてしまった。3dモデルの名誉のために追記しておくと、動画ではにっちもさっちもだったが、運転モードをlocal_angleにするときちんと周回することができる。

結局はthrottle値をいかに高出力にしておくかだけど、機械学習のアウトプットにたよると学習データ収集時よりも速度がどうしても落ちてしまう。

..もっと学習データをクレンジングさせたほうがいいかもしれないなあ..

2020年5月12日火曜日

AVS SDK チュートリアル startsample.sh 実行中に GStreamer-CRITICAL が発生した件

以下の情報は2020/5/12時点のものです。

AVS SDK をためそうとRaspberry Pi 4B/Raspbean buster lite上でSDKを使ったプロトタイプを実行するチュートリアルの

Authenticate your device
https://developer.amazon.com/ja-JP/docs/alexa/alexa-voice-service/get-a-refresh-token.html

のなかで sudo bash startsample.sh を動かそうとしたら

(env) pi@heavensdoor:~/projects/avs $ sudo bash startsample.sh
2020-05-12 02:30:16.675 [  1] I sdkVersion: 1.19.1
configFile /home/pi/projects/avs/build/Integration/AlexaClientSDKConfig.json
Running app with log level: DEBUG9
2020-05-12 02:30:16.921 [  1] 0 ConfigurationNode:initializeSuccess
2020-05-12 02:30:16.933 [  1] 1 InMemoryEqualizerConfiguration:validateConfiguration::Validating Equalizer configuration
2020-05-12 02:30:16.933 [  1] 1 InMemoryEqualizerConfiguration:validateConfiguration::Validating default Equalizer state
2020-05-12 02:30:16.934 [  1] 1 InMemoryEqualizerConfiguration:validateConfiguration::Validating Equalizer configuration
2020-05-12 02:30:16.934 [  1] 1 InMemoryEqualizerConfiguration:validateConfiguration::Validating default Equalizer state
2020-05-12 02:30:16.964 [  1] 9 MediaPlayer:createCalled:name=SpeakMediaPlayer
2020-05-12 02:30:17.437 [  1] E MediaPlayer:setupPipelineFailed:name=SpeakMediaPlayer,reason=createAudioSinkElementFailed,audioSinkElement=alsasink
2020-05-12 02:30:17.437 [  1] E MediaPlayer:initPlayerFailed:name=SpeakMediaPlayer,reason=setupPipelineFailed
2020-05-12 02:30:17.437 [  1] 9 MediaPlayer:~MediaPlayer:name=SpeakMediaPlayer

(略)

(SampleApp:630):
GStreamer-CRITICAL **: 03:30:17.437: gst_object_unref: assertion 'object != NULL' failed
2020-05-12 02:30:17.437 [  1] 9 MediaPlayer:resetPipeline:name=SpeakMediaPlayer
2020-05-12 02:30:17.437 [  1] E RequiresShutdown:~RequiresShutdownFailed:reason=notShutdown,name=SpeakMediaPlayer
2020-05-12 02:30:17.438 [  1] C SampleApplication:Failed to create media player for speech!
2020-05-12 02:30:17.438 [  1] C SampleApplication:Failed to initialize SampleApplication
Failed to create SampleApplication!



というエラーメッセージが出て止まってしまった。

調べてみるとGStreamerプラグインが足りていないということで、以下のコマンドを実行して関連パッケージをインストールしてから  sudo bash startsample.sh を動かしたら..うまくいった。




sudo apt install -y libgstreamer1.0-dev gstreamer1.0-plugins-base gstreamer1.0-plugins-good gstreamer1.0-plugins-bad gstreamer1.0-plugins-ugly gstreamer1.0-libav gstreamer1.0-doc gstreamer1.0-tools gstreamer1.0-x gstreamer1.0-alsa gstreamer1.0-gl gstreamer1.0-pulseaudio

ちなみに使ったマイク・スピーカは SENNHEISER USB ヘッドセット PC 8 。

マイクのミュートボタンをOFFにしてから startsample.sh を実行すると、コンソール上に以下のようなメッセージがでてくるので


2020-05-12 04:18:38.481 [  2] 5 CBLAuthDelegate:setAuthError:authError=SUCCESS
2020-05-12 04:18:38.481 [  2] 5 CBLAuthDelegate:handleRequestingToken
2020-05-12 04:18:38.481 [  2] 5 CBLAuthDelegate:requestToken
##################################
#       NOT YET AUTHORIZED       #
##################################

################################################################################################
#       To authorize, browse to: 'https://amazon.com/us/code' and enter the code: XXXX9Z       #
################################################################################################

#################################################
#       Checking for authorization (1)...       #
#################################################

2020-05-12 04:18:38.555 [  1] 9 ThreadPool:obtainWorker:created=15,obtained=15,releasedToPool=0,releasedFromPool=0,outstanding=15
:


PC などからブラウザで https://amazon.com/us/code へアクセスし developer.amazon.com にログインしたときと同じアカウントでログインした後認証コード(上記メッセージ例だと XXXX9Z )をサイトに入力すると、発話待機状態になるので、ヘッドセットマイクから「Alexa, what time is it?」と英語で発話すると、ヘッドセットスピーカに時刻が音声で(もちろん英語で)かえってくる。

なおコマンドライン上の操作は以下の通り。終了する場合は「q」を入力してEnterキーを押せば良い。

Wake word:
"alexa"と発話し、クエリを開始する

Tap to talk:
「t」を押してEnterを押してからクエリを実行します( "alexa"は必要ありません)。

Hold to talk:
「h」を押してからEnterキーを押すと、ボタンの押しをシミュレートします。
次に、クエリを言います("alexa"は必要ありません)。
「h」を押してからEnterキーを押すと、ボタンの解放をシミュレートできます。

Stop an interaction:
「s」とEnterキーを押して、進行中の対話を停止します。

Privacy mode (microphone off):
マイクのオンとオフを切り替えるには、「m」とEnterキーを押します。

Firmware Version:
「f」キーを押してからEnterキーを押すと、いつでも別のファームウェアバージョンを報告できます。

Info:
「i」を押してからEnterキーを押すと、いつでもヘルプ画面が表示されます。

Reset device:
デバイスをリセットするには、「k」キーを押してからEnterキーを押します。 これにより、デバイスに保存されているデータがすべて消去され、デバイスを再登録する必要があります。
このオプションは、アプリケーションも終了します。

Reauthorize device:
「z」キーを押してからEnterキーを押すと、いつでもデバイスを再認証できます。 これにより、デバイスに保存されているデータがすべて消去され、再認証が開始されます。

Quit:
「q」を押してからEnterキーを押すと、いつでもアプリケーションを終了できます。



デバイス開発とあるからもっとロボティクス的な低レベルのAlexaフレームワーク開発ができるのかと期待していたが..Alexaの認証系を独自にできるとかしかバリエーションがなさそうだ..もっとSDKのコードをハックすればもっとふかいところまでできるかもしれないけど..たとえばDonkeycarにマイクとスピーカをつけてコントローラ不要にするとか..


..先は長そうだ...



p.s.

AVS プロトタイプを使う場合はマイクとスピーカをRaspberry Piに接続する必要がある。しかしRaspbery Piでオーディオ出力を使用する場合は注意が必要だ。

Raspberry Piの音声出力の選択肢としてはイヤホンジャック、HDMI出力の音声、USBデバイスがある。

イヤホンジャックやHDMI音声を使う場合sudo raspi-configでの有効化設定が必要だ。

さらにRaspberry Pi本体のイヤホンジャックは音声だけでなくマイク入力にもなっているので、有効化したら音声入出力はすべてこのジャック1本でまかわなくてなならない。スピーカだけイヤホンジャック、マイクをUSBデバイスで行いたい場合の対応方法は自分は未だ見つけられれいない。

USBデバイスを使う場合、自分はマイクとスピーカで2個のUSBコネクタを消費したくないので、結局テレワーク用に購入したUSB接続するヘッドセットをつないで使用した。テストレベルではこれでよいが、独立したスマートスピーカとして成立させる場合はヘッドセットではだめになる。USBコネクタを消費せずマイク・スピーカピンジャック化するデバイスを使えばよいのだけど、Amazonで売られているこの手のデバイスはジャック自体が箱状態になっているものが多く、消費コネクタは1つでも干渉して下や横のコネクタが使えなくなってしまう。

なので、プロトタイプであろうと、物理的なデバイス選択をしっかりシミュレートしておかないと、買っては失敗し、を繰り返すことになる。



..私のように...

2020年4月23日木曜日

pygameパッケージを使って画面を表示せずにジョイスティックを使おうとしたらキーボード操作を一切受け付けなくなった件

最近Donkeycar のマイナーバージョンアップ(3.1.2)があったのだけど、更新内容の一つにDonkeycar Simulatorが挙げられている。

Donkey Simulatorは、Donkeycarアプリケーションを実際の車ではなくSimulator上の仮想車体として動かすというもの。Donkeycarアプリケーションの設定を変更して起動すると、Donkey Simulator APIを呼び出しステアリング値とスロットル値を継続的に送信、その結果をDonkey Simulator側の画面上に表示するというもの。


今回のDonkey Simulator更新で機能がついたのかはわからないけど、どうもこのSimulatorは複数のDonkeycarアプリケーションからのデータを受け付けるようになっており、同じコース上で複数の仮想Donkeycarでレースができるようになっている。

なのでAWS EC2やGCPなどのIP reachableな仮想マシンでDonkey Simulatorを起動しておき、各自の家のPC上でDonkeycarアプリケーションの設定をDonkey Simulatorが起動しているマシンのIPアドレスorFQDNを設定し起動すれば複数のDonkeycarでレースができるのだ。ただ画面を共有しないといけないのでそのあたりはYoutube Liveなどで行えばいい(UltraVNCなどのリモート接続系ソフトだとキーボードやマウスなどの操作も渡してしまうので)。

レースを行う場合、手動運転による学習データ収集を行わないといけないのだけど、WindowsPCの場合現時点ではWebアプリ経由の操作しかない。なぜならdonkeycarパッケージ上のジョイスティックパーツは/dev/input/js0fctlパッケージで排他管理しつつ読むことで実装しているので、そもそも/devのないWindows PCでは動作しない。ただコードをよく読むとWindows PCでも動作するpygameパッケージベースのジョイスティックパーツクラスもいくつか存在する。しかしジョイスティックオブジェクトを取得するget_js_controllerというファクトリ関数はこのパーツクラスに対応していない。

そこでmanage.pyのファクトリ関数部分を直接パーツクラスを生成して動かそうとしたけど..動かない。

しょうがないので、調べてみた。


pygameパッケージはもともと画面のあるゲームをPythonで実装するために便利な機能を提供する統合ゲームアプリフレームワークだ。Window画面を表示したり描画する機能やマウス操作やキーボード入力を管理する機能などを提供してくれる。その中のひとつにジョイスティック操作をPythonプログラム側で取得できるようにするサブモジュールpygame.joystickが存在する。

donkeycarパッケージのdonkeycar.parts.controllerモジュールにあるpygame用ジョイスティックパーツはこのサブモジュールを使用している。..が、pygame自体の初期化処理が実装されていない..

pygameパッケージを使用するにはまず
インポート

import pygame

pyagmeモジュールの初期化

pygame.init()

pygame.joystickサブモジュールの初期化

pygame.joystick.init()

そしてWindows PCに接続されているジョイスティックをあらわすオブジェクトの生成

myjoystick = pygame.joystick.Joystick(0)

を行わなければならないのだけど、ソースコードに pygame.init() 処理が書かれていなかった。


で、pygame.init() を追加したのだけど..今度はなぜかジョイスティックオブジェクトから操作情報を取り出そうとしても全部ゼロ..

これも調べてみると、pygame.eventサブモジュールを使って直近に発生するイベント群をとりださないと操作情報を取得できない仕組みになっていた。

events = pygame.event.get()

を加えてみると、たしかにジョイスティック操作したデータを取得できるようになった。
しかし今度はDonkeycarアプリケーションが終了できなくなった。Donkeycarアプリケーションはコマンドライン実行内で無限ループ処理しているので、Ctrl+Cを押して停止させなくてはならない。でもこのCtrl+Cを何度入力しても...全く止まる気配がない...

ということでまたもや調べてみた。

どうも pygame.init() を実行するとpygameサブモジュールのうち使用可能なすべてが初期化される。初期化対象には画面系のpygame.screenサブモジュールも含まれている。Window画面を使ったゲームの操作は、キーボードやジョイスティック操作をベースにすることが多い。このため pygame.init() を実行した以降は、すべてのキーボード入力がフックされ pygame.event管理下のイベント化されてしまうのだ。いくらKeyboardInterrupt 例外を出しても pygame.event が奪い取ってしまうように設計されているのだ。

つまり Ctrl+C 押下した場合は pygame を終了( pygame.quit() )させてからKeyboardInterrupt例外を自分でraiseするように実装してやらなくてはならない。

for events in pygame.event.get():
    if event.type == pygame.QUIT:
        pygame.quit()
        import time
        time.sleep(2)
        raise('catch pygame.QUIT event')

これでようやくCtrl+Cで脱出できるジョイスティックパーツを作成することができた。

以下作成した pygame ベースのLogicool Wireless Gamepad F710 donkeycarパーツクラスのリポジトリリンクである。

GitHubリポジトリ
https://github.com/coolerking/mysim_joystick

動作サンプル動画


pygame パッケージがゲームアプリケーション用統合フレームワークであることを知らないと、おそらくリファレンスドキュメントだけでは見抜けない人、少なくないのではなかろうか..

製作者・設計者の想定した用途に合致しない使い方、たとえば

  • ドキュメント共有のために開発されたWebシステムでアプリを組む
  • Webアプリで帳票を出す


などのようなことをすると、
痛いしっぺ返しが来ることは身にしみてわかっていたことなのに、
今回は気づくのに時間がかかってしまった...

ご参考まで。

2020年2月27日木曜日

Raspberry Pi 4B(Raspbean buster lite) へ pyrealsense2 をインストールする

DonkeycarでIntel RealSense T265 トラッキングカメラを使おうと思ったのだけど、ドキュメントにUSB3.0推奨とある記述をみつけだ。

どうもUSB2.0でも動作するのだけど、イメージストリームを使う場合などは3.0のほうが良いのだろう。白黒の800X848イメージ2個を200MHzで送信するくらい2.0でもできそうだとは思うのだけど..

で Donkeycar のベースコンピュータは Raspberry Pi 3B+ を使うのが一般的なのだけど、librealsenseのGitHub Issueに4Bサポートしましたとの記述も発見した。

ただ..4BはRaspbean Liteイメージをstreachではなくbusterを使うように書かれている。ということで、busterでlibrealsenseとpython3ベースのpyrealsense2(librealsenseのpythonラッパ、Donkeycar上のT265用パーツクラスが使用している)をインストールしてみた。

以下、その手順(参照の際はAT YOUR OWN RISKでお願いします)。



  • 最新版のRaspbean Lite buster をSDカードへ書き込む
  • SDカードに対してSSH有効・WiFiを設定し、Raspberry Pi4Bへ刺して起動
  • sudo vi /etc/dphys-swapfile を実行しCONF_SWAPSIZE=2048と書き換え保存
  • sudo /etc/init.d/dphys-swapfile restart swapon -s
  • sudo raspi-config を実行
  • "7.Advanced Options" > "A7 GL Driver" > "Y" > "G2 GL (Fake KMS)" を選択
  • "7.Advanced Options" > "A1 Expand Filesystem"
  • 終了時rebootを選択し、リブート実行
  • sudo apt update && sudo apt upgrade -y
  • sudo apt install -y libdrm-amdgpu1 libdrm-dev libdrm-exynos1 libdrm-freedreno1 libdrm-nouveau2 libdrm-omap1 libdrm-radeon1 libdrm-tegra0 libdrm2 libglu1-mesa libglu1-mesa-dev glusterfs-common libglu1-mesa libglu1-mesa-dev libglui-dev libglui2c2 mesa-utils mesa-utils-extra xorg-dev libgtk-3-dev libusb-1.0-0-dev cmake libprotobuf-dev libtbb-dev build-essential i2c-tools avahi-utils joystick libopenjp2-7-dev libtiff5-dev gfortran libatlas-base-dev libopenblas-dev libhdf5-serial-dev git ntp libilmbase-dev libopenexr-dev libgstreamer1.0-dev libjasper-dev libwebp-dev libatlas-base-dev libavcodec-dev libavformat-dev libswscale-dev libqtgui4 libqt4-test
  • (python3 を使う場合) sudo apt install -y python3 python3-dev python3-opencv python3-pip python3-protobuf python3-opengl python3-virtualenv python3-numpy python3-picamera python3-pandas python3-rpi.gpio
  • sudo -H pip3 install pyopengl
  • sudo -H pip3 install pyopengl_accelerate
  • cd ~/ && mkdir -p projects && cd projects
  • git clone https://github.com/IntelRealSense/librealsense.git
  • cd librealsense
  • sudo cp config/99-realsense-libusb.rules /etc/udev/rules.d/
  • sudo udevadm control --reload-rules && sudo udevadm trigger
  • sudo reboot
  • vi ~/.bashrc を実行し以下の2行を末尾に追加、保存する
  • export LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH
  • export PYTHONPATH=$PYTHONPATH:/usr/local/lib
  • source ~/.bashrc
  • cd ~/projects/librealsense
  • mkdir build && cd build
  • (python3を使う場合) cmake .. -DBUILD_EXAMPLES=bool:true -DCMAKE_BUILD_TYPE=Release -DFORCE_LIBUVC=bool:true -DBUILD_PYTHON_BINDINGS=bool:true -DPYTHON_EXECUTABLE=$(which python3)
  • make -j4
  • sudo make install
  • sudo ldconfig


librealsense2 の Radspberry Pi 3へのインストールドキュメントをそのまま実行するとインストールできない。(執筆時点で)以下の点に注意が必要である。

  • busterパッケージリポジトリにはデバッガオプション付きライブラリ(末尾-dbg)がない
  • udevadm triggersudo をつけて実行(リブートすれば大丈夫?)
  • cmake、TBB、protobuf、OpenCV はbusterパッケージリポジトリのものを使っても動作する
  • make は時間がかかるので一度にpythonラッパpyrealsense2のインストールまで一気に行う

あと私の場合は make -j4 でも大丈夫だった。落ちた場合は、make cleanしてやりなおしになる。3B+よりは早いが時間がむちゃくちゃかかる



次にT265の動作確認をするのだけど、realsense-viewerが使えない(liteを使ってるのでそもそもXサーバが別途必要になる、あればDISPLAY環境変数を設定すれば動くはず)。なので、pythonのサンプルコード t265_example.py で行う。


  • lsusb ※接続前の状態を確認(接続後との比較用)
  • dmesg |grep -i usb ※接続前の状態を確認(接続後との比較用)
  • Raspberry Pi 4B の青い USB3.0 コネクタにT265を接続し、電源を接続して起動
  • lsusb
※私の環境の場合、接続後以下のメッセージが追加
Bus 001 Device 003: ID 03e7:21XX Intel Myriad VPU [Movidius Neural Compute Stick]
  • dmesg |grep -i usb
※私の環境の場合、接続後以下のメッセージが追加
[  133.058275] usb 1-1.2: new high-speed USB device number 3 using xhci_hcd
[  133.189229] usb 1-1.2: New USB device found, idVendor=03e7, idProduct=21XXX, bcdDevice= 0.01
[  133.189245] usb 1-1.2: New USB device strings: Mfr=1, Product=2, SerialNumber=3
[  133.189258] usb 1-1.2: Product: Movidius MA2X5X
[  133.189271] usb 1-1.2: Manufacturer: Movidius Ltd.
[  133.189283] usb 1-1.2: SerialNumber: 03e72XXX
  • cd ~/projects/librealsense/wrapper/python/example
  • python3 t265_example.py を実行しエラーがないことを確認


上記手順では/usr/bin/python3を使用している。Donkeycar用python3のvirtualenv環境は上記手順の後に作成すれば、再度env環境上でpyrealsense2をインストールしなくても使用可能になる。donkeycar及び関連パッケージとTensorFlowはenv環境下でインストールされるので、deactivateすればインストール前の状態の環境に戻ることができる。


  • cd ~/
  • python3 -m virtualenv -p python3 env --system-site-packages
  • echo "source ~/env/bin/activate" >> ~/.bashrc
  • source ~/.bashrc
  • cd ~/projects
  • git clone https://github.com/autorope/donkeycar
  • cd donkeycar
  • git checkout master
  • git pull
  • pip install -e .[pi]
  • pip install tensorflow==1.14.0
  • sudo shutdown -h now

この記事執筆時点ではまだPyPIに登録されていなかった。ので上記手順ではlibrealsenseからソースコードビルドしている。
PyPI登録されているかもしれないので pip3 search pyrealsense2 して確認のこと。



ちなみに4BをUSBハブ経由で動かすと電力不足のメッセージが頻出した。PC直付けにしたらエラーが無くなったので、4Bって結構電源にシビアなのかもしれない..

..これまでRaspberry Pi3B+環境でつかっていたモバイルバッテリが、4B&T265の電力消費に対応できるか..だけど、これは実際に繋いで試してみるしか無いか..


2020年2月17日月曜日

Jetson Nano 上で RealSense T265 用 Donkeycar パーツを試す

Donekycar (v3.1.1マスタブランチ)コードには
RealSense T265用パーツクラス
donkeycar.parts.realsense2.pyRS_T265という名前のクラス)
がすでに用意されている。

realsense2.py は、pythonパッケージ pyrealsense2 を先に導入しておかなくてはならない。

通常のUbuntuなら pip install pyrealsense2 でインストールするのだが、Jetson Nano用のJetpack (v43)アーキテクチャ用whlモジュールがPyPIに登録されていないので、シェアードライブラリ含めてソースコードビルドを選択せざるを得ない。

Donkeycar環境を構築済みのJetson Nanoへ

GitHub: IntelRealSense/librealsense
https://github.com/IntelRealSense/librealsense

上記のリポジトリからソースコードビルドした。
#ビルド方法の概要は前記事のps以下を参照のこと

python -c "import pyrealsense2" を実行してエラーが出なければ、python ラッパパッケージ pyrealsense2 のインストールが成功しているかがわかる。

また、librealsense/wrapper/python/examplesにある t265_example.py を実行(JetsonにUSBケーブル経由でT265をつないで起動し、python t265_example.pyを実行)することで、実際に値が取れているかどうか疎通確認テストができる。

以下のコードは、Veichleフレームワークを使ってRS_T265パーツを動かすサンプルである。

    import donkeycar as dk
    V = dk.vehicle.Vehicle()

    from donkeycar.parts.realsense2 import RS_T265
    rs = RS_T265(image_output=False)
    V.add(rs, outputs=['pos', 'vel', 'acc', 'image_array'], threaded=False)
    class PrintIMU:
        def run(self, pos, vel, acc):
            print('pos:({:.5g}, {:.5g}, {:.5g}) vel:({:.5g}, {:.5g}, {:.5g}) acc:({:.5g}, {:.5g}, {:.5g})'.format(
                pos.x, pos.y, pos.z, vel.x, vel.y, vel.z, acc.x, acc.y, acc.z))
    V.add(PrintIMU(), inputs=['pos', 'vel', 'acc'])

    V.start(rate_hz=20, max_loop_count=20 * 60)


RS_T265run すると6軸IMUデータ+位置座標(位置座標(X, Y, Z)、速度(X, Y, Z)、加速度(X, Y, Z))とイメージが取得できる。

ただし、RS_T265クラスのコンストラクタ引数に何も指定しない場合(デフォルトの場合)は、イメージ配列が出力されない(Noneで出力される)。

T265カメラの画像のイメージ配列を取得したい場合は、RS_T265(image_output=True)と指定するとnd.array形式の配列として出力される。

ただし、2つあるカメラの片方(多分..左側かな..)のみでかつnd.array形式のshape(800, 848)となる(白黒画像なので2次元配列)。

Donkeycar のカメラの代替として使用する場合は、myconfig.pyIMAGE_HIMAGE_WIMAGE_DEPTHを書き換えるか、イメージ配列を加工して(120, 160, 3)reshapeするパーツクラスを作成すれば良い。


なお pyrealsense2 パッケージを直接使うと、四元数(qw, qx, qy, qz)が取得できる。
詳細は librealsense/wrapper/python/examples/t265_rpy.py を参照のこと。


RS_T265クラスのコンストラクタのデフォルト引数がイメージを使用しない仕様から、おそらくカメラは既存のデバイスを使って、IMU(MPU6050)がわりにT265を使い、機械学習モデル'imu'で動かすことを想定して作成されたものと推測できる。

それならMPU6050のほうが安上がりだ。位置情報もSLAMを使うにはDonkeycarの機械学習モデル自体の入力層をいじらなくてはならないし..


..やはりROSで使うのが、一般的なのかな..
シェアードライブラリのみ(Pythonラッパなし)や
ROS上で動作する T265モジュール(melodic)も
apt で入れられるし..

2020年2月12日水曜日

Jetson Nano 上で Realsense T265 を動かす


Intel Realsense トラッキングカメラ T265 をJetson Nanoにつないで使いたい。
Donkeycarで使用するには、Pythonライブラリをインストールする前に、シェアードライブラリをインストールシなくてはならない。なのでとりあえずシェアードライブラリとX上で動作する realsense-viewer を動してみた。



1. Jetson Nano のセットアップ


Getting Started With Jetson Nano Development KitをもとにJetpackをセットアップ(詳細略)。


2. librealsense 及びビューアなどのインストール


Jetson Nanoを起動し、ターミナルを開いたら、以下のコマンドを実行する。

sudo apt update && sudo apt upgrade -y
sudo apt-key adv --keyserver keys.gnupg.net --recv-key F6E65AC044F831AC80A06380C8B3A55A6F3EFCDE || sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-key F6E65AC044F831AC80A06380C8B3A55A6F3EFCDE
sudo add-apt-repository "deb http://realsense-hw-public.s3.amazonaws.com/Debian/apt-repo bionic main" -u
sudo apt install -y librealsense2-dev librealsense2-utils  librealsense2-dbg

上記の手順でうまく行かない場合は、情報が更新されているかもしれません。

[GitHub] libsense Linux Distribusionに最新情報が掲載されるので、こちらを参照してください

3. ビューアの実行


  1. Jetson Nanoをシャットダウンする
  2. USBケーブルでRealsense T265 を接続する
  3. Jetson Nanoを起動する
  4. Xがあがったら、realsense-viewer を実行する


左やや上にある赤いトグルボタンを押し緑色にすると、画像が表示される
右上の2Dリンクを押すと、左右カメラ画像、位置、速度、加速度情報が表示される

 



右上の3Dリンクを押すと、動かしたカメラの軌跡を緑線で表示される



  • Ctrl+C で終了



ps 2020/02/12 追記

..とまあ、ここまではよかったのだけど..

Jetson Nano でPython ライブラリ pyrealsense2 を使用する場合は、pip install pyrealsense2 ではインストールできない。

このため、librealsenseをソースコードからコンパイルしていかないと駄目だ。

ということで

  1. cd ~/
  2. mkdir projects
  3. git clone https://github.com/IntelRealSense/librealsense.git
  4. cd libreamsense
  5. mkdir build
  6. cd build
  7. cmake ../ -DPYTHON_EXECUTABLE=~/env/bin/python -DBUILD_PYTHON_BINDINGS:bool=true
  8. make -j4
  9. sudo make install
  10. vi ~/.bashrc を実行して export PYTHONPATH=$PYTHONPATH:/usr/local/lib を最終行に追加
  11. source ~/.bashrc

を実行しpython 上で import pyrealsense2 がエラーなく動けばOKである。
注意:PYTHON実行バイナリのパスは環境に合わせて変更すること

OpenAI Gym 環境クラスを独自実装してみる

強化学習を行う場合、Stable BaselinesやTensorFlowなどのAPIではOpenAI Gym準拠の環境クラスを使用することができる。 ので、個別案件に対応する独自環境クラスを作りたい.. ということで実際に作ってみた。 独自環境クラスを実装するには、まずOpen...