2021/10/30

UbuntuでGithubからクローンできないときにする設定

 UbuntuでGithubからSSHクローンができないとき、以下の設定をする

~/.ssh/configファイルに以下を加える

Host github.com
  IPQoS none

ポイントはIPQoS設定。"github ssh ipqos"で調べると色々出てくる
回線、IPv6など特定条件で起きるらしい


2021/10/06

Gitリポジトリを幾つか公開しました

 大したものではありませんが、以前作成したものを整理してGithubで公開しました。

最近はWebとかAIとかの仕事が増えて、デバイスよりのことは趣味でしかできなくなってきたのがちょっと寂しい。。。

2021/08/17

UnitVでsensor 20fpsオーバー

 前回かいた記事 で UnitVでsensor.snapshot()だけで10fpsと書いたんですが、ファームの問題でした。今更感がありますが、ファームについて書いておこうと思います。

https://docs.m5stack.com/en/quick_start/unitv/unitv_quick_start_maixpy を参照して素直にセットアップすると、M5StickVと同じファームを入れる流れになりますが、このファームが sensor の動作周波数を12MHzと設定しているためでした。しかもこのページ内のファームはファイル名はv5.1.2となっていますが、中のファームは0.5.0で結構古い。

新しいファームは https://dl.sipeed.com/shareURL/MAIX/MaixPy/release/master に置いてあって現時点では、maixpy_v0.6.2_58_g783789e06 が最新。

ここに _m5stickv.bin がありますが、これではなく普通のmaixファームを使うと倍速の24MHzになり、UnitVのmicroPython上では20fpsで動くようになります。

前記事で書いたような mobilenet 1000class動かそうとするとフルスペックのファームだとメモリが足りません。よって minimum構成のファームにしたいわけですが、ここに置いてあるminimumたちは微妙に自分が使いたい組み合わせじゃないんですよね。

自分が使いたいのは IDE + _thread + ws2812 + sensor 24MHz + KPU V3 です。kpu V4サポートは今の所いらない。ws2812は大して使わないけど入れても大差ない。_threadはちょっと使いたい。IDEないと辛すぎ。こういう組み合わせがほしい。

ということでファームをビルドしよう!

オンラインでビルドできるサービスがあるようですがユーザ登録とかしないといけないので、ローカルに環境構築してビルドします。

https://github.com/sipeed/MaixPy/blob/master/build.md を見ながらやりますが、ここで要求されている環境はすでに入っているので以下の手順だけでOK。

$ git clone --recursive https://github.com/sipeed/MaixPy.git
$ wget http://dl.cdn.sipeed.com/kendryte-toolchain-ubuntu-amd64-8.2.0-20190409.tar.xz
$ sudo tar -Jxvf kendryte-toolchain-ubuntu-amd64-8.2.0-20190409.tar.xz -C /opt
下が今回ビルドするときに使ったコンフィグファイルの内容(config_tiny.mk)
CONFIG_TOOLCHAIN_PATH="/opt/kendryte-toolchain/bin"
CONFIG_TOOLCHAIN_PREFIX="riscv64-unknown-elf-"
CONFIG_BOARD_MAIX=y
CONFIG_LCD_DEFAULT_WIDTH=240
CONFIG_LCD_DEFAULT_HEIGHT=135
CONFIG_LCD_DEFAULT_FREQ=15000000
CONFIG_SENSOR_FREQ=24000000
CONFIG_CPU_DEFAULT_FREQ=400000000
CONFIG_COMPONENT_DRIVERS_ENABLE=y
CONFIG_SPI_SD_CARD_FORCE_HIGH_SPEED=y
CONFIG_WS2812_ENABLE=y
CONFIG_COMPONENT_KENDRYTE_SDK_ENABLE=y
CONFIG_SDK_LOG_LEVEL=5
CONFIG_FREERTOS_ENABLE=y
CONFIG_STATIC_TASK_CLEAN_UP_ENABLE=y
CONFIG_FREEROTS_MINIMUM_STACK_SIZE=2048
CONFIG_COMPONENT_MICROPYTHON_ENABLE=y
CONFIG_MAIXPY_GC_HEAP_SIZE=0x80000
CONFIG_MAIXPY_IDE_SUPPORT=y
CONFIG_MAIXPY_THREAD_ENABLE=y
CONFIG_MAIXPY_OMV_MINIMUM=y
CONFIG_MAIXPY_OMV_DOUBLE_BUFF=y
CONFIG_MAIXPY_WS2812_ENABLE=y
CONFIG_MAIXPY_BUILTIN_PY_BOARD=y
CONFIG_MAIXPY_BUILTIN_PY_FPIOA_MANAGER=y
CONFIG_BUILTIN_PY_DIR=""
CONFIG_COMPONENT_SPIFFS_ENABLE=y
CONFIG_SPIFFS_CACHE=y
CONFIG_SPIFFS_CACHE_WR=y
CONFIG_SPIFFS_SIZE=0x300000
CONFIG_SPIFFS_START_ADDR=0xD00000
CONFIG_SPIFFS_EREASE_SIZE=0x1000
CONFIG_SPIFFS_LOGICAL_BLOCK_SIZE=0x20000
CONFIG_SPIFFS_LOGICAL_PAGE_SIZE=0x1000
CONFIG_SPIFFS_OBJ_NAME_LEN=128
CONFIG_SPIFFS_USE_MAGIC=y
CONFIG_SPIFFS_USE_MAGIC_LENGTH=y
CONFIG_SPIFFS_META_LENGTH=0
CONFIG_COMPONENT_UTILS_ENABLE=y
ビルド
$ cd MaixPy/projects/maixpy_k210
$ python project.py clean_conf
$ python project.py build --config_file config_tiny.mk
すると、buildフォルダに maixpy.bin が出来上がります。サイズは736192でした。
このファームだとビッグサイズのmobilenetも動くし、sensorも倍速だし、_threadもちゃんとスレッド動作しました。
ビルドはあっという間に完了するので、ちょこちょこ変えながら色々試すにはローカル環境でやるのが一番いいです。

