一 背景:
????????我們都知道如果要在android上面使用rknn推理模型需要按照如下的步驟:
???詳細請參考筆者的文章:Android11-rk3566平臺上采用NCNN,RKNN框架推理yolo11官方模型的具體步驟以及性能比較-CSDN博客
???簡而言之就是
- 模型轉換:把模型文件轉換成rknn格式
- 建立一個C++Native項目,在native層連接 librknnrt.so, 調用其中的API進行推理
那在openharmon4.0上面可行嗎?根據實際的動手試驗答案是:如果完全按照android上面的思路不可行,但是如果我們換一種思路的話,那就可行了。本文將給大家分享一下如何做到。
二 經典思路碰到的問題:
????????我們使用DevEco IDE(4.0)新建一個Native工程,在native(c++)端實現我們一個so,假設叫做rknnwrapper.so,由于我們要使用librknnrt.so這個庫里面的函數,因此就需要在rknnwrapper.so的CMakeLists.txt文件中鏈接這個庫,另外需要把librknnrt.so這個庫拷貝到正確的libs目錄下面。之后編譯鏈接,一切順利,于是信心滿滿的運行了程序,BUT,此時程序出現了異常:rknnwrapper.so加載失敗。通過分析我們發現:
librknnrt.so加載失敗了,進而導致rknnwrapper.so加載失敗,但是為什么會失敗呢?
于是我又把rknn-toolkit2-2.3.2里面的資料翻了一遍:功夫不負有心人,通過樣例代碼發現
Openharmony下面librknnrt.so是rknn-toolkit2-2.3.2\rknpu2\runtime\Linux目錄下面的so,而樣例代碼rknn-toolkit2-2.3.2\rknpu2\examples\rknn_matmul_api_demo目錄下面的build-linux.sh腳本里面有詳細的交叉編譯工具鏈信息:
所以我就想既然rknn_matmul_api_demo是用gcc-linaro-7.5.0-2019.12-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu這個生產的而且rknn_matmul_api_demo也鏈接了librknnrt.so,那我把gcc-linaro-7.5.0-2019.12-x86_64_aarch64-linux-gnu/aarch64-linux-gnu/libc/lib下面的所有的庫和librknnrt.so一起拷貝到我工程的libs目錄下不就行了嗎。于是就試了一下,發現還是報同樣的錯誤:librknnrt.so加載時失敗了,于是我認為通過DevEco(4.0)開發的Native程序在啟動的時候只會加載系統指定的運行時庫,就算我把librknnrt.so和依賴的運行時庫一起考到libs目錄下面也沒有任何用處。看來只能想其他辦法了
三 如何解決:
通過上面的試驗我們得出結論:
- librknnrt.so依賴的運行時庫為一個版本,設為runtime1
- DevEco(4.0)編譯出來的程序依賴的運行時庫是 runtime2
- 鴻蒙系統(4.0)本身依賴的運行時庫是runtime2
于是我想出了一個辦法如下圖:
思路:
????????1. 寫一個服務進程,假設為rknn_server,使用gcc-linaro-7.5.0-2019.12-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu交叉編譯工具鏈編譯,其從系統啟動的時候就一直常駐在后臺,隨時準備為客戶端提供rknn推理服務
????????2. 寫一個rknn_client.so 使用DevEco(4.0)開發,把librknnrt.so庫中提供的接口根據需要暴露出來給給應用層使用,這樣rknn_client.so就相當于librknnrt.so的代理
????????3. rknn_client.so和rknn_server間的進程間通信需要用到內存映射技術,因為推理輸入張量和輸出張量占用的內存大小往往很大,就拿輸入張量做個例子,假設輸入張量的格式為“NHWC int8" NHWC分別為 1,640,640,3,那么其占用的內存大小就是 640*640*3=1228800,差不多1.2M,為了保證性能最優,這么大級別的內存拷貝需要采用高性能的IPC技術,比如內存映射。除了傳遞數據,我們還需要一個通道用來發送命令(比如加載模型命令和推理命令),這時候套接字或者管道就可以用來做這個事情。
運行結果:
- 數據:
模型 | 單次推理耗時 | CPU idle | rknn_server CPU |
yolo11n | 300ms | 280% | 50% |
yolo11n_pose | 380ms | 280% | 50% |
yolo11s_pose | 580ms | 300% | 40% |
說明:
????????A:rk3566 是4核CPU(400%),NPU算力是0.8TOPS(int8)。
? ? ? B:Demo的CPU消耗還包含攝像頭采集、ISP算法、UI渲染和目標框的渲染,我們看下yolo11n下的top的數據(其他兩種情況差不多):
讀者可能會問,你的客戶端進程CPU怎么這么低,主要的原因是作者把攝像頭采集這塊優化到了極致,大家可以參考作者的另外一篇文章:
Openharmony4.0攝像頭采集+編碼器+預覽的優化_openharmony 相機預覽-CSDN博客,里面攝像頭方面的優化思路,上面三種情況下對象檢測和姿態檢測是在最高分辨率下的實時識別(3264x2448x15fps采集,識別的頻率是同一時刻只能有一次識別在進行,如果攝像頭采集的幀率大于識別的幀率就采用丟幀策略)
實際效果:
四 結尾:
?Openharmony4.0的官方系統代碼里面沒有rknn驅動的支持,需要大家自行移植并且成功驅動rknpu,網上參考有不少,這里就不在深入說明了 :)
如果大家有更好的思路或者建議,歡迎評論區留言 :)