Translate

2021年1月27日水曜日

Donkeycar V4.1.0 のデータセット構成を調べる

 Donkeycar の github 上の master ブランチが 4.1.0 になった。


V4はでていたが、masterブランチは3.1.xのままだったので、V4についてはとくに調べることをせずに放置していたのだが..ひさしぶりにインストールしようとしたら..エラー乱発..それでmasterブランチがV4にマージされたことを知った。

Discordにもはいっていたが、やたらPCから音が出るのでアンインストールしていたので、アナウンスが多分あったのだとは思うが、知らなかった。

 V4で何が変わったかというと、一番がTubデータの構造だ。

V3までのTubデータはトップディレクトリdataの下にtub_<1からの連番>_YY-MM-DDというサブディレクトリが運転を実行するたびに作成されこの下に1レコードにつきJSONファイル1つとJPEGファイルが1つ作成されるという構成だった。

V3の構造で最も問題になっていたのが、トレーニング処理がレコード件数に比例して時間がかかるというもの。

1レコード2ファイルのI/O処理がはいるこの構造は、正直機械学習向きではない。とはいえDonkeycarの機械学習モデルはせいぜいが5層で深層学習と言えるほど深くない。ためしていないが数理最適化アルゴリズムのほうがよいかもな..とも思えるくらいの小規模モデルだ。なのでトレーニング時間はそれほど長くはないので、これでも問題なく利用されていた。

また、手動走行中にミスが有りデータを削除する場合、連番に飛びが発生するが、そのままではトレーニング処理でエラーが出てしまうという問題もあった。どうしてもクラッシュしたデータレコードを削除したくなると、歯抜けのTubデータになってしまい、連番をつけなおすスクリプトを作って対処していた。

どうもこれらの問題を解決するためにV4ではdatastore自体を見直したようだ。

で、ソースコードをよんだり、実際にデータを出力させてみた結果、以下の構造をしていることがわかってきた。


 

V3でJSONファイルになっていた数値データは、Catalogファイルに格納される。Catalogファイルは、デフォルト1000レコードごとに切り出されており、Catalogファイルとしての連番がつくようになっている(1000件未満の場合は1ファイルになる)。

 Catalogファイルが複数になる場合があるため、各Catalogファイルに対応したマニフェストファイルが作られている。この中にはCatalogファイルの先頭レコードがレコード連番の南蛮から始まるかや、1行のバイト長リストなどがはいっている。いわばCatalogファイルごとのメタ情報が入っているファイルだ。バイト帳リストは、コード内でランダムアクセス操作をするために使われている(詳細はSeekableクラスを参照してもらえばわかる)。 Catalogマニフェストファイルといえばいいのかな。

そして複数のCatalogファイルをまたいだメタ情報をまとめたのがmanifest.jsonというファイル。ここに入力データのラベルリストや型リストが入っている。また削除対象のインデックス番号リストを格納できるようになっており、ここに削除したいレコードのインデックスをリスト形式で書いておけば削除したものとみなして処理してくれる仕様だ。

 上記のファイルのI/O操作はdatastore_v2.pyにかかれている。Seekableクラスもここに格納されている。

 V3のデータをコンバートするツールも提供されているので、これまでの学習データが無駄になることはないが、ひと手間確実に面倒になることに注意。

 また、Donkeycar上で、python manage.py drive を複数回実行すると、V3の頃はdataの下にサブディレクトリができるが、V4ではインデックス番号はつづきからふられるため、分離できないことも注意が必要だ。dataディレクトリを削除してしまうか移動させておかないと各レコードの_created_atで調整しなくてはならなくなる。

 

 

p.s.

運転する際は、Tubデータを書き込むだけなので、ここまでの内容を理解していれば問題はない。

しかしもし機械学習モデルの入力データ項目にIMU情報はその他の情報をくわえたり、出力データ項目を増やしてモータ数を増やそうとしている場合は、機械学習モデルにデータセットとして渡す処理donkey.parts.pipelineパッケージ以下のコードを理解する必要がある。


