リモートサーバーのDockerでGUIアプリを動かす

リモートサーバー上にあるDockerのGUIアプリを動かしてみた。いろいろと苦労したが、最終的に成功した方法を示す。というかログメッセージが少なすぎる。。Can't open displayってもう少し詳しく書いてくれ。。

構成

ホストPCからリモートサーバーにSSHでアクセスして操作する。

  • ホストPC(自分が操作するPC)
  • リモートサーバー(Docker(GUI)のあるPC)

操作手順

ホストPCからXフォワーディングオプションをつけてアクセスする。ラージXなので注意。

$ ssh -X ubuntu@192.168.11.100

下記のようにオプションをつけてDockerを起動。今回は手元にあったPythonイメージを使用。

$ docker run -it -e DISPLAY=$DISPLAY --net host -v $HOME/.Xauthority:/root/.Xauthority:rw --name x11_test python:3.8-slim-buster bash
  • -it
    • コンテナ入出力を現在のターミナル標準入出力にマウントする。
  • -e DISPLAY=$DISPLAY
    • DISPLAY変数を引き継ぐ。
  • -v $HOME/.Xauthority:/root/.Xauthority:rw
    • .Xauthorityをマウントする。これはXServerの認証の際に使用される。
  • --net host
    • ホスト側のネットワークIFを使用する。
  • --name x11_test
    • コンテナの名前。なんでもいい。

重要なのは--net host。リモートサーバーでDockerを動かすにはこれが必要。

xorgをインストール。 当然、Dockerコンテナ内にXサーバーがないと表示ができないのでインストール。自分はここでハマった。

$ apt update
$ apt install xorg

テスト。 目が表示されればOK。

$ xeyes

防備録

下記のエラーが出たら、Xauthorityが正しく設定されているか、RW権限があるかを確認。

X11 connection rejected because of wrong authentication.

X11フォワーディングが有効になっているか確認する。

vim /etc/ssh/sshd_config
X11Forwarding yes

Docker 起動時に下記のオプションをつければソケットがバインドできるが、別途認証などが必要なのでやめたほうがいい。

-v /tmp/.X11-unix/:/tmp/.X11-unix

下記コマンドをホスト側で実行すると、認証なしで画面が表示できる。何故か映らない場合に試すといい。

$ xhost +
access control disabled, clients can connect from any host

JetsonNano でYOLO v5

Jetson NanoでYOLO v5を動かしてみました。

YOLO v5 って?

YOLO v5とは物体検出モデルのこと。YOLO系は他のモデル(Efficient detとか)と比較して軽量であり、Jetsonのようなシングルボードコンピュータでも動作できるのが強み。

個人的にYOLO v5がいいなと思ったのは以下の2点。

  • モデルサイズがS, M, L, Xと4つも用意してある
  • 学習がとても楽(水増しとかもコンフィグを変更するだけで使える)

今回はJetson Nano上で推論のみをおこなっていく。

github.com

前準備

今回DockerとCUDAが必要なので、Jetpackをいれておくこと。

  • L4T:R32.4.4 (まあ、Dockerで動かすので最新ので問題ない)
  • L4T:R32.5

Docker で動かすから問題ないと考えていたが、DockerイメージのL4TバージョンとホストのL4Tバージョンが一致していないと動かない。現在Pytorch1.7以上のイメージは、L4T R32.5以上でしか配布していないので、R32.5以上をインストールするか、バージョンアップしておくこと。

準備

YOLO v5はPytorchで動いているので、PytorchのDockerイメージを起動する。なければ 勝手にダウンロードしてくれる。Pytorchは1.7以上が必要なので1.7以上のイメージをインストールすること。

$ docker run --runtime nvidia -it --net host nvcr.io/nvidia/l4t-pytorch:r32.5.0-pth1.7-py3 bash

上のコマンドを実行するとDockerコンテナ内に移動するので、必要なパッケージ、ライブラリをインストールする。SSHはいらないが、推論結果をやりとりするのに便利なのでいれる。 YOLO v5 のrequire.txtを使用して入れると、OpenCVが動かないので下記コマンドで入れること。

$ apt update && apt install -y python3-opencv ssh && pip3 install -U pip && pip3 install requests tqdm pyyaml seaborn pycocotools thop

最後にYOLO v5 をクローンしてくる。

$ git clone https://github.com/ultralytics/yolov5.git

今回Jetson Nano用にパッケージを入れているので、バージョンは一致しない。YOLOv5ではバージョンチェックが入るのでコメントアウトしておく。

$ vim requirements.txt

以下をコメントアウト

#opencv-python>=4.1.2
#tensorboard>=2.2
#torchvision>=0.8.1

実行

サンプルとして用意されている画像を推論してみる。 まずはモデルサイズが最も小さい、yolov5sを試してみる。

/yolov5# python3 detect.py --source data/images --weights yolov5s.pt --conf 0.25
Namespace(agnostic_nms=False, augment=False, classes=None, conf_thres=0.25, device='', exist_ok=False, img_size=640, iou_thres=0.45, name='exp', nosave=False, project='runs/detect', save_conf=False, save_txt=False, source='data/images', update=False, view_img=False, weights=['yolov5s.pt'])
YOLOv5 \U0001f680 v4.0-170-g866bc7d torch 1.7.0 CUDA:0 (NVIDIA Tegra X1, 3964.1328125MB)

