2024艾迈斯欧司朗竞赛 - 用TMF8821实现手势识别
该项目使用了TMF8821,实现了手势识别的设计,它的主要功能为:可识别挥动,接近/远离等手部动作,并使用手势动作控制屏幕上的菜单功能。
标签
手势识别
TMF8821
2024艾迈斯欧司朗竞赛
N947
冷月烟
更新2025-03-05
39

项目介绍

使用N947开发板驱动TMF8821实现手势识别功能,可识别挥动,接近/远离等手部动作,并使用手势动作控制屏幕上的菜单功能。

硬件介绍

MCXN947 MINI

MCX N94x采用两个高性能Arm®Cortex®-M33内核,运行频率高达150MHz,提供2MB闪存以及可配置的带完整ECC的RAM、DSP协处理器、并集成了eIQ Neutron NPU。与单独的CPU内核相比,NPU可提供高达42倍的机器学习(ML)吞吐量提升,从而能够减少系统唤醒的时间,并降低整体功耗。

多核设计通过智能、高效地将工作负载分配到模拟和数字外设,提高了系统性能并降低了功耗。这些器件配备了MCUXpresso Developer Experience(MCUXpresso开发人员体验)支持,可优化、简化和加速嵌入式系统的开发工作。

MCX N94x系列具有更广泛的模拟和电机控制外设,而MCX N54x系列集成了众多外设,包括带PHY的高速USB、安全数字化SD卡和智能卡接口等。

TMF8821

dToF模块是基于 TMF8821 设计的直接飞行时间 (dToF) 传感器模块,TMF8821采用单个模块化封装,带有相关的 VCSEL(垂直腔面发射激光器)。dToF 设备基于 SPAD、TDC 和直方图技术,可实现 5000 mm 的检测范围。由于它的镜头位于 SPAD 上,它支持 3x3、4x4 和 3x6 多区域输出数据以及宽广的、动态可调的视野。VCSEL 上方的封装内的多透镜阵列 (MLA) 拓宽了 FoI(照明场)。原始数据的所有处理都在片上进行,TMF8821在其 I2C 接口上提供距离信息和置信度值。

方案框图

未命名绘图.png

项目设计思路

TMF8821是一颗可以支持区域测量的dToF芯片,可以输出3X3的距离测量矩阵,同时还有一套置信度矩阵数据。

手势特点

这次实现的手势识别功能一共支持4种手势,向上挥手、向下挥手,手掌接近,手掌远离,分析这几种手势的特点。

向上挥手:根据手掌与传感器的距离涉及多种情况。这里我以较近的时候为主,手掌从传感器接受范围外移动到传感器接受范围外下方的距离检测优先检测到数据,之后是中间,最后是上面。

向下挥手:根据手掌与传感器的距离涉及多种情况。这里我以较近的时候为主,手掌从传感器接受范围外移动到传感器接受范围外上方的距离检测优先检测到数据,之后是中间,最后是下面。

手掌接近:根据手掌与传感器的距离涉及多种情况。这里我以较近的时候为主,手掌从传感器接受范围外移动到传感器接受范围内,接近传感器,最后移动到传感器接受范围外

实测现象

根据以上特点,经过实测后发现,当传感器上方无遮挡时,也就是超出最大量程的时候,数据置信度均为0,为减少感应范围,将TMF8821配置为近距离测量模式。

当手从上方或下方挥动是,先经过的测量位置部分数据置信度会先达到255,同时得到准确测量距离,可以根据此特点判断挥手方向。

当手接近或远离时,测量到的数据置信度均为255,同时测量到的距离会变化。

功能实现

根据实测完成功能逻辑开发,当输出数据置信度全0时为空模式,当检测到有上方或下方某一域的置信度先变为255时,得到手挥动方向。当置信度全为255时,记录最开始测量到的距离和最后测量到的距离。当重新变为空模式后,判定两次测量到的距离差是否大于阈值,并根据正负得到手接近或远离手势,当不为接近或远离时,根据手挥动方向,得到向上挥手、向下挥手两种手势结果,并根据得到手势操作方式控制菜单界面。

软件流程图