2021年1月26日火曜日

Donkeycar の Mobile App を試す

Donkeycarの公式ドキュメントに Mobile App というセクションが増えていた。

Donkeycar: Mobile App
http://docs.donkeycar.com/guide/mobile_app/


OSSであるDonkeycarをそのまま使う場合、PCやRaspberry Pi上でpythonをコマンドラインで動かす必要があったが、このスマホアプリとDonkeycarのOSイメージを使うとそれが不要になるらしい。


ということで試してみた。

必要なものを準備

使用前提は、Raspberry Piに接続するモータドライバの種類がPCA9685もしくはRoboHAT MM1 のどちらかであることと、搭載しているマイクロコンピュータがRaspberry Pi4Bのみということだったので、昔Robocar Storeで購入した標準仕様のDonkeycarのRaspberry Pi3B+を4Bに取り替えて使った。

4Bのメモリサイズは書かれていなかった。2GBのものを使ったが今の所動作している。


他に必要なものはインターネットに接続できるWiFiルータ、SDカード書き込み可能なPC、それにAndroidもしくはAppleのスマートフォン。
F710 Wireless Gamepad も動作するが、なくてもアプリ上から操作が可能になっている。

RoboHAT MM1を使うと電源の電圧状況がアプリからチェックできるらしいが、持っていないので試していない。Raspberry Pi上に乗せるタイプのHAT基板なので初心者はこっちのほうがピンコネクタの接続に迷わなくてすむ。香港のRobocar Storeでもかえるが、スイッチサイエンスでもとりあつかっているようだ。


スイッチサイエンス:Robo HAT MM1
https://www.switch-science.com/catalog/6381/?gclid=CjwKCAiA9bmABhBbEiwASb35V_3CvCwUEIQk95rCpQly56jUx6-lM61jeg6__svzRFkDHcZJ3PwxZBoCvAoQAvD_BwE

あと余談だが、どうも自分の持っているバッテリがへたったのだとおもうが、4BになってしまったのでラジコンバッテリでのRaspberry Piの起動ができなくなっていた。のでスマホ用モバイルバッテリをくくりつけてそちらから給電させて動かした。

 


 

PCを使ってイメージをSDカードへ書き込みDonkeycarへさす(以降PC不要)


イメージをダウンロードしてきてEtcherなどでフォーマット済みのSDカード(自分は32GBを使った)へ書き込む。
wpa_supplicant.confの書き換えとかは不要だ。

なおF710を使いたい場合は、起動前にドングルを指しておくこと。

Donkeycarを起動して2分ほど待つ


初期セットアップがかかるのだとおもうが、まつようにドキュメントに書かれていた。Raspberry Piのランプが安定したら、スマホのWiFi設定画面で接続先として「donkey-<MACアドレス後半>」という名前が出てきたら準備完了だ。

スマホにRobocar Controllerをインストール


セットアップ中にApple Storeで「Robocar Controller」で検索すると、コミュニティロゴであるロバマークのついたアプリがあるのでこれをインストールする。

スマホのWiFi設定をSSID 「donkey-<MACアドレス後半>」 に変更


Donkeycarが起動すると本体がWiFiルータの接続ポイントのようにアクセスポイントが見えるようになる。初回の場合は、まずこれにつないでDonkeycarの設定を色々変更することになる。

アプリで初期設定をおこない反映させる


Robocar Controllerを起動すると初期設定画面がでてくるので、国をJapanに、コントローラタイプをPCA9685(もしくはMM1)に、ホスト名を適当に、WiFi設定をインターネットへ接続可能なSSIDに変更する。走行会の場合はホスト名とパスワードはかならず設定しておくこと。

ちなみにここでSSIDを変更しないでおくと、トレーニング処理を実行できない。ただ、手動運転までは可能なので、外につなぎたくないとか手動運転だけで遊びたい人はやらなくても良い(ただし、スマホはDonkeycar側につなぐので使用中はインターネット接続できなくなる)

 

アプリから一旦ぬけ、スマホのWiFi設定をDonkeycarに指定したSSIDと同じアクセスポイントに変更。