Fusing layers... 
Model Summary: 224 layers, 7266973 parameters, 0 gradients, 17.0 GFLOPS
image 1/2 /yolov5/data/images/bus.jpg: 640x480 4 persons, 1 bus, Done. (0.247s)
image 2/2 /yolov5/data/images/zidane.jpg: 384x640 2 persons, 1 tie, Done. (0.158s)
Results saved to runs/detect/exp3
Done. (0.938s)

推論にかかった時間は0.247秒と0.158秒であった。リアルタイム推論も行けそうな感じではある。

他のサイズのモデルも試してみる。

  • M
/yolov5# python3 detect.py --source data/images --weights yolov5m.pt --conf 0.25
Namespace(agnostic_nms=False, augment=False, classes=None, conf_thres=0.25, device='', exist_ok=False, img_size=640, iou_thres=0.45, name='exp', nosave=False, project='runs/detect', save_conf=False, save_txt=False, source='data/images', update=False, view_img=False, weights=['yolov5m.pt'])
YOLOv5 \U0001f680 v4.0-170-g866bc7d torch 1.7.0 CUDA:0 (NVIDIA Tegra X1, 3964.1328125MB)

Downloading https://github.com/ultralytics/yolov5/releases/download/v4.0/yolov5m.pt to yolov5m.pt...
100%|##########################################################################################| 41.1M/41.1M [00:16<00:00, 2.57MB/s]

Fusing layers... 
Model Summary: 308 layers, 21356877 parameters, 0 gradients, 51.3 GFLOPS
image 1/2 /yolov5/data/images/bus.jpg: 640x480 4 persons, 1 bus, Done. (0.605s)
image 2/2 /yolov5/data/images/zidane.jpg: 384x640 2 persons, 1 tie, Done. (0.772s)
Results saved to runs/detect/exp4
Done. (2.603s)

0.605秒と0.772秒

  • L
/yolov5# python3 detect.py --source data/images --weights yolov5l.pt --conf 0.25
Namespace(agnostic_nms=False, augment=False, classes=None, conf_thres=0.25, device='', exist_ok=False, img_size=640, iou_thres=0.45, name='exp', nosave=False, project='runs/detect', save_conf=False, save_txt=False, source='data/images', update=False, view_img=False, weights=['yolov5l.pt'])
YOLOv5 \U0001f680 v4.0-170-g866bc7d torch 1.7.0 CUDA:0 (NVIDIA Tegra X1, 3964.1328125MB)

Downloading https://github.com/ultralytics/yolov5/releases/download/v4.0/yolov5l.pt to yolov5l.pt...
100%|##########################################################################################| 90.2M/90.2M [00:26<00:00, 3.60MB/s]

Fusing layers... 
Model Summary: 392 layers, 47025981 parameters, 0 gradients, 115.4 GFLOPS
image 1/2 /yolov5/data/images/bus.jpg: 640x480 4 persons, 1 bus, 1 potted plant, Done. (1.129s)
image 2/2 /yolov5/data/images/zidane.jpg: 384x640 3 persons, 2 ties, Done. (0.953s)
Results saved to runs/detect/exp5
Done. (3.817s)

1.129秒と0.953秒

  • X
/yolov5# python3 detect.py --source data/images --weights yolov5x.pt --conf 0.25
Namespace(agnostic_nms=False, augment=False, classes=None, conf_thres=0.25, device='', exist_ok=False, img_size=640, iou_thres=0.45, name='exp', nosave=False, project='runs/detect', save_conf=False, save_txt=False, source='data/images', update=False, view_img=False, weights=['yolov5x.pt'])
YOLOv5 \U0001f680 v4.0-170-g866bc7d torch 1.7.0 CUDA:0 (NVIDIA Tegra X1, 3964.1328125MB)

Downloading https://github.com/ultralytics/yolov5/releases/download/v4.0/yolov5x.pt to yolov5x.pt...
100%|############################################################################################| 168M/168M [00:56<00:00, 3.11MB/s]

Fusing layers... 
Model Summary: 476 layers, 87730285 parameters, 0 gradients, 218.8 GFLOPS
image 1/2 /yolov5/data/images/bus.jpg: 640x480 4 persons, 1 bus, 1 potted plant, Done. (2.373s)
image 2/2 /yolov5/data/images/zidane.jpg: 384x640 2 persons, 3 ties, Done. (1.819s)
Results saved to runs/detect/exp6
Done. (6.118s)

2.373秒と1.819秒。 ひとまず、どのサイズのモデルであっても、動かすことは可能な模様。

実行(失敗バージョン)

なお、これはL4T R32.4で動かしたときの動作であり、GPUドライバーが動かないとのことでCPUでの推論になっている。参考までに残しておく。

/yolov5# python3 detect.py --source data/images --weights yolov5s.pt --conf 0.25
Namespace(agnostic_nms=False, augment=False, classes=None, conf_thres=0.25, device='', exist_ok=False, img_size=640, iou_thres=0.45, name='exp', project='runs/detect', save_conf=False, save_txt=False, source='data/images', update=False, view_img=False, weights=['yolov5s.pt'])
/usr/local/lib/python3.6/dist-packages/torch/cuda/__init__.py:52: UserWarning: CUDA initialization: Found no NVIDIA driver on your system. Please check that you have an NVIDIA GPU and installed a driver from http://www.nvidia.com/Download/index.aspx (Triggered internally at  /media/nvidia/WD_NVME/PyTorch/JetPack_4.4.1/pytorch-v1.7.0/c10/cuda/CUDAFunctions.cpp:100.)
  return torch._C._cuda_getDeviceCount() > 0
YOLOv5 v4.0-121-gba18528 torch 1.7.0 CPU

