704|1

7

帖子

5

TA的资源

一粒金砂(中级)

楼主
 

【得捷电子Follow me第2期】手势识别极简实现&光线传感器数据记录 [复制链接]

  本帖最后由 夷则玖 于 2023-11-6 14:32 编辑

//内容一:3-5分钟短视频//

视频链接:得捷电子Follow me第2期 活动视频-得捷电子Follow me第2期-EEWORLD大学堂

//内容二: 任务/项目总结报告//

1. 项目介绍

    "Follow me活动”是DigiKey联合EEWORLD发起的为期一年的“跟技术大咖学技术,完成任务返现”活动。2023年共有4期,每3个月技术大咖推荐可玩性与可学性较强的开发板/仪器套件,带着大家实际操作。

        本次活动使用Adafruit ESP32-S3 TFT Feather 开发板,使用Circuit Python语言实现。

 

2. 各任务功能对应的主要代码片段、说明及展示

 

    任务①:控制屏幕显示中文

            任务要求:完成屏幕的控制,并且能显示中文      器件:Adafruit ESP32-S3 TFT Feather

        代码:

  • import board
  • import displayio
  • import adafruit_imageload
  • from adafruit_display_text import label
  • from adafruit_bitmap_font import bitmap_font

        首先导入必要的库和包,其中 adafruit_imageload 用于显示图片,本任务可不必实现。

  • #使用固件自带的屏幕设备,不需要另行初始化屏幕参数
  • display = board.DISPLAY
  • #创建本例程的显示组
  • group = displayio.Group()
  • #加载字体
  • font = bitmap_font.load_font("/font/Slidexiaxing-32.pcf")
  • #并定义字体颜色为黑色
  • color = 0x000000

       显示字体必备的语句。

  • #加载图片
  • image, palette = adafruit_imageload.load("/images/yuanshen.png")
  • #开启透明(这里缺少会有问题,不同图片不一定需要)
  • palette.make_transparent(1)
  • #创建图片布局
  • grid = displayio.TileGrid(image, pixel_shader=palette)
  • #将图片添加到显示组
  • group.append(grid)

        显示图片,注意adafruit_imageload.load中路径,对于部分图片 make_transparent 可能不需要

  • #初始化文本标签
  • text1 = label.Label(font, text="将进酒", color=color)
  • #设置x,y轴绘图坐标
  • text1.x = 0
  • text1.y = 15
  • #将标签添加到图像组
  • group.append(text1)
  • #注释同上
  • text2 = label.Label(font, text="杯莫停", color=color)
  • text2.x = 0
  • text2.y = 120
  • group.append(text2)
  • #显示修改后的显示组
  • display.show(group)
  • #死循环保持屏幕的显示
  • while True:
  • pass

        添加文本标签并保持屏幕显示,每次更新文本都需要执行display.show(group)以刷新显示

 

