

























相信很多读者和我一样,都有一个巫师梦,等着自己的那只猫头鹰送来霍格沃茨的入学通知书。世界各地的麻瓜们也在乐此不疲地使用科技构筑自己的魔法世界。前段时间B站就有一个视频大火,使用自制魔杖控制家居设备,很是浪漫!1但很多巫师丢失魔杖后只能任人摆布,还是邓布利多和伏地魔的无杖施法更吸引人,本麻瓜当然更向往这种高阶法术(不是)。

无杖施法虽然摆脱了魔杖,但还是至少要挥一挥手,摆个 pose 的。那么是否有识别我的姿态和手势,来理解我的意图的方法呢。
经过调研,使用机器视觉是一个可行的方案,我发现不少人已经做了相关尝试,甚至商用摄像头也初步做过简单手势控制功能。而大多数开源项目采用 Mediapipe 方案,这是一个 Google 开源的手势识别套件,使用简单,但缺陷在于仅支持单人、近距离识别。2且多数项目仅仅在 PC 上调用 Mediapipe 库做了一些简单的特性演示,没有进一步扩展实用功能。
我曾计划在自己的服务器上实现该项目,但考虑到项目对实时性要求高,采用了边缘计算(Edge Computing)方案。对物联网而言,边缘计算意味着许多控制将通过本地设备实现而无需交由云端,大大提升处理效率,由于更加靠近用户,还可提供更快的响应。但是,边缘设备上部署需要克服额外的困难,包括模型的转换与调整,精度与实时性能的权衡。
最终,基于边缘设备(香橙派),我开发了一个手势自动化项目,「Gesture-Based-Home-Automation」。
由于少数派附件的大小限制,难以展示长时高清的动画;本文所有演示将人与被控主体放在同一个画面,且手部尽可能正对镜头以展示手势细节;画面有裁剪、无加速。
「Gesture-Based-Home-Automation」的关键特性有:
下面展示了项目的部分关键特性。第一组为手部的 5 种基础手势与手掌旋转状态识别;第二组为左右手识别与跟踪、长距离手部定位与手势识别;第三组图为「抬手」触发与基础的开关灯控制演示;第四组图,通过空间定位与手势指令,呼唤扫地机器人前往人体地点进行清扫。(这里使用的是石头 G10S。)常规的做法是「点开 app-进入设备-设置-指哪到哪-点选-确认」,而语音控制无法完成该指令。








以上先演示了部分基础指令,下面先介绍其基本原理,然后引入「高级手势控制」,将更加符合人们习惯的动态手势转译为控制信号,进行更复杂的控制,更多演示可以跳转至高级手势与智能控制章节部分查看。
硬件组成:一个香橙派 5 Pro (Orange Pi 5 Pro)4,一个 5V4A 电源,一个 USB 摄像头。
Orange Pi 5 Pro,尺寸 89mm*56mm*1.6mm,采用了瑞芯微 RK3588S 处理器,四核 A76 + 四核 A55,主频 2.4GHz,集成 ARM Mali-G610;内嵌的 NPU 支持 INT4/INT8/INT16 混合运算,算力 6TOPS,4GB LPDDR5,Wi-Fi 5、BLE。支持 Orange Pi OS、Ubuntu、Android 12、Debian 等操作系统。摄像头为无畸变 86° 视角,1080P/30FPS 的 USB 摄像头。
这颗 6TOPS 的 NPU 是我选择香橙派 5 Pro 的主要理由,我们需要在上面做各种神经网络推理,同时它的价格足够低,总成本约 650 元。

在家居场景中,我们的关键需求是全天候运行、使用流畅。具体分析下来,手势使用频率相对不高,这决定了我们并不需要时时刻刻获取手势信息,而为了兼顾长时间运行,设计了这样一个流程:

在常规时间中(生活中的绝大多数时间),系统保持较低的功耗运行。需要手势控制时,又能快速切换模式,并实时地检测。设计切换信号(手腕高于肩部)可以避免误触发。基本满足了全天候运行、使用流畅的需求。小目标的检测往往是具有挑战的,通过人体框的裁剪,采用 Top-Down 模式,实现最远 5 米的手势识别,基本可以覆盖屋内的环境了。
模型选择方面,个人水平有限,考虑到时间成本,贯彻了「拿来主义」,搜刮了一些优秀的模型。但在过程中,转换为 3588 NPU 的模型格式时,又出现了各种问题,包括 rknn 的各种 bug,算子不支持等。后续会根据需要更换模型。当前版本的模型选择如下:
为了获取目标在空间中的位置,需要先估算深度,即目标距相机的距离(光轴方向)。常用的深度估计方法主要包括双目视觉深度估计和单目视觉深度估计。双目视觉使用两个相机,类似于人类的双眼。通过捕捉从两个不同视角的图像,利用视差来估计物体的深度信息。单目视觉的挑战在于,单个相机只能提供二维图像,没有直接的深度信息。因此,需要借助一些额外的信息来推断深度,如通过训练深度神经网络,利用标注数据来学习从单目图像中估计深度;利用物体的已知尺寸、平行线的消失点、阴影等几何特征来推断深度;通过分析物体在多帧图像中的移动情况来估计深度。