Fusing layers... 
Model Summary: 224 layers, 7266973 parameters, 0 gradients, 17.0 GFLOPS
image 1/2 /yolov5/data/images/bus.jpg: 640x480 4 persons, 1 bus, Done. (1.417s)
image 2/2 /yolov5/data/images/zidane.jpg: 384x640 2 persons, 1 tie, Done. (1.104s)
Results saved to runs/detect/exp2
Done. (2.762s)

推論処理に1.4秒と1.1秒かかっている。

他のモデルも使ってみた。

  • M
/yolov5# python3 detect.py --source data/images --weights yolov5m.pt --conf 0.25
Namespace(agnostic_nms=False, augment=False, classes=None, conf_thres=0.25, device='', exist_ok=False, img_size=640, iou_thres=0.45, name='exp', project='runs/detect', save_conf=False, save_txt=False, source='data/images', update=False, view_img=False, weights=['yolov5m.pt'])
/usr/local/lib/python3.6/dist-packages/torch/cuda/__init__.py:52: UserWarning: CUDA initialization: Found no NVIDIA driver on your system. Please check that you have an NVIDIA GPU and installed a driver from http://www.nvidia.com/Download/index.aspx (Triggered internally at  /media/nvidia/WD_NVME/PyTorch/JetPack_4.4.1/pytorch-v1.7.0/c10/cuda/CUDAFunctions.cpp:100.)
  return torch._C._cuda_getDeviceCount() > 0
YOLOv5 v4.0-121-gba18528 torch 1.7.0 CPU

Downloading https://github.com/ultralytics/yolov5/releases/download/v4.0/yolov5m.pt to yolov5m.pt...
100%|##########################################################################################| 41.1M/41.1M [00:15<00:00, 2.87MB/s]

Fusing layers... 
Model Summary: 308 layers, 21356877 parameters, 0 gradients, 51.3 GFLOPS
image 1/2 /yolov5/data/images/bus.jpg: 640x480 4 persons, 1 bus, Done. (2.956s)
image 2/2 /yolov5/data/images/zidane.jpg: 384x640 2 persons, 1 tie, Done. (2.315s)
Results saved to runs/detect/exp3
Done. (5.412s)
  • L
/yolov5# python3 detect.py --source data/images --weights yolov5l.pt --conf 0.25
Namespace(agnostic_nms=False, augment=False, classes=None, conf_thres=0.25, device='', exist_ok=False, img_size=640, iou_thres=0.45, name='exp', project='runs/detect', save_conf=False, save_txt=False, source='data/images', update=False, view_img=False, weights=['yolov5l.pt'])
/usr/local/lib/python3.6/dist-packages/torch/cuda/__init__.py:52: UserWarning: CUDA initialization: Found no NVIDIA driver on your system. Please check that you have an NVIDIA GPU and installed a driver from http://www.nvidia.com/Download/index.aspx (Triggered internally at  /media/nvidia/WD_NVME/PyTorch/JetPack_4.4.1/pytorch-v1.7.0/c10/cuda/CUDAFunctions.cpp:100.)
  return torch._C._cuda_getDeviceCount() > 0
YOLOv5 v4.0-121-gba18528 torch 1.7.0 CPU

Downloading https://github.com/ultralytics/yolov5/releases/download/v4.0/yolov5l.pt to yolov5l.pt...
100%|##########################################################################################| 90.2M/90.2M [00:38<00:00, 2.44MB/s]

Fusing layers... 
Model Summary: 392 layers, 47025981 parameters, 0 gradients, 115.4 GFLOPS
image 1/2 /yolov5/data/images/bus.jpg: 640x480 4 persons, 1 bus, 1 potted plant, Done. (5.590s)
image 2/2 /yolov5/data/images/zidane.jpg: 384x640 3 persons, 2 ties, Done. (4.408s)
Results saved to runs/detect/exp4
Done. (10.148s)
  • X
/yolov5# python3 detect.py --source data/images --weights yolov5x.pt --conf 0.25
Namespace(agnostic_nms=False, augment=False, classes=None, conf_thres=0.25, device='', exist_ok=False, img_size=640, iou_thres=0.45, name='exp', project='runs/detect', save_conf=False, save_txt=False, source='data/images', update=False, view_img=False, weights=['yolov5x.pt'])
/usr/local/lib/python3.6/dist-packages/torch/cuda/__init__.py:52: UserWarning: CUDA initialization: Found no NVIDIA driver on your system. Please check that you have an NVIDIA GPU and installed a driver from http://www.nvidia.com/Download/index.aspx (Triggered internally at  /media/nvidia/WD_NVME/PyTorch/JetPack_4.4.1/pytorch-v1.7.0/c10/cuda/CUDAFunctions.cpp:100.)
  return torch._C._cuda_getDeviceCount() > 0
YOLOv5 v4.0-121-gba18528 torch 1.7.0 CPU

Downloading https://github.com/ultralytics/yolov5/releases/download/v4.0/yolov5x.pt to yolov5x.pt...
100%|############################################################################################| 168M/168M [01:00<00:00, 2.93MB/s]

Fusing layers... 
Model Summary: 476 layers, 87730285 parameters, 0 gradients, 218.8 GFLOPS
image 1/2 /yolov5/data/images/bus.jpg: 640x480 4 persons, 1 bus, 1 potted plant, Done. (8.954s)
image 2/2 /yolov5/data/images/zidane.jpg: 384x640 2 persons, 3 ties, Done. (7.382s)
Results saved to runs/detect/exp5
Done. (16.482s)

