手势识别算法¶
Copyright © Quectel Wireless Solutions Co., Ltd. 2026. All rights reserved.
在 Quectel Pi M2 Debian 系统 上,基于 手势识别算法 SDK,从零搭建一个带桌面图形界面的手势识别项目。
界面采用三栏布局:
左栏 —— 算法模块列表(当前为「Gesture Recognition」);
中栏 —— 标题 + 实时显示区 + 控制按钮;
右栏 —— 设备状态面板( RAM / ROM / CPU )。
提供两种体验方式:
USB 摄像头 —— 打开 USB 摄像头,实时识别画面中的手势;
本地图片 —— 选择一张本地图片,离线识别其中的手势。
算法能力¶
项目 |
说明 |
|---|---|
算法类型 |
手势检测 + 手势分类(级联) |
检测模型 |
board6_RK3576_Ubuntu_hand_det.engine(640²,手部框) |
分类模型 |
board6_RK3576_Ubuntu_gesture_cls.engine(224²,13 类) |
输入 |
BGR 连续内存(cv::Mat.data,rows×cols×3) |
输出 |
每只手一个 DetRes(框 xywh + 分类标签 + 置信度) |
手势标签示例:like(点赞)、dislike(倒赞)、call(打电话)、fist(握拳)、four(四指)、mute(静音)、ok、one(食指)、palm(手掌)、three(三指)、yeah(V 字)、other(其他)。
部署前准备¶
资源获取¶
名称 |
说明 |
|---|---|
|
手势识别算法包,含公开头、动态库、第三方运行时、模型与样例图 |
本教程提供的示例工程,含源码、Makefile 与三个脚本(复制依赖 / 编译 / 运行) |
算法 SDK 包 gesture_RK3576 的关键目录:
gesture_RK3576/
├── include/gesture.h # 算法公开头(集成时唯一需要包含的头)
├── lib/libgesture.so # 算法主库(检测+分类级联)
├── 3rd_party/
│ ├── modelinfer/ # 推理引擎 libinference.so + 授权 libauthorization.so
│ ├── rknn/librknnrt.so # RKNN 硬件运行时
│ └── opencv/ # 随包 OpenCV world(头 + libopencv_world.so)
├── models/ # 检测/分类 .engine 模型
├── images/hand_input.jpg # 验收样例图
└── example.cpp # 官方最小调用示例
硬件/系统要求¶
项目 |
要求 |
|---|---|
主控 |
Quectel Pi M2 |
系统 |
Debian / Ubuntu aarch64(带桌面环境,Wayland 或 X11) |
外设 |
USB 摄像头(体验实时识别时需要) |
连接 |
PC 可通过 |
软件要求¶
Demo 在Quectel Pi M2上本地编译,需确认以下软件已安装:
g++ --version # C++17 编译器
make --version
pkg-config --exists Qt5Widgets Qt5Gui Qt5Core && echo "Qt5 OK" # 显示用 Qt5(LGPL)
v4l2-ctl --version # 用于枚举和排查 USB 摄像头节点
界面方案说明:显示层采用系统自带的 Qt5,直接在桌面显示、支持全屏;采集/推理/绘制使用 工程内随包的 OpenCV world 单库,与系统 OpenCV 隔离,避免版本冲突。
工程结构¶
gesture_project/
├── src/
│ └── gesture_cam_demo.cpp # Demo 主程序(Qt 界面 + 工作线程推理)
├── include/ # setup 后:SDK 公开头 gesture.h
├── lib/ # setup 后:libgesture.so
├── third_party/ # setup 后:modelinfer / rknn / opencv 运行时
├── models/ # setup 后:检测/分类 .engine 模型
├── images/ # setup 后:样例图 hand_input.jpg
├── Makefile # 编译规则
├── build.sh # 一键编译
├── setup_from_sdk.sh # 从 SDK 包复制依赖到本工程
├── run.sh # 一键运行(配置环境变量 + 启动)
└── README.md # 说明文档
include/ lib/ third_party/ models/ images/ 由 setup_from_sdk.sh 从原始 SDK 包复制生成。
核心代码讲解¶
Demo 基于 SDK 公开头 gesture.h 的单类 Gesture,调用生命周期为:构造 →set_param(设阈值)→init(加载模型+授权)→inference(逐帧推理)→uinit(释放)。
源码位于 src/gesture_cam_demo.cpp,下面按调用顺序讲解关键片段。
初始化¶
Gesture g;
float conf = 0.25f, iou = 0.45f, det_thr = 0.0f, cls_thr = 0.20f;
g.set_param(conf, iou, det_thr, cls_thr); // 引用 4 参:检测置信度/NMS/det 过滤/分类阈值
char det_model[] = "models/board6_RK3576_Ubuntu_hand_det.engine";
char cls_model[] = "models/board6_RK3576_Ubuntu_gesture_cls.engine";
char license_buf[512] = "authorization.lic";
if (const char* license_path = std::getenv("GESTURE_LICENSE_FILE")) {
if (license_path[0]) std::snprintf(license_buf, sizeof(license_buf), "%s", license_path);
}
// p_auth 为算法授权码,经环境变量 GESTURE_AUTH_CODE 或命令行下发(SDK 不内置)
char auth_buf[512] = {0};
const char* auth = std::getenv("GESTURE_AUTH_CODE");
char* p_auth = nullptr;
if (auth && auth[0]) { std::snprintf(auth_buf, sizeof(auth_buf), "%s", auth); p_auth = auth_buf; }
GESTURE_RES_CODE rc = g.init(det_model, cls_model, license_buf, 3, p_auth);
if (rc != GESTURE_PROCESS_OK) { /* 初始化失败:检查授权码/模型路径 */ }
参数说明:
set_param(conf, iou, det_thr, cls_thr)(四个参数均为引用传入):conf:手部 检测 置信度阈值,默认0.25。低于此分数的候选框在检测阶段被丢弃;调高减少误检,调低召回更多手。iou:检测框 NMS(非极大值抑制) 的 IoU 阈值,默认0.45。两个重叠框 IoU 超过该值时只保留分数更高的,避免同一只手出多框。det_thr:检测结果 输出过滤 阈值,默认0.0(不过滤)。对最终输出的det_score再做一次下限筛选。cls_thr:分类 阈值,默认0.20。分类 top1 分数低于此值时,label置为other(不确定手势)。
init(p_det_model, p_cls_model, p_license_file, module, p_auth_code)加载两颗模型并完成授权,五个参数依次为:p_det_model:手部 检测 模型路径(hand_det.engine,输入 640²)。p_cls_model:手势 分类 模型路径(gesture_cls.engine,输入 224²,13 类)。p_license_file:算法授权文件的可写路径。项目中的run.sh通过GESTURE_LICENSE_FILE传入工程内绝对路径。module:平台标识,RK3576 固定传3。p_auth_code:算法授权码(SDK 不内置,经GESTURE_AUTH_CODE环境变量或参数下发)。
返回码
GESTURE_RES_CODE:0=GESTURE_PROCESS_OK成功;-1初始化失败(授权/模型路径);-2未初始化;-3推理失败;-4图像/参数非法。rc必须判等GESTURE_PROCESS_OK后才能推理。get_status(opt)查询状态:get_status(0)返回初始化状态(0成功);get_status(1)返回授权状态(0正式授权)。
推理¶
每一帧(摄像头帧或本地图片)都调用一次 inference,SDK 内部完成「检测 → 裁剪 → 分类」的完整级联,返回每只手的结果。
std::vector<DetRes> results;
// 入参:BGR 数据指针、行(高)、列(宽)、通道数(必须 3)、输出结果向量
GESTURE_RES_CODE rc = g.inference(frame.data, frame.rows, frame.cols, frame.channels(), results);
if (rc != GESTURE_PROCESS_OK) { /* 推理失败 */ }
for (const auto& r : results) {
// r.x, r.y, r.width, r.height : 检测框(原图像素坐标,xywh)
// r.det_score : 检测置信度
// r.class_id : 分类 ID(0~12)
// r.label : 手势标签(如 "fist")
// r.cls_score : 分类置信度
cv::rectangle(frame, cv::Rect(r.x, r.y, r.width, r.height), cv::Scalar(0,255,0), 2);
}
输入约定:
必须是 BGR 连续内存(
cv::Mat默认即连续;若isContinuous()为假需先clone()再传.data)。channels必须为3;灰度图需先cv::cvtColor转 BGR。SDK 内部自动完成 BGR→RGB、letterbox 缩放到 640²、
/255归一化与 int8 量化,调用方 无需做任何预处理。
级联流程(SDK 内部):
整图过
hand_det检测所有手,得到候选框并做 NMS;每只手按框 +10% 上下文裁剪,resize 到 224²,按 ImageNet 均值方差归一化;
过
gesture_cls得到 13 类 softmax,取 top1;分数低于cls_thr则标other。
输出结构DetRes(每只手一个):
字段 |
含义 |
|---|---|
|
检测框在 原图 的像素坐标(左上角 + 宽高) |
|
检测置信度(手部框可信度) |
|
手势分类 ID(0~12) |
|
手势名字符串(如 |
|
分类置信度(top1 softmax 分数) |
结果坐标已换算回原图,可直接用于
cv::rectangle/cv::putText叠加绘制;results为空表示当前帧未检测到手。
释放¶
g.uinit(); // 释放内部引擎资源
部署与运行¶
传输文件¶
以下分为 PC 端和设备端命令。PC 示例使用 Windows PowerShell;adb push 先把目录传到设备 /tmp
adb传输示例:
# adb push 传输文件夹先放到 Quectel Pi M2 的/tmp下
adb push gesture_RK3576 /tmp/
adb push gesture_project /tmp/
推送完成后,在Quectel Pi M2桌面会话中打开终端,以普通用户执行下列命令。
#$HOME 和 $USER 由设备 shell 展开;不要在 root adb shell 中执行,否则它们会指向 root 用户
sudo mv /tmp/gesture_RK3576 /tmp/gesture_project "$HOME"/
sudo chown -R "$USER:$USER" "$HOME/gesture_RK3576" "$HOME/gesture_project"
cd "$HOME/gesture_project" && chmod +x *.sh
安装编译环境和开发工具¶
在Quectel Pi M2终端中输入以下命令:
# 安装 make,g++等开发工具
sudo apt update && sudo apt install -y build-essential pkg-config qtbase5-dev
复制依赖¶
setup_from_sdk.sh 从 SDK 包把头文件、动态库、第三方运行时、模型、样例图复制到工程对应目录:
cd $HOME/gesture_project
sh setup_from_sdk.sh $HOME/gesture_RK3576 # 不传参数则默认 ~/gesture_RK3576
编译¶
sh build.sh
#产物:$HOME/gesture_project/gesture_cam_demo
build.sh内部调用make:用系统 Qt5 做界面,链接工程内libgesture.so与随包 OpenCV world生成可执行文件
运行¶
在Quectel Pi M2 桌面终端 中运行:
./run.sh # 自动探测 USB 摄像头
./run.sh /dev/video73 # 指定 USB 摄像头节点(跳过自动探测)
推荐从设备桌面会话的终端启动。若通过
adb shell启动,需切换到桌面登录用户,并确保XDG_RUNTIME_DIR、WAYLAND_DISPLAY或DISPLAY/XAUTHORITY与桌面会话一致;
界面操作¶
窗口为三栏布局:左栏算法模块、中栏显示与控制、右栏设备状态。
操作 |
说明 |
|---|---|
[Open Camera] |
打开/关闭 USB 摄像头,实时识别手势;关闭时自动清屏 |
[Local Image] |
弹出文件选择框,选一张图片离线识别 |
[Full Screen] |
切换全屏 / 窗口显示 |
[Quit] |
关闭程序 |
快捷键 |
Esc/Q 退出,F 全屏 |
设备状态面板(右栏) 每秒刷新一次:
指标 |
说明 |
|---|---|
RAM |
已用百分比 + 已用/总量(MB) |
ROM |
根文件系统已用百分比 + 已用/总量(GB) |
CPU |
CPU 占用百分比 |
授权说明¶
SDK 采用 先引擎、后算法 的两层授权,集成方只需提供 算法授权码(p_auth_code),其余随包固定:
授权码通过环境变量
GESTURE_AUTH_CODE下发(run.sh已export,集成方替换为自己的码即可);授权文件路径由
GESTURE_LICENSE_FILE指定,run.sh默认设为工程目录内的绝对路径authorization.lic;引擎授权件ql_ans_license.txt也保存在工程目录。首次联网授权成功后文件会生成,后续启动复用。正确算法码 → 正式授权(
get_status(1)=0);授权校验返回
100010且 SDK 探测确认授权服务器不可达时,进入算法 30 分钟 Trial(get_status(1)=100010,到期映射100002);空码或在线错码会使
init失败,不进入算法 Trial。
查询状态:get_status(0) 初始化状态(0 成功),get_status(1) 授权状态。
应用演示¶
常见问题排查¶
问题 |
可能原因 |
解决方案 |
|---|---|---|
初始化失败 rc=-1 |
授权码为空/错误,或模型路径不对 |
设置 GESTURE_AUTH_CODE;确认 models/ 下两个 .engine 存在 |
窗口不显示 |
非桌面会话 / QT_QPA_PLATFORM 未匹配 |
在设备桌面会话运行;或由 run.sh 自动探测 Wayland/xcb |
无法打开摄像头 |
USB 摄像头未插入 / 指定节点错误 |
默认 auto 会自动探测 USB 采集节点;可用 v4l2-ctl --list-devices 确认后 ./run.sh /dev/videoN |
链接/运行报找不到 .so |
LD_LIBRARY_PATH 未设置 |
用 run.sh 启动(已配置),或手动 export 工程内 lib 与 third_party/* 路径 |
识别结果为空 |
画面无手势 / 置信度过低 |
让手部清晰入镜;可调 set_param 的检测置信度与分类阈值 |
技术支持与贡献¶
如在使用过程中遇到问题,欢迎在 移远官方论坛 提交技术咨询,移远技术支持团队将及时为您解答。
项目开源地址: https://github.com/Quectel-Pi/demo-edge-algorithms
欢迎提交Issue反馈问题或Pull Request贡献代码改进!