显示效果:

            显示中文需要自建字库,字库的建立参考了交流群大佬们的帖子,在此表示感谢。

    任务②网络功能使用(必做任务) 

            任务要求:完成网络功能的使用,能够创建热点和连接到WiFi    器件:Adafruit ESP32-S3 TFT Feather

       

            首先,连接到WiFi只需要配置“settings.toml”文件即可

  • #配置WIFI名称
  • CIRCUITPY_WIFI_SSID = "需要连接的WIFI的名称"
  • #配置WIFI密码
  • CIRCUITPY_WIFI_PASSWORD = "需要连接的WIFI的密码"
  • #配置网页工作流密码
  • CIRCUITPY_WEB_API_PASSWORD = "设置你的web端密码"
  • #配置网页工作流端口
  • CIRCUITPY_WEB_API_PORT = 80

       需要注意的是CIRCUITPython提供了WEB访问的功能,CIRCUITPY_WEB_API_PASSWORD 语句所设置的,就是你在网页输入开发板IP地址后,提示你输入的密码。(交流群有过的疑问,在此唠叨一下)

 

            然后,创建热点:

  • import os
  • import time
  • import ssl
  • import wifi
  • import socketpool
  • import microcontroller
  • import adafruit_requests

        首先是导包

  • #配置WIFI
  • wifi.radio.start_ap("ESP32-WIFI", "00000000")
  • #输出WIFI信息
  • print("SSID: ESP32-WIFI")
  • print("PASSWORD: 00000000")
  • #保持程序运行
  • while True:
  • pass

        这个很简单,官方文档和乔老师的例程都已经很详细了

        效果:

            屏幕显示了ip地址,连接上了网络

 

        

                                                                                                                            开启热点

        

                连接上所创建的热点,可以看到获取到了IP地址

            以上就是网络功能的使用,得益于CIRCUITPython,使得联网十分便捷

    任务③:控制WS2812B(必做任务)

            任务要求:使用按键控制板载Neopixel LED的显示和颜色切换    器件:Adafruit ESP32-S3 TFT Feather

        代码:

  • import time
  • import board
  • import neopixel
  • import digitalio
  • from rainbowio import colorwheel

        首先是导包,circuitpython,提供了neopixel库,可以很方便地实现RGB的控制,如色轮的多种显示效果

  • #WS2812B 电源控制
  • power = digitalio.DigitalInOut(board.NEOPIXEL_POWER)
  • power.direction = digitalio.Direction.OUTPUT
  • power.value = True
  • #WS2812B设置
  • pixel = neopixel.NeoPixel(board.NEOPIXEL, 1)
  • #亮度
  • pixel.brightness = 0.5

        然后是neopixel led的配置

  • #配置板载按键
  • button = digitalio.DigitalInOut(board.BUTTON)
  • button.switch_to_input(pull=digitalio.Pull.UP)
  • status = 1
  • #彩虹效果
  • def rainbow(delay):
  • for color_value in range(255):
  • pixel[0] = colorwheel(color_value)
  • time.sleep(delay)
  • #定义函数切换状态
  • def trans():
  • global status
  • if status == 5:
  • status = 1
  • else:
  • status = status + 1

            配置按键相关的功能

  • #定义函数,根据不同状态,改变led效果
  • def RGB():
  • global status
  • if status == 5:
  • print("rainbow")
  • rainbow(0.02)
  • elif status == 4:
  • pixel[0] = 0,0,255
  • print("BLUE")
  • elif status == 3:
  • pixel[0] = 0,255,0
  • print("GREEN")
  • elif status == 2:
  • pixel[0] = 255,0,0
  • print("RED")
  • else:
  • pixel[0] = 0,0,0
  • print("OFF")
  • while True:
  • if not button.value:
  • #延时函数去除按键抖动
  • time.sleep(0.2)
  • trans()
  • RGB()
  • else:
  • pass

        根据按键次数,循环切换显示效果,time.sleep()是为了去除按键抖动,防止响应过快。

                    效果:

按键控制NeoPixel LED
 

    ④分任务3:数据检测与记录——

        要求:按一定时间间隔连续记录温度/亮度信息,保存到SD卡,并可以通过按键调用查看之前的信息,并在屏幕上绘图

                            搭配器件:Adafruit ESP32-S3 TFT Feather、光传感器、微型 SD卡模块

        代码:

            由于本任务较难,代码冗杂,故只放出关键代码,完整代码在文末。

                

                记录数据部分代码:

  • #"START"被按下,开始记录数据
  • if (i==0 and j==3):
  • #更改记录状态文本显示为"recording"
  • record.text="recording"
  • display.show(menu2)
  • print("write")
  • #打开文件
  • with open("/sd/test.txt", "w") as f:
  • stop = True
  • #每隔0.5s写入一次光强数据,直到"END"被按下
  • while stop:
  • f.write(str(analog_pin.value))
  • f.write("\r\n")
  • time.sleep(0.5)
  • key = keyvalue()
  • if key!= None:
  • time.sleep(0.2)
  • i,j = key
  • key = None
  • if (i==1 and j==3):
  • print("end")
  • #更改"stop"的布尔值以停止记录数据
  • stop = False
  • #更改记录状态文本显示为"recorded"
  • record.text="recorded"
  • display.show(menu2)
  • else:
  • #如果是其他按键被按下则跳过
  • pass
  • elif(i==2 and j==3):
  • #返回上一级菜单
  • condition2 = False
  • else:
  • pass