import numpy で core dump

現象

Jetson nanoでいろいろUpdateしたあと、Numpyをインポートしたらコアダンプするようになった。

$ python3
Python 3.6.9 (default, Jan 26 2021, 15:33:00) 
[GCC 8.4.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import numpy as np
Illegal instruction (core dumped)

対処法

Numpyのバージョンが良くないらしい。

$ pip3 list | grep numpy
numpy                         1.19.5

バージョン下げると動いた。

$ pip3 install numpy==1.19.4

デバッグログ

(gdb) file python3
Reading symbols from python3...(no debugging symbols found)...done.
(gdb) run -c 'import numpy'
Starting program: /usr/bin/python3 -c 'import numpy'
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/aarch64-linux-gnu/libthread_db.so.1".

Program received signal SIGILL, Illegal instruction.
0x0000007fb601af54 in gotoblas_dynamic_init ()
   from /home/nvidia/.local/lib/python3.6/site-packages/numpy/core/../../numpy.libs/libopenblasp-r0-32ff4d91.3.13.so
(gdb) bt
#0  0x0000007fb601af54 in gotoblas_dynamic_init ()
   from /home/nvidia/.local/lib/python3.6/site-packages/numpy/core/../../numpy.libs/libopenblasp-r0-32ff4d91.3.13.so
#1  0x0000007fb5e9d72c in gotoblas_init ()
   from /home/nvidia/.local/lib/python3.6/site-packages/numpy/core/../../numpy.libs/libopenblasp-r0-32ff4d91.3.13.so
#2  0x0000007fb7fdea34 in call_init (l=<optimized out>, argc=argc@entry=3, argv=argv@entry=0x7ffffff3d8, env=env@entry=0x955ee0)
    at dl-init.c:72
#3  0x0000007fb7fdeb38 in call_init (env=0x955ee0, argv=0x7ffffff3d8, argc=3, l=<optimized out>) at dl-init.c:118
#4  _dl_init (main_map=main_map@entry=0x9825d0, argc=3, argv=0x7ffffff3d8, env=0x955ee0) at dl-init.c:119
#5  0x0000007fb7fe2cd8 in dl_open_worker (a=0x7fffff8538) at dl-open.c:522
#6  0x0000007fb7f5e694 in __GI__dl_catch_exception (exception=0xfffffffffffffffe, operate=0x7fffff835c, args=0x7fffff8520)
    at dl-error-skeleton.c:196
#7  0x0000007fb7fe2418 in _dl_open (
    file=0x7fb7619e60 "/home/nvidia/.local/lib/python3.6/site-packages/numpy/core/_multiarray_umath.cpython-36m-aarch64-linux-gnu.so", mode=-2147483646, caller_dlopen=0x501900 <_PyImport_FindSharedFuncptr+384>, nsid=-2, argc=3, argv=0x7ffffff3d8, 
    env=<optimized out>) at dl-open.c:605
#8  0x0000007fb7e13014 in dlopen_doit (a=0x7fffff87f8) at dlopen.c:66
#9  0x0000007fb7f5e694 in __GI__dl_catch_exception (exception=0x7c5d90 <__stack_chk_guard>, exception@entry=0x7fffff8790, 
    operate=0x7fffff85ec, args=0x7fffff8770) at dl-error-skeleton.c:196
#10 0x0000007fb7f5e738 in __GI__dl_catch_error (objname=0x8dc4a0, errstring=0x8dc4a8, mallocedp=0x8dc498, operate=<optimized out>, 
    args=<optimized out>) at dl-error-skeleton.c:215
#11 0x0000007fb7e14780 in _dlerror_run (operate=operate@entry=0x7fb7e12fb0 <dlopen_doit>, args=0x7fffff87f8, 
    args@entry=0x7fffff8808) at dlerror.c:162
