【教程】M5Stack UnitV2应用之手写识别简易计算器-Arduino中文社区 - Powered by Discuz! Archiver

沧海笑1122 发表于 2021-8-14 20:44

【教程】M5Stack UnitV2应用之手写识别简易计算器

本帖最后由 沧海笑1122 于 2021-8-14 20:50 编辑

【项目故事】这是一个M5Stack UnitV2的趣味应用,使用V-Training平台训练自己的手写数字笔迹,然后用M5Stack UnitV2利用目标检测识别出你的手写算式,将结果用Json串发送到M5Stack Basic上进行展示(屏显并发出语音提示)。关于M5StackUnitV2这块AI智能摄像头的基本情况,我在前一篇文章中详述,在此不再赘述,大家可以参考我的上一篇《工业应用场景下的M5Stack UnitV2应用之不良品分选》https://www.arduino.cn/thread-104690-1-1.html。一起先看看手写识别简易计算器的效果。https://www.bilibili.com/video/BV1F44y1y7D9/所谓简易计算器,是指本文仅实现了个位数的加减法计算,目的是展示UnitV2的训练、使用方式,抛砖引玉,吸引更多玩家以及更好的玩法。有趣度:★★★★难度:★★这是我一如既往的风格:好玩不难。话不多说,我们开始制作吧。
【软硬件准备】软件:

名称版本备注
1UIFLOW1.8.1设计Basic端的界面
2Thonny IDE3.3.13调试Basic的micropython程序
3V-Training平台
训练手写数字及运算符
4M5Stack UnitV2内置应用目标检测,固件版本0604

5串口调试工具
用于分步调试
硬件:

名称型号备注
1M5Stack UnitV2V2.0AI智能摄像头
2M5Stack Basic
上位机,用于解析、展示手写式以及计算结果
3USB-TTLCH340调试串口时使用,正常不需要
4Uart线三根(Tx\Rx\Gnd)

再就是一支笔、一张白纸、一个俯拍支架,再就是您的手(手写嘛)。【软件原理】lM5Stack UnitV2侧:使用内置应用的目标检测功能,将手写的数字以及运算符(如+、-、=)进行识别,并且以json格式通过uart发送至上位机处理。lM5Stack Basic侧: 这是M5Stack Basic侧的UI以及主循环设计。【制作步骤】Step1:训练手写数字及运算符模型(1)    准备若干纸卡片,用墨水笔手写数字,尽可能大小一致、字体保持一致。我准备了约30张卡片,每张有2个算式。为了简化期间,只训练了+、-、=三种运算符以及若干数字。(2)    使用自己编写的连拍程序,用UnitV2连续拍摄样本,做好取样准备。
(3)    将拍摄的样本,上传http://v-training.m5stack.com/,进行上传、建立lable以及标注工作,这些工作在M5Stack官网以及我上篇文章中都有详述,此处不再赘述。Step2:上传模型后,打开目标检测功能,测试识别以及json串的传送情况
将UnitV2固定在俯拍支架上,光源充足的情况下,手写算式,测试目标检测的效果。效果如下:注:1是刚才训练好并且上传的模型,UnitV2就是靠这个模型通过目标检测来识别你的手写录入。2是识别并且标注兴趣框的情况,3是识别后的json字符,通过uart发送至上位机进行展示。Step3:分步调试,用usb-ttl模块加上串口调试器,与M5Stack Basic通信,将收到的json串进行解析和展示。
将UnitV2识别出的Json字符串,复制在串口调试器里,就可以脱离UnitV2进行分步调试。好处是可以简化调试步骤,减少不定因素,分别对M5StackBasic以及UnitV2进行调试。注:1、减号识别并且替换成“-”;2、等号识别并且替换成“=”,3、4分别是数字的识别。
Step4:整体联调:将UnitV2(grove)与M5Stack Basic(pin21\pin22\gnd)连接,实现整体联调,在手写一个算式后,V2识别出来并且发送给M5Stack Basic,后者解析、组装算式、计算结果,并且将带有运算结果的算式展示在屏幕上,同时发出“滴”声,提醒玩家识别、计算过程结束。
如果你需要再算一组算式,只需要按“reset”键,即可清屏,为下一次识别计算做准备。
【代码】
M5Stack Basic侧:# UNITV2 之手写计算器
# 2021-08-06 测试uart1接受V2传送来的识别串,并且生成算式、计算结果并且在屏幕展示
# 2021-08-08 v2送来的Num=4的json串已经可以解析,且放入一个四元素二维数组。
# 2021-08-11 一是按钮reset。二是识别后,显示计算式以及答案,并且确保只要识别到就仅处理一次。三是识别后带语音提示
# 沧海


from m5stack import *
from m5ui import *
from uiflow import *
from easyIO import *
import json

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