2021/08/15

UnitVでも load_flashなら MobileNet V1 1000 class 動くね

maix_train は何とか動いたけれど結果が0,1しか出ないのはやはりつまらないです。
探してたら、MaxiPy_scripts に1000クラス分類できるやつあるじゃないですか。
この中の machine_vision/mobilenet_1000_class です。

ただ、ここには本体はなく説明だけです。目当てのkmodelは、説明にある通り https://dl.sipeed.com/MAIX/MaixPy/model に mobilenet_0x300000.kfpkg が入っているのでダウンロードします。

これをkflashで焼いて使うわけですが、でかいのでファームをminimunにして、GCヒープサイズも半分の256kbにしろとドキュメントには書いてあります。動くんでしょうけど、それは辛いです。。

そこで kpu.load()ではなく、kpu.load_flash()なら動くんじゃないかな?と試してみました。
ダウンロードした mobilenet_0x300000.kfpkg をただ焼いても動きません。
ここがポイントです!
load_flash()はメモリーにロードすることなくkpuが直接モデルのアドレスを見に来るようにするものらしいので、モデルデータをビッグエンディアンに変換しておく必要があります。

まずこのkfpkgをunzipで解凍します。すると flash-list.json と m.kmodel が得られます。
このm.kmodelをビッグエンディアンに変換します。
$ ../model_le2be.py m.kmodel
いきなり出てきたmodel_le2be.pyですが、MaixPy_scripts/machie_vision に入っているスクリプトです。これを実行すると m_be.kmodel が作成されます。

で、flash-list.jsonのbinをm_be.kmodelに書き換えて、zipしてkfpkgにしてkflashします。
手作業でこれを何度もやるのが辛かったので、簡単なfpkgツクールスクリプト kfpkg.py 書きました。
import sys
import json
import zipfile

MODEL_LOAD_ADDRESS = 0x300000
FLASH_LIST_JSON = '{"version":"0.1.0","files":[{"address":0,"bin":null,"sha256Prefix":false}]}'