#12 0x0000007fb7e130e8 in __dlopen (file=<optimized out>, mode=<optimized out>) at dlopen.c:87
#13 0x0000000000501900 in _PyImport_FindSharedFuncptr ()
#14 0x000000000050ef04 in _PyImport_LoadDynamicModuleWithSpec ()
#15 0x0000000000510b3c in ?? ()
#16 0x00000000005bc030 in PyCFunction_Call ()
#17 0x00000000005330dc in _PyEval_EvalFrameDefault ()
#18 0x000000000052b108 in ?? ()
#19 0x000000000052b69c in ?? ()
#20 0x000000000052b8f4 in ?? ()
#21 0x00000000005306c0 in _PyEval_EvalFrameDefault ()
#22 0x0000000000529978 in ?? ()
#23 0x000000000052b8f4 in ?? ()
---Type <return> to continue, or q <return> to quit---
#24 0x00000000005306c0 in _PyEval_EvalFrameDefault ()
#25 0x0000000000529978 in ?? ()
#26 0x000000000052b8f4 in ?? ()
#27 0x00000000005306c0 in _PyEval_EvalFrameDefault ()
#28 0x0000000000529978 in ?? ()
#29 0x000000000052b8f4 in ?? ()
#30 0x00000000005306c0 in _PyEval_EvalFrameDefault ()
#31 0x0000000000529978 in ?? ()
#32 0x000000000052b8f4 in ?? ()
#33 0x00000000005306c0 in _PyEval_EvalFrameDefault ()
#34 0x0000000000529978 in ?? ()
#35 0x0000000000607a20 in _PyObject_FastCallDict ()
#36 0x00000000006087c0 in _PyObject_CallMethodIdObjArgs ()
#37 0x0000000000429144 in ?? ()
#38 0x00000000005329e0 in _PyEval_EvalFrameDefault ()
#39 0x000000000052b108 in ?? ()
#40 0x00000000005397cc in ?? ()
#41 0x00000000005bc030 in PyCFunction_Call ()
#42 0x00000000005330dc in _PyEval_EvalFrameDefault ()
#43 0x000000000052b108 in ?? ()
#44 0x000000000052b69c in ?? ()
#45 0x000000000052b8f4 in ?? ()
#46 0x00000000005306c0 in _PyEval_EvalFrameDefault ()
#47 0x0000000000529978 in ?? ()
#48 0x000000000052b8f4 in ?? ()
#49 0x00000000005306c0 in _PyEval_EvalFrameDefault ()
#50 0x0000000000529978 in ?? ()
#51 0x000000000052b8f4 in ?? ()
#52 0x00000000005306c0 in _PyEval_EvalFrameDefault ()
#53 0x0000000000529978 in ?? ()
#54 0x000000000052b8f4 in ?? ()
#55 0x00000000005306c0 in _PyEval_EvalFrameDefault ()
#56 0x0000000000529978 in ?? ()
#57 0x0000000000607a20 in _PyObject_FastCallDict ()
---Type <return> to continue, or q <return> to quit---
#58 0x00000000006087c0 in _PyObject_CallMethodIdObjArgs ()
#59 0x0000000000429144 in ?? ()
#60 0x000000000053506c in ?? ()
#61 0x00000000005bbffc in PyCFunction_Call ()
#62 0x00000000005330dc in _PyEval_EvalFrameDefault ()
#63 0x000000000052b108 in ?? ()
#64 0x000000000052b69c in ?? ()
#65 0x000000000052b8f4 in ?? ()
#66 0x00000000005306c0 in _PyEval_EvalFrameDefault ()
#67 0x000000000052b108 in ?? ()
#68 0x000000000052b42c in _PyFunction_FastCallDict ()
#69 0x0000000000607a20 in _PyObject_FastCallDict ()
#70 0x00000000006087c0 in _PyObject_CallMethodIdObjArgs ()
#71 0x0000000000429248 in ?? ()
#72 0x00000000005329e0 in _PyEval_EvalFrameDefault ()
#73 0x000000000052b108 in ?? ()
#74 0x00000000005397cc in ?? ()
#75 0x00000000005bc030 in PyCFunction_Call ()
#76 0x00000000005330dc in _PyEval_EvalFrameDefault ()
#77 0x000000000052b108 in ?? ()
#78 0x000000000052b69c in ?? ()
#79 0x000000000052b8f4 in ?? ()
#80 0x00000000005306c0 in _PyEval_EvalFrameDefault ()
#81 0x0000000000529978 in ?? ()
#82 0x000000000052b8f4 in ?? ()
#83 0x00000000005306c0 in _PyEval_EvalFrameDefault ()
#84 0x0000000000529978 in ?? ()
#85 0x000000000052b8f4 in ?? ()
#86 0x00000000005306c0 in _PyEval_EvalFrameDefault ()
#87 0x0000000000529978 in ?? ()
#88 0x000000000052b8f4 in ?? ()
#89 0x00000000005306c0 in _PyEval_EvalFrameDefault ()
#90 0x0000000000529978 in ?? ()
#91 0x0000000000607a20 in _PyObject_FastCallDict ()
---Type <return> to continue, or q <return> to quit---
#92 0x00000000006087c0 in _PyObject_CallMethodIdObjArgs ()
#93 0x0000000000429144 in ?? ()
#94 0x000000000053506c in ?? ()
#95 0x00000000005bbffc in PyCFunction_Call ()
#96 0x00000000005330dc in _PyEval_EvalFrameDefault ()
#97 0x000000000052b108 in ?? ()
#98 0x000000000052b69c in ?? ()
#99 0x000000000052b8f4 in ?? ()
#100 0x00000000005306c0 in _PyEval_EvalFrameDefault ()
#101 0x000000000052b108 in ?? ()
#102 0x000000000052b42c in _PyFunction_FastCallDict ()
#103 0x0000000000607a20 in _PyObject_FastCallDict ()
#104 0x00000000006087c0 in _PyObject_CallMethodIdObjArgs ()
#105 0x0000000000429248 in ?? ()
#106 0x00000000005329e0 in _PyEval_EvalFrameDefault ()
#107 0x000000000052b108 in ?? ()
#108 0x00000000005397cc in ?? ()
#109 0x00000000005bc030 in PyCFunction_Call ()
#110 0x00000000005330dc in _PyEval_EvalFrameDefault ()
#111 0x000000000052b108 in ?? ()
#112 0x000000000052b69c in ?? ()
#113 0x000000000052b8f4 in ?? ()
#114 0x00000000005306c0 in _PyEval_EvalFrameDefault ()
#115 0x0000000000529978 in ?? ()
#116 0x000000000052b8f4 in ?? ()
#117 0x00000000005306c0 in _PyEval_EvalFrameDefault ()
#118 0x0000000000529978 in ?? ()
#119 0x000000000052b8f4 in ?? ()
#120 0x00000000005306c0 in _PyEval_EvalFrameDefault ()
#121 0x0000000000529978 in ?? ()
#122 0x000000000052b8f4 in ?? ()
#123 0x00000000005306c0 in _PyEval_EvalFrameDefault ()
#124 0x0000000000529978 in ?? ()
#125 0x0000000000607a20 in _PyObject_FastCallDict ()
---Type <return> to continue, or q <return> to quit---
#126 0x00000000006087c0 in _PyObject_CallMethodIdObjArgs ()
#127 0x0000000000429144 in ?? ()
#128 0x000000000053506c in ?? ()
#129 0x00000000005bbffc in PyCFunction_Call ()
#130 0x00000000005330dc in _PyEval_EvalFrameDefault ()
#131 0x000000000052b108 in ?? ()
#132 0x000000000052b69c in ?? ()
#133 0x000000000052b8f4 in ?? ()
#134 0x00000000005306c0 in _PyEval_EvalFrameDefault ()
#135 0x000000000052b108 in ?? ()
#136 0x000000000052b42c in _PyFunction_FastCallDict ()
#137 0x0000000000607a20 in _PyObject_FastCallDict ()
#138 0x00000000006087c0 in _PyObject_CallMethodIdObjArgs ()
#139 0x0000000000429248 in ?? ()
#140 0x00000000005329e0 in _PyEval_EvalFrameDefault ()
#141 0x000000000052b108 in ?? ()
#142 0x00000000005397cc in ?? ()
#143 0x00000000005bc030 in PyCFunction_Call ()
#144 0x00000000005330dc in _PyEval_EvalFrameDefault ()
#145 0x000000000052b108 in ?? ()
#146 0x000000000052b69c in ?? ()
#147 0x000000000052b8f4 in ?? ()
#148 0x00000000005306c0 in _PyEval_EvalFrameDefault ()
#149 0x0000000000529978 in ?? ()
#150 0x000000000052b8f4 in ?? ()
#151 0x00000000005306c0 in _PyEval_EvalFrameDefault ()
#152 0x0000000000529978 in ?? ()
#153 0x000000000052b8f4 in ?? ()
#154 0x00000000005306c0 in _PyEval_EvalFrameDefault ()
#155 0x0000000000529978 in ?? ()
#156 0x000000000052b8f4 in ?? ()
#157 0x00000000005306c0 in _PyEval_EvalFrameDefault ()
#158 0x0000000000529978 in ?? ()
#159 0x0000000000607a20 in _PyObject_FastCallDict ()
---Type <return> to continue, or q <return> to quit---
#160 0x00000000006087c0 in _PyObject_CallMethodIdObjArgs ()
#161 0x0000000000429144 in ?? ()
#162 0x00000000005329e0 in _PyEval_EvalFrameDefault ()
#163 0x000000000052b108 in ?? ()
#164 0x0000000000630cd8 in PyRun_StringFlags ()
#165 0x00000000006368f0 in PyRun_SimpleStringFlags ()
#166 0x00000000006209f0 in Py_Main ()
#167 0x0000000000420d3c in main ()