反映させると、ぐるぐる回る画面が永遠に続いてしまうので、アプリを早々にとじ、WiFi設定変更画面でSSIDを変更して、Donkeycarと同じセグメントのLANにつないでおく。




アプリを再度起動、先ほど指定したホスト名を選択


出てこない場合は何度かRefreshする。星をつけていても、ここで認識していないと接続することはできない。星は走行会でたくさんのDonkeycarが動いているとき自分の車かどうかを判別させるためだとおもう。

キャリブレーションを実行


標準Donkeycarの車体は意外とずれているので最初にしておく。ステアリングがフリー状態で中央にきていること、フルで右左にした時の状態はとくに綿密に設定する。
ステアリングも最大スピードがだせるかを確認する。走行会ではない場合はそれなりの速度しか出ないように設定してもいい。

よさそうなら保存する。

ドライブを選択し手動走行を実行


F710で操作したい場合は「Physical Joystick」を、スマホの場合は「Virtual Joystick」を選択する。

F710の場合は、前方カメラ画像は見ることができないが操作しやすい。ただ、走行会だと混線するのでVirtualを選択せざるを得ないかもしれない。

右上の白丸を押すとTubデータのレコーディングが開始される。ある程度慣れたら、レコーディングを開始して、学習データを収集する。



 

終了は左端のバツを押す。すると取得したデータの統計情報が表示される。データ自体はDonkeycar本体に保存される。削除したい場合はアプリから実行できる。


 

 1万件くらいで十分なのでとりあえず頑張って取得する。

トレーニングを実行


トレーニングアイコンを押すと何も出てこないので+を押してジョブを追加する。トレーニングジョブは、1日1車両5回までしかできない。Tubデータは複数選択可能だが、100MBまでという制約があるので注意。またトレーニング処理は最大15分までで強制終了になるため、そのあたりは気をつけて使用する。

15分ほど放置し、再度アプリに接続しトレーニングが終了していることを確認する。

Donkeycarを走行位置において、自動運転開始


トレーニングが完了したジョブを選択すると、AutoPilotが選択できるようになっているはずなので、これを押すと自動運転が開始される。

----


PCは最初のSDカードを作成するときだけしか使用しない。トレーニングはアプリ側が用意したAWSインスタンスを使用する。つまり無料だ(いまのところ?)。太っ腹ではあるが、複数同時にジョブキックされたらどうなるかとかは試していない。おそらく失敗になって再度キックしないとだめなんじゃないかとおもう。

4B前提ということなので、今から始める人は Robocar Store の Donkeycar S1 というモデルを選択することになるとおもう。こちらも試していないので、わからないが、おそらくMM1が搭載されているのではないだろうか。


たぶんこれを作ったのは、Pythonを知らなくてもDonkeycarを扱えるようにしたかったのだとおもう。独自のDonkeycarを作っている人はモータコントローラとRaspberryPiの種類に気をつければだいじょうぶだとおもう。


2020年12月15日火曜日

pigpiod を systemctl で起動しようとしたら 8888 番ポートがすでに使われていて動作しないと怒られた件

注意:2021/1/13追記までできれば読んでください。

 

久しぶりに Raspberry Pi3B+をセットアップする要件があり、12月2日版busterを入れ、pigpioを以下の手順でインストールした。

sudo apt update && sudo apt upgrade -y
sudo apt install pigpio pigpiod -y
sudo systemctl enable pigpiod
sudo systemctl start pigpiod



そしてsudo systemctl status pigpiod してみると..

20.12.09 10:30:11 (+0800)  pigpiod  2020-12-09 10:30:11 initInitialise: bind to port 8888 failed (Address already in use)
20.12.09 10:30:11 (+0800)  pigpiod  Can't initialise pigpio library


なるエラーが出てデーモンが起動しない..

昔はこの手順で動いたと思ったのだけど..

いろいろしらべたら、手動でsudo pigpiodしたら動作したので、/lib/systemd/system/pigpiod.serviceの以下の赤字部分を削除してみた

