RK3588芯片NPU的使用:Windows11 Docker中编译YOLOv8-Pose C Demo并在开发板运行实践
本文的目标
本文将在RKNN Docker环境中编译YOLOv8-Pose C Demo,并通过adb工具部署到RK3588开发板。
开发环境说明
- 主机系统:Windows11
- 目标设备:搭载RK3588芯片的安卓开发板
- 核心工具:包含rknn-toolkit2、rknn_model_zoo等工具的Docker镜像、ADB调试工具
YOLOv8-pose(姿态检测Pose Estimation)
姿态检测技术,简单来说就是让计算机看懂"人(或物体)在做什么动作"的技术。从最初只能模糊识别几个人体部位,到现在能实时追踪几十个人的复杂动作。
早期要画一个人的动作,计算机需要分两步:先拿红笔在照片上圈出所有人(目标检测);再换蓝笔在每个人身上标出关节(关键点检测)。那么它有两个问题:一是速度比较慢,二是如果第一步圈人圈错了,后面全错(比如把抱在一起的情侣识别成一个人)。
后来,工程师们发明了"多功能笔":一支笔同时画红圈和蓝点(检测+姿态估计同步进行),但笔的内部结构复杂,需要很贵的"画板"(GPU)才能运行。这次它的准确率大幅提升了(能区分重叠的人),也可以可以同时识别动作和物体(比如"举着杯子"的手)了。但是它还是不够快,安装和使用也比较复杂。
后来,YOLOv8-pose就来了。它能够按下快门瞬间,同时完成找人和标动作,又能适应各种设备,从千元机到专业相机都能用(对应YOLOv8n到YOLOv8x不同型号)。所以,它就火起来了。
有三个非常经典的案例参考:
- 健身房镜子:实时纠正你的瑜伽姿势
- 工厂质检:检查工人组装动作是否规范
- 游戏控制:用摄像头玩体感游戏不再卡顿
启动RKNN Docker环境
# 使用 docker run 命令创建并运行 RKNN Toolkit2 容器,如下命令仅供参考
# 并通过附加 -v <host src folder>:<image dst folder> 参数,将本地文件映射进容器中
docker run -t -i --privileged -v /dev/bus/usb:/dev/bus/usb -v D:\rknn\rknn_model_zoo-v2.3.0:/rknn_model_zoo rknn-toolkit2:2.3.0-cp38-custom /bin/bash
跑YOLOv8-pose例子
下载yolov8n-pose.onnx
cd /rknn_model_zoo/examples/yolov8_pose/model
./download_model.sh
将onnx转为RKNN格式
cd ../python
# python convert.py <onnx_model> <TARGET_PLATFORM> <dtype(optional)> <output_rknn_path(optional)>python convert.py ../model/yolov8n-pose.onnx rk3588
运行结果:
I rknn-toolkit2 version: 2.3.0
--> Config model
done
--> Loading model
done
I rknn building ...
I rknn building done.
done
--> Export rknn model
output_path: ../model/yolov8_pose.rknn
done
运行安卓系统C++例子
1. 编译
# go back to the rknn_model_zoo root directory
cd ../../
./build-android.sh -t rk3588 -a arm64-v8a -d yolov8_pose
毫无悬念的编译成功。
===================================
BUILD_DEMO_NAME=yolov8_pose
BUILD_DEMO_PATH=examples/yolov8_pose/cpp
TARGET_SOC=rk3588
TARGET_ARCH=arm64-v8a
BUILD_TYPE=Release
ENABLE_ASAN=OFF
DISABLE_RGA=
INSTALL_DIR=/rknn_model_zoo/install/rk3588_android_arm64-v8a/rknn_yolov8_pose_demo
BUILD_DIR=/rknn_model_zoo/build/build_rknn_yolov8_pose_demo_rk3588_android_arm64-v8a_Release
ANDROID_NDK_PATH=/rknn_model_zoo/android-ndk-r19c
===================================
-- Install configuration: "Release"
-- Installing: /rknn_model_zoo/install/rk3588_android_arm64-v8a/rknn_yolov8_pose_demo/./rknn_yolov8_pose_demo
-- Installing: /rknn_model_zoo/install/rk3588_android_arm64-v8a/rknn_yolov8_pose_demo/./model/bus.jpg
-- Installing: /rknn_model_zoo/install/rk3588_android_arm64-v8a/rknn_yolov8_pose_demo/./model/yolov8_pose_labels_list.txt
-- Installing: /rknn_model_zoo/install/rk3588_android_arm64-v8a/rknn_yolov8_pose_demo/model/yolov8_pose.rknn
-- Installing: /rknn_model_zoo/install/rk3588_android_arm64-v8a/rknn_yolov8_pose_demo/lib/librknnrt.so
-- Installing: /rknn_model_zoo/install/rk3588_android_arm64-v8a/rknn_yolov8_pose_demo/lib/librga.so
2.推送到开发板
# 切换到 root 用户权限
adb root
adb remount
adb push install/rk3588_android_arm64-v8a/rknn_yolov8_pose_demo /data/rknn-test
3.开发板上运行demo
adb shell
# 进入开发板中rknn_yolov5_demo目录
cd /data/rknn-test/rknn_yolov8_pose_demo/
# 设置依赖库环境
export LD_LIBRARY_PATH=./lib
# 运行可执行文件
./rknn_yolov8_pose_demo model/yolov8-pose.rknn model/bus.jpg
运行结果如下:
load lable ./model/yolov8_pose_labels_list.txt
model input num: 1, output num: 4
model is NHWC input fmt
model input height=640, width=640, channel=3
origin size=640x640 crop size=640x640
input image: 640 x 640, subsampling: 4:2:0, colorspace: YCbCr, orientation: 1
scale=1.000000 dst_box=(0 0 639 639) allow_slight_change=1 _left_offset=0 _top_offset=0 padding_w=0 padding_h=0
rga_api version 1.10.1_[0]
rknn_run
rknn_run time=22.99ms, FPS = 43.49
post_process time=0.47ms, FPS = 2145.92
person @ (108 234 223 536) 0.889
person @ (211 241 284 508) 0.872
person @ (477 235 560 518) 0.853
write_image path: out.png width=640 height=640 channel=3 data=0xb400007b08fc6000
main函数源码
int main(int argc, char **argv)
{if (argc != 3){printf("%s <model_path> <image_path>\n", argv[0]);return -1;}const char *model_path = argv[1];const char *image_path = argv[2];int ret;rknn_app_context_t rknn_app_ctx;memset(&rknn_app_ctx, 0, sizeof(rknn_app_context_t));init_post_process();ret = init_yolov8_pose_model(model_path, &rknn_app_ctx);if (ret != 0){printf("init_yolov8_pose_model fail! ret=%d model_path=%s\n", ret, model_path);goto out;}image_buffer_t src_image;memset(&src_image, 0, sizeof(image_buffer_t));ret = read_image(image_path, &src_image);if (ret != 0){printf("read image fail! ret=%d image_path=%s\n", ret, image_path);goto out;}object_detect_result_list od_results;ret = inference_yolov8_pose_model(&rknn_app_ctx, &src_image, &od_results);if (ret != 0){printf("inference_yolov8_pose_model fail! ret=%d\n", ret);goto out;}// 画框和概率char text[256];for (int i = 0; i < od_results.count; i++){object_detect_result *det_result = &(od_results.results[i]);printf("%s @ (%d %d %d %d) %.3f\n", coco_cls_to_name(det_result->cls_id),det_result->box.left, det_result->box.top,det_result->box.right, det_result->box.bottom,det_result->prop);int x1 = det_result->box.left;int y1 = det_result->box.top;int x2 = det_result->box.right;int y2 = det_result->box.bottom;draw_rectangle(&src_image, x1, y1, x2 - x1, y2 - y1, COLOR_BLUE, 3);sprintf(text, "%s %.1f%%", coco_cls_to_name(det_result->cls_id), det_result->prop * 100);draw_text(&src_image, text, x1, y1 - 20, COLOR_RED, 10);for (int j = 0; j < 38/2; ++j){draw_line(&src_image, (int)(det_result->keypoints[skeleton[2*j]-1][0]),(int)(det_result->keypoints[skeleton[2*j]-1][1]),(int)(det_result->keypoints[skeleton[2*j+1]-1][0]),(int)(det_result->keypoints[skeleton[2*j+1]-1][1]),COLOR_ORANGE,3);}for (int j = 0; j < 17; ++j){draw_circle(&src_image, (int)(det_result->keypoints[j][0]),(int)(det_result->keypoints[j][1]),1, COLOR_YELLOW,1);}}write_image("out.png", &src_image);out:deinit_post_process();ret = release_yolov8_pose_model(&rknn_app_ctx);if (ret != 0){printf("release_yolov5_model fail! ret=%d\n", ret);}if (src_image.virt_addr != NULL){free(src_image.virt_addr);}return 0;
}