Python datetime データをスッキリさせて保存

タイムゾーン設定&現在時刻の取得

現在時刻を取得するには次のように書く。 タイムゾーンを指定しないと標準時になるため注意。

import datetime

import pandas as pd 
import random
import time
import pytz

dt_now = datetime.datetime.now(pytz.timezone('Asia/Tokyo'))
print(dt_now)
2021-01-27 00:10:11.951768+09:00

ただこの形式のままだとタイムゾーン情報まで記録してしまう。また、精度もマイクロ秒となっている。正直ここまで詳しい情報は必要ないし、時間情報は常に記録するものなのでデータ量削減のためにもスッキリさせたい。 なのでタイムゾーンなし+秒単位までで記録を行う。

現状の確認

まずはそのままDataFrameとして保存するとどうなるか見てみる。1秒おきに時間と乱数を保存してみた。

d = {}

d['time'] = []
d['val'] = []

for i in range(5):
    d['time'].append(datetime.datetime.now(pytz.timezone('Asia/Tokyo')))
    d['val'].append(random.random())
    time.sleep(1)
df = pd.DataFrame(d)
df
time val
0 2021-01-27 00:10:12.954969+09:00 0.531003
1 2021-01-27 00:10:13.956126+09:00 0.020670
2 2021-01-27 00:10:14.957337+09:00 0.562972
3 2021-01-27 00:10:15.958551+09:00 0.749389
4 2021-01-27 00:10:16.959756+09:00 0.635614
df['time']
0   2021-01-27 00:10:12.954969+09:00
1   2021-01-27 00:10:13.956126+09:00
2   2021-01-27 00:10:14.957337+09:00
3   2021-01-27 00:10:15.958551+09:00
4   2021-01-27 00:10:16.959756+09:00
Name: time, dtype: datetime64[ns, Asia/Tokyo]

DataFrameの中でもマイクロ秒単位まで記録されており、ひじょうに長い。

文字列に変換して保存

そこで一旦文字列に変換して保存する。

d2 = {}

d2['time'] = []
d2['val'] = []

for i in range(5):
    t = datetime.datetime.now(pytz.timezone('Asia/Tokyo')).strftime('%Y-%m-%d %H:%M:%S')
    d2['time'].append(t)
    d2['val'].append(random.random())
    time.sleep(1)
df2 = pd.DataFrame(d2)
df2
time val
0 2021-01-27 00:37:36 0.395559
1 2021-01-27 00:37:37 0.793888
2 2021-01-27 00:37:38 0.417575
3 2021-01-27 00:37:39 0.455042
4 2021-01-27 00:37:40 0.482387
df2["time"]
0    2021-01-27 00:37:36
1    2021-01-27 00:37:37
2    2021-01-27 00:37:38
3    2021-01-27 00:37:39
4    2021-01-27 00:37:40
Name: time, dtype: object

