查看: 1776|回复: 2

【教程】工业应用场景下的M5Stack UnitV2应用之不良品筛选

[复制链接]

该用户从未签到

发表于 2021-7-7 18:46 | 显示全部楼层 |阅读模式
本帖最后由 沧海笑1122 于 2021-7-7 18:55 编辑

   【教程】工业应用场景下的M5Stack UnitV2应用之不良品筛选
【项目故事】
    这是一个严肃的话题,从流水线产品质量控制、到矿石分选,利用AI技术,将不良品或者不同种类的矿石进行分选,都是非常常见的工业AI应用场景。
    我们先看一下真实的工业场景,图片来自网络(http://www.tjmeiteng.com/)。
这是一个原煤分选的案例,识别装置使用了视觉+X光等双源方式,来判别是精煤还是矸石,用识别后的结果(目标物的坐标)驱动储气罐+电磁阀,对需要分选的对象(精煤或者矸石)进行吹选,落在不同的仓内,从而达到分选目的。
美腾1.jpg
     本文尝试使用M5Stack出品的最新Linux AI智能摄像头UnitV2,搭建一个模拟工业应用场景下的不良品筛选功能。我们看看成品的运行状况。
▶▶▶

    我们看到,上一个工序形成的一个个零件,均匀布撒在传送带上。良品上面有一个螺丝,而“不良品”我们设定是漏装这颗螺丝的零件。经过AI摄像头后,会通过目标检测功能,识别出零件的类别(良品还是不良品)以及兴趣框的左上角坐标、兴趣框长宽等四个数据,通过uart串口,将json格式的识别结果发送到一台M5Stack Basic上,后者会进行简单的数据统计(良品、不良品以及总零件数),良品通过时,会以一个1/4拍的低音A来告知,不良品被发现时,会发出一个1/4拍的高音A来警示用户。对于不良品,M5StackBasic将驱动两只电磁阀,喷出压缩空气,将不良品吹至不良品仓,从而完成分选。
这个项目的推荐评级:
有趣度:四星★★★★
难度:三星★★★
同样是一个易于上手复现的玩具,却可以真实再现工业场景。
下面我们就一起动手试试吧。

【硬件准备】
  
  
内容
数量
型号或参数
备注
1
Linux AI摄像头
1
M5Stack UnitV2
AI识别模块
2
M5Stack Basic
1

执行元件
3
电动传送带
1
27*6.6*8.6cm(长宽高)
儿童科教玩具,来自网购,自带3V电机
4
电磁阀
2
台湾新恭12V 电磁气阀
一进一出
5
压力水壶
  
2

带自锁功能
6
硅胶软管、连接件
若干

喷吹用管路以及连接件
7
铜质天线
1

制作空气喷嘴
8
玩具积木模块
10
乐高
作为良品、不良品样本

在这里穿插一下背景知识,介绍一下基于Linux的新品M5Stack UnitV2。
v2.jpg
    我原先文章中的AI摄像头是基于K210的,这次UnitV2是M5Stack最新推出的一款高效率的AI识别模块, 采用Sigmstar SSD202D(集成双核Cortex-A7 1.2Ghz处理器)控制核心,集成128MB-DDR3内存,512MBNAND Flash, 1080P摄像头。内嵌Linux操作系统,集成软硬件资源与开发工具。你可以理解为一个由M5Stack高度定制的带摄像头的Linux小电脑,将识别结果用串口送出,格式是标准的json,便于玩家在PC、树莓派或者esp32单片机上将识别结果进一步解析、运用、展示及控制。
【软件准备】
  
  
内容
型号
备注
  
1
  
UIFLOW
1.7.5
为了便于Thonny调试,我选择了离线版,在线版功能更强、版本更高
  
2
  
Thonny IDE
3.3.11
优秀的轻量级micropython调试工具
  
3
  
Python jupyter notebook

M5Stack UnitV2内置
  
4
  
V-trianing
M5Stack在线AI训练平台

【主要原理流程图】
M5Stack Basic部分:
主程序流程图.jpg
【制作过程】
    1、 目标检测模型训练(新一代的v-training)
(1)    批量采样
    UnitV2本身带有1080P摄像头,支持完整的pytho3(不再是micropython),所以我用python写了一个小程序,实现了对样本的采集,每隔3秒拍摄一张照片,照片320*240,保存在UnitV2里,然后通过http方式访问、下载。本次采样我一共拍摄了190张样本。
(2)    模型训练
适配UnitV2的训练也同步升级,V-Training(m5stack.com)。官网有比较详细的教程,在此大致描述一下训练过程。
第一步:注册训练用户
第二步:上传样本,本次上传190张,为提升训练效果,样本是多多益善,尽可能采用实际应用中的环境、采光条件来取样。
训练01.jpg 训练0.jpg
第三步:建立标签,比如本文建立了两个样本标签,i_yes:良品,i_no:不良品。
训练02.jpg
良品与不良品.jpg
①   设定为不良品(缺螺丝);②设定为良品

第四步:标记。这个步骤比较枯燥,需要手工将图片中的目标标记出来,同时匹配相应的标签。对残缺不全的目标可以放弃。注意,这个过程中切勿刷新页面,否则前功尽弃
训练1.jpg 训练2.jpg 训练3.jpg
第五步:训练、等待结果返回。如果你漏标了样本,系统也会提醒你完善。
训练4.jpg
(1)    成果应用
    结果返回后,是一个压缩文件,比如我的模型是:v2model_3c6b26928a46a81e.tar。这个文件并不需要像k210那样拷贝到TF卡上,UnitV2提供了AP模式和Ethernet模式连接,我是用后者连接到UnitV2设备,访问域名unitv2.py或IP:10.254.239.1访问预览网页。切换功能至Object Recognition,点击add按键,在本地路径中上传模型即可。
此刻就可以看到,在目标检测中,增加了一个模型(就是刚才训练的模型),在UnitV2镜头下放上样品,就可以看到在右侧的结果框,出现识别结果(json格式)。此刻我们可以把UnitV2当成一个AI摄像头,将目标检测结果从串口送出,我们用PC、树莓派或者单片机进行后期的结果处理、展示和执行。本文我们使用esp32为核心的M5StackBasic来作为执行元件。
Snipaste_2021-07-04_23-03-44.jpg Snipaste_2021-07-04_23-04-28.jpg
2、 传送带
    为了这个工业场景,我在网购平台找到了这款木质结构的微型电机传送带,尺寸是27*6.6*8.6cm(长宽高),电机靠两节1.5V干电池驱动,可以控制正反转。我测试了一下转速,基本满足我的试验要求。很可惜,运输过程中,整个传送带收到了很严重的破损,商家很给力(必须点个赞),重新发货一台。由于是成品,所以可以直接使用。
传送带俯视图a.jpg
3、 吹选系统(压缩空气装置+电磁阀+管路以及喷嘴)
(1)    喷嘴制作

    电磁阀系统,由压缩空气装置+电磁阀+管路以及喷嘴组成,喷嘴要求稳定、便于连接、口径合适。我找到了一些收音机天线(铜质镀锌),天线实质就是一节节优质的铜管,用电磨截断、打磨后,我得到了两根内径4mm的铜管,作为我们两只电磁阀的喷嘴。
打磨好的喷嘴.jpg 喷嘴与传送带2.jpg 喷嘴示意a.jpg

(2)    压缩空气装置+电磁阀+管路以及喷嘴组装

    压缩空气装置就是两只带有自锁功能的压力喷水壶,我把壶嘴做了改装,与硅胶软管连接,电磁阀是执行元件,一端接压力壶,一端接刚才制作的喷嘴。这样就形成了一套完整的压缩空气系统。

电磁阀与控制板.jpg
注:①是电磁阀驱动板;②是电磁阀(两只)
(3)    电控部分以及吹选系统联调:

    我在制作“高速水滴碰撞摄影装置”时,制作了一块电磁阀扩展板,扩流采用了ULN2803A 8通道达林顿阵列。本次使用M5StackBasic的第16、17引脚,通过ULN2803A扩流后,驱动2只12V电磁阀。ULN2803A和M5StackBasic的GND相连接。电磁阀系统单独调试,用3.3V点击ULN2803A的相应输入端,验证电磁阀动作可靠、压缩空气排出正常、整个系统无漏气。
电磁阀扩展板.jpg
①   电磁阀引线;②ULN2803达林顿;③12V直流输入
4、 系统总装测试

    我使用了一个俯拍支架,将UnitV2和M5StackBasic固定在俯拍支架上,准备了两个成品盒,一个放置良品(在传送带末端),另一个放置不良品(在传送带末端垂直于传送带方向)。样品一共准备了十只,乐高积木+连接件(模拟螺丝)构成良品,不良品就是不带连接件的积木块。
俯视全景图2.jpg
注:①传送带以及样品;②UnitV2;③M5StackBasic;④电磁阀驱动板;⑤电磁阀;⑥压力壶;⑦零件盒(待分选);⑧传送带开关;⑨有源hub以及俯拍支架底座。

    使用UIFLOW设计了UI和基本流程后,主要的调试工作在Thonny IDE完成,这是一个轻量级的python和micropython调试软件。调试M5StackBasic非常方便,调试过程中,可以观察各个变量以及程序节点的状态。

Snipaste_2021-07-04_23-07-51.jpg Snipaste_2021-07-04_23-11-34.jpg


【代码】

一、M5StackBasic侧代码
  1. # UNITV2 之传送带质量监测
  2. # 2021-07-04
  3. # 0620 使用Basic 的grove tx=21, rx=22 与V2建立uart通信
  4. # 0620 完成了识别、计数、LED闪动等功能联调
  5. # 0621 编制电磁阀吹零件功能,联调电磁阀+管路以及压力壶
  6. # 0704 在传送带垂直方向安装2只电磁阀,将不良品吹入不良品仓,调试完成
  7. # 沧海


  8. from m5stack import *
  9. from m5ui import *
  10. from uiflow import *
  11. from easyIO import *
  12. import json


  13. setScreenColor(0x222222)
  14. print("Begin...")

  15. # 变量init
  16. rev_str = None
  17. rev_obj = None
  18. vprob = None
  19. vtype = None
  20. vx = None
  21. vy = None
  22. vw = None
  23. vh = None

  24. #vexe 执行控制字,确保目标进入执行框后,只执行一次(计数以及吹不良品)
  25. vexe=0
  26. #c_ok累计良品
  27. c_ok=0
  28. #c_ng累计次品
  29. c_ng=0
  30. #c_total累计总零件数量
  31. c_total=0
  32. #v_timer
  33. v_timer=0
  34. #vobj_x 目标中心的x值
  35. vobj_x =0
  36. #vobj_y 目标中心的y值
  37. vobj_y =0
  38. okorng= None


  39. #UI INIT
  40. title0 = M5Title(title="QC 202106", x=3, fgcolor=0xFFFFFF, bgcolor=0x0000FF)
  41. label0 = M5TextBox(13, 47, "OK=", lcd.FONT_DejaVu40, 0x30f905, rotate=0)
  42. label1 = M5TextBox(9, 94, "NG=", lcd.FONT_DejaVu40, 0xf70606, rotate=0)
  43. label2 = M5TextBox(13, 149, "TOTAL=", lcd.FONT_DejaVu40, 0xf3f4f6, rotate=0)
  44. i_ok = M5TextBox(204, 47, "0", lcd.FONT_DejaVu40, 0x04fe03, rotate=0)
  45. i_ng = M5TextBox(204, 99, "0", lcd.FONT_DejaVu40, 0xf10303, rotate=0)
  46. i_total = M5TextBox(204, 149, "0", lcd.FONT_DejaVu40, 0xFFFFFF, rotate=0)
  47. label3 = M5TextBox(52, 217, "Reset", lcd.FONT_Default, 0xf8db04, rotate=0)


  48. #Uart INIT
  49. uart1 = machine.UART(1, tx=21, rx=22)
  50. uart1.init(115200, bits=8, parity=None, stop=1)



  51. # 电磁阀1:Pin 16   电磁阀2:Pin 17
  52. # 执行函数,将驱动2只电磁阀吹动不良品进入不良品仓
  53. # 本程序中,电磁阀排列在传送带垂直方向。
  54. # 良品使用低音作为指示
  55. def f_exe1(okorng,vobj_y,vobj_x):
  56.     if vobj_y>=150 and vobj_y<310 and okorng=="i_no":

  57.         speaker.sing(889, 1/4) #不良品声音
  58.         digitalWrite(16, 1)
  59.         digitalWrite(17, 1)
  60.         wait_ms(60)
  61.         digitalWrite(16, 0)
  62.         digitalWrite(17, 0)

  63.     if okorng=="i_yes":
  64.         speaker.sing(220, 1/4) #良品声音




  65. def buttonA_wasPressed():
  66.   global c_ok,c_ng,c_total
  67.   #计数以及变量清零
  68.   i_ok.setText('0')
  69.   i_ng.setText('0')
  70.   i_total.setText('0')
  71.   c_ok=0
  72.   c_ng=0
  73.   c_total=0

  74.   pass
  75. btnA.wasPressed(buttonA_wasPressed)

  76. #目标检测函数,检测到良品(不良品),分别进行计数,并且驱动电磁阀吹动不良品
  77. def f_detection(vx,vy,vw,vh,vprob,vtype):
  78.     #计算目标坐标
  79.     global vexe,c_ok,c_ng,c_total
  80.     vobj_x=(vx+vw/2)
  81.     vobj_y=(vy+vh/2)
  82.     print(vobj_x)
  83.     print(vobj_y)
  84.     print(vexe)
  85.    
  86.     #目标进入执行框(如x=260~400),则执行一次(计数以及吹不良品)
  87.     if vprob>0.5 and vobj_x>260 and vobj_x<400:
  88.         print("in exe area")
  89.         if vtype=="i_yes" and vexe==0:
  90.             c_ok=c_ok+1
  91.             i_ok.setText(str(c_ok))
  92.             f_exe1(vtype,vobj_x,vobj_y) #绿灯
  93.             c_total=c_total+1
  94.             i_total.setText(str(c_total))
  95.             vexe=1 #确保在整个执行框只动作一次
  96.         else:
  97.             if vtype=="i_no" and vexe==0:
  98.                 c_ng=c_ng+1
  99.                 i_ng.setText(str(c_ng))
  100.                 c_total=c_total+1
  101.                 i_total.setText(str(c_total))
  102.                 f_exe1(vtype,vobj_x,vobj_y) #电磁阀吹动50ms
  103.                 vexe=1 #确保在整个执行框只动作一次
  104.     else:
  105.         print("out exe area")
  106.         vexe=0
  107.    
  108.         
  109. #Main loop
  110. while True:
  111.     try:
  112.         if uart1.any():
  113.          rev_str = json.loads((uart1.read()).decode())
  114.          rev_obj=rev_str["obj"]
  115.          #print(rev_obj)
  116.          for zidian in rev_obj:
  117.             #print(type(zidian))
  118.             vtype=zidian.get("type")
  119.             vprob=zidian.get("prob")
  120.             vx=zidian.get("x")
  121.             vy=zidian.get("y")
  122.             vw=zidian.get("w")
  123.             vh=zidian.get("h")
  124.             f_detection(vx,vy,vw,vh,vprob,vtype)
  125.             #print(vx,vy,vw,vh,vtype,vprob)
  126.         wait_ms(2)
  127.         #f_detection(vx,vy,vw,vh,vporb,vtype)
  128.     except:
  129.         print("except")
复制代码


二、UnitV2侧连续拍摄采样代码
  1. import cv2
  2. import time
  3. camera = cv2.VideoCapture(0)
  4. camera.set(3, 320)  # width=320
  5. camera.set(4, 240)  # height=320

  6. path = "train/0613/b"
  7. ext=".jpg"
  8. cnt=0

  9. def control_white_led(value):
  10.     open('/sys/class/gpio/export', 'w').write('0') # Export the GPIO0
  11.     open('/sys/class/gpio/gpio0/direction', 'w').write('out') # Set the direction of the GPIO
  12.     open('/sys/class/gpio/gpio0/value', 'w').write(str(value)) # Set the calute, '1' or '0'

  13. try:
  14.     while True:
  15.         ret, frame = camera.read()
  16.         if ret:
  17.             cnt+=1
  18.             fname=path+str(cnt)+ext
  19.             #led blink
  20.             control_white_led(0)
  21.             print(fname+" is saved")
  22.             cv2.imwrite(fname,frame)
  23.             time.sleep(0.2)
  24.             control_white_led(1)
  25.             #time.sleep(2.8)
  26.         else:
  27.             print('OOps, we get some trouble!')
  28.         time.sleep(2.8)

  29. except KeyboardInterrupt:
  30.     pass
复制代码
【小结】
  •     小电脑还是AI摄像头?
    UnitV2升级为Linux 系统的AI摄像头后,实际上是一个带有1080P摄像头的完整Linux计算机系统,只是对系统高度定制后,给用户开放了更加便捷、友好的训练、使用界面,尤其是通过grove接口,将识别出来的结果,以json格式传送给上位机进行二次开发利用,用户可以更加关注训练和应用,而无须关注UnitV2的Linux 系统,最好不要把它定位成一个小电脑(虽然它就是一个小电脑,你可以通过SSH进入系统里),而是当成一个友好的AI摄像头,我理解这样是正确的使用方式。
  • 善用Jupyter Notebook

    UnitV2的出厂Linux镜像中集成了JupyterNotebook开发调试工具, Jupyter Notebook是一款网页形式的开发工具,用户可以直接在网页上编写代码和运行代码。这次我写的连续拍摄取样工具,就是在JupyterNotebook上编写、运行的,非常便捷。这也是M5Stack提供给用户的一个方便的调试工具,用好这一工具,往往事半功倍。
Snipaste_2021-07-04_23-09-37.jpg
  • 性能和温度
    本文尝试用一个完整的工业场景,呈现UnitV2在目标检测方面的性能提升,在K210使用中,同一画面多目标往往会出现漏识别、误识别的现象。在UnitV2得到了大幅提升,多目标识别得到了很大改善。会给玩家更多创意实现的可能。比如下图就是我上次写的井字棋,当时使用了遮罩来提升识别率,本次用UnitV2训练后,同一画面多目标的识别是比较顺畅的。
捕获(2).jpg
    由于集成度高,发热问题也随之而来,所以UnitV2集成了一个内置风扇,来尽可能控制装置温升,在实际运行过程中,也感受到温度的确比较高。看来在兼顾性能、体积以及温度控制等方面还需要做进一步的努力。
    今日小暑,万物勃发。
    感谢M5Stack开发的新品给玩家以实现创意的可能。感谢笑笑、社区各位师兄的指导帮助。
    感谢arduino.cn提供交流平台。

    沧海合十。

附上程序附件供学习交流。

code.zip (2.46 KB, 下载次数: 4)

签到天数: 279 天

[LV.8]以坛为家I

发表于 2021-7-7 23:56 | 显示全部楼层
沧海笑1122老师出品必是精品,谢谢分享,学习一下。

该用户从未签到

 楼主| 发表于 2021-7-8 11:44 | 显示全部楼层
topdog 发表于 2021-7-7 23:56
沧海笑1122老师出品必是精品,谢谢分享,学习一下。

谢谢师兄支持
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

热门推荐

Arduino数字与字母字体应该如何设置?
Arduino数字与字母字体应
尝试用Arduino uno 做了个小工具,用来控制电脑水冷的运行。 洞洞板已经测试成功,完
WEMOS LOLIN32Lite(ESP32v1.0.0Rev1)入手+引脚图
WEMOS LOLIN32Lite(ESP32v
突发奇想做个精致的蓝牙遥控平衡小车(大学时做过一次,比较笨,用洞洞板焊电路做的比
【Arduino】168种传感器模块系列实验(129)---BH1750光照传感器
【Arduino】168种传感器模
37款传感器与模块的提法,在网络上广泛流传,其实Arduino能够兼容的传感器模块肯定是
16度双足机器人舵机驱动板pca9685连接舵机,舵机没反应
16度双足机器人舵机驱动板
各位同为arduino爱好者的大佬们大家好!想请教一下大家有关舵机驱动板pca9685
pca9685+arduino驱动舵机失败
pca9685+arduino驱动舵机
我网上别人的代码,编译通过且上传,但舵机不转,怎么解决?Arduino uno使用PCA9685模
Copyright   ©2015-2016  Arduino中文社区  Powered by©Discuz!   
快速回复 返回顶部 返回列表