2020年9月23日 星期三

Jetson Nano DeepStream YoloV3 tiny custom model

紀錄關於在Jetson Nano 使用YoloV3 tiny 自製的預訓練檔案

1.預訓練檔案來源

https://github.com/amineHY/YOLOv3-for-custum-objects


2.將以下檔案放置到Jetson Nano上 /opt/nvidia/deepstream/deepstream-5.0//sources/objectDetector_Yolo目錄下

fire/model/yolov3-tiny-obj_final.weights

cfg/yolov3-tiny-obj.cfg


3.切換至 /opt/nvidia/deepstream/deepstream-5.0//sources/objectDetector_Yolo目錄

4.新建檔案 lables-obj.txt

fire


5.編輯檔案 config_infer_primary_yoloV3_tiny.txt

#custom-network-config=yolov3-tiny.cfg

custom-network-config=yolov3-tiny-obj.cfg

#model-file=yolov3-tiny.weights

model-file=yolov3-tiny-obj_final.weights

#labelfile-path=labels.txt

labelfile-path=labels-obj.txt


#network-mode=1

network-mode=2

#num-detected-classes=80

num-detected-classes=1


6.編輯檔案 nvdsinfer_custom_impl_Yolo/nvdsparsebbox_Yolo.cpp

-static const int NUM_CLASSES_YOLO = 80;

+static const int NUM_CLASSES_YOLO = 1;

7.Compile 檔案

export CUDA_VER=10.2
make -C nvdsinfer_custom_impl_Yolo

8.清除暫存檔 rm -rf ~/.cache/gstreamer-1.0/registry.aarch64.bin

9.執行 deepstream-app -c deepstream_app_config_yoloV3_tiny.txt

(deepstream-app:18137): GLib-GObject-WARNING **: 01:42:51.150: g_object_set_is_valid_property: object class 'GstNvArgusCameraSrc' has no property named 'maxperf'

