手势识别算法

Copyright © Quectel Wireless Solutions Co., Ltd. 2026. All rights reserved.


在 Quectel Pi M2 Debian 系统 上,基于 手势识别算法 SDK,从零搭建一个带桌面图形界面的手势识别项目。

界面采用三栏布局:

  • 左栏 —— 算法模块列表(当前为「Gesture Recognition」);

  • 中栏 —— 标题 + 实时显示区 + 控制按钮;

  • 右栏 —— 设备状态面板( RAM / ROM / CPU )。

提供两种体验方式:

  • USB 摄像头 —— 打开 USB 摄像头,实时识别画面中的手势;

  • 本地图片 —— 选择一张本地图片,离线识别其中的手势。

../../../_images/image_HZtebOMjjo686cxlxVDcGUrpnuf.webp

算法能力

项目

说明

算法类型

手势检测 + 手势分类(级联)

检测模型

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(其他)。

部署前准备

资源获取

名称

说明

gesture_RK3576(算法 SDK 包)

手势识别算法包,含公开头、动态库、第三方运行时、模型与样例图

gesture_project(Demo 工程)

本教程提供的示例工程,含源码、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 可通过 adb shell 进入设备

软件要求

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 内部):

  1. 整图过 hand_det 检测所有手,得到候选框并做 NMS;

  2. 每只手按框 +10% 上下文裁剪,resize 到 224²,按 ImageNet 均值方差归一化;

  3. 过 gesture_cls 得到 13 类 softmax,取 top1;分数低于 cls_thr 则标 other。

输出结构DetRes(每只手一个):

字段

含义

x / y / width / height

检测框在 原图 的像素坐标(左上角 + 宽高)

det_score

检测置信度(手部框可信度)

class_id

手势分类 ID(0~12)

label

手势名字符串(如 fist、ok、palm)

cls_score

分类置信度(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) 授权状态。

应用演示

gesture-demo

常见问题排查

问题

可能原因

解决方案

初始化失败 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贡献代码改进!