未命名绘图.drawio.png

关键代码

TMF8821初始化

void tmf8821_init()
{
    tmf8821_iic_write_byte(0xe0,0x01);
    SDK_DelayAtLeastUs(100 * 1000, SystemCoreClock);
    while(tmf8821_iic_read_byte(0xe0) != 0x41)
    {
        SDK_DelayAtLeastUs(1 * 1000, SystemCoreClock);
    }
   
    uint8_t appid = tmf8821_iic_read_byte(0x00);
   
    if(appid == 0x80)
    {
        tmf8821_iic_write(0x08, cmd_download_init, 4);
        cmd_status_read_data[2] = 0;
        while(cmd_status_read_data[0] != 0x00 || cmd_status_read_data[1] != 0x00 || cmd_status_read_data[2] != 0xFF)
        {
            SDK_DelayAtLeastUs(1 * 1000, SystemCoreClock);
            tmf8821_iic_read(0x08, cmd_status_read_data, 3);
        }
       
        tmf8821_iic_write(0x08, cmd_set_addr, 5);
        cmd_status_read_data[2] = 0;
        while(cmd_status_read_data[0] != 0x00 || cmd_status_read_data[1] != 0x00 || cmd_status_read_data[2] != 0xFF)
        {
            SDK_DelayAtLeastUs(1 * 1000, SystemCoreClock);
            tmf8821_iic_read(0x08, cmd_status_read_data, 3);
        }
       
        uint32_t image_pointer = 0;
        while(image_pointer < tmf882x_image_length)
        {
            uint16_t len = 128;
            if((tmf882x_image_length - image_pointer) < 128)
            {
                len = tmf882x_image_length - image_pointer;
            }
            data_ram[0] = 0x41;
            data_ram[1] = len;
            for(uint16_t i=0; i<len; i++)
            {
                data_ram[2+i] = tmf882x_image[image_pointer + i];
            }
            data_ram[2+len] = calculate_checksum(data_ram, len+2);
           
            tmf8821_iic_write(0x08, data_ram, 2+len+1);
            cmd_status_read_data[2] = 0;
            while(cmd_status_read_data[0] != 0x00 || cmd_status_read_data[1] != 0x00 || cmd_status_read_data[2] != 0xFF)
            {
                SDK_DelayAtLeastUs(1 * 1000, SystemCoreClock);
                tmf8821_iic_read(0x08, cmd_status_read_data, 3);
            }
           
            image_pointer += len;
        }
       
        tmf8821_iic_write(0x08, cmd_ramremap_reset, 3);
        SDK_DelayAtLeastUs(3 * 1000, SystemCoreClock);
        appid = tmf8821_iic_read_byte(0x00);
    }
   
    if(appid == 0x03)
    {
       
        tmf8821_iic_write_byte(0x08,0x16);
        SDK_DelayAtLeastUs(1 * 1000, SystemCoreClock);
        while(tmf8821_iic_read_byte(0x08) != 0x00)
        {
            SDK_DelayAtLeastUs(1 * 1000, SystemCoreClock);
        }
       
        tmf8821_iic_read(0x20, data_ram, 3);
       
        data_ram[0] = 1;
        data_ram[1] = 0x00;
        tmf8821_iic_write(0x24, data_ram, 2);
       
        tmf8821_iic_write_byte(0x34,6);
       
        tmf8821_iic_write_byte(0x31,0x03);
        tmf8821_iic_write_byte(0x08,0x15);
        while(tmf8821_iic_read_byte(0x08) != 0x00)
        {
            SDK_DelayAtLeastUs(1 * 1000, SystemCoreClock);
        }
       
        tmf8821_iic_write_byte(0xe2,0x02);
        tmf8821_iic_write_byte(0xe1,0xff);
       
        tmf8821_iic_write_byte(0x08,0x6E);
       
        tmf8821_iic_write_byte(0x08,0x10);
        while(tmf8821_iic_read_byte(0x08) != 0x01)
        {
            SDK_DelayAtLeastUs(1 * 1000, SystemCoreClock);
        }
    }
}

TMF8821读取

