美文网首页工作生活
基于CNN的婴儿睡觉状态识别(下)—导入单片机

基于CNN的婴儿睡觉状态识别(下)—导入单片机

作者: 比死宅还鹅心 | 来源:发表于2019-06-30 21:26 被阅读0次

    前言

      模型训练部分已在上篇介绍:
    https://www.jianshu.com/p/22b691696d53
      本章将主要讲述如何将模型导入到单片机里,实时地根据摄像头读取的信息,经过我们的网络输出后反馈到单片机,控制输出的高低电平,从而实现温度控制。

    设备介绍

      我们使用了一款叫做角蜂鸟的可以外接在树莓派的神经网络加速器,它的神经网络计算部分是基于movidius芯片,而且有个大大的好处就是,一般模型转化还需要movidius神经计算棒,而角峰鸟貌似是自带此模块的,可以直接调用相应接口就能实现模型转换。


    角峰鸟

    树莓派也不用多介绍了,很有名的一块板子


    树莓派(左)角峰鸟(右)
    上图中,左边是树莓派,右边是角峰鸟,可以看到角峰鸟自带了摄像头,导入进去后直接通过接口调取自带摄像头就好了。

    不过要在树莓派上调用角峰鸟,需要事先配置好角峰鸟运行环境

    git clone --recursive https://github.com/HornedSungem/SungemSDK-Python.git 
    git clone https://github.com/HornedSungem/SungemSDK-Python.git
    cd SungemSDK-Python
    git submodule update --init --recursive # 初始化并更新子模块
    
    

    模型转换

    按照官方的说法,角峰鸟只支持caffe和tensorflow模型,刚好我们的模型符合条件,模型转换接口如下

    python3 mvNCCompile.py network # network 为模型文件,如 *.prototxt(Caffe)* 或 *.meta(Tensorflow)*
    -h                             # 显示帮助信息
    -w WEIGHTS                     # 模型权重文件(Caffe), 如 *.caffemodel*
    -in INPUTNODE                  # 输入节点名称,默认Caffe为 *data*, TF为 *input*
    -on OUTPUTNODE                 # 输出节点名称,默认Caffe为最后一层,TF为 *output*
    -o OUTFILE                     # 输出HS Graph路径
    -s NSHAVES                     # 使用Movidius的Shave数量,建议为8,默认为1
    -is INPUTSIZE INPUTSIZE        # 更改输入网络尺寸,默认为网络输入尺寸。
    

    把之前保存的四个文件meta、index、checkpoint等拉到指定目录,在tool目录下打开命令行,或者cd到tool目录,根据接口的的用法输入对应语句

    $ python3 mvNCCompile.py -s 8 ./example/baby_6_26/baby.ckpt-79.meta -in input -on softmax_output -o graph_baby_6_26
    

    注意这里的in和on 就是之前tensorflow模型里面定义的输入和输出节点的名字,分别对应节点为xs,prediction_softmax。

    然而万万没想到的是,转换报错了:

    Erro:you can not feed a number with type[?,128,128,3]:first dimension must be 1
    

    这才想起来了我们定义xs这个placeholder的时候为了图方便,把格式定义成了

    xs = tf.placeholder(tf.float32, [None, 128, 128,3], name = 'input')   # 128x128
    

    第一维是None,而这个模型转换只支持第一维是1.

    关于转换失败的解决方法

    这个问题看起来挺好解决的,但实际操作起来挺让人抓狂的,各种方法都不奏效,甚至卡了我整整一星期·····

    用restore重载训练好的参数

    首先我想着利用把之前训练好的模型参数,重写一个一样结构的网络,但xs的维度设置为一维

    xs = tf.placeholder(tf.float32, [1, 128, 128,3], name = 'input')   # 128x128
    

    用save.restore的方式,重新加载网络的参数,就像以前用迁移学习的方法加载预训练的vgg-16网络参数一样,然而不知为何总是失败,也不知道是哪一步写错了,反正最后没走通。

    换用mini-batch

    后来又想了一个办法,重新换用Mini-batch的思想训练我们的网络,只不过这次每个batch只有一个样本。然而还是失败了,每步训练的loss严重震荡,在正样本时变为0.3,负样本时又突然上升到1.4...

    突然意识到可能是优化器的学习率设置太大了,所以每一步训练的长迈的过大,导致一直在局部最优头顶跨来跨去,因此把学习率从1e-4改到1e-5

    train_step = tf.train.AdamOptimizer(1e-5).minimize(_loss)
    

    结果证明可行,很好地收敛了


    训练结果

    将重新训练的模型再次导入树莓派,调用模型转换接口,这次终于转换成功了,获得了转换后的graph文件。

    但还是有点想不通的是,之前学习吴恩达机器学习的课程时,说到了mini-batch和full-batch是可以采用一样步长的,mini-batch虽然训练过程会绕来绕去,最终会和full-batch收敛到同一处。
    然而我们此例如果不修改步长则会无法收敛,想不明白是什么原因··但总归收敛了,等项目做完了再回头琢磨


    紫线mini-batch/蓝线full-batch

    调用模型

    将刚才生成的graph文件保存在转化模型的统一文件夹里,可以调用角峰鸟的api来开始识别了
    首先包含好需要的头文件,记得需要包含接口的头文件hspai,和opencv的头文件cv2,因为需要在输出界面做一些操作

    import cv2, numpy, sys
    sys.path.append('../../../')
    import hsapi as hs # 导入 hsapi 模块, 注意导入路径
    

    设置预处理参数,就是归一化参数了,因为我们是(0~1)范围的归一化,只需要每个像素值乘以0.007843/2就行了,不需要偏移

    scale = 0.007843/2 # 图像预处理参数
    mean = 0 # 图像预处理参数
    

    开启相机,读取模型

    device_list = hs.EnumerateDevices() # 获取所有已连接的角蜂鸟
    device = hs.Device(device_list[0]) # 获取Device实例
    
    device.OpenDevice() # 打开角蜂鸟设备
    
    with open('/home/pi/SungemSDK-GraphModels/graphs/graph_baby_6_26', mode='rb') as f:
        data = f.read()
    graph = device.AllocateGraph(data, scale, mean) # 获取Graph实例
    

    之后相机就会对每张读取的参数进行操作,使用while true循环

    try:
        while True:
            # 使用自带摄像头作为输入
            image = graph.GetImage(True) # 用角蜂鸟设备图像作为神经网络输入
            size=image.shape
            cx=int(size[0]/2)
            cy=int(size[1]/2)
            dx=int(size[0]*0.3)
            dy=int(size[1]*0.3)
            output, _ = graph.GetResult() # 获取神经网络输出结果
            if output[0] >0.5:
                label='baby is covered'
            else:
                label='baby is not covered'
            cv2.putText(image,label,(cy-dy, cx-int(dx*1.1)),cv2.FONT_HERSHEY_SIMPLEX,0.5,(0,255,255),2)
            print(output)
            cv2.rectangle(image,(cy-dy, cx-dx), (cy+dy, cx+dx),(255,255,0),5)
            cv2.imshow("baby_recgonition", image)
            cv2.waitKey(1)
    finally:
        graph.DeallocateGraph() # 释放神经网络资源
        device.CloseDevice() # 关闭角蜂鸟设备
    

    上面的output就是图像输入模型得到的输出

     output, _ = graph.GetResult() # 获取神经网络输出结果
    

    然后我们需要对输出作翻译,利用opencv将翻译结果输出到屏幕上

     if output[0] >0.5:
                label='baby is covered'
            else:
                label='baby is not covered'
            cv2.putText(image,label,(cy-dy, cx-int(dx*1.1)),cv2.FONT_HERSHEY_SIMPLEX,0.5,(0,255,255),2)
            print(output)
    

    再画一个框,来显示识别的区域,我们先前定义cx,cx,dx,dy就是框的位置和大小

            cv2.rectangle(image,(cy-dy, cx-dx), (cy+dy, cx+dx),(255,255,0),5)
            cv2.imshow("baby_recgonition", image)
            cv2.waitKey(1)
    

    模型嵌入的代码请移步至我的github:https://github.com/huangchuhccc/baby_recognition_by_Horned_sungem_using_tensorflow
    最终识别演示如下
    https://v.youku.com/v_show/id_XNDI1MDAzOTc0OA==.html?spm=a2h3j.8428770.3416059.1
    后续又实现了利用婴儿是否入睡的判定与微信的实时提醒,请移步这篇博客:
    https://www.jianshu.com/p/45918d2ed025

    相关文章

      网友评论

        本文标题:基于CNN的婴儿睡觉状态识别(下)—导入单片机

        本文链接:https://www.haomeiwen.com/subject/vhemcctx.html