def kfpkg(fname):
    f = json.loads(FLASH_LIST_JSON)
    f["files"][0]["address"] = MODEL_LOAD_ADDRESS
    f["files"][0]["bin"] = fname + ".kmodel"
    j = json.dumps(findent=2separators=(','': '))
    n = fname + ".kfpkg"
    print(n)
    with zipfile.ZipFile(n"w"compression=zipfile.ZIP_DEFLATEDas z:
        z.writestr("flash-list.json"j)
        z.write(f["files"][0]["bin"])

kfpkg(sys.argv[1])
$ python kfpkg.py m_be
一発で m_be.kfpkg を作ってくれます。そしたらこれを kflash します。
maixPyコードは以下のようなシンプルコード
import sensor
import KPU as kpu
import gc
gc.collect()

sensor.reset()
sensor.set_pixformat(sensor.RGB565)
sensor.set_framesize(sensor.QVGA)
sensor.set_windowing((224224))
sensor.set_hmirror(False)
sensor.set_vflip(False)
sensor.run(1)

model = kpu.load_flash(0x30000010x400060000000)

while(True):
    img = sensor.snapshot()
    fmap = kpu.forward(model, img)
    plist = fmap[:]
    pmax = max(plist)
    max_index = plist.index(pmax)
    print("%.2f : %d" % (pmax, max_index))

これでほぼ準備は整いました。

ファームは maixpy_v0.6.2_57_gae955b706_m5stickv.bin を使いました。フル機能ファームってことです。だめでした。メモリ不足エラーが出ました。そこで、
model = kpu.load_flash(0x30000010x400060000000)

model = kpu.load_flash(0x30000000x400060000000)
に変更

動きました!
今一度、load_flash()の確認 https://en.bbs.sipeed.com/t/topic/1811 によると
task=kpu.load_flash(model_addr, is_dual_buf(0/1), batch_size, spi_speed)
  1. model_addr: flash addr store your model, note, you need flip the model endian, use convert_le.py to convert normal model. and only support V3 model now.
  2. is_dual_buf: 0, single buf, use less ram and slower speed; 1, dual buf, more ram and faster speed.
  3. batch_size: when choose dual_buf, you need set load batch_size, suggestion value is 0x4000~0x10000, you can test out best value for your model.
  4. spi_speed: when use flash runner, we will temporary set flash to high speed mode, set the spi speed you want. the value should <= 80000000
内部バッファをシングルバッファにしてギリ入ったということですね。
ちなみにファームを minimum_with_ide_support.bin にすればダブルバッファでも動きました。
パフォーマンスの違いはちゃんと調べてないけれど、体感ではそんなに大きく変わらなかった。
これで、M5StickCと繋げればちょっと楽しくなるかな。

JetsonNanoでmaix-trainしてUnitVで動かすまで

以前UnitV+M5StickCplusでサンプルの顔認識とカメラ映像転送をやってみたが、JetsonNanoでSipeedのmaix-trainが動くか試してみた。

maix-trainは、classifier(mobilenet v1)とdetector(yolo v2)の2つできる。どちらもスクリプトを実行すると、mobilenet_7_5_224_tf_no_top.h5をベースに転移学習たネットワークを作って、h5,tflite,kmodelを一気に作成するスクリプトになっている。
UnitVでclassifierを動かすまでの流れを記載する。

Tensorflow2.xが必要なので前回構築した tensorflow2.5.0+nv21.7 環境で行う
$ . ~/tf2/bin/activate
maix-trainクローン
$ git clone https://github.com/sipeed/maix_train.git
ライブラリの事前インストール
$ cd maix_train
$ pip install -r requirements.txt
$ sudo apt install libgeos-dev
初期化
$ python train.py init
instance/config.pyが作成される。中に色々設定が記述されている。プラットフォームに合わせて編集しろと説明がされているが、そのままトレーニング開始!
classifierの方を実行する。
$ python train.py -t classifier -z datasets/test_classifier_datasets.zip train
約20分で転移学習完了。out/m.tfliteが作成された。ここまでは問題なしだが、肝心のkmodelファイルは作成されません。下がout/train_log.logの最後の部分
2021-08-11 16:16:25,330 - [INFO]:  now generate kmodel
2021-08-11 16:16:25,331 - [INFO]:  save model as .h5 file
2021-08-11 16:16:26,166 - [INFO]:  save model as .tflite file
2021-08-11 16:19:08,334 - [ERROR]:  failed: TrainFailReason.ERROR_INTERNAL, node error:[Errno 2] No such file or directory: '/home/jetson/maix/maix_train/instance/../tools/ncc/ncc_v0.1/ncc': '/home/jetson/maix/maix_train/instance/../tools/ncc/ncc_v0.1/ncc'
最後にnccがないってエラーが出てkmodelはできません。当然ですね。
入れてませんから。。。というかarm64版のnccがないんですよ。

JetsonNanoでnccのビルドに挑戦してみます。。。
下準備(gcc8.xが必要。8が優先されるよう設定)
$ sudo apt install -y gcc-8 g++-8
$ sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-7 7
$ sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-7 7
$ sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-8 8
$ sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-8 8
$ sudo apt install libgtk2.0-dev -y
$ pip install conan
現時点の最新を取得してcmake & make
$ git clone --recursive -b v0.2.0-beta4 https://github.com/kendryte/nncase.git
$ cd nncase
$ mkdir out && cd out
$ cmake .. -DCMAKE_BUILD_TYPE=Release -DNNCASE_TARGET=k210
かなり時間かかる。conan関係のビルドが。。。
そして以下のようなエラーがでる
ERROR: boost/1.71.0@conan/stable: Error in source() method, line 151
        tools.get(url, sha256=sha256)
        AuthenticationException: Forbidden!
bintrayから取得できない(死んでる?)ようなので、URLを変更する。
~/.conan/data/boost/1.71.0/conan/stable/export/conanfile.py 151行目の
url = "https://dl.bintray.com/boostorg/release/%s/source/%s" % (self.version, zip_name)
を以下のように書き換える。
url = "https://boostorg.jfrog.io/artifactory/main/release/%s/source/%s" % (self.version, zip_name)
そして再度
$ cmake .. -DCMAKE_BUILD_TYPE=Release -DNNCASE_TARGET=k210
cmakeは成功したのでビルドしてみる
$ make -j4
だめだー
mutex.cc:(.text+0x12c): undefined reference to `absl::base_internal::SpinLock::SlowUnlock(unsigned int)'
mutex.cc:(.text+0x160): undefined reference to `absl::base_internal::LowLevelAlloc::Alloc(unsigned long)'
mutex.cc:(.text+0x1f4): undefined reference to `absl::base_internal::SpinLock::SlowLock()'
・・・とかどっさりリンクエラー
軽く調べてみると、conanでortools/7.3@sunnycase/testing をrequireしていて、ortoolsがrequireしている abseil/20190619@sunnycase/testing がだめらしい。あちこちでabseilがリンクエラーするという記事が上がっているが、ドンピシャの解決方法が発見できず。conanバージョンを1.21.1にしてみろとか(やってみたがcmakeでエラー)、abseilのリンクライブラリ順番を依存順に変えてみろとか(どこでー?)・・・
これはしばらく様子見です。

仕方ないので、nncaseビルドは諦めて、windows-wsl2環境でkmodel作成
(ここからはwsl2での操作)

V3モデル用 latest
V4モデル用 latest currentry
がありますが、v4モデルはまだ謎が多く。情報が少ない。
自分でも色々やってみたけどまともに動かない。しばらく様子見です。

ということで、V3モデルを作成します。
JetsonNanoで作成した outフォルダごとwsl環境にコピーします。で、
$ ../ncc-0.1.0rc5/ncc -i tflite -o k210model --dataset datasets/ m.tflite m_v3.kmodel
kmodelが約2MBで出来ました。これをflashかsdに書いて、kpu.load()で読んでkpu.forward()する流れです。このとき、立地なファームを使っている場合メモリ不足でロードが失敗したり、ロードは成功するけど、今度はsensorのFB確保時にメモリ不足になったりします。その場合は、 https://dl.sipeed.com/shareURL/MAIX/MaixPy/release/master/maixpy_v0.6.2_57_gae955b706 から maixpy_v0.6.2_57_gae955b706_minimum_with_ide_support.bin あたりを使えば大丈夫です。
ただ、このモデルが動いても、0と1しか結果が出ないので面白くはないです・・・

2021/08/11

Jetson Nano JetPack4.6 tensorflow2.x/1.x installメモ

 なんだかちょっと使おうかなと思うたびにやっている気がするが。。。

JetsonNanoでSSDブートとかやってたが、ブートしなくなった。SDブートに切り替えしばらく使っていたが、それもブートしなくなった。多分ちゃんとシャットダウンしなかったからかなぁと思っている。かなりセットアップが面倒になってきたので、基本SDブートで、/homeをSSDに分けることにした。壊れてもSD焼きだけで済むように。

JetPackは4.6のイメージを焼いた。ストレージ壊れる前は4.5.xだったがついでに上げる。

また同じことが起きる可能性大なので、Tensorflowセットアップ手順だけメモっておく。今回は https://docs.nvidia.com/deeplearning/frameworks/install-tf-jetson-platform/index.html に沿って素直な手順で。ただ、そのとおりやっても引っかかる部分があるのでちょっとだけ手を加える。

最初にライブラリインストール

$ sudo apt install python3-dev libhdf5-serial-dev hdf5-tools libhdf5-dev zlib1g-dev zip libjpeg8-dev liblapack-dev libblas-dev gfortran

venvでTF2.x用のローカル環境作って、TF2インストール

$ sudo apt install python3-venv
$ python3 -m venv tf2
$ . ~/tf2/bin/activate
$ pip install -U pip testresources setuptools==49.6.0 wheel
$ pip install -U numpy==1.19.4 future==0.18.2 mock==3.0.5 h5py==2.10.0 keras_preprocessing==1.1.1 keras_applications==1.0.8 gast==0.2.2 futures protobuf pybind11
$ pip install --pre --extra-index-url https://developer.download.nvidia.com/compute/redist/jp/v46 tensorflow
h5pyのビルドに時間かかる。tensorflowインストール時にはgrpcioのビルドあたりからすごい時間かかる。https://developer.download.nvidia.com/compute/redist/jp/v46 をブラウザからアクセスしてもファイルリストが取れないため不安になるがpipからはアクセスできるようだ。grpcioが無事ビルドされたあとに以下のメッセージが。。

  Attempting uninstall: six
    Found existing installation: six 1.16.0
    Uninstalling six-1.16.0:
      Successfully uninstalled six-1.16.0
  Attempting uninstall: keras-preprocessing
    Found existing installation: Keras-Preprocessing 1.1.1
    Uninstalling Keras-Preprocessing-1.1.1:
      Successfully uninstalled Keras-Preprocessing-1.1.1
  Attempting uninstall: gast
    Found existing installation: gast 0.2.2
    Uninstalling gast-0.2.2:
      Successfully uninstalled gast-0.2.2
keras-preprocessing, gast入れなくてよかったんじゃ?

続いて、TF1.xのローカル環境でTF1.xインストール
$ deactivate
$ python3 -m venv tf1
$ . ~/tf1/bin/activate
$ pip install -U pip testresources setuptools==49.6.0
$ pip install -U numpy==1.18.5 future==0.18.2 mock==3.0.5 h5py==2.10.0 keras_preprocessing==1.1.1 keras_applications==1.0.8 gast==0.3.3 futures protobuf pybind11
$ pip install --pre --extra-index-url https://developer.download.nvidia.com/compute/redist/jp/v46 'tensorflow<2'
numpyのバージョンとかちょいと変更。
これでTF2.x/1.xのローカル環境構築完了。

ブートしなくなるのはどうもSSDドライブの相性が良くないのかも。と言っても他にないので細かい工夫続けながらですか。

2021/08/09

M5StickCplus と UnitV で YOLOv2

 M5StickCplus買いました。最初やりたかったのはBLEをプログラムして、ChromeのWebBluetoothから接続してみたかったから。BLE通してジャイロ情報送って簡単なアプリ作って。この時点でPlusじゃなくても良かったなと思ったけど、せっかく結構きれいなLCDが付いてるんで、カメラ接続してみたくなり、UnitV AI Camera買ってみた。

なぜかカメラだけのHatがないんですよね。M5CameraとかTimerCameraになってしまう。UnitVはM5StickVから色々取り除いた廉価版。AI処理用のKPUというのが入っているので簡単なモデルなら動くらしい。いずれやってみよう。

ここでは、StickCにCamera映像を描画することが第一目的だが、せっかくなのでUnitVに標準で入っている顔認識(tiny yolo v2)もついでにやってみたという内容。

まずはUnitV側のプログラム(MicroPython)

import time
import KPU as kpu
import sensor
import struct
from fpioa_manager import fm
from machine import UART

fm.register(35, fm.fpioa.UART1_TX, force=True)
fm.register(34, fm.fpioa.UART1_RX, force=True)
uart = UART(UART.UART1, 115200800timeout=1000read_buf_len=4096)

sensor.reset()
sensor.set_pixformat(sensor.RGB565)
sensor.set_framesize(sensor.QVGA)
sensor.run(1)

model = kpu.load(0x300000)  # Load Model File from Flash
anchor = (1.8892.52452.94653.940563.99987,
          5.36585.1554376.922756.7183759.01025)
kpu.init_yolo2(model0.50.35anchor)

def transfer(img):
    header = struct.pack('>BBH'00img.size())
    uart.write(header)
    uart.write(img)

clock = time.clock()

while(True):
    clock.tick()
    img = sensor.snapshot()
    bbox = kpu.run_yolo2(modelimg)
    if bbox:
        for i in bbox:
            img.draw_rectangle(i.rect(), thickness=4)
    transfer(img.resize(160120).compress(10))
    print(clock.fps())

UnitVとStickCとは4Pinケーブルで接続してシリアル通信するので、UARTの設定をする。次にカメラ(sensor)の設定、KPUの設定、ループ内でカメラ>yolo処理>転送の流れ。

ポイントとしては、QVGAのまま転送すると超遅いので160x120にリサイズして、それをJPEG圧縮して転送している。だいたい1.5kbytesくらいになるので5fps位になる。

単純にカメラ処理だけなら10fps。KPU処理入れると8fpsくらい。転送が一番やばい。この時点で素直にM5StickVにしとけばと思ったが、遊びだしこの不自由さがまたたまらない!

最初からQQVGA(160x120)でやればと思ったが、yolo2がそのサイズを受け付けなかった。

そして以下がStickCplus側のプログラム

#include <M5StickCPlus.h>
#include <esp_timer.h>

#define STB_IMAGE_IMPLEMENTATION
#define STBI_NO_STDIO
#define STBI_ONLY_JPEG
#include "stb_image.h"

void *buffer = NULL;
TFT_eSprite canvas = TFT_eSprite(&M5.Lcd);

void *decode(void *bufferint lengthint *wint *h)
{
  int comp = 3;
  stbi_uc *image = stbi_load_from_memory((stbi_uc *)bufferlengthwh, &compcomp);
  uint8_t *src = (uint8_t *)image;
  uint16_t *dst = (uint16_t *)image;
  for (int i = 0i < *w * *hi++)
  {
    const uint8_t r = *src++;
    const uint8_t g = *src++;
    const uint8_t b = *src++;
    *dst++ = canvas.color565(rgb);
  }
  return image;
}

void setup()
{
  M5.begin();
  M5.Axp.ScreenBreath(13);
  M5.Lcd.setRotation(3);
  Serial2.begin(115200, SERIAL_8N1, 3233);

  canvas.createSprite(M5.Lcd.width(), M5.Lcd.height());
  canvas.setSwapBytes(false);
  canvas.setTextSize(2);

  size_t size = 4 * 1024;
  buffer = malloc(size);
  M5.Lcd.printf("alloc: %d"size);
}

void loop()
{
  static int64_t t0 = 0;

  if (Serial2.available() && Serial2.read() == 0 && Serial2.read() == 0)
  {
    int64_t t1 = esp_timer_get_time();

    int size = (Serial2.read() << 8) + Serial2.read();
    size = Serial2.readBytes((uint8_t *)buffer, (size_t)size);
    int64_t t2 = esp_timer_get_time();

    int wh;
    void *image = decode(buffersize, &w, &h);
    int64_t t3 = esp_timer_get_time();

    canvas.pushImage(canvas.width() - w, (canvas.height() - h) / 2wh, (uint16_t *)image);
    free(image);
    int64_t t4 = esp_timer_get_time();

    canvas.setCursor(00);
    canvas.printf("S %d\n"size);
    canvas.printf("R %d\n", (t2 - t1) / 1000);
    canvas.printf("D %d\n", (t3 - t2) / 1000);
    canvas.printf("I %d\n", (t4 - t3) / 1000);
    canvas.printf("T %d\n", (t3 - t0) / 1000);
    if (t0)
    {
      int ms = (t1 - t0) / 1000;
      canvas.printf("F %d\n"ms);
      canvas.printf("  %.1f\n"1000.0f / ms);
    }
    canvas.pushSprite(00);
    t0 = t1;
  }
}

色々書いてあるけど、やっていることは受信したらcanvasへ描画するだけ。

ポイントは、jpegデコードにnothing/stbのstb_image.hを使っていること。
M5ライブラリ内にも実はJpegデコードがあるがなぜかコメントアウトされている。無理やり復活させるのも気持ち悪いので、昔からよく利用しているstb_imageを使うことにした。

実はstbの前に、picojpeg 使ってやろうとした。メモリが少ない組み込み系ではこっちがいいかなと思った。PCでテストする分には問題なかったが実際組み込んでみると何故かMCU単位で変になる。UnitVのJpegエンコードとの相性かもしれない。
stb_imageは問題なかったし、パフォーマンスもstbの方が若干良かった。

_threadについて
UnitVのmicropython(OpenMV)には_threadが一応実装されている?ので、カメラとYOLOをメイン。転送をスレッド化すれば並列処理されていいのでは?と思った。
で実際に_threadで転送部を回して、queueがないので代わりにuheapqを介してイメージデータをスレッドに渡すようにしてみた。安定させるためにメイン、スレッドともにtime.sleep_ms(1)が必要だった。がしかし、スピードは変わらなかった。。。
内部実装がただのコルーチンなのではないかと思った。それぞれにsleep入れないと処理が回らないのもそれでうなずける。コルーチンのyield的なものが必要なのだろうと。

2021/07/30

lightdm ログイン画面にIPアドレス表示

起動直後に自身のIPアドレスを表示させたい!ログインせず!それもデカく!
下の写真だと分かりにくいけど、Ubuntuでlightdmのログインウィンドウ表示したときにIPアドレスを表示した状態がこれです。マシンはJetsonNano。アドレスが複数出てるのはDockerなど複数あるから。
まず、表示スクリプトはこんな感じです
hostname -I > /tmp/ip.txt
zenity --text-info --filename /tmp/ip.txt --width=800 --height=400 --font "Monaco 70"
これを実行可能なスクリプトファイルとしてどこかに作成する

ログイン前に表示する設定(lightdm)
/etc/lightdm/lightdm.conf.d/99-showip.conf を作成して
[Seat:*]
greeter-setup-script=<スクリプトへのパス>
を記入。99-showip.confとかじゃなくても何でもいいでしょう。全ファイル実行するっぽいので。

JetsonNanoのUbuntuDesktopの初期DMはgdm3なのでgdmで実現したかったが、残念ながらうまく機能しなかった。/etc/gdm3/Init/Default、/usr/share/gdm/greeter/autostartとか、色々記事探しながらやってみたが、記事通りには動作しなかった。。。ということでlightdmに変更した。

HDMI 3.5"LCDディスプレイ
写真のモニタはラズパイ向けのモニターで約3千円位で買えるスグレモノ。これはKumanというメーカーのものだが最近見かけない。代わりにOSOYOOってところのやつが同じ価格帯で出てる。
ちょっと繋ぎたいときに重宝する。このモニターサイズなのでデカく表示したかった。

なぜ表示したかったか
時々使用するLinuxマシンが複数あり、常時モニタやキーボードなどは接続してない。メインモニタは1つしか持ってない。sshとかVNCで使うのが常なのだけど、ふとIPアドレスが不明になってしまう。どれがどれだっけ?とか、DHCPで時々変わったりする。固定にすればいいんだけど結局忘れるし。面倒だし。
ということで、とにかく起動して、このチビモニタに接続すれば判明するようにしたかった。

2021/05/03

Jetson-nano で tensorflow & tfjs-node-gpu 動かすまで(多分最短手順)

install

Jetson-nano devkit 買ったはいいものの、CUDAとかTensorRTとかでガリガリやる時間はないのですよ。もっと簡単に手早く既存の学習済みモデルで遊びたいのですよ。

python3 + tensorflow と nodejs + tfjs-node-gpu を動かしたい。だけど普通のインストールだとまともに動かない。特にtfjsはaarch64のプリビルドパッケージがないため、自分でビルドしないとならないが、tfjs-nodeをビルドするために必要な libtensorflow をまずは作らないとならず、これが尋常じゃないほど大変。
というか、自分はまだ成功していない。
ということで、先人の力を借りて、とにかく最短で動くところまでの手順を紹介します。

自分のJetson-nano環境

installed JetPack 4.5.1

$ cat /etc/nv_tegra_release
# R32 (release), REVISION: 5.1, GCID: 26202423, BOARD: t210ref, EABI: aarch64, DATE: Fri Feb 19 16:45:52 UTC 2021

$ cat /usr/local/cuda-10.2/version.txt 
CUDA Version 10.2.89

$ node -v
v14.16.1
$ npm -v
6.14.12

Tensorflow 手順

$ python3 -m venv tf
$ . ~/tf/bin/activate
$ pip install --upgrade pip
$ pip install numpy==1.19.4
$ sudo apt-get install libhdf5-dev
$ pip install --pre --extra-index-url https://developer.download.nvidia.com/compute/redist/jp/v45 tensorflow 
  1. venvでtensorflow用のpython3環境を作る
  2. 作った環境をアクティベートする(pipが使えるようになる)
  3. pipアップグレード(しないとその後のインストールが上手く出来ない)
  4. numpyとlibhdf5-devを先に入れる
    • numpy 1.19.5はnanoのarm64でサポートしてない命令を使っているらしく、importすると Illegal instructionになる。いずれ解決されるだろう。
  5. tensorflowをnvidiaが提供するパッケージをインストールする
    • 芋づる式に依存パッケージがどっさり入る
    • h5pyとかが自動的にビルドされる(時間かかる)

以下のようにtensorflowをインポートしてみてlibcudart.soがロードされればOK

$ python -c "import tensorflow"
2021-05-03 13:43:44.320234: I tensorflow/stream_executor/platform/default/dso_loader.cc:49] Successfully opened dynamic library libcudart.so.10.2

tfjs-node-gpu 手順

$ npm init
$ npm install @tensorflow/tfjs-node-gpu@3.3.0
  1. 適当なディレクトリ掘ってnpm init
  2. tfjs-node-gpuをインストール
    • 3.4.0以上だと以下の手順で入れるlibtensorflowではビルドできなかった

node_modules/@tensorflow/tfjs-node-gpu/scripts/custom-binary.json を作成して以下を記述

{
  "tf-lib": "https://s3.us.cloud-object-storage.appdomain.cloud/tfjs-cos/libtensorflow-gpu-linux-arm64-1.15.0.tar.gz"
}
$ npm rebuild @tensorflow/tfjs-node-gpu
  1. tfs-node-gpu をビルド(custom-binary.jsonで指定したライブラリが使用される)
  2. 時々libtensorflowダウンロードがスタックする時がある。その時はkillして再実行

https://www.tensorflow.org/js/guide/nodejs?hl=ja を試してみて動けばOK

import * as tf from '@tensorflow/tfjs-node-gpu'

libtensorflow-gpu-linux-arm64-1.15.0.tar.gz のダウンロード先は、 https://enricopiccini.com/en/kb/HOW_TO_RUN_TensorflowJs_in_NodeJs_on_NVidia_Jetson_Nano_arm64-658 を参照させてもらいました。

まだ色々試したわけじゃないけど、とりあえず絶望的な状況からは脱した。
できれば最新のlibtensorflow-gpu-linux-arm64を作るか、cdn見つけたい。

2021/02/08

Jetson Nano devkit(B01)+USBドライブブート

 久しぶりの投稿です。数年GPU/AI関連の組み込み系の仕事をしていましたが、昨年からWeb系になり、なんとなくブログも再開ですかね。

で、久しぶりのネタは、NVIDIA Jetson Nanoです。前回の仕事でTegraTX2とかいじってましたが、そういう仕事から離れてしまったので個人的に購入してみました。

セットアップする際にSDcardからブートするのはいいとして、自分がやりたいのはUSBでつないだSSDドライブからのブートです。

色々記事を読むと rootOnUSB を使うのが良さそう?と、やってみましたが、うまく起動しませんでした。無理やり起動させることは出来ましたが、安定しませんでした。rootOnUSBのリリースノートには、L4T 32.2.3 と書かれてますね。なるほど。今現在のJetPackSDK(4.5)のL4Tは32.5です。だからだめなんですかね。

つうことで、JetPackSDK(4.5)のリリースノートを読むと、なんとブータブルUSB drive対応と書かれています。やってみました。ちゃんと出来ました。ブート時、SDcardも不要です。SDcardがなければ自動的にUSBドライブからブートするシーケンスになっています。

以下自分がやったこと

昔何かで使って余っていたIntel SSD 600pシリーズ 512GB M.2 PCIEx4をUSB-SSDケースに入れ、Nanoに接続

JetsonNanoのJ50の9と10をショートさせて、リカバリーモードで起動する

JetsonNanoのflush port(microUSB port)からLinuxホストマシンへ、USBシリアルデバイス ftdi232 経由で接続。ホストでlsusbすると

Bus 001 Device 029: ID 0955:7f21 NVIDIA Corp. APX

って出てくる。7f21ってのがリカバリーモードで接続されているってことになるらしい。

LinuxホストにSDKManagerをインストール。手動でもできるようだが、勝手がわからないので無難に。と思ったがSDKManagerはUbuntu 16.04か18.04じゃないと動かないと!自分の環境は20.04。実際に動かしてみるとバージョンが合わないというメッセージが出て先に進めない。。。dockerで18.04動かしてX動かしてとも考えたが、超めんどい。

/etc/os-releaseのVERSION_IDを"18.04"に書き換えると動くという情報があり、やってみたら無事に動いた!よかった。。。

SDKManagerでの作業を進める。SSDドライブへのフラッシュも問題なく行えた。
こちらからフラッシュするドライブを選択とかしないのかな?と思ったけど特に指定することなく自動認識されたようだ。
フラッシュが完了したあたりで、JetsonNanoがリブートするので、リカバリージャンパーは外しておく。あとはSDcardでやることと同じ。

シリアルコンソールで確認
Jetsonのシリアルコンソールにドライブのinodeが破損している的なメッセージが出たが、何度かe2fsckを行って修復した。
あと、tegra-xusb 70090000.xusb: ERROR Transfer event for disabled endpoint or incorrect stream ring とかなんとかエラーが出てる。これは Quick Summary on Using USB external disk for Jetson Nano で解決した。
シリアル接続は こちら を参考にした。手元にesp32cam用に使っていたftdi232を使って接続した。
シリアル端末はminiconやcuを使う話が多いが、自分はscreenを使った。cuだとキー入力が送られなかった。sudo screen /dev/ttyUSB0 115200 って感じ。

ここまで動いたら、JetPack SDKはSDKManager使わなくても
sudo apt update
sudo apt install nvidia-jetpack

 で入る。この方がアップデートもしやすい。

次やりたいのが、headless起動でもx11vnc使って1920x1080リアルフレームバッファのリモートデスクトップ環境が作れないかと。edid読むようにカーネルビルドしただけではだめっぽい。やってる人いたら教えてほしいです。

$ xrandr --fb 1920x1080

で普通にできた!ポイントは、x11vnc経由でちゃんとログインすること。その後ならxrandrが使える。ディスプレイ出力しないので、fb解像度だけ設定すれば良い。~/.xsessionrcの最後に書いておけばログインしたときに自動的に拡張される。