双目相机比单目相机贵得多,我想控制住预算,同时,我们只要十分粗略的空间位置,对精度要求很低。通过实验发现,在多数生活情况下,Gold Yolo 识别出的头部检测框宽度相对稳定(即使头部发生旋转),因此我们使用头部的宽度信息来标定人体距离相机的深度。获取头部宽度像素数,进而得到人体深度值,与相机的视角、拍摄像素计算,可以得到 (x, y, z) 坐标,组合得到每个手掌的 (x, y, z) 坐标值。该值为相机局部坐标系下的坐标,通过坐标变换,转换为全局坐标系下的坐标值:只需预设相机 x 轴和 y 轴在全局坐标系中的向量、相机原点在全局坐标系中的坐标值即可,分别为「vector_x」「vector_y」和「vector_o」。
获取手部的 21 个关键点坐标后,通过计算关节点的弯曲角度得到每个手指的弯曲状态(弯曲或伸直),组合每个手指的弯曲状态,定义了 5 种手势:「握拳」/「fist」,「一」/「one」,「二」/「two」,「三」/「ok」,「五」/「open」。由于拇指非常容易被遮挡,其弯曲状态容易误判,因此在这 5 种手势的判断过程中,拇指的状态不纳入考虑,即「四指张开」=「五指张开」。

使用手掌的关键点(0、5、9、13号关键点),计算得到手掌的「旋转角度」/「rotation」。从人的视角看,五指竖直向上为 0,逆时针为正,顺时针为负,单位为弧度,范围(-pi,pi)。
记录每一次检测的食指指尖坐标,获得手指移动轨迹,用线性回归拟合趋势线,根据纵横比和趋势线系数判断手势,分类为 4 种轨迹「上滑」/「UP」,「下滑」/「DOWN」,「左滑」/「LEFT」,「右滑」/「RIGHT」。拟合趋势线前会移除离群点,增加判断的准确性。轨迹判断具有冗余度,允许轨迹有一定倾角。这里只预设了最简单的 4 种轨迹,读者可以根据需要自行编写判断轨迹类别(如顺时针、逆时针)。对于更为复杂的手势,可以参考 hand-gesture-recognition-using-mediapipe 项目,训练轨迹分类模型。
总结下来,单次单手检测具有 5 种手势 4 种轨迹 N 种旋转角度类别,通过左右手的组合以及空间位置的判断,可以获得数千种手势控制信号。
现在我们已经能获取每一个时刻的手势信号了,即使对检测结果已经进行了初步处理,但是还是会有一些漏检误检。除此之外,我们还要将人类习惯的动态手势转译为控制信号。这里参考 GitHub - geaxgx/depthai_hand_tracker) 构建了一个 HandController 类。
HandController 基于事件循环,提前在配置中定义一个手势动作列表。手势动作将手势(例如,右手的 OPEN 姿势)与回调(识别手势时调用的函数)相关联。根据回调的运行时间和频率设置了 4 个模式:

同时引入以下三个参数:
下面举几个例子说明使用方法,更多高级手势可以由读者根据手势控制信号和 4 种控制模式自由组合而成(如单手单手势+多轨迹的组合,单手 0 多手势+时序组合判断等)。




家居的智能控制比较简单,可以直接在回调函数中调用相关 API 进行控制,如此例中的 Sony Bravia 电视5。也可以通过 Node-Red、n8n、Home Assistant 中的无代码组件设置触发流程。
代码示例中,提供了一个 Flask API 服务,调用可以暂停或恢复服务,以降低能耗或在特定情况下避免误触发:如人离家时,通过小米无线按钮,暂停服务;如人在家健身时,肢体变化较大,可能造成控制信号的误触发,需要暂停服务。当然,还有更多的切换方式,如通过人体存在传感器切换服务,或使用其它智能语音切换服务,或通过特定手势暂停服务,或通过无检测阈值暂停服务等等。
项目源码托管在 Github。考虑到项目的独立性,同时时间有限,有很多想法没融入到该项目中,欢迎大家一起讨论。
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。