[Unit]
Description=Daemon required to control GPIO pins via pigpio
[Service]
ExecStart=/usr/bin/pigpiod -l
ExecStop=/bin/systemctl kill pigpiod
Type=forking
[Install]
WantedBy=multi-user.target


sudo rebootして再度sudo systemctl status pigpiodしたら..動作していた。

以前のpigpiod.service がどうなっていたかは確認していないのだけど-lオプションってremote socket をdisableにする設定だからポート番号8888のlistenでエラーになるのはおかしいと思うんだけどなあ..ドキュメントが間違ってるのかもしれないなあ..

pigpiod ..なにかあったのかな..

 

2021/1/13 追記:

sudo raspi-config して「3 Interface Options」>「P8 Remote GPIO」というメニューが新設されていて、これを先にenableするとsudo apt install pigpio -y && sudo systemctl start pigpiod で Active になった。

おそらく、このオプションを有効にしないとpigpiodがsystemctl で起動できなくなったようだ。

IoTデバイスのセキュリティポリシー厳格化の風潮に合わせたアップデートなのだとおもう。

想像だけど。

2020年12月2日水曜日

Stable Baselines の使いやすいインターフェイスを使ったままで、独自の方策ネットワークを定義する

 Stable Baselines の使いやすいインターフェイスを使ったままで、独自の方策ネットワークを定義することも可能です。

以下、Stable Baselinesドキュメントの該当箇所の翻訳です。

-----

本ドキュメントは、Stable Baselines3 ドキュメントの Custom Policy Network を翻訳したものです。

カスタム方策ネットワーク


Stable Baselines3 は、画像(CnnPolicy)及びその他タイプの入力機能(MlpPolicy)の方策ネットワークを提供します。
 

警告
A2CおよびPPOの場合、トレーニングやテスト中に(範囲外のエラーを回避するため)継続的な行動がクリップされます。SAC、DDPG、TD3は、`tanh()` 境界をより正確に処理する変換を使って、行動を押しつぶします。

カスタム方策アーキテクチャ


カスタム方策アーキテクチャをカスタマイズするひとつの方法として、モデルを生成する際の引数に policy_kwargs パラメータを渡すことです。


import gym
import torch as th

from stable_baselines3 import PPO

# 活性化関数にRelu を使う、サイズが32である2つのレイヤを備えたカスタムMLP(多層パーセプトロン)Policy
policy_kwargs = dict(activation_fn=th.nn.ReLU, net_arch=[32, 32])
# エージェントの生成
model = PPO("MlpPolicy", "CartPole-v1", policy_kwargs=policy_kwargs, verbose=1)
# 環境の取得
env = model.get_env()
# エージェントのトレーニング
model.learn(total_timesteps=100000)
# エージェントの保存
model.save("ppo-cartpole")

del model
# policy_kwargs も自動的にロードされる
model = PPO.load("ppo-cartpole")


方策(もしくは価値)ネットワークのカスタムアーキテクチャも簡単に定義することができます。
 

注意
カスタム方策クラスを定義することと、引数として policy_kwargs を渡すことは同じ意味をさしていますが、ポリシーに名前をつけることができるため、コード上では意図が明確に示しやすくなります。policy_kwargs は、ハイパーパラメータ検索を行う場合には便利です。

カスタム特徴抽出機(Feature Extractor)


(画像を使用する際のカスタムCNNなど)カスタム特徴抽出器が必要な場合は、 BaseFeatureExtractor を継承したクラスを定義して、トレーニング処理にてモデルを生成する差異に引数として渡します。


import gym
import torch as th
import torch.nn as nn

from stable_baselines3 import PPO
from stable_baselines3.common.torch_layers import BaseFeaturesExtractor