# 变量init
rev_str = None
rev_obj = None
rev_obj_num = None
vprob = None
vtype = None
vx = None
vy = None
vw = None
vh = None
string_result= None
#生成一个2维、四元素数组temp1 ,放置中间结果
temp1 = [['' for i in range(2)] for j in range(4)]
#temp2 是表达式以及答案
temp2 =None
#vexe 执行控制字,确保目标进入执行框后,只执行一次(计数以及吹不良品)
vexe=0
#UI INIT
Calculator = M5Title(title="Calculator 2021-08", x=3, fgcolor=0xFFFFFF, bgcolor=0x0034ff)
label1 = M5TextBox(52, 71, "Text", lcd.FONT_DejaVu56, 0xff8d08, rotate=0)
rectangle0 = M5Rect(30, 187, 80, 40, 0x0efdf9, 0xFFFFFF)
label2 = M5TextBox(37, 194, "Reset", lcd.FONT_Comic, 0x100000, rotate=0)

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

def buttonA_wasPressed():
global vexe
label1.setText('') #清屏
vexe=0 #复位执行控制字
pass
btnA.wasPressed(buttonA_wasPressed)
   
#Main loop
while True:
    try:
      if uart1.any():
            wait_ms(100)
            rev_str = json.loads((uart1.read()).decode())
            rev_obj_num=rev_str["num"]
            rev_obj=rev_str["obj"]
            #print(rev_obj_num)
            #print(vexe)
            if rev_obj_num==4 and vexe==0: #识别出完整表达式(四元素)且vexe=0
                x=0
                for zidian in rev_obj:
                  #print(type(zidian))
                  vtype=zidian.get("type")
                  vprob=zidian.get("prob")
                  vx=zidian.get("x")
                  vy=zidian.get("y")
                  vw=zidian.get("w")
                  vh=zidian.get("h")
                  print(vx,vy,vw,vh,vtype,vprob)
                  temp1=vx
                  if vtype is 'plus':
                        temp1='+'
                        print(temp1)
                  elif vtype is 'minus':
                        temp1='-'
                        print(temp1)
                  elif vtype is 'equal':
                        temp1='='
                        print(temp1)
                  else:
                        temp1=vtype.strip('n')
                        print(temp1)
                  x=x+1
                temp1.sort() #按照坐标排序
                temp2=temp1+temp1+temp1+temp1 #组装算式
                temp2=temp2.strip('=')#去除'='符号
                #print(temp2)
                #print(eval(temp2)) #运行计算式
                #eval(temp2)为执行表达式,并组装成结果字符串
                string_result= temp2+'='+str(eval(temp2))
                label1.setText(string_result) #在Basic上显示
                speaker.sing(489, 1/4) #提示音
                wait_ms(2)
                vexe=1 #每次识别+显示只进行一次,随后手动清零
                print('ok')
    except:
      print("except")
【软件知识点简述】这个小玩具里面有三个知识点需要关注学习。1、识别的中间结果怎么保存?我们建立了一个一个二维、四元素数组temp1 ,放置中间结果:temp1 = [['' for i in range(2)] for j inrange(4)]temp1 放置x坐标,temp1 放置识别出来的类型(数字或者运算符)2、我们对这个二维数组进行排序,使用以下语句:temp1.sort() 。实际上是按照temp1(即x坐标)排序,这样就使得识别出来的数据按照从左往右的顺序排列好,和我们的手写习惯是一致的。3、根据字符表达式计算运算结果, str(eval(temp2))这个语句就是完成此功能。整个M5Stack Basic侧的代码,加上注释也不到100行。UI设计及主逻辑搭建是通过UIFLOW完成的,调试工作通过thonny ide这个轻量级调试工具完成。【小结】
[*]简易手写计算器是一个有趣但简单的小玩具,由于手写数字比较简单,所以我训练了大概不到50组数据,就可以达到不错的识别效果。这也是V2的硬件支撑以及软件优化的结果吧,作为玩家,我感觉用户体验还不错。
[*]在B站有玩家留言,是否可以做得更复杂些(比如三角函数、对数运算),答案是:当然可以。
这是一个深度学习的应用,只要你的样本足够多、足够合理,识别出更加复杂的数学表达式当然没有问题,本文只是抛砖引玉,我连乘除功能都没有训练,请玩家一起进阶更加有趣的玩法吧。
[*]感谢笑笑师兄的指导以及M5Stack一众师兄的指导、鼓励,谢谢大家。

沧海抱拳。
代码和UIFLOW设计文件附后。

topdog 发表于 2021-8-14 22:27

沧海笑1122老师写的文章条理清晰,逻辑严密值得推荐,一起学习一起进步。

沧海笑1122 发表于 2021-8-15 11:30

topdog 发表于 2021-8-14 22:27
沧海笑1122老师写的文章条理清晰,逻辑严密值得推荐,一起学习一起进步。

感谢师兄支持

eagler8 发表于 2021-8-16 08:22

                                          
:lol 点赞,这个模块没有用过


                                                                           
                  
                                          
                        
                  
               

页: [1]
查看完整版本: 【教程】M5Stack UnitV2应用之手写识别简易计算器