此段代码实现了数据记录的开始与停止,理论上可以无限制地记录数据但受限于屏幕的像素以及有限的功能,建议只记录10s以内的数据。

 

            绘图代码段:

  • #当"view data"被按下
  • elif (i==1 and j==3):
  • #获取总的数据数目
  • print("read")
  • with open("/sd/test.txt", "r") as f:
  • print("Calculate total lines in file:")
  • total = 0
  • for line in f:
  • total = total +1
  • print("total:",total)
  • #定义delta,求出步长,使横坐标均匀分布在屏幕上
  • delta = math.floor(240/total)
  • print("delta:",delta)
  • #创建点列表,逐一存储SD卡返回的未处理数据
  • dot_list=[]
  • with open("/sd/test.txt", "r") as f:
  • print("Printing lines in file:")
  • for line in f:
  • dot_list.append(int(line))
  • print("listcomplete")
  • #定义最大值点
  • maxdot = max(dot_list)
  • #处理dot_list的数据,实现纵坐标自适应放缩
  • index = 0
  • for item in dot_list:
  • dot_list[index] = math.floor((item/maxdot)*134)
  • index = index +1
  • #逐一添加线,n个记录可以绘制n-1个线
  • x=0
  • y=135
  • axisx = delta
  • for n in range(1,total):
  • #以(0,135)为坐标原点,以前一条记录为起始点,后一条为终止点建立line,然后将终止点赋值给起始点,便于后续使用
  • line = Line(x0=x, y0=y, x1=axisx, y1=134-dot_list[n], color=0x00FF00)
  • x=axisx
  • y=134-dot_list[n]
  • axisx = axisx + delta
  • shape_group.append(line)
  • #显示绘图
  • display.show(shape_group)
  • condition3 = True
  • #按任意键关闭图形
  • while condition3:
  • key = keyvalue()
  • if key!= None:
  • time.sleep(0.2)
  • condition3 = False

        以上是绘图代码段,实现了图像均匀分布在屏幕上,可避免光线较弱时,图像变化不明显的问题

            绘图效果:

                分任务5:AI功能应用

                        任务要求:结合运动传感器,完成手势识别功能,至少要识别三种手势(如水平左右、前后、垂直上下、水平画圈、垂直画圈,或者更复杂手势

                                                                                        器件:Adafruit ESP32-S3 TFT Feather、运动传感器

                        代码:

  • import time
  • import math
  • import board
  • import digitalio
  • import adafruit_lis3dh

                导包

  • #使用板载SCL、SDA
  • i2c = board.I2C()
  • lis3dh = adafruit_lis3dh.LIS3DH_I2C(i2c)
  • #根据axi的值判断移动的轴
  • def recognize_gesture(axi):
  • if axi == 1:
  • print("left&right")
  • elif axi == 2:
  • print("front&back")
  • elif axi == 3:
  • print("up&down")
  • else:
  • time.sleep(0.01)
  • #获取传感器初始加速度信息
  • init = lis3dh.get_init()

            前置代码

  • while True:
  • axis=lis3dh.decide(threshold= 1.0,init_accel= init,wait = 0.3)
  • recognize_gesture(axis)

        while循环,不断判断加速度改变量是否超出范围threshold,设定的threshold越大则需要越大的加速度才能响应,反之亦然。如果没有超出范围则lis3dh.decide返回数值0,超出范围则返回变化最大的轴,以此判断手势。

  • def get_init(
  • self, count: int=10, delay: float = 0.1
  • ) -> tuple:
  • init_accel = (0,0,0)
  • for _ in range(count):
  • init_accel = tuple(map(sum, zip(init_accel, self.acceleration)))
  • time.sleep(delay / count)
  • return tuple(value / count for value in init_accel)

            在adafruit_lis3dh库中添加如上函数,以获取初始加速度值,参数count是取数据的总数,delay是函数总的延迟。每隔delay/count秒读取一个数据并累加,再取平均值,然后返回一个包含各轴初始加速度元组

  • def decide(
  • self,threshold:float = 1.1,init_accel: tuple = (1,1,9),wait:float = 0.2
  • ) -> int:
  • delta_accel = tuple(map(lambda x,y:abs(abs(x-y)-threshold) ,init_accel,self.acceleration))
  • ifintsh= tuple(map(lambda x : x < threshold,delta_accel))
  • time.sleep(wait)
  • if ifintsh[0] == ifintsh[1] == ifintsh[2] == True:
  • return 0
  • else:
  • return delta_accel.index(max(delta_accel)) + 1

            在adafruit_lis3dh库中添加如上函数,delta_accel 得到的返回是当前加速度和初始加速的差的绝对值。ifintsh 是一个三元素元组,存储三个布尔值,分别代表x,y,z轴是否超出设定的threshold。如果三轴的变化均没有超出设定范围,则返回零,如果超出范围,则返回delta_accel中的最大值的序号加1。

    效果:

                这个手势识别的精度有限,本应对加速度求微分再进一步判断手势,由于本人高数水平有限,暂时没能进一步优化,后续水平提高之后再优化一下。
 
 

3. 对本活动的心得体会

    致谢:

        首先,感谢eeworld和digikey,没有你们我就没法参加此次活动,也就没法学到这么多东西;

        其次,感谢@HonestQiao 乔老师精心制作的教程以及简明的例程,带我走进了circuitpython的世界;

        最后,感谢社区的所有小伙伴,一路走来你们让我不觉得孤独。

    收获:

        本次活动我学习了circuitpython的开发,见识到了它的便捷性,同时也加深了我对python的理解。在不断学习和探索的过程中激发了我的创造力。

    建议

        但同时circuitpython的中文资源较少,少有的一个中文站也只是对英文文档的翻译,并不能适应国人的开发习惯,建议官方或者有能力的组织或个人合作起来,完善circuitpython的生态。

 

//内容三:可编译下载的代码//

打包后的链接:

【得捷电子Follow me第2期】代码-夷则玖

最新回复

其次,感谢@HonestQiao   乔老师精心制作的教程以及简明的例程,带我走进了circuitpython的世界; 乔老师,好老师!   详情 回复 发表于 2023-11-8 13:25
点赞 关注
 
 

回复
举报

7173

帖子

11

TA的资源

版主

沙发
 

其次,感谢@HonestQiao   乔老师精心制作的教程以及简明的例程,带我走进了circuitpython的世界;

乔老师,好老师!

 
 
 

回复
您需要登录后才可以回帖 登录 | 注册

随便看看
查找数据手册?

EEWorld Datasheet 技术支持

相关文章 更多>>
关闭
站长推荐上一条 1/10 下一条
【有奖直播】2025是德科技数字月-数字新品来助阵
直播时间:3月19日(周三)14:00
直播奖励:小米口红充电宝、倍思充电线、是德科技十周年鼠标垫

查看 »

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

 
机器人开发圈

About Us 关于我们 客户服务 联系方式 器件索引 网站地图 最新更新 手机版

站点相关: 国产芯 安防电子 汽车电子 手机便携 工业控制 家用电子 医疗电子 测试测量 网络通信 物联网

北京市海淀区中关村大街18号B座15层1530室 电话:(010)82350740 邮编:100190

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2025 EEWORLD.com.cn, Inc. All rights reserved
快速回复 返回顶部 返回列表