2024艾迈斯欧司朗竞赛 - 基于dToF手势识别的PC万能控制器
该项目使用了艾迈斯欧司朗的dToF传感器,实现了PC万能控制器的设计,它的主要功能为:基于手势识别的PC自定义控制。
标签
嵌入式系统
枫雪天
更新2025-03-06
56

任务介绍

    本项目实现了dToF传感器光电设计竞赛的自由命题,使用官方提供的dToF传感器和RP2040 Game Kit开发板,实现了基于手势识别与PyQt上位机的PC万能控制器。

硬件平台

    首先介绍本次用到的开发板:RP2040 Game Kit,这是一块基于RP2040微控制器的游戏机开发板,它板载了LCD、蜂鸣器、摇杆、按键和姿态传感器等常用外设,并且预留了扩展排针以及Debug接口,可玩性非常高,因此它在硬禾项目中的出场率非常高,本次也将作为主控来驱动活动的传感器。

接下来是本次活动的核心器件:艾迈斯欧司朗dToF传感器模块是基于 TMF8821 设计的直接飞行时间 (dToF) 传感器模块,支持 3x3、4x4 和 3x6 多区域输出数据以及宽广的、动态可调的视野。这次TMF8821 dToF传感器模块与RP2040 Game Kit管脚匹配,插上直接可以使用,虽然官方也放开可以使用其他开发板来作为主控,但Game Kit已经足够使用,本次我会使用官方提供的两个原装设备,做一个基于手势识别与PC上位机的PC万能控制器



任务分析与实现

这次主办方出了四种任务,侧重于传感器的各种应用场景

  • 任务一是角度测算,计算传感器平面与桌面的夹角;
  • 任务二是手势识别,通过识别结果控制屏幕上的菜单;
  • 任务三是液体种类识别,通过测量折射率区分液体种类;
  • 最后是自由命题。

    这次我选择了自由命题。和任务二的手势识别很像,因为我感觉相比小屏幕上的菜单。用手势识别的结果来控制电脑更有实用价值,比赛结束后板卡和传感器不用吃灰,可以直接投入日常使用。

方案框图:

PlantUML diagram

一、硬件感知层

  1. ToF传感器模块
    • 采用TMF882X激光测距芯片,通过I2C总线(引脚16/17)获取9通道距离数据
    • 工作参数:50ms采样周期,200+置信度阈值,有效检测范围30-120cm
    • 输出3x3距离矩阵,每个单元存储毫米级精度测量值
  2. 显示输出模块
    • 240x240 TFT显示屏,采用SPI接口驱动
    • 方向指示:使用黄色三角显示手势识别方向
    • 文本显示区域固定在屏幕底部,使用20号字体并预留10像素边距

二、数据处理层

  1. 数据预处理管道
    • 双缓冲机制:prevDistanceMatrix/distanceMatrix存储连续两帧数据
    • 一阶差分:逐元素相减生成3x3变化量矩阵
    • 数据校验:通道索引有效性检查,过滤异常值
  2. 核心识别算法
    • 特征提取:计算行差(row0_sum-row2_sum)和列差(col0_sum-col2_sum)
    • 双重阈值
      • 动态比例阈值:maxVal/minVal > 1.5(判断手势存在)
      • 绝对差值阈值:行/列差绝对值 > 100mm(识别运动方向)
    • 决策逻辑:优先水平方向判断,正差左移/负差右移;垂直方向次优判断
    • 跳帧机制:识别成功后跳过下一处理周期

三、人机交互层

  1. 本地显示
    • 图形引擎:基于TFT_eSPI库实现,采用直接写屏模式
  2. 串口通信协议
    • 数据格式:ASCII文本行,带UNICODE方向符号
      • 有效数据:"↑ UP\n"、"↓ DOWN\n"等
      • 无效数据:"NONE\n"
    • 传输参数:115200bps波特率,8N1格式,硬件流控关闭

四、上位机联动层

  1. 数据解析模块
    • 关键词匹配:识别包含UP/DOWN/LEFT/RIGHT的字符串
    • 有效性校验:仅接受带方向符号的标准格式数据
  2. 系统集成
    • 热键映射:固定组合键触发(如Ctrl+Alt+方向键)
    • 状态反馈:通过系统托盘图标显示连接状态
    • 配置存储:Windows注册表保存端口和快捷键设置

代码详解

下位机整体软件流程图:

本次项目涉及到了几个关键技术,接下来结合相关代码来进行讲解

  • 手势识别算法: 基于TMF882X dToF传感器采集3x3空间距离矩阵,采用帧间差分法计算动态变化量。通过极值比阈值(MAX/MIN>1.5)过滤噪声,最后执行手势方向检测算法。
    • 帧间差分法
      • 使用变化量代替原始值进行判断,可以提高对不同传感器位置的适应性。
void updateDistanceMatrix() {
    // 增强型数据保存(带溢出保护)
    static bool firstUpdate = true;
    if(firstUpdate) {
        memset(prevDistanceMatrix, 0, sizeof(prevDistanceMatrix));
        firstUpdate = false;
    }
   
    // 使用memcpy替代循环拷贝
    memcpy(prevDistanceMatrix, distanceMatrix, sizeof(distanceMatrix));


    // 带数据校验的更新
    for (int i = 0; i < myResults.num_results; ++i) {
        if(myResults.results[i].confidence >= 200) {
            int channel = myResults.results[i].channel - 1;
            if(channel < 0 || channel >= 9) continue;  // 新增通道校验
           
            int row = channel / 3;
            int col = channel % 3;
            distanceMatrix[row][col] = myResults.results[i].distance_mm;
        }
    }
}