class CustomCNN(BaseFeaturesExtractor):
    """
    :param observation_space: (gym.Space) 観測空間
    :param features_dim: (int) 抽出される特徴量の次元(最後のレイヤのユニット数に相当)
    """

    def __init__(self, observation_space: gym.spaces.Box, features_dim: int = 256):
        super(CustomCNN, self).__init__(observation_space, features_dim)
        # (チャネルが最初にくる) C×H×W イメージを想定
        # 並べ直しは事前処理もしくはラッパクラスにより実行されることを想定
        n_input_channels = observation_space.shape[0]
        self.cnn = nn.Sequential(
            nn.Conv2d(n_input_channels, 32, kernel_size=8, stride=4, padding=0),
            nn.ReLU(),
            nn.Conv2d(32, 64, kernel_size=4, stride=2, padding=0),
            nn.ReLU(),
            nn.Flatten(),
        )

        # 観測空間のサンプルを1件取得して形式を算出
        with th.no_grad():
            n_flatten = self.cnn(
                th.as_tensor(observation_space.sample()[None]).float()
            ).shape[1]

        self.linear = nn.Sequential(nn.Linear(n_flatten, features_dim), nn.ReLU())

    def forward(self, observations: th.Tensor) -> th.Tensor:
        return self.linear(self.cnn(observations))

policy_kwargs = dict(
    features_extractor_class=CustomCNN,
    features_extractor_kwargs=dict(features_dim=128),
)
model = PPO("CnnPolicy", "BreakoutNoFrameskip-v4", policy_kwargs=policy_kwargs, verbose=1)
model.learn(1000)

 

オンポリシーアルゴリズム

共有ネットワーク


A2CやPPOの方策パラメータ `net_arch` を使用すると、非表示レイヤ数やサイズ、方策ネットワークと価値ネットワーク間で共有されるレイヤ数を指定することができます。これは次のようなリストであると想定されています。
 

  1. 0を含む任意の共有レイヤのユニット数を指定する任意の長さをあらわす整数(0を含む)。intの数が0の場合、共有レイヤはありません。
  2. 価値ネットワークや方策ネットワークの非共有レイヤを指定するための辞書(オプション)。dict(vf=[<価値ネットワークレイヤサイズ>], pi=[<方策ネットワークレイヤサイズ>]) のような形式。pi や vf のいずれかのキーが欠落している場合、非共有レイヤ(空のリスト)は想定されません。


要約すると、 [<共有レイヤ数>, dict(vf=[<非共有価値ネットワークレイヤ数>], pi=[<非共有方策ネットワークレイヤ数>])] という形式のリストを指します。
 

例


サイズが128である2層の共有レイヤの場合:net_arch=[128, 128]


    観測
     |
   <128>
     |
   <128>
   /   \
行動    価値


方策ネットワークより価値ネットワークのほうが層が厚く、第1層を共有する場合:net_arch=[128, dict(vf=[256, 256])]

    観測
     |
   <128>
   /   \
行動  <256>
        |
      <256>
        |
       価値


第1層を共有し、第2層目から分岐する場合:net_arch=[128, dict(vf=[256], pi=[16])]

    観測
     |
   <128>
   /   \
 <16> <256>
  |     |
行動   価値

 

高度な例


方策/価値アーキテクチャをさらに細かく制御する必要がある場合は、方策を直接再定義することができます。


from typing import Callable, Dict, List, Optional, Tuple, Type, Union

import gym
import torch as th
from torch import nn

from stable_baselines3 import PPO
from stable_baselines3.common.policies import ActorCriticPolicy


