本帖最后由 -YAYA- 于 2023-8-28 17:13 编辑
基于KPU卷积神经网络的智慧无人超市解决方案
作品来源:第五届全国青少年人工智能挑战赛-开源硬件创意智造专项赛优秀作品
参赛学生:浙江省诸暨市海亮高级中学 李孟阳
指导老师:赵鑫荣
【项目概述】
现代科技的不断发展,让诸多黑科技已经成为生活中的常态。人工智能逐渐替代人力,以其省时,省力,高效,安全,受到越来越多行业的追捧。二十年前,没人驾驶,飞机汽车也能跑,无人服务,商店也能运营,这些好像存在科幻小说的场景,如今已经出现并且作为一种新形态冲击着世人的传统观念。
无人超市的一经推出,就获得很多人的关注。消费者被这样的“无人”噱头所吸引,试想下,没有烦人的导购,没有令人头疼的长队,购物的过程是多么欢快。还有些聪明人却看到了无人超市里面所蕴含的无线商机。用技术替代昂贵的人工劳动力。原先运营一家超市,即使再小,也需要两到三人才能运行起来,而新技术的出现,一个人便可以管控一个或者多个无人超市,极大的降低了经营成本。
相比于传统的购物方式,无人超市在一些方面存在着局限性:
1、想要在无人超市购物,首先你需要有一部智能手机和移动支付的账号,而老年人作为零售业中的重要群体,只能被“拒之门外”。
2、无人超市的运营成本较高,不但要支付租金和装修费用,还要购买各种价格昂贵的电子辅助设备,这无疑提高了无人超市的入市门槛。
3、无人超市虽然理论上实现了无人管理,但是在进货、摆货,还要对过期食品进行处理等许多方面依然需要人力支持。用电子设备替代人力的确是大势所趋,但是完全替代人工在短期内依旧难以实现。
本项目正是基于这些问题,提出一个合理且易于实现的基于KPU卷积神经网络的无人超市解决方案
【功能简介】
1、人脸识别进入超市:在进入超市前,需要通过手机端将个人信息、人脸照片和余额进行绑定,绑定后进行人脸识别,即可进入超市。
2、商品识别:通过卷积神经网络的训练,我们将以可乐、雪碧和芬达为例,给展示训练模型的过程,并进行模型测试演示,摄像头会对商品区域进行监测,判断当前货物的数量、状态、哪位顾客购买了哪种商品,并进行实时余额扣除。
3、云管理:为减少人力成本,本项目连接Easy_iot云管理平台,通过云平台,实现对顾客进出超市、商品缺货、人流量、异常报警等信息的检测,同时也可以对进出口门控制,而且,可以将商品缺货信息直接发送到对应的供应商,让商品的供应商来负责补货、过期商品检测、商品摆放等共工作,进一步节约成本,也可以帮助分析商品的经营收入和不同时段人流量,从而进行进一步的优化。
4、其他功能:为了超市安全,本项目给安装了火灾报警,温度报警、并且安装360度摄像头,全天无死角监控超市环境,保障超市安全,,一键报警功能,并且会将信息发送到云端,提醒管理员,快速处理突发事件
【制作过程】
步骤1:商品分类神经网络模型训练
1.1使用后羿采集器爬虫工具爬取商品图片
该项目以可乐、雪碧和芬达为例,进行商品识别模型的训练,在网页每种饮料爬取1000张图片,下载到不同目录中,删除错误图片,为了加快模型训练速度,裁剪图片尺寸到224*224,然后重命名kele_1到kele_1000
1.2标注裁剪好的图片,并按照训练网址模型进行压缩
逐张框选有效部位,并进行标注,进一步提升识别准确率
1.3在线训练kmodules
由于电脑算力有限,借助maix强大的算计进行线上训练模型,耐心等待模型训练完成
步骤2:编程测试商品识别模型成功率
2.1拷贝模型,烧录固件
将模型命名为shop.kmodel,拷贝到SD卡中备用,开发板烧录最新固件
2.2代码测试模型成功率
- camera_init() 屏幕初始化
-
- lcd.init(freq=15000000, color=65535, invert=0)
- anchor = (25.0, 31.0, 42.0, 51.0, 60.0, 79.0, 84.0, 113.0, 105.0, 138.0, 128.0, 172.0) 设置描点参数
- KPU = kpu.load("/sd/shop.kmodel") 调取模型
- kpu.init_yolo2(KPU,0.5,0.3,5,anchor)初始化神经网络
复制代码
屏幕显示识别有无三种饮料,如果有,就框选并用十字标注,测试成功,测试多次后,准确度为84% - while True:
- img = sensor.snapshot()
- code = kpu.run_yolo2(KPU,img)
- if bool(code):
- for i in code:
- img = img.draw_rectangle(i.rect(),(255,0,0),2,0)
- x = (i.x() + i.w()//2) y = (i.y() + i.h()//2)
- img = img.draw_cross([x,y],(255,0,0),30,1)
- lcd.display(img)
复制代码
步骤3:入口人脸识别模型测试与编写
通过对比当前摄像头检测到的人脸与已存人脸库中的人脸的相似程度,判断顾客具体信息,可信度达到0.8时,认为本次识别成功,控制舵机,将门自动打开,通过一人后自动关闭,防止多人进入
3.1人脸识别测试
加载各种模型,然后运行人脸检测模型,在图片中找到人脸位置并框出人脸,将裁出的人脸图片转换成kpu接收的格式
运行人脸5点关键点模型,获取到左眼、右眼、鼻子、左嘴角、右嘴角的位置;对原始图片人脸图片进行仿射变换,变换为正脸图像,将正脸图像转为kpu格式,使用人脸196维特征值模型计算正脸图片的196维特征值,计算得到最终的人脸特征feature;再将得到的人脸特征与之前保存过的人脸特征进行对比得到一组分数,选择其中最大的一个分数,且该分数超过0.8(可以自己设置)就认为识别出该人,并根据对应下标从names列表中得到该人的姓名
3.2加入舵机,人脸识别成功,舵机打开
- if code: #如果识别到人脸
-
- facename()#检测对比人脸,得到人脸信息
-
- servo.angle(board_info.PIN2, 0, 90)#舵机开关门
-
- time.sleep(3)
-
- servo.angle(board_info.PIN2, 0, 180)
复制代码
步骤4:云服务器测试
为了能够让工作人员清楚知道哪位顾客进入,哪些货品缺失,添加通过WiFi将各种信息传到云端,将各种信息存入云端,及时提醒补货
4.1建立k210与easy_iot之间的连接,订阅消息
登录EasyIoT物联网平台(https://iot.dfrobot.com.cn/)完成用户注册及设备添加,这里不再描述过程。初始化信息用于记录当前初始化状态、人脸信息记录顾客进出信息,商品信息用于记录当前商品的信息,判断是否缺货,门状态用于记录当前门的开关状态,同时可以云端操控开关门。同时需要记录Iot_id、Iot_pwd处的值,后续编写程序中会使用到。
4.2测试收发各种信息
在云端建立好各种信息的话题,在终端进行订阅查收,并进行通信,可以远程控制开关门,异常报警等。
步骤5:项目整体程序编写
步骤6:外观制作
6.1图纸设计
【项目总结】
在七天的努力下,我的基于KPU卷积神经网络的智能无人超市解决方案已经全部完成,这对我来说是一次非常有意义的体验和竞赛,从最开始一步一步学习卷积神经网络,再到模型训练,标注几千张图片,再到测试模型,一步一步的尝试,遇到问题,解决问题,突破自己,这将对我后续的生活也会有很大的帮助,非常感谢我的指导老师,在过程中给予我支持,并且一直鼓励我,一起讨论难题;也要感谢df官方论坛,遇到很多的问题都在论坛上找到了答案,非常感谢!
【项目展望】
本项目虽然基本完成了预期的功能,但是还有以下几点可以优化,使得项目更加的智能,识别速度更快,对店家、顾客和供应商也更加的方便
1、可以通过联网的方式,连接国家人脸系统或者连接支付宝系统,获取更为准确和详细的信息,也可以更为方便的实时扣除费用;
2、可以制作一个APP,通过APP连接Easy_iot的服务器,管理在手机上即可查询当前超市的各种信息,控制进出门,操作起来更加方便,同时可以设置区域的供应商补货员联系方式,会自动生成“NumX号店铺缺哪些货,需要在XX时间内补货完成”的短信,一键发送给供应商补货员;
3、APP可以收集每批商品的生产日期和过期时间,自动提醒移除过期商品
4、在训练KPU模型时,可以上传更多的图片,优化模型参数,达到更快的识别速度和更好的准确率
【硬件清单】
- Maixduino x1
- 摄像头 x1
- lcd屏幕 x1
- 杜邦线 x若干
- 7.4V锂电池组 x1
- 舵机mg90s x1
- 罐装饮料 x1
- 火焰传感器 x1
- 温湿度传感器 x1
【代码】
-
- import sensor,image,lcd # import 相关库
- import KPU as kpu
- import time
- from Maix import FPIOA,GPIO
- task_fd = kpu.load(0x200000) # 从flash 0x200000 加载人脸检测模型
- task_ld = kpu.load(0x300000) # 从flash 0x300000 加载人脸五点关键点检测模型
- task_fe = kpu.load(0x400000) # 从flash 0x400000 加载人脸196维特征值模型
- clock = time.clock() # 初始化系统时钟,计算帧率
- key_pin=16 # 设置按键引脚 FPIO16
- fpioa = FPIOA()
- fpioa.set_function(key_pin,FPIOA.GPIO7)
- key_gpio=GPIO(GPIO.GPIO7,GPIO.IN)
- last_key_state=1
- key_pressed=0 # 初始化按键引脚 分配GPIO7 到 FPIO16
- def check_key(): # 按键检测函数,用于在循环中检测按键是否按下,下降沿有效
- global last_key_state
- global key_pressed
- val=key_gpio.value()
- if last_key_state == 1 and val == 0:
- key_pressed=1
- else:
- key_pressed=0
- last_key_state = val
- def facename():
- for i in code: # 迭代坐标框
- # Cut face and resize to 128x128
- a = img.draw_rectangle(i.rect()) # 在屏幕显示人脸方框
- face_cut=img.cut(i.x(),i.y(),i.w(),i.h()) # 裁剪人脸部分图片到 face_cut
- face_cut_128=face_cut.resize(128,128) # 将裁出的人脸图片 缩放到128 * 128像素
- a=face_cut_128.pix_to_ai() # 将猜出图片转换为kpu接受的格式
- #a = img.draw_image(face_cut_128, (0,0))
- # Landmark for face 5 points
- fmap = kpu.forward(task_ld, face_cut_128) # 运行人脸5点关键点检测模型
- plist=fmap[:] # 获取关键点预测结果
- le=(i.x()+int(plist[0]*i.w() - 10), i.y()+int(plist[1]*i.h())) # 计算左眼位置, 这里在w方向-10 用来补偿模型转换带来的精度损失
- re=(i.x()+int(plist[2]*i.w()), i.y()+int(plist[3]*i.h())) # 计算右眼位置
- nose=(i.x()+int(plist[4]*i.w()), i.y()+int(plist[5]*i.h())) #计算鼻子位置
- lm=(i.x()+int(plist[6]*i.w()), i.y()+int(plist[7]*i.h())) #计算左嘴角位置
- rm=(i.x()+int(plist[8]*i.w()), i.y()+int(plist[9]*i.h())) #右嘴角位置
- a = img.draw_circle(le[0], le[1], 4)
- a = img.draw_circle(re[0], re[1], 4)
- a = img.draw_circle(nose[0], nose[1], 4)
- a = img.draw_circle(lm[0], lm[1], 4)
- a = img.draw_circle(rm[0], rm[1], 4) # 在相应位置处画小圆圈
- # align face to standard position
- src_point = [le, re, nose, lm, rm] # 图片中 5 坐标的位置
- T=image.get_affine_transform(src_point, dst_point) # 根据获得的5点坐标与标准正脸坐标获取仿射变换矩阵
- a=image.warp_affine_ai(img, img_face, T) #对原始图片人脸图片进行仿射变换,变换为正脸图像
- a=img_face.ai_to_pix() # 将正脸图像转为kpu格式
- #a = img.draw_image(img_face, (128,0))
- del(face_cut_128) # 释放裁剪人脸部分图片
- # calculate face feature vector
- fmap = kpu.forward(task_fe, img_face) # 计算正脸图片的196维特征值
- feature=kpu.face_encode(fmap[:]) #获取计算结果
- reg_flag = False
- scores = [] # 存储特征比对分数
- for j in range(len(record_ftrs)): #迭代已存特征值
- score = kpu.face_compare(record_ftrs[j], feature) #计算当前人脸特征值与已存特征值的分数
- scores.append(score) #添加分数总表
- max_score = 0
- index = 0
- for k in range(len(scores)): #迭代所有比对分数,找到最大分数和索引值
- if max_score < scores[k]:
- max_score = scores[k]
- index = k
- if max_score > 85: # 如果最大分数大于85, 可以被认定为同一个人
- a = img.draw_string(i.x(),i.y(), ("%s :%2.1f" % (names[index], max_score)), color=(0,255,0),scale=2) # 显示人名 与 分数
- else:
- a = img.draw_string(i.x(),i.y(), ("X :%2.1f" % (max_score)), color=(255,0,0),scale=2) #显示未知 与 分数
- if key_pressed == 1: #如果检测到按键
- key_pressed = 0 #重置按键状态
- record_ftr = feature
- record_ftrs.append(record_ftr) #将当前特征添加到已知特征列表
- break
-
- lcd.init() # 初始化lcd
- sensor.reset() #初始化sensor 摄像头
- sensor.set_pixformat(sensor.RGB565)
- sensor.set_framesize(sensor.QVGA)
- sensor.set_hmirror(1) #设置摄像头镜像
- sensor.set_vflip(1) #设置摄像头翻转
- sensor.run(1) #使能摄像头
- anchor = (1.889, 2.5245, 2.9465, 3.94056, 3.99987, 5.3658, 5.155437, 6.92275, 6.718375, 9.01025) #anchor for face detect 用于人脸检测的Anchor
- dst_point = [(44,59),(84,59),(64,82),(47,105),(81,105)] #standard face key point position 标准正脸的5关键点坐标 分别为 左眼 右眼 鼻子 左嘴角 右嘴角
- a = kpu.init_yolo2(task_fd, 0.5, 0.3, 5, anchor) #初始化人脸检测模型
- img_lcd=image.Image() # 设置显示buf
- img_face=image.Image(size=(128,128)) #设置 128 * 128 人脸图片buf
- a=img_face.pix_to_ai() # 将图片转为kpu接受的格式
- record_ftr=[] #空列表 用于存储当前196维特征
- record_ftrs=[] #空列表 用于存储按键记录下人脸特征, 可以将特征以txt等文件形式保存到sd卡后,读取到此列表,即可实现人脸断电存储。
- names = ['Mr.1', 'Mr.2', 'Mr.3', 'Mr.4', 'Mr.5', 'Mr.6', 'Mr.7', 'Mr.8', 'Mr.9' , 'Mr.10'] # 人名标签,与上面列表特征值一一对应。
- while(1): # 主循环
- check_key() #按键检测
- img = sensor.snapshot() #从摄像头获取一张图片
- clock.tick() #记录时刻,用于计算帧率
- code = kpu.run_yolo2(task_fd, img) # 运行人脸检测模型,获取人脸坐标位置
- if code: # 如果检测到人脸
- facename()
- servo.angle(board_info.PIN2, 0, 90)
- time.sleep(3)
- servo.angle(board_info.PIN2, 0, 180)
- fps =clock.fps() #计算帧率
- print("%2.1f fps"%fps) #打印帧率
- a = lcd.display(img) #刷屏显示
- #kpu.memtest()
-
- #a = kpu.deinit(task_fe)
- #a = kpu.deinit(task_ld)
- #a = kpu.deinit(task_fd)
复制代码
【附件下载】
附件包含文件:接线图、元件清单、程序截图、sb3源程序
datasets.zip智慧无人超市代码.zip
智慧无人超市图纸.zip
|