するとかなりスッキリし、想定していた形式にすることができた。

ただしData TypeがDatetimeでなくなっているのでグラフとして書くときに困ってしまう。 なのでその場合は読み込むときにDate timeに変換すればOK。

df2['time'] = pd.to_datetime(df2['time'], format="%Y-%m-%d %H:%M:%S")
df2['time']
0   2021-01-27 00:37:36
1   2021-01-27 00:37:37
2   2021-01-27 00:37:38
3   2021-01-27 00:37:39
4   2021-01-27 00:37:40
Name: time, dtype: datetime64[ns]

プライバシポリシー

①個人情報の利用目的

ぼうびろく(以下当ブログ)では、メールでのお問い合わせ、メールマガジンへの登録などの際に、名前(ハンドルネーム)、メールアドレス等の個人情報をご登録いただく場合がございます。

これらの個人情報は質問に対する回答や必要な情報を電子メールなどをでご連絡する場合に利用させていただくものであり、個人情報をご提供いただく際の目的以外では利用いたしません。

②個人情報の第三者への開示

当サイトでは、個人情報は適切に管理し、以下に該当する場合を除いて第三者に開示することはありません。

・本人のご了解がある場合

・法令等への協力のため、開示が必要となる場合

個人情報の開示、訂正、追加、削除、利用停止

ご本人からの個人データの開示、訂正、追加、削除、利用停止のご希望の場合には、ご本人であることを確認させていただいた上、速やかに対応させていただきます。

③広告の配信について

当サイトは第三者配信の広告サービス「Google Adsense グーグルアドセンス」を利用しています。

広告配信事業者は、ユーザーの興味に応じた広告を表示するためにCookie(クッキー)を使用することがあります。

Cookie(クッキー)を無効にする設定およびGoogleアドセンスに関する詳細は「広告 – ポリシーと規約 – Google」をご覧ください。

また、当ブログでは、Amazon.co.jpを宣伝しリンクすることによってサイトが紹介料を獲得できる手段を提供することを目的に設定されたアフィリエイトプログラムである、Amazonアソシエイト・プログラムの参加者です。

三者がコンテンツおよび宣伝を提供し、訪問者から直接情報を収集し、訪問者のブラウザにCookie(クッキー)を設定したりこれを認識したりする場合があります。

アクセス解析ツールについて

当サイトでは、Googleによるアクセス解析ツール「Googleアナリティクス」を利用しています。

このGoogleアナリティクスはトラフィックデータの収集のためにCookieを使用しています。このトラフィックデータは匿名で収集されており、個人を特定するものではありません。この機能はCookieを無効にすることで収集を拒否することが出来ますので、お使いのブラウザの設定をご確認ください。この規約に関して、詳しくはここをクリックしてください。

⑤当サイトへのコメントについて

当サイトでは、スパム・荒らしへの対応として、コメントの際に使用されたIPアドレスを記録しています。

これはブログの標準機能としてサポートされている機能で、スパム・荒らしへの対応以外にこのIPアドレスを使用することはありません。また、メールアドレスとURLの入力に関しては、任意となっております。全てのコメントは管理人が事前にその内容を確認し、承認した上での掲載となりますことをあらかじめご了承下さい。加えて、次の各号に掲げる内容を含むコメントは管理人の裁量によって承認せず、削除する事があります。

・特定の自然人または法人を誹謗し、中傷するもの。

・極度にわいせつな内容を含むもの。

・禁制品の取引に関するものや、他者を害する行為の依頼など、法律によって禁止されている物品、行為の依頼や斡旋などに関するもの。

・その他、公序良俗に反し、または管理人によって承認すべきでないと認められるもの。

⑥免責事項

当サイトで掲載している画像の著作権・肖像権等は各権利所有者に帰属致します。権利を侵害する目的ではございません。記事の内容や掲載画像等に問題がございましたら、各権利所有者様本人が直接メールでご連絡下さい。確認後、対応させて頂きます。

当サイトからリンクやバナーなどによって他のサイトに移動された場合、移動先サイトで提供される情報、サービス等について一切の責任を負いません。

当サイトのコンテンツ・情報につきまして、可能な限り正確な情報を掲載するよう努めておりますが、誤情報が入り込んだり、情報が古くなっていることもございます。

当サイトに掲載された内容によって生じた損害等の一切の責任を負いかねますのでご了承ください。

JetsonNano GPIO

JetsonNanoでGPIOを使ってみました。どのPINが何番に対応してるの?みたいにわかりづらいところも多かったのでまとめてみました。

やったこと

  • ターミナルから直接操作
  • Pythonから操作(ライブラリ使用)

ピン対応表

下記のサイトにピン配置が載っている。中央のPin列が実際の位置を示している。

www.jetsonhacks.com

Name列に3.3VやI2Cとつけられているが、これが各ピンの役割である。GPIOとして使えるピンそんなにないやんけ、と思うだろうが、初期設定では実際にその役割を割り当てられているものは少なく、Sysfs GPIO列にgpioxxxと書いてあるピンはどれも使用することができる。入力出力の違いはないの?と思われる方もいるかもしれないがGPIOはSW的に入出力が決定できる。この対応表は頻繁に使用するので、PCの壁紙にするといいかもしれない。

Sysfs GPIO列はターミナルからの操作で使用し、Pin番号はPythonでの制御で使用する。

操作するピン

今回は以下のピンのみを使用する。 - Pin番号:37 - Name:SPI_2_MOSI - sysfs:gpio12

準備