(deepstream-app:18137): GStreamer-CRITICAL **: 01:42:51.152: passed '0' as denominator for `GstFraction'
Unknown or legacy key specified 'is-classifier' for group [property]
Warn: 'threshold' parameter has been deprecated. Use 'pre-cluster-threshold' instead.

Using winsys: x11 
gstnvtracker: Loading low-level lib at /opt/nvidia/deepstream/deepstream-5.0/lib/libnvds_mot_klt.so
gstnvtracker: Optional NvMOT_RemoveStreams not implemented
gstnvtracker: Batch processing is OFF
gstnvtracker: Past frame output is OFF
0:00:00.258438073 18137      0x983e930 INFO                 nvinfer gstnvinfer.cpp:619:gst_nvinfer_logger:<primary_gie> NvDsInferContext[UID 1]: Info from NvDsInferContextImpl::buildModel() <nvdsinfer_context_impl.cpp:1715> [UID = 1]: Trying to create engine from model files
Loading pre-trained weights...
Loading weights of yolov3-tiny complete!
Total Number of weights read : 8676244
Loading pre-trained weights...
Loading weights of yolov3-tiny complete!
Total Number of weights read : 8676244
Building Yolo network...
      layer               inp_size            out_size       weightPtr
(0)   conv-bn-leaky     3 x 608 x 608      16 x 608 x 608    496   
(1)   maxpool          16 x 608 x 608      16 x 304 x 304    496   
(2)   conv-bn-leaky    16 x 304 x 304      32 x 304 x 304    5232  
(3)   maxpool          32 x 304 x 304      32 x 152 x 152    5232  
(4)   conv-bn-leaky    32 x 152 x 152      64 x 152 x 152    23920 
(5)   maxpool          64 x 152 x 152      64 x  76 x  76    23920 
(6)   conv-bn-leaky    64 x  76 x  76     128 x  76 x  76    98160 
(7)   maxpool         128 x  76 x  76     128 x  38 x  38    98160 
(8)   conv-bn-leaky   128 x  38 x  38     256 x  38 x  38    394096
(9)   maxpool         256 x  38 x  38     256 x  19 x  19    394096
(10)  conv-bn-leaky   256 x  19 x  19     512 x  19 x  19    1575792
(11)  maxpool         512 x  19 x  19     512 x  19 x  19    1575792
(12)  conv-bn-leaky   512 x  19 x  19    1024 x  19 x  19    6298480
(13)  conv-bn-leaky  1024 x  19 x  19     256 x  19 x  19    6561648
(14)  conv-bn-leaky   256 x  19 x  19     512 x  19 x  19    7743344
(15)  conv-linear     512 x  19 x  19      18 x  19 x  19    7752578
(16)  yolo             18 x  19 x  19      18 x  19 x  19    7752578
(17)  route                  -            256 x  19 x  19    7752578
(18)  conv-bn-leaky   256 x  19 x  19     128 x  19 x  19    7785858
INFO: [TRT]: mm1_19: broadcasting input0 to make tensors conform, dims(input0)=[1,38,19][NONE] dims(input1)=[128,19,19][NONE].
INFO: [TRT]: mm2_19: broadcasting input1 to make tensors conform, dims(input0)=[128,38,19][NONE] dims(input1)=[1,19,38][NONE].
(19)  upsample        128 x  19 x  19     128 x  38 x  38        - 
(20)  route                  -            384 x  38 x  38    7785858
(21)  conv-bn-leaky   384 x  38 x  38     256 x  38 x  38    8671618
(22)  conv-linear     256 x  38 x  38      18 x  38 x  38    8676244
(23)  yolo             18 x  38 x  38      18 x  38 x  38    8676244
Output yolo blob names :
yolo_17
yolo_24
Total number of yolo layers: 49
Building yolo network complete!
Building the TensorRT Engine...
INFO: [TRT]: mm1_19: broadcasting input0 to make tensors conform, dims(input0)=[1,38,19][NONE] dims(input1)=[128,19,19][NONE].
INFO: [TRT]: mm2_19: broadcasting input1 to make tensors conform, dims(input0)=[128,38,19][NONE] dims(input1)=[1,19,38][NONE].
INFO: [TRT]: Some tactics do not have sufficient workspace memory to run. Increasing workspace size may increase performance, please check verbose output.
INFO: [TRT]: Detected 1 inputs and 2 output network tensors.
Building complete!
0:02:35.752148292 18137      0x983e930 INFO                 nvinfer gstnvinfer.cpp:619:gst_nvinfer_logger:<primary_gie> NvDsInferContext[UID 1]: Info from NvDsInferContextImpl::buildModel() <nvdsinfer_context_impl.cpp:1748> [UID = 1]: serialize cuda engine to file: /opt/nvidia/deepstream/deepstream-5.0/sources/objectDetector_Yolo/model_b1_gpu0_fp16.engine successfully
INFO: [Implicit Engine Info]: layers num: 3
0   INPUT  kFLOAT data            3x608x608       
1   OUTPUT kFLOAT yolo_17         18x19x19        
2   OUTPUT kFLOAT yolo_24         18x38x38        

0:02:35.795951757 18137      0x983e930 INFO                 nvinfer gstnvinfer_impl.cpp:313:notifyLoadModelStatus:<primary_gie> [UID 1]: Load new model:/opt/nvidia/deepstream/deepstream-5.0/sources/objectDetector_Yolo/config_infer_primary_yoloV3_tiny.txt sucessfully

Runtime commands:
h: Print this help
q: Quit

p: Pause
r: Resume

NOTE: To expand a source in the 2D tiled display and view object details, left-click on the source.
      To go back to the tiled display, right-click anywhere on the window.


**PERF:  FPS 0 (Avg)
**PERF:  0.00 (0.00)
** INFO: <bus_callback:181>: Pipeline ready

** INFO: <bus_callback:167>: Pipeline running

GST_ARGUS: Creating output stream
CONSUMER: Waiting until producer is connected...
GST_ARGUS: Available Sensor modes :
GST_ARGUS: 3264 x 2464 FR = 21.000000 fps Duration = 47619048 ; Analog Gain range min 1.000000, max 10.625000; Exposure Range min 13000, max 683709000;

GST_ARGUS: 3264 x 1848 FR = 28.000001 fps Duration = 35714284 ; Analog Gain range min 1.000000, max 10.625000; Exposure Range min 13000, max 683709000;

GST_ARGUS: 1920 x 1080 FR = 29.999999 fps Duration = 33333334 ; Analog Gain range min 1.000000, max 10.625000; Exposure Range min 13000, max 683709000;

GST_ARGUS: 1280 x 720 FR = 59.999999 fps Duration = 16666667 ; Analog Gain range min 1.000000, max 10.625000; Exposure Range min 13000, max 683709000;

GST_ARGUS: 1280 x 720 FR = 120.000005 fps Duration = 8333333 ; Analog Gain range min 1.000000, max 10.625000; Exposure Range min 13000, max 683709000;

GST_ARGUS: Running with following settings:
   Camera index = 0 
   Camera mode  = 4 
   Output Stream W = 1280 H = 720 
   seconds to Run    = 0 
   Frame Rate = 120.000005 
GST_ARGUS: Setup Complete, Starting captures for 0 seconds
GST_ARGUS: Starting repeat capture requests.
CONSUMER: Producer has connected; continuing.
KLT Tracker Init
**PERF:  29.67 (29.46)
**PERF:  29.04 (29.10)
**PERF:  29.10 (29.20)
**PERF:  29.04 (29.10)
**PERF:  29.10 (29.16)
**PERF:  29.04 (29.10)
**PERF:  28.87 (29.11)
**PERF:  29.24 (29.10)

10.使用Overlay sink直接輸出結果至HDMI Screen. 

編輯檔案 deepstream_app_config_yoloV3_tiny.txt

[sink0]
enable=1
#Type - 1=FakeSink 2=EglSink 3=File 5=overlaysink
type=5
width=1280
height=720

編輯 /etc/ssh/sshd_config
+   #X11Forwarding no
-    X11Forwarding yes

設定環境參數

unset DISPLAY

11.使用 CSI camera作為輸入來源

編輯檔案 deepstream_app_config_yoloV3_tiny.txt

[source0]
enable=1
#Type - 1=CameraV4L2 2=URI 3=MultiURI 5=CsiCamera
type=5
camera-width=1280
camera-height=720

12. Reference


 

2020年7月21日 星期二

BMW E53/E38 Cluster mileage and VIN recoding

BMW E53/E38 更換拆車的儀表板(IKE)需要更改
a.里程數(mileage)要調整數值與車上相同
b.車身碼(VIN)要更改與車上相同
c.可能還需要做coding更改一些參數設定,例如油箱容量及時速表微調等.

以上需要更改的資料都儲存在儀表板上的一顆EEPROM IC裡面, 這顆IC有兩種比較早期的是M93S66 而後期的是M35080, 這兩顆IC硬體都是八根腳但腳位定義完全不同, 軟體讀寫的方式也不同, 因此不能互換.

這裡只針對M35080來做研究.

M35080 (注意後面有V3 V6是不同的IC喔) 是一顆SPI界面可讀寫的ROM, 儲存容量有1024 bytes.
因此讀寫的位址範圍是 0x0000 ~ 0x03ff, 位址最開頭的32 bytes是 incremental register, 每兩個bytes為一個單位,一共16個. 里程數就是放在這16個 Incremental register 中, Incremental register有個限制就是寫入的數值不能比原本儲存的小, 實務上的意義就是里程數只能增加不能減少.



1.Mileage decode

實際來看這32 bytes的資料(十六進位數值), 里程數是139148 km

0x0000 10 fd 10 fd 10 fd 10 fd 10 fd 10 fd 10 fc 10 fc 
0x0010 10 fc 10 fc 10 fc 10 fc 10 fc 10 fc 10 fc 10 fc 

由六個 10 FD 及 十個 10 FC 組成

首先 10FC 左移一個bit 變成 21F8, 一共有6個10 FD, 6 左移一個bit 變成C
兩個數值組合起來變成 21F8C 轉換為十進位數值就是 139148

演算過程 (((0x10 << 1) << 8 | (0xFC << 1)) << 4) | (6 << 1) = 0x21F8C = 139148

2.VIN decode

車身碼VIN code有固定的格式, 由兩個英文字母加上5個數字組成

例如 LN73888

VIN的儲存位址沒有固定,各車型可能位置都不同,甚至有可能有兩個以上的位址儲存VIN code

以下VIN位址在0x022C

0x0220 8f ff fe 5e ff 2c fe 3c f6 91 49 31 4c 4e 73 88 
0x0230 12 80 12 09 81 92 12 80 43 20 0a 29 00 09 0d ee 

資料是 4c 4e 73 88 12 80
4c : ASCII code 字元 L
4e: ASCII code 字元 N
73: 數字 73
88: 數字 88
12: 忽略這個byte
80: 取高位數字8

這樣就能得到 VIN : LN73888

3.回到更換拆車的儀表板的主題, VIN 及 mileage 儲存在儀表板 及 LCM燈光模組.
當車輛鎖匙轉至紅火,LCM及IKE會互相比對VIN及mileage,如果不同就會亮防偽燈.
所以更換儀表板要更改VIN及mileage. 

4.更改 VIN
可透過BMW Scanner / PA Soft軟體來更改,不用拆解儀表板

5.更改 mileage
要拆儀表板分解並解焊M35080, 又分以下兩種作法
a. 用專用的M35080 programmer可以清除mileage
b.透過BMW Scanner / PA Soft軟體dump M35080的全部的資料到電腦檔案中, 手動修改檔案將前32 bytes清成 00, 找到VIN 所在位址修改為 FF FF FF FF 12 FF, 然後拆下M35080 換一顆全新資料空的上去, 再用BMW Scanner / PA Soft軟體寫回去.

上車後儀表板會自動把LCM上儲存的VIN 及 mileage 寫入M35080

這樣就完成了

6.這次我採取的作法是購買新的M35080來更新, 萬一弄壞了還有舊的備份.
首先解焊M35080, 值得一提是電路板的IC上幾乎都有打樹脂, 一定要先用熱風槍加熱再仔細清除.
參閱M35080 Datasheet及網路上Arduino的實做範例程式, 我用STM32F103 SPI實做了一個簡易的programmer. 可以讀取及寫入M35080, 另外也有Encode / Decode VIN / Search VIN 及 Encode / Decode mileage. 有了這個工具我把整個M35080的內容讀出然後將該內容的前32 bytes清成 00 
, 然後改寫 VIN, 接著把這份修改過的內容寫到新的M35080, 最後把新的M35080焊回去儀表板.








Reference














2020年7月12日 星期日

Jetson Nano Wifi Hotspot

一直以來都是透過ethetnet以ssh方式登入Jetson Nano, 這個方式有兩個很大的缺點,一是需要先取得Jetson Nano在區域網路分配到的IP address, 而且只要換個網路IP address也跟著改變, 二是Jetson Naon在移動的載台上拉著網路線極不方便.

經過一番研究,最好的方式是Jetson Nano + AC 8265無線網卡做Wifi AP

1.Jetson Nano在完成SD card初次開機時需要做Initial setup, 選擇語言, 建立user / password.
這時PC先透過USB OTG連線Jetson Nano.

$ sudo apt-get install screen

$ screen /dev/ttyACM0 115200

2.在完成以上初始化設定後登入Jetson Nano, 建立Wifi Hotspot

$ nmcli dev wifi hotspot ifname wlan0 ssid <SSID> password <PASSWORD>

編輯Wifi hotspot設定檔, 開機自動啟用Wifi Hotspot及設定Wifi hotspot的IP address & netmask

$ sudo vi /etc/NetworkManager/system-connections/Hotspot

-autoconnect=false
+autoconnect=true

[ipv4]
dns-search=
method=shared
+address1=10.0.0.1/24

3.重啟Network Manager

$ sudo systemctl restart network-manager.service

4.PC連線Jetson Nano wifi hotspot. 這個IP address永遠不變

$ ssh <USER>@10.0.0.1

5.這個作法PC仍然可以透過Jetson Nano的有線ethernet連上internet

[ PC ] <---Wifi ---> [ Hotspot / Jetson Nano / Ethernet ] <--- Local LAN ---> Internet


6.Disable wifi power save

$ sudo vi /etc/NetworkManager/conf.d/default-wifi-powersave-on.conf

-wifi.powersave = 3
+wifi.powersave = 2

 

2020年3月9日 星期一

Jetson Naon DeepStream SDK Getting Started

DeepStream SDK 基於 GStreamer library. 透過plug-in的方式使用GPU來處理串流資料. 例如Encode / Decode H.264串流, 物件辨識及目標追蹤, On Screen Display標示物件等.

1.Install DeepStream SDK - 4.0.2

https://docs.nvidia.com/metropolis/deepstream/dev-guide/

最容易的方式是Method 3 以Debian package安裝, 相依的套件會全部自動安裝

$ sudo apt-get install ./deepstream-4.0_4.0.2-1_arm64.deb

安裝完成後 DeepStream SDK 在 /opt/nvidia/deepstream/deepstream-4.0

2.DeepStream SDK的測試範例

$ cd /opt/nvidia/deepstream/deepstream-4.0/sources/apps/sample_apps/deepstream-test1

編輯檔案 dstest1_pgie_config.txt, 修改network-mode (0 : FP32, 1 : Int8, 2 : FP16)
因為 Jetson Naon 不支援 Int8 inference

#network-mode=1
network-mode=2

$ deepstream-test1-app /opt/nvidia/deepstream/deepstream-4.0/samples/streams/sample_720p.h264

執行後要等一陣子產生model engine之後才以此處理並輸出影像串流



執行完成後model engine file會儲存起來,這時再度編輯dstest1_pgie_config.txt, 修改

model-engine-file=../../../../samples/models/Primary_Detector/resnet10.caffemodel_b1_fp16.engine

再次執行deepstream-test1-app就會直接使用之前產生的 .engine file

若出現以下錯誤

No EGL Display 
nvbufsurftransform: Could not get EGL display connection

編輯 ~/.bashrc 在檔案最後加上

unset DISPLAY
export DISPLAY=:0

執行 source ~/.bashrc

修改 /etc/ssh/sshd_config
+   #X11Forwarding no
-    X11Forwarding yes

執行 xrandr 確認 X11有跑起來

3.DeepStream Application

由以上的測試範例可知DeepStream Application是以c code來寫程式組合GStreamer串流, Inference則以 .txt 作為設定檔, 設定串流的source / sink, inference的各項參數等等

4.DeepStream Yolo

Yolo物件辨識的範例

$ cd /opt/nvidia/deepstream/deepstream-4.0/sources/objectDetector_Yolo
$ ./prebuild.sh
$ cd nvdsinfer_custom_impl_Yolo/
$ export CUDA_VER=10.0
$ make
$ cd ../

採用YoloV3 tiny
編輯 config_infer_primary_yoloV3_tiny.txt, 修改network-mode (0 : FP32, 1 : Int8, 2 : FP16)

#network-mode=1
network-mode=2
model-engine-file=model_b1_fp16.engine

$ deepstream-app -c deepstream_app_config_yoloV3_tiny.txt

Using winsys: x11 
Creating LL OSD context new
Deserialize yoloLayerV3 plugin: yolo_17
Deserialize yoloLayerV3 plugin: yolo_24

Runtime commands:
h: Print this help
q: Quit

p: Pause
r: Resume

NOTE: To expand a source in the 2D tiled display and view object details, left-click on the source.
      To go back to the tiled display, right-click anywhere on the window.


**PERF: FPS 0 (Avg)
**PERF: 0.00 (0.00)
** INFO: <bus_callback:189>: Pipeline ready

Opening in BLOCKING MODE 
NvMMLiteOpen : Block : BlockType = 261 
NVMEDIA: Reading vendor.tegra.display-size : status: 6 
NvMMLiteBlockCreate : Block : BlockType = 261 
** INFO: <bus_callback:175>: Pipeline running

Creating LL OSD context new
**PERF: 29.62 (29.62)
**PERF: 29.80 (29.71)
**PERF: 29.94 (29.79)
**PERF: 29.78 (29.79)
**PERF: 29.92 (29.81)
**PERF: 29.61 (29.78)
**PERF: 29.69 (29.77)
**PERF: 29.66 (29.75)
**PERF: 29.80 (29.76)
** INFO: <bus_callback:212>: Received EOS. Exiting ...

Quitting
App run successful