void tmf8821_read_distance(uint8_t *confidence, uint16_t* distance)
{
    while(GPIO_PinRead(BOARD_INITPINS_TMF_INT_GPIO, BOARD_INITPINS_TMF_INT_GPIO_PIN) != 0)
    {}


    uint8_t int_status = tmf8821_iic_read_byte(0xe1);
    tmf8821_iic_write_byte(0xe1, int_status);
   
    tmf8821_iic_read(0x38, data_ram, 3*18);
    for(int i=0; i<18; i++)
    {
        confidence[i] = data_ram[3*i];
        distance[i] = (data_ram[3*i + 2] << 8) + data_ram[3*i + 1];
    }
}

手势识别部分

void gesture_recognition()
{
    tmf8821_read_distance(confidence_original, distance_original);
    for(int i=0;i<3;i++)
    {
        for(int j=0;j<3;j++)
        {
            confidence[i*3+j] = confidence_original[j*3+i];
            distance[i*3+j] = distance_original[j*3+i];
        }
    }
   
    if((confidence[0] == 255 || confidence[1] == 255 || confidence[2] == 255)
    && (confidence[6] != 255 && confidence[7] != 255 && confidence[8] != 255))
    {
        if(brandish_flag == 0)
            brandish_flag = 1;
    }
    if((confidence[0] != 255 && confidence[1] != 255 && confidence[2] != 255)
    && (confidence[6] == 255 || confidence[7] == 255 || confidence[8] == 255))
    {
        if(brandish_flag == 0)
            brandish_flag = 2;
    }
    if(confidence[0] == 255 && confidence[1] == 255 && confidence[2] == 255
    && confidence[3] == 255 && confidence[4] == 255 && confidence[5] == 255
    && confidence[6] == 255 && confidence[7] == 255 && confidence[8] == 255)
    {
        if(pressure1 == 0)
        {
            pressure1 = distance[4];
            pressure2 = pressure1;
        }
        else
        {
            pressure2 = distance[4];
        }
    }
    if(confidence[0] == 0 && confidence[1] == 0 && confidence[2] == 0
    && confidence[3] == 0 && confidence[4] == 0 && confidence[5] == 0
    && confidence[6] == 0 && confidence[7] == 0 && confidence[8] == 0)
    {
        if(((int32_t)pressure1 - (int32_t)pressure2) > 30)
        {
            mode = 3;
        }
        else if(((int32_t)pressure1 - (int32_t)pressure2) < -30)
        {
            mode = 4;
        }
        else if(brandish_flag != 0)
        {
            if(brandish_flag == 1)
            {
                mode = 1;
            }
            else
            {
                mode = 2;
            }
        }
        brandish_flag = 0;
        pressure1 = 0;
        pressure2 = 0;
    }
}

主代码

int main(void)
{
    BOARD_InitHardware();
    LCD_init();
   
    GPIO_PinWrite(BOARD_INITPINS_TMF_EN_GPIO, BOARD_INITPINS_TMF_EN_GPIO_PIN, 1);
    SDK_DelayAtLeastUs(100 * 1000, SystemCoreClock);
    tmf8821_init();


    table[func_index].current_operation();
    LCD_update();
    while(1)
    {
        gesture_recognition();
       
        if(mode != 0)
        {
            LCD_Clear(0);
            if(mode == 1)      func_index=table[func_index].down;
            else if(mode == 2) func_index=table[func_index].up;
            else if(mode == 3) func_index=table[func_index].enter;
            else if(mode == 4) func_index=table[func_index].back;


            table[func_index].current_operation();
            LCD_update();
            mode = 0;
        }
    }
}

功能展示及说明

一级页面

05ce951ec751c0c5ec9f2555c60e04f.jpg

LED的二级界面

ffa2c5003dfafe05aeb18e80c93689b.jpg

LED1的三级界面

b9458f40fdfc66a33d86e59e1aa9a50.jpg

项目中遇到的难题和解决方法

问题:配置TMF8821 GPIO寄存器的代码无法生效

方法:最后加入tmf8821_iic_write_byte(0x08,0x10)后正常

对本次竞赛的心得体会

活动挺好的,希望加大力度。

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