class CustomNetwork(nn.Module):
    """
    方策および価値関数のカスタムネットワークをあらわすクラス。
    特徴抽出器(Feature Extractor)によって抽出された特徴量を入力として受け取る。

    :param feature_dim: feature_extractor で抽出された特徴量の次元(例:CNNにより抽出された特徴量)
    :param last_layer_dim_pi: 方策ネットワーク最終層のユニット数(int)
    :param last_layer_dim_vf: 価値ネットワーク最終層のユニット数(int)
    """

    def __init__(
        self,
        feature_dim: int,
        last_layer_dim_pi: int = 64,
        last_layer_dim_vf: int = 64,
    ):
        super(CustomNetwork, self).__init__()

        # 重要:
        # 確率分布の作成に使用される出力層次元数を保存
        self.latent_dim_pi = last_layer_dim_pi
        self.latent_dim_vf = last_layer_dim_vf

        # 方策ネットワーク
        self.policy_net = nn.Sequential(
            nn.Linear(feature_dim, last_layer_dim_pi), nn.ReLU()
        )
        # 価値ネットワーク
        self.value_net = nn.Sequential(
            nn.Linear(feature_dim, last_layer_dim_vf), nn.ReLU()
        )

    def forward(self, features: th.Tensor) -> Tuple[th.Tensor, th.Tensor]:
        """
        :return: (th.Tensor, th.Tensor) 指定ネットワークの latent_policy(th.Tensor形式), latent_value(th.Tensor形式) 。
            すべての層が共有の場合、``latent_policy == latent_value`` となる。
        """
        return self.policy_net(features), self.value_net(features)


class CustomActorCriticPolicy(ActorCriticPolicy):
    def __init__(
        self,
        observation_space: gym.spaces.Space,
        action_space: gym.spaces.Space,
        lr_schedule: Callable[[float], float],
        net_arch: Optional[List[Union[int, Dict[str, List[int]]]]] = None,
        activation_fn: Type[nn.Module] = nn.Tanh,
        *args,
        **kwargs,
    ):

        super(CustomActorCriticPolicy, self).__init__(
            observation_space,
            action_space,
            lr_schedule,
            net_arch,
            activation_fn,
            # 残りの引数群はベースクラスに渡される
            *args,
            **kwargs,
        )
        # 直行初期化の無効化
        self.ortho_init = False

    def _build_mlp_extractor(self) -> None:
        self.mlp_extractor = CustomNetwork(self.features_dim)


model = PPO(CustomActorCriticPolicy, "CartPole-v1", verbose=1)
model.learn(5000)


 

オフポリシーアルゴリズム


SACやDDPG、TD3を使用する際に行動器(actor)と評価器(critic)が異なるネットワークアーキテクチャが必要な場合、次のような形式の辞書を引数に渡します: dict(qf=[<評価器ネットワークアーキテクチャ>], pi=[<行動器ネットワークアーキテクチャ>])

例えば、行動器ネットワーク(別名pi)と評価器ネットワーク(別名qf)を別のアーキテクチャにする場合、net_arch=dict(qf=[400, 300], pi=[64, 64]) のように指定します。

行動器ネットワークと評価器ネットワークを同一アーキテクチャを使う場合は、net_arch=[256, 256] のように指定します(ここでは256ユニットの2つの隠れ層を指定しています)。
 

注意
オンポリシーとは対照的に、行動器と評価器の間の(特徴抽出器以外の)共有ネットワークは許可されていません。


from stable_baselines3 import SAC

# それぞれ64ユニットの2層を持つカスタム行動器アーキテクチャ
# 400ユニットと300ユニットの2層を持つカスタム評価器アーキテクチャ
policy_kwargs = dict(net_arch=dict(pi=[64, 64], qf=[400, 300]))
# エージェントの生成
model = SAC("MlpPolicy", "Pendulum-v0", policy_kwargs=policy_kwargs, verbose=1)
model.learn(5000)

-----
カスタムSIで独自モデルを作ることは殆どないとは思いますが、ご参考まで。

p.s.
Donkeycar のモデルを強化学習化するには、最後の「高度な例」セクションの手段を取れば可能になります。

..というか、gym-donkeycar使うか..ふつー..


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_timesteps は n_envs、env.step() を呼び出すたびに、(環境数)ずつ増加する。


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



EventCallback


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

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

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


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つでも干渉して下や横のコネクタが使えなくなってしまう。

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



..私のように...

Google Colab相当の環境をローカルPC上のDocker Composeで実現する

 Docker Composeの有料版に、お金を払いたくない ^H^Hが使えない ため古いゲーミングPC(GeForce3080)にDocker Composeを入れてその上のコンテナでGoogle Colab相当のノート操作ができないかためしてみた: コンテナイメージとして a...