// 计算差分数据
    int diffMatrix[3][3] = {0};
    for (int i = 0; i < 3; i++) {
        for (int j = 0; j < 3; j++) {
            diffMatrix[i][j] = distanceMatrix[i][j] - prevDistanceMatrix[i][j];
        }
    }
    • 手势存在判断:
      • 对于整个手势识别系统的工作过程中,不存在手势才是常态。因此我们需要首先判断当前数据是否存在手势,随后在进行进一步的手势判断,这样可以极大的节省资源和算力。本项目我们使用极值比进行判断,当比值超过1.5时,认为存在手势。
    • 方向判断算法:
      • 以手势从上向下挥动为例,当手势进入传感器范围时,距离矩阵的第一行数据会明显变化;当手势离开时,最后一行的数据也会明显变化,我们通过阈值检测这一变化,就可以得到手势方向。其他方向的检测也同理。
// 手势参数
enum GestureDirection {
    NONE,
    UP,
    DOWN,
    LEFT,
    RIGHT
};

GestureDirection getGestureDirection(int diffMatrix[3][3]) {
    int rowSums[3] = {0};
    int colSums[3] = {0};


    for (int i = 0; i < 3; i++) {
        for (int j = 0; j < 3; j++) {
            rowSums[i] += diffMatrix[i][j];
            colSums[j] += diffMatrix[i][j];
        }
    }


    // 计算各方向差值
    int rowDiff = rowSums[0] - rowSums[2];  // 行差(左-右)
    int colDiff = colSums[0] - colSums[2];  // 列差(上-下)
    int absRow = abs(rowDiff);
    int absCol = abs(colDiff);


    // 候选方向存储
    struct {
        int value;
        GestureDirection positive;
        GestureDirection negative;
    } directions[2] = {
        {absRow, LEFT, RIGHT},   // 行差值对应左右
        {absCol, UP,   DOWN}     // 列差值对应上下
    };


    GestureDirection finalDir = NONE;
    int maxDiff = 0;


    // 遍历所有可能方向
    for (int i = 0; i < 2; i++) {
        if (directions[i].value > DIRECTION_THRESHOLD) {
            if (directions[i].value > maxDiff) {
                maxDiff = directions[i].value;
                finalDir = (i == 0 ? (rowDiff > 0 ? directions[i].positive : directions[i].negative)
                                   : (colDiff > 0 ? directions[i].positive : directions[i].negative));
            }
        }
    }


    return finalDir;
}

PyQt5 GUI与串口编程

采用PyQt5框架构建跨平台应用,通过QSerialPort实现自适应串口通信。使用Windows注册表持久化存储快捷键配置,结合pyautogui库实现系统级热键注入,支持毫秒级响应延迟。

  • 多线程通信架构
class SerialWorker(QObject):
dataReceived = pyqtSignal(str)

def __init__(self):
super().__init__()
self.serial = QSerialPort()
self.serial.readyRead.connect(self._readData)

def _readData(self):
while self.serial.canReadLine():
line = self.serial.readLine().data().decode()
self.dataReceived.emit(line.strip())
    • 专用工作线程避免界面冻结
    • 信号槽机制实现线程间通信
    • 自动重连功能(每秒检测端口状态)
  • 数据解析
def processData(data):
patterns = {
r'.*UP.*': 'UP',
r'.*DOWN.*': 'DOWN',
r'.*LEFT.*': 'LEFT',
r'.*RIGHT.*': 'RIGHT'
}
for pattern, cmd in patterns.items():
if re.search(pattern, data, re.IGNORECASE):
return cmd
return None
  • 自定义快捷键管理
class HotkeyManager:
def __init__(self):
self.mapping = self._loadConfig()

def _loadConfig(self):
return {
'UP': 'ctrl+alt+up',
'DOWN': 'ctrl+alt+down',
'LEFT': 'ctrl+alt+left',
'RIGHT': 'ctrl+alt+right'
}

def trigger(self, direction):
if combo := self.mapping.get(direction):
pyautogui.hotkey(*combo.split('+'))

上位机整体软件流程图:

效果展示

上位机GUI

1740758309801.png

开发板状态

遇到的难题与解决办法

重复检测与跳帧机制

我们知道,手势从上向下滑动时,会连续触发两次检测,因为我们的算法是根据实时数据计算的,就会先检测到第一行矩阵的变化,然后检测到第三行矩阵的变化,连续输出两次相反的结果。因此,我们抑制第二次输出,这里我们会用到跳帧机制:现在如果检测到了手势,那么就先停止下一次的数据处理与判断。

活动感想

本次大赛是我第一次接触dToF传感器,在本次手势识别算法的开发过程中,我深刻体会到嵌入式系统中数据驱动开发的魅力。通过3x3距离矩阵的实时处理,完成了从原始信号到有效手势的转化,这个过程让我对嵌入式系统中的工程算法设计有了新的认知,并最终完成了一个可以投入日常使用的手势识别控制器,非常有成就感

    感谢硬禾科技和艾迈斯欧司朗联合举办的竞赛,祝硬禾的活动越办越好!

软硬件
电路图
附件下载
源码.zip
团队介绍
个人
团队成员
枫雪天
评论
0 / 100
查看更多
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2024 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号