原创 基于TOF微型多区激光传感器在MCU上的AI手势识别

2024-12-4 11:20 83 0 分类: 智能硬件 文集: senser
TOF多区传感器: ND06  

 ND06是一款微型多区高集成度ToF测距传感器,其支持24个区域(6 x 4)同步测距,测距范围远达5m,具有测距范围广、精度高、测距稳定等特点。适用于投影仪的无感自动对焦和梯形校正、AIoT、手势识别、智能面板和智能灯具等多种场景。


          
       如果用ND06进行手势识别,只需要经过三个步骤:

 第一步  :  对ND06的数据采集,要多快好省。
            多  ---  指的是一次采集24个区域(6 x 4) 的数据。
            快  ---  30HZ 的采集速度。
            好---数据要准确
            省是省电  。

            你想,每秒30*24 个数据,  假设一个手势用时1.2s,   则需要 1.2*30*24 个数据来分析。
           每次手势还不同,如果同这么多数据分析出您是左滑还是右滑,还是画个Z,   
            如果用单纯程序来分析出来,估计会让所有的程序员崩溃。 
            好在现在这种分析,可以让AI 来干。 

              采集数据 就是在传感器前面,做出100个(这个越多越好,我是由于体力不支外加懒惰)正确的动作,譬如说左滑,让传感器采集。
             从而得到要训练的数据。
             鉴于数据量大,保证在MCU不合适, 因此每采集一帧,用UART口传到PC 上进行保存。
            无论动作快慢,都上传保存1.2s 的数据。

            MCU 用的是ESP32,   软件可以用arduino IDE,  但是我用的PlatformIO    
             ESP32 Arduino  2.0.3 
             关于安装之类的就不进行基础说明了。

     第二步是训练。 
             得到训练的数据后,首先要进行数据的转换。
             因为上传上来的数据是BIN格式, 是16进制的数据, 存放在文件中。
             这时候 BIN 文件 转换成 SCV 文件 ,格式转换是为了给PYTHON 分析和使用。 
             转换文件:bin2scv.py
             这个程序的作用就是打开,取出数据,转换成字符串, 每个字符串中间加分隔符,每一帧字符串后 加 换行符。

              数据分析用的一下版本:  这个要注意,有些软件版本兼容性比较差,一旦改版本,出现的错误会让人头大。
               python 3.8.10
               TensorFlowLite_esp32 0.9.0

                训练程序 :tinyml.py    (注明:本程序是大神HIGHT 所写,我只是为自己的应用改动了一点点参数)
                 
                   tinyml.py  的主要作用有5个步。
                 
                 1) 数据标准化
                           把数据变成-1 至1 之间的浮点数,并存入数组。 
                          这个过程内存消耗有点惊人。

             
                  2) 创建模型:       一共创建了3层模型。
                    第一层输入层是1.2 秒采集的所有数据。 偏置是32;
                    model.add(layers.Dense(32, activation='relu', input_shape=(16*SAMPLES_PER_GESTURE,)))
                    第二层是隐藏层   ,偏置16
                  model.add(layers.Dense(16, activation='relu'))             #隐藏层 
                     第三层是输出层
                    model.add(layers.Dense(2, activation='softmax'))           #最后输出2个。  输出层

          3) 训练模型 (核心)
  SampleCount = len(x)
                 # split into train, validation, test
             TRAIN_SPLIT =  int(0.8 * SampleCount)           #用80%的用来训练
             x_train, x_validate = np.split(x, [TRAIN_SPLIT, ])  #训练集
             y_train, y_validate = np.split(y, [TRAIN_SPLIT, ])
 
             history = model.fit(x_train, y_train, epochs=600, batch_size=16, validation_data=(x_validate, y_validate))   
             可以看出,训练的程序也很简单,就一句话的事情。
                          关于为什么用80%的数据来训练,是因为要保留20% 的数据来进行验证。
                       我理解这个训练 ,  就是拟合。
           
                   我增加了一段图形程序 ,来看看是否有过拟合的现象
              # 用图形软件 查看是否过拟合,欠拟合
                loss = history.history['loss']
                val_loss = history.history['val_loss']
            epochs = range(1,len(loss)+1)
            plt.plot(epochs,loss,'g.',label = 'Training loss')
            plt.plot(epochs,val_loss,'b',label='Validation loss')
            plt.title('Training and validation loss')
            plt.xlabel('Epochs')
            plt.ylabel('loss')
            plt.legend()
            plt.show()

                  过拟合就是你给的数据都太一致,重复性太强,AI学习的曲线太一致,造成的结果就是一旦给出的手势稍微不一样,AI就会判断出错。  太认死理。

            4) #模型优化  把tensorflow 的模型 转换成 tensorflow lite ;  做个简化版本
                 这步的作用是我们用的MCU ,资源受限,不要太臃肿的模型。

            5)把 简化版本tensorflow lite  转换成能用于C 编程的头文件
                   最终的结果是得到一个生成的model.h



第三步   得到.H  文件后, 放入MCU

              #include
#include
#include
#include "tensorflow/lite/experimental/micro/kernels/all_o
#include "tensorflow/lite/experimental/micro/micro_error_r
#include "tensorflow/lite/experimental/micro/micro_interpr
#include  
// #include "sine_model_data.h"
#include "model.h"            // 加载TENSORflow LITE  模型


在MCU 程序中,采集到1.2秒的数据后,就把数据标准化后 放在模型的端口上,让其分析。
根据输出的tflOutputTensor->data 的值,比较阀值 ,得出结论。
if (Inference()) Result();

核心程序:
bool Inference()  
{
  bool ret = false;

  // Copy Data to Model Input
  for (int k = 0; k < SAMPLE_COUNT; k++) {
    for (int i = 0; i < 16; i++)  {
      tflInputTensor->data.f[k * 16 + i] = Samples[k];  //把模型数据  放到模型的端口上  
    }
  }
//   ExeTimer.Start();
  TfLiteStatus invokeStatus = tflInterpreter->Invoke();     // 启动模型推理
//   unsigned long t = ExeTimer.End();

  // Check Result
  if (invokeStatus == kTfLiteOk) 
  {
    // sprintf(SerialBuffer, "Invoke In %8.6f(ms)", t / 1000.0);
    // Serial.println(SerialBuffer);

    ret = true;
  }
  else 
  {
    Serial.println("Invoke failed!");
  }

  return ret;
}  

  return ret;
}  

作者: esad0, 来源:面包板社区

链接: https://mbb.eet-china.com/blog/uid-me-1039027.html

版权声明:本文为博主原创,未经本人允许,禁止转载!

文章评论0条评论)

登录后参与讨论
我要评论
0
0
关闭 站长推荐上一条 /2 下一条