入出力が実際に動いているか確認するために、LEDやら用意しておくと良い。注意点としては、入力を見るときは3.3VまたはGNDに確実に接続すること。ピンが刺さっていない状態はLowでもHighでもない状態である。(自分がやらかしたので)

あと、sudoつけなくてもgpioが操作できるよう、下記のようにグループに追加しておく。

$ sudo groupadd -f -r gpio
$ sudo usermod -a -G gpio $USER

ターミナルからの制御

ターミナルからどうやってGPIO操作するの?というとSysfsを使用する。

sysfs - Wikipedia

わかりやすく言うとファイル操作によって、デバイスが操作できるという仕組みである。

入力

下記をターミナルから実行する。Pinの'12'を使用する。

$ echo 12 > /sys/class/gpio/export
$ echo in > /sys/class/gpio/gpio12/direction

Pin12を3.3VやGNDに接続して下記を実行すると、その状態がわかる。

$ echo /sys/class/gpio/gpio12/value

出力

下記をターミナルから実行する。実行するとPin12がHighになり、LEDが接続されていると光る。

$ echo 12 > /sys/class/gpio/export
$ echo out > /sys/class/gpio/gpio12/direction
$ echo 1 > /sys/class/gpio/gpio12/value

ターミナルで操作後は

ちゃんとunexportしておく。

$ echo 12 > /sys/class/gpio/unexport

python

Pythonで操作するにはjetson.GPIOというライブラリを使用する。下記コマンドでインストール。

$ pip3 install Jetson.GPIO

※ Jetson.GPIOのReadmeにはsudo をつけるとなっているが個人的にはおすすめしない。あまり詳しくは書かないが、sudoをつけるとRootに、つけないとユーザーにインストールされるので、あれ?インストールしたはずのライブラリがないぞ?となりがち。セットアップするなら統一したほうが良い。

入力

下記を実行するとPinの入力状態がわかる。

import Jetson.GPIO as GPIO

ch = 37

GPIO.setmode(GPIO.BOARD)
mode = GPIO.getmode()
GPIO.setup(ch, GPIO.IN)

print(GPIO.input(ch))

ここでGPIO.setmodeはPinの指定方法を設定できる。詳しくはライブラリ参照。

GPIO.BOARDは物理Pinに対応した記法。GPIO.BCMは役割に対応したもの、らしい。

出力

下記を実行するとGPIOの出力ができる。値はGPIO.HIGHまたはGPIO.LOWを選択する。

import Jetson.GPIO as GPIO

ch = 37

GPIO.setmode(GPIO.BOARD)
mode = GPIO.getmode()
GPIO.setup(ch, GPIO.OUT, initial=GPIO.HIGH)
GPIO.output(ch, GPIO.HIGH)

Jetson NanoでWebカメラ

Jetson Nanoでカメラ使いたいけど、組み込み向けのはよくわかんねえよ。と思ったのでWebカメラで簡単に撮像してみた。安物なので画質はよろしくないが、とりあえず試してみたい、という方は参考にしてください。

使ったもの

  • Jetson Nano 4GB

  • BUFFALO マイク内蔵120万画素Webカメラ

www.amazon.co.jp

セットアップ

前準備としてJetson Nanoは起動できるところまで済ませておくこと。

接続

Jetson Nanoの電源を入れて、ターミナルを開く。

接続確認のためにWebカメラを接続(すでに接続してある場合は抜き差し)して、ターミナルで下記コマンドを実行。

$ dmesg

すると、BAFFALOのカメラが接続されているっぽいことがわかる。この出力だとusb 1-2.1に接続されている。

[  110.858165] usb 1-2.1: new high-speed USB device number 5 using tegra-xusb
[  111.225241] usb 1-2.1: New USB device found, idVendor=0411, idProduct=0260
[  111.225246] usb 1-2.1: New USB device strings: Mfr=1, Product=2, SerialNumber=0
[  111.225249] usb 1-2.1: Product: BUFFALO BSWHD06M USB Camera
               
[  111.225252] usb 1-2.1: Manufacturer: KYE Systems Corp.
[  111.234231] uvcvideo: Found UVC 1.00 device BUFFALO BSWHD06M USB Camera
                (0411:0260)

無事に接続できたので、実際に動かしてみます。 いくつかサンプルプログラムは入っているのですが、自分のNanoがモニターにつながっておらず使えなかったのでPythonでコードを書きました。

準備:OpenCVのビルド

カメラ操作にはOpenCVが楽なのでOpenCVのインストールを行います。ただ、PIP等ではインストールできなかったのでソースコードからビルドします。

幸いビルド用のスクリプトを配布してくれている方がいるのでそれを使用します。

GitHub - mdegans/nano_build_opencv: Build OpenCV on Nvidia Jetson Nano

$ git clone https://github.com/mdegans/nano_build_opencv.git
$ cd nano_build_opencv
$ ./build_opencv.sh

待ちます。(完了まで1時間くらい?とにかく長いです)

完了したら動作確認します。

$ python3
>>>import cv2
>>>

import cv2でエラーがでなければひとまずOKです。

カメラ撮像

実際に動かしてみます。Jupyter notebookで下記を実行してください。

import cv2
%matplotlib inline
import matplotlib.pyplot as plt

DEVICE_ID = 0 

# open
cap = cv2.VideoCapture (DEVICE_ID)

# capture
_, frame = cap.read()
if(frame is not None):
    # show
    frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
    plt.imshow(frame)
    
else:
    print("Failed video capture!")

# releace 
cap.release()
cv2.destroyAllWindows()

無事撮影できればOKです。