sipower

  • 2023-11-25
  • 回复了主题帖: 阿尔达H-30T高温休眠版恒温电烙铁---一台实用的好工具

    lkh747566933 发表于 2023-11-23 23:40 看起来结构也挺简单的啊,应该是电阻发热放方式吧!
    差不多,就是一个pwm控制通电,有一个反馈可以控制温度

  • 回复了主题帖: 阿尔达H-30T高温休眠版恒温电烙铁---一台实用的好工具

    秦天qintian0303 发表于 2023-11-24 11:49 调温盒的重量如何,会不会线直接个带跑?  
    调温盒距离插头很近,倒不会带跑,就是不方便操作

  • 2023-11-22
  • 回复了主题帖: 【玄铁杯第三届RISC-V应用创新大赛】LicheePi 4A+YOLOX,启动

    你用的哪个版本的镜像?

  • 2023-11-21
  • 发表了日志: 阿尔达H-30T高温休眠版恒温电烙铁---一台实用的好工具

  • 发表了主题帖: 阿尔达H-30T高温休眠版恒温电烙铁---一台实用的好工具

      1、开箱 收到包裹后,拆开袋子,是如下图一个盒子,由于快递运输中有挤压,盒子有点变形。打开盒子后,可见主角阿尔达H-30T套在一个袋子里面,还配有一张说明书,上面写了使用注意事项。包装整体上看有点朴素,盒子在运输中虽然有点变形,好在没有影响到内部产品。 2、外观 阿尔达H-30T主体如下图所示,三芯电源插头代表着有接地功能。电源线上接有一个温控盒子,盒子上有调温旋钮、指示灯和铭牌。直柄烙铁,把手位置有隔热胶圈,手感很好。 3、烙铁头组件 对阿尔达H-30T最关键的部位---烙铁头组件拆解,如下图所见,用料诚意满满,金属件厚实,接口精致。烙铁头拆卸也很方便,更换无忧。 4、手柄拆解 考虑有可能会换加热芯,对阿尔达H-30T手柄也进行了拆解,如下图所示,内部采用白色T型PCB,这个形状在组装时可以防呆。PCB上采用中文印刷接线焊盘指示,这样在换加热芯时就不会出错了。 5、焊接测试 烙铁头表面处理的很好,只在刀头刃部沾锡,其他地方不会堆积,焊接时取锡,刮锡都能很容易控制,加热点集中,焊接体验很舒服。 6、视频展示 如下视频展示了融锡时间,休眠唤醒功能,可视升温过程,未接地报警功能,功率变化等。 [localvideo]a762ea0954db178627df76d419f85eb1[/localvideo]   7、总结 由以上图文、视频可见,阿尔达H-30T是一台很实用的电烙铁,在节能,温控,操作方面体验非常好,是硬件工程师的好帮手。 8、建议 这里再提一些建议:包装可以做的更美观一些,内部用泡棉或外面用气柱袋固定,防止暴力运输损坏产品。控温盒上旋钮太大挡住丝印,建议调整丝印适配。插头到控温盒的线太短,导致控温盒是歪的不好操作,建议要么加长这段线缆,要么插头和控温盒做成一体。 以上是阿尔达H-30T高温休眠版恒温电烙铁的评测,感谢阿尔达和EEWORLD提供这次测试机会。

  • 2023-11-17
  • 回复了主题帖: 【DigiKey“智造万物,快乐不停”创意大赛】智能家居中控屏 3. 运行 LVGL

    lugl4313820 发表于 2023-11-15 11:12 stm32cbueIDE移植LVGL还是非常方便的,就是在实际的工程中,freeRTOS配合起来有点麻烦,内存管理还是需要很 ...
    我用lvgl官方的例程,用stm32cubeide编译,只要一起开启LTDC DMA2D程序就报错,不知道咋回事。

  • 2023-11-13
  • 回复了主题帖: 【玄铁杯第三届RISC-V应用创新大赛】LicheePi 4A+便携移动内窥镜硬件搭建完成

    这一套硬件做的很精致啊,期待软件完成效果

  • 2023-11-11
  • 回复了主题帖: 测评入围名单:阿尔达H-30T高温休眠版恒温电烙铁

    个人信息无误,确认可以完成评测计划

  • 2023-11-09
  • 发表了主题帖: 【玄铁杯第三届RISC-V应用创新大赛】LicheePi 4A+005基于LicheePi 4A手势识别的像素灯

    [localvideo]93e51d1f1ae1ed74469e92943910810c[/localvideo]   基于LicheePi 4A手势识别的像素灯 一、项目背景 本作品的创意来源于以前看的小米发布会,当时小米发布了一款有意思的产品:米家皮皮灯。这个俏皮的台灯很是吸引眼球,后来看拆机视频,原来里面有一个摄像头做图像识别,然后实现各种好玩的功能。这次比赛用到的LicheePi 4A,在算力上,功能上都很优秀,非常适合做图像识别处理。加上近期做了一些使用寻址RGB灯相关的小制作,然后就萌生了采用像素屏做一个手势识别台灯的想法。从网上查阅了一些资料,发现可行性还是很高的,索性就提交创意参赛了。 二、作品简介 本作品使用LicheePi 4A识别手势形态,使用Arduino控制灯光效果,使用16*16的WS2812B矩阵组成的像素屏作为灯头。工作过程为:LicheePi 4A识别手势形态后进行编码,通过串口发给Arduino小板,Arduino小板控制像素屏显示不同形态的图像和色彩,实现灯光炫彩效果和实用功能。 最终的作品我设计了四个功能:默认第一个是时钟功能,用于显示时间。第二个是台灯功能,可以调亮度和色调。第三个是像素秀,用来展示各种炫彩效果。第四个是一个打砖块的小游戏,可以通过手势控制。 具体实现上,原计划使用基于OpenCV+MediaPipe进行手势识别,使用 Arduino库驱动像素屏,这些操作在Windows平台大部分实际用过,这次整合一下应该就能完成任务。但是实际上手后,发现发现MediaPipe没有基于Risc-V的轮子,只好从头学习YOLO平台的手势识别训练和部署。在交叉编译也遇到一些问题,模型推理计算部分没能编译出可执行程序在开发板上运行,只好放在PC上作为服务端,开发板负责图像采集和整体控制,Arduino负责像素屏显示,最终通过这样间接的方式实现了计划中的各个功能。下图是最终的作品照片。 三、系统框图 该作品系统框图如下,具体包含以下部分:LicheePi 4A开发板,摄像头,显示器组成上位机控制端。Arduino小板和像素屏组成下位执行端。PC机作为服务器进行模型运算。 四、各部分功能说明 下面从控制端、执行端、服务端三个方面介绍作品实现过程。 1、控制端 此部分主要由LicheePi 4A开发板,摄像头,显示器组成。通过摄像头拍摄图片,然后在开发板上运行openCV对图片进行简单处理,通过WiFi发送给服务端调用模型计算,然后开发板接收处理完的图片和识别结果,将结果显示在屏幕上,同时对手势识别结果进行分类。本次模型总共训练了11个手势: "zero","one", "two", "three", "four", "five", "little", "ok", "hand", "horns", "thumbup"。由于预期的功能没有这么多,挑选了几个识别率比较高的手势作为控制信号。然后将手势在窗口中的坐标映射为16*16点阵对应的坐标,和手势编号一起打包,通过USB转串口发给执行端。 其中坐标映射算法是本次作品一个关键点。手掌距离摄像头远近不同,大小也不同,为了让手势不管在什么的位置,只要识别成功就要有一个坐标和像素屏对应,我采用一个取余再等比映射的方法,具体算法就是摄像头窗口尺寸减去手掌轮廓尺寸,然后用手掌坐标和这个差值做比,最后乘以16就转换为了像素屏坐标,这样就映射成功了。部分代码如下图。 2、执行端 此部分主要由Arduino小板和像素屏组成。这里选用的是一块基于ESP32的Arduino小板,这块板子带WiFi功能,通过一个IO口和像素屏相连,采用C++编程。为了有较好的显示效果,我给像素屏打印了一套外壳。其中为了不串光,在隔光栅格上还费了一番功夫,具体的可以参见我的第三篇文章。下图是下位机合照。 下位机执行端主要实现如下四个功能: 2.1时钟 下位机上电默认界面是显示一个时钟,如下图所示,上面数值是小时,下面是分钟,中间一条横线每秒闪烁一次,最右边是60个点,每秒钟点亮一个,对应秒数。 考虑到下位机上电就能够单独使用,我在程序中做了一个使用WiFi自动通过NTP校时功能,直接调用ESP32的库,比较好实现。 在手势操作方面,主要实现以下功能:"horns"手势可以从其他界面进入时钟界面;"five", "little"两个中的任意一个手势上下滑动可以调整屏幕亮度;"hand"手势用来启动一次时间校准。 2.2台灯功能 像素屏作为照明使用,界面就比较简单,整个屏幕填充同一种颜色即可。在手势操作方面,主要实现以下功能:"one"手势可以从其他界面进入台灯界面;"five"手势上下滑动可以调整屏幕亮度,左右滑动调整灯光颜色;"hand"手势显示中等亮度白光,满足最常用照明需求。 2.3像素秀 此功能用来展示各种炫彩效果,显示一些动态logo,未来有机会作为商品的话,可以播放一些图标、广告。在手势操作方面,主要实现以下功能:"two"手势可以从其他界面进入像素秀界面;"five", "little"两个中的任意一个手势上下滑动可以调整屏幕亮度。 2.4游戏 此功能是一个打砖块的小游戏,手掌左右移动控制底部滑块位置,反射弹珠轰击顶部砖块,是本作品一个趣味性的功能。在手势操作方面,主要实现以下功能:" three "手势可以从其他界面进入游戏界面;"five"手势左右移动控制底部滑块位置;"hand"手势启动弹珠发射;"little"手势上下滑动可以调整屏幕亮度。 以上功能可以参见我的视频演示,会有一个更直观的印象。 3、服务端 此部分程序运行在PC上,主要功能是用fastapi和uvicorn构建一个服务器,接收控制端发过来的图片并跑模型计算,然后再将识别结果发回去。之所以没有在开发板上部署模型识别,是因为交叉编译没搞定,时间紧迫只好采用这种“曲线救国”的方法。具体操作过程详见我的四篇文章。 五、视频演示 手势识别成功,并把坐标映射到像素屏上的效果,不同手势对应不同颜色,像素屏亮点随着手势移动。 http://bbsvideo.eeworld.com.cn/2023103121/c/9f922ff1674f.mp4 作品介绍和功能演示,共四个功能:默认第一个是时钟功能,用于显示时间。第二个是台灯功能,可以调亮度和色调。第三个是像素秀,用来展示各种炫彩效果。第四个是一个打砖块的小游戏,可以通过手势控制。 http://training.eeworld.com.cn/course/68205/lesson/38261/media 六、项目源码 http://download.eeworld.com.cn/detail/sipower/629835 七、发布的博文 设计过程中,同时在平头哥和EEWorld发帖分享经验总结,链接汇总如下: 【玄铁杯第三届RISC-V应用创新大赛】LicheePi 4A+001开箱 https://xuantie.t-head.cn/community/post/detail?spm=a2cl5.27438731.0.0.41082918n6xlFV&id=4230772844897832960 http://bbs.eeworld.com.cn/thread-1258242-1-1.html 【玄铁杯第三届RISC-V应用创新大赛】LicheePi 4A+002系统软件准备 https://xuantie.t-head.cn/community/post/detail?spm=a2cl5.27438731.0.0.4108RdLARdLA0W&id=4231598609260417024 http://bbs.eeworld.com.cn/thread-1258551-1-1.html 【玄铁杯第三届RISC-V应用创新大赛】LicheePi 4A+003打印外壳 https://xuantie.t-head.cn/community/post/detail?spm=a2cl5.27438731.0.0.4108RdLARdLA0W&id=4239391426934280192 http://bbs.eeworld.com.cn/thread-1261281-1-1.html 【玄铁杯第三届RISC-V应用创新大赛】LicheePi 4A+004曲线救国的手势识别 https://xuantie.t-head.cn/community/post/detail?spm=a2cl5.27438731.0.0.4108RdLARdLA0W&id=4239899735109996544 http://bbs.eeworld.com.cn/thread-1261514-1-1.html 八、项目总结 本作品通过部署YOLOv5,实现了11种手势的识别。LicheePi 4A开发板通过USB转串口方式与Arduino小板连接,将手势识别数据传递给下位机,然后使用Arduino编程驱动像素屏进行效果展示。本作品中,手势采集,模型数据识别,像素屏控制都是非常独立的模块,我通过多种通信方式把它们整合到一起,最终成为一个有机的整体,是本作品的创新点。另外手势坐标转为换像素屏坐标,采用的取余再等比映射的方法也是一个关键点。 这个作品大概用了一个月的业余时间设计完成。感谢平头哥提供的开发板和热心的技术支持,感谢EEWORLD提供展示的平台。通过本次作品设计,我学习了图像识别和RISC-V的相关知识,感受到了国产芯片和开发平台的进步,相信总有一天,我们终将让国产芯片在世界各个地方发挥其不可替代的作用。 九、其他 从使用中遇到的问题看,目前RISC-V相关的库还是比较缺乏,对应的编程、编译环境也有待完善。也希望出一些更详细的教程,能够让初学者更容易上手相关的开发。

  • 发表了日志: 【玄铁杯第三届RISC-V应用创新大赛】LicheePi 4A+005基于LicheePi 4A手势识别的像素灯

  • 加入了学习《基于LicheePi 4A手势识别的像素灯》,观看 基于LicheePi 4A手势识别的像素灯

  • 加入了学习《得捷电子Follow+me第2期》,观看 得捷电子Follow+me第2期

  • 加入了学习《Follow me第2期》,观看 得捷电子Follow me第2期任务视频

  • 上传了资料: 基于LicheePi 4A手势识别的像素灯--作品代码

  • 2023-10-31
  • 回复了主题帖: 【玄铁杯第三届RISC-V应用创新大赛】LicheePi 4A+003打印外壳

    dql2016 发表于 2023-10-31 21:31 打印外壳多少刀
    蹭的朋友的设备,没花钱

  • 发表了日志: 【玄铁杯第三届RISC-V应用创新大赛】LicheePi 4A+004曲线救国的手势识别

  • 发表了主题帖: 【玄铁杯第三届RISC-V应用创新大赛】LicheePi 4A+004曲线救国的手势识别

    本篇介绍实现手势识别的曲折过程。在程序调试通过后,做了一个视频演示。 1、最初的方案 在我最开始的任务申请中,计划是采用mediapipe识别手势,然后分类,操控下位机在像素屏上显示不同的灯光效果。拿到开发板之前,我先在PC上把框架搭建好了,基本实现手势识别并把对应代码发给下位机,只等拿到板子移植一下就大功告成。 开发板拿到手后,仔细查阅相关文档,发现mediapipe没有基于risc-v的轮子,这个开发库自己编译也不太现实,因为用到的编译工具也没有risc-v平台的示例。 后来咨询官方技术支持,建议使用平头哥提供好的AI推理框架,也就是yolox教程里的步骤。mediapipe暂时是不用考虑了。然后,我开始了苦逼的YOLO平台学习。 2、学习YOLOv5 首先说一下为什选择YOLOv5这个版本,因为我从网上查到的做手势识别的教程,基本都是在这个版本上实现的,为了少走弯路,最好的方式是拿来主义。 2.1、开发环境安装 本着直冲目标的学习方式,先研读了网上能搜到的大部分中文资料,基本上搞明白了图像识别的训练、部署过程,虽然没搞明白具体的工作原理,但是好歹可以试着上手实操一下。 搭建开发环境还算顺利,主要遇到的问题是在CUDA适配的时候,总也不好使,不能启动GPU做运算。反反复复重装了好多次,才发现下载的驱动版本不对,按照官网指定的对应关系重新安装,终于好用了。 如下是安装好的轮子: (venv) PS E:\TEST\Python\test> pip list Package Version ----------------------- ------------ absl-py 1.4.0 annotated-types 0.6.0 anyio 3.7.1 asttokens 2.4.0 attrs 23.1.0 backcall 0.2.0 cachetools 5.3.1 certifi 2023.7.22 cffi 1.15.1 charset-normalizer 3.3.0 click 8.1.7 colorama 0.4.6 contourpy 1.1.0 cycler 0.11.0 Cython 3.0.4 decorator 5.1.1 EasyProcess 1.1 entrypoint2 1.1 executing 2.0.0 fastapi 0.104.0 filelock 3.12.4 flatbuffers 23.5.26 fonttools 4.42.1 fsspec 2023.9.2 google-auth 2.23.3 google-auth-oauthlib 1.1.0 grpcio 1.59.0 h11 0.14.0 idna 3.4 ipython 8.16.1 jedi 0.19.1 Jinja2 3.1.2 joblib 1.3.2 keyboard 0.13.5 kiwisolver 1.4.5 Markdown 3.5 MarkupSafe 2.1.3 matplotlib 3.7.2 matplotlib-inline 0.1.6 mediapipe 0.10.3 MouseInfo 0.1.3 mpmath 1.3.0 mss 9.0.1 networkx 3.2 numpy 1.25.2 oauthlib 3.2.2 opencv-contrib-python 4.8.0.76 opencv-python 4.8.0.76 packaging 23.1 pandas 2.1.1 parso 0.8.3 pickleshare 0.7.5 Pillow 10.0.0 pip 23.3 prompt-toolkit 3.0.39 protobuf 3.20.3 pure-eval 0.2.2 pyasn1 0.5.0 pyasn1-modules 0.3.0 PyAutoGUI 0.9.54 pycparser 2.21 pydantic 2.4.2 pydantic_core 2.10.1 PyGetWindow 0.0.9 Pygments 2.16.1 PyMsgBox 1.0.9 pyparsing 3.0.9 pyperclip 1.8.2 PyRect 0.2.0 pyscreenshot 3.1 PyScreeze 0.1.29 pyserial 3.5 python-dateutil 2.8.2 pytweening 1.0.7 pytz 2023.3.post1 PyYAML 6.0.1 requests 2.31.0 requests-oauthlib 1.3.1 rsa 4.9 scikit-learn 1.3.1 scipy 1.11.3 seaborn 0.13.0 setuptools 65.5.1 six 1.16.0 sniffio 1.3.0 sounddevice 0.4.6 stack-data 0.6.3 starlette 0.27.0 sympy 1.12 tensorboard 2.15.0 tensorboard-data-server 0.7.1 threadpoolctl 3.2.0 torch 2.1.0+cu121 torchaudio 2.1.0+cu121 torchvision 0.16.0+cu121 tqdm 4.66.1 traitlets 5.11.2 typing_extensions 4.8.0 tzdata 2023.3 urllib3 2.0.7 uvicorn 0.23.2 wcwidth 0.2.8 Werkzeug 3.0.0 wheel 0.38.4 [notice] To update, run: python.exe -m pip install --upgrade pip (venv) PS E:\TEST\Python\test> 2.2、数据集收集 想训练出好的模型,首先得有足够的大数据集。最开始我想自己建数据集,然后一通检索,发现如果只有很少的训练图片,训练的模型基本不能用,弄太多图片,自己一个人精力有限,做出来不太现实。想在网上找现成标注好的,发现都是收费的,而且也不知道能不能用。下图是找到的部分训练集图片,存在各种问题。 找数据集过程中,在GitHub上找到几个训练好的手势识别模型,基于此,我决定换个思路,先看看这些模型是不是好用,如果好用,直接拿来主义,还避免我这小破本跑模型训练再累死。 2.3测试现成模型 从诸多分享的模型中,筛选出两个可用的,我先测试了来自Northwestern University大学的研究生Jordan Zeeb分享的方案。他一共尝试了三种方法: 第一种方法使用openCV的基本图像阈值来寻找应用掩模后手指的轮廓。这个作为最基础的方法,受限较多,只能在特定情形下识别成功,仅作为openCV学习的初探。第二种方法使用pytorch中的机器学习原理来创建用于图像分类的CNN。这个也是应用场景受限,我实测识别率也不太理想。不过通过这个可以熟悉搭建神经网络过程。第三个使用YOLOv5的对象检测来识别美国手语中字母表的所有字母。从作者演示视频看,识别成功率还是很高的,但是我实际测试,并不是很理想。可能是因为作者自己建的数据库,样本数量和种类太少造成的,只是识别他自己比较准确。通过该作者3个演示方案,基本就能学会实际应用中手势识别的相关训练和部署了。下面是该作者的GitHub地址: https://github.com/SegwayWarrior/Gesture_Recognition_opencv_yolov5/ 接下来测试从百度网盘找到的一个训练好的模型,地址如下: https://pan.baidu.com/s/1AYmZmr5K2f5fGnizjdyZ4w 提取码:68vu 这个模型使用datawhale组织提供的数据集,链接如下: https://gas.graviti.cn/dataset/datawhale/HandPose 教程参考: https://github.com/datawhalechina/HandPoseKeyPoints/blob/main/利用关节点做手势识别.md 实测识别准确率还是不错的,具体测试代码如下: import argparse import time from pathlib import Path import cv2 import torch import torch.backends.cudnn as cudnn from numpy import random from models.experimental import attempt_load from utils.datasets import LoadStreams, LoadImages from utils.general import check_img_size, check_requirements, check_imshow, non_max_suppression, apply_classifier, \ scale_coords, xyxy2xywh, strip_optimizer, set_logging, increment_path from utils.plots import plot_one_box from utils.torch_utils import select_device, load_classifier, time_synchronized def detect(save_img=False): source, weights, view_img, save_txt, imgsz = opt.source, opt.weights, opt.view_img, opt.save_txt, opt.img_size save_img = not opt.nosave and not source.endswith('.txt') # save inference images webcam = source.isnumeric() or source.endswith('.txt') or source.lower().startswith( ('rtsp://', 'rtmp://', 'http://', 'https://')) # Directories save_dir = Path(increment_path(Path(opt.project) / opt.name, exist_ok=opt.exist_ok)) # increment run (save_dir / 'labels' if save_txt else save_dir).mkdir(parents=True, exist_ok=True) # make dir # Initialize set_logging() device = select_device(opt.device) half = device.type != 'cpu' # half precision only supported on CUDA # Load model model = attempt_load(weights, map_location=device) # load FP32 model stride = int(model.stride.max()) # model stride imgsz = check_img_size(imgsz, s=stride) # check img_size if half: model.half() # to FP16 # Second-stage classifier classify = False if classify: modelc = load_classifier(name='resnet101', n=2) # initialize modelc.load_state_dict(torch.load('weights/resnet101.pt', map_location=device)['model']).to(device).eval() # Set Dataloader vid_path, vid_writer = None, None if webcam: view_img = check_imshow() cudnn.benchmark = True # set True to speed up constant image size inference dataset = LoadStreams(source, img_size=imgsz, stride=stride) else: dataset = LoadImages(source, img_size=imgsz, stride=stride) # Get names and colors names = model.module.names if hasattr(model, 'module') else model.names colors = [[random.randint(0, 255) for _ in range(3)] for _ in names] # Run inference if device.type != 'cpu': model(torch.zeros(1, 3, imgsz, imgsz).to(device).type_as(next(model.parameters()))) # run once t0 = time.time() for path, img, im0s, vid_cap in dataset: img = torch.from_numpy(img).to(device) img = img.half() if half else img.float() # uint8 to fp16/32 img /= 255.0 # 0 - 255 to 0.0 - 1.0 if img.ndimension() == 3: img = img.unsqueeze(0) # Inference t1 = time_synchronized() pred = model(img, augment=opt.augment)[0] # Apply NMS pred = non_max_suppression(pred, opt.conf_thres, opt.iou_thres, classes=opt.classes, agnostic=opt.agnostic_nms) t2 = time_synchronized() # Apply Classifier if classify: pred = apply_classifier(pred, modelc, img, im0s) # Process detections for i, det in enumerate(pred): # detections per image if webcam: # batch_size >= 1 p, s, im0, frame = path[i], '%g: ' % i, im0s[i].copy(), dataset.count else: p, s, im0, frame = path, '', im0s, getattr(dataset, 'frame', 0) p = Path(p) # to Path save_path = str(save_dir / p.name) # img.jpg txt_path = str(save_dir / 'labels' / p.stem) + ('' if dataset.mode == 'image' else f'_{frame}') # img.txt s += '%gx%g ' % img.shape[2:] # print string gn = torch.tensor(im0.shape)[[1, 0, 1, 0]] # normalization gain whwh if len(det): # Rescale boxes from img_size to im0 size det[:, :4] = scale_coords(img.shape[2:], det[:, :4], im0.shape).round() # Print results for c in det[:, -1].unique(): n = (det[:, -1] == c).sum() # detections per class s += f"{n} {names[int(c)]}{'s' * (n > 1)}, " # add to string # Write results for *xyxy, conf, cls in reversed(det): if save_txt: # Write to file xywh = (xyxy2xywh(torch.tensor(xyxy).view(1, 4)) / gn).view(-1).tolist() # normalized xywh line = (cls, *xywh, conf) if opt.save_conf else (cls, *xywh) # label format with open(txt_path + '.txt', 'a') as f: f.write(('%g ' * len(line)).rstrip() % line + '\n') if save_img or view_img: # Add bbox to image label = f'{names[int(cls)]} {conf:.2f}' plot_one_box(xyxy, im0, label=label, color=colors[int(cls)], line_thickness=3) # Print time (inference + NMS) print(f'{s}Done. ({t2 - t1:.3f}s)') # Stream results if view_img: cv2.imshow(str(p), im0) cv2.waitKey(1) # 1 millisecond # Save results (image with detections) if save_img: if dataset.mode == 'image': cv2.imwrite(save_path, im0) else: # 'video' or 'stream' if vid_path != save_path: # new video vid_path = save_path if isinstance(vid_writer, cv2.VideoWriter): vid_writer.release() # release previous video writer if vid_cap: # video fps = vid_cap.get(cv2.CAP_PROP_FPS) w = int(vid_cap.get(cv2.CAP_PROP_FRAME_WIDTH)) h = int(vid_cap.get(cv2.CAP_PROP_FRAME_HEIGHT)) else: # stream fps, w, h = 30, im0.shape[1], im0.shape[0] save_path += '.mp4' vid_writer = cv2.VideoWriter(save_path, cv2.VideoWriter_fourcc(*'mp4v'), fps, (w, h)) vid_writer.write(im0) if save_txt or save_img: s = f"\n{len(list(save_dir.glob('labels/*.txt')))} labels saved to {save_dir / 'labels'}" if save_txt else '' print(f"Results saved to {save_dir}{s}") print(f'Done. ({time.time() - t0:.3f}s)') if __name__ == '__main__': parser = argparse.ArgumentParser() # 使用什么权重 parser.add_argument('--weights', nargs='+', type=str, default='best.pt', help='model.pt path(s)') # 测试的文件来源 parser.add_argument('--source', type=str, default='0', help='source') # file/folder, 0 for webcam # 测试的图像大小,这个参数和使用的model相关 parser.add_argument('--img-size', type=int, default=640, help='inference size (pixels)') # 筛选候选框的置信度阈值,值越大框越少 parser.add_argument('--conf-thres', type=float, default=0.25, help='object confidence threshold') # NMS舍去其他临近框的iou的阈值,越大同一个目标可能的框就越多 parser.add_argument('--iou-thres', type=float, default=0.45, help='IOU threshold for NMS') # 使用的gpu编号 parser.add_argument('--device', default='0', help='cuda device, i.e. 0 or 0,1,2,3 or cpu') # 是否将结果展示出来,加上 --view-img 即可 parser.add_argument('--view-img', action='store_true', help='display results') # 是否保存检测结果为 *.txt parser.add_argument('--save-txt', action='store_true', help='save results to *.txt') # 是否保存置信度到本地 parser.add_argument('--save-conf', action='store_true', help='save confidences in --save-txt labels') # 不保存检测结果的txt和img到本地 parser.add_argument('--nosave', action='store_true', help='do not save images/videos') # 按照某个类的label标签号,在检测结果中过滤掉这个类 parser.add_argument('--classes', nargs='+', type=int, help='filter by class: --class 0, or --class 0 2 3') # parser.add_argument('--agnostic-nms', action='store_true', help='class-agnostic NMS') parser.add_argument('--augment', action='store_true', help='augmented inference') parser.add_argument('--update', action='store_true', help='update all models') # 保存检测结果的文件夹 parser.add_argument('--project', default='runs/detect', help='save results to project/name') # 每一次检测结果保存的子文件夹名 parser.add_argument('--name', default='exp', help='save results to project/name') # parser.add_argument('--exist-ok', action='store_true', help='existing project/name ok, do not increment') opt = parser.parse_args() print(opt) with torch.no_grad(): if opt.update: # update all models (to fix SourceChangeWarning) for opt.weights in ['yolov5s.pt', 'yolov5m.pt', 'yolov5l.pt', 'yolov5x.pt']: detect() strip_optimizer(opt.weights) else: detect() 效果图如下: 这个一定要用GPU做测试,用CPU的话,感觉它着急的快要爆掉了。然后我决定就用这个现成模型完成作业。另外也尝试了一些其他模型,效果都不理想,略过不表。 2.4、模型部署 研究教程,要想把模型搞到开发板上运行,需要先在Linux里的容器中交叉编译成th1520 平台上的可执行文件,然后再导入到开发板中执行,步骤狠多。对于仅有一点微薄Linux知识的我来说,可谓步步艰辛,处处遇卡,直到我最终放弃了这个方式,因为再墨迹下去,时间就来不及了。 我的目标,还是先完成作业为先。经过多方查找资料,我采用“曲线救国”方式实现功能。具体实施分两部分:在我的小破本上用fastapi和uvicorn构建一个服务端,用来跑模型识别运算。在开发板上跑一个客户端,用来采集图片发送给服务端,然后接收服务端识别完的结果,对结果进行解析,并转化成需要的控制数据。 服务端代码如下: import time import torch import torch.backends.cudnn as cudnn from numpy import random from models.experimental import attempt_load from utils.datasets import LoadStreams, LoadImages from utils.general import check_img_size, check_requirements, check_imshow, non_max_suppression, apply_classifier, \ scale_coords, xyxy2xywh, strip_optimizer, set_logging, increment_path from utils.plots import plot_one_box from utils.torch_utils import select_device, load_classifier, time_synchronized from fastapi import FastAPI, File, UploadFile from pydantic import BaseModel import json import uvicorn import cv2 import numpy as np import io import base64 app = FastAPI() def letterbox(img, new_shape=(640, 640), color=(114, 114, 114), auto=True, scaleFill=False, scaleup=True, stride=32): # Resize and pad image while meeting stride-multiple constraints shape = img.shape[:2] # current shape [height, width] if isinstance(new_shape, int): new_shape = (new_shape, new_shape) # Scale ratio (new / old) r = min(new_shape[0] / shape[0], new_shape[1] / shape[1]) if not scaleup: # only scale down, do not scale up (for better test mAP) r = min(r, 1.0) # Compute padding ratio = r, r # width, height ratios new_unpad = int(round(shape[1] * r)), int(round(shape[0] * r)) dw, dh = new_shape[1] - new_unpad[0], new_shape[0] - new_unpad[1] # wh padding if auto: # minimum rectangle dw, dh = np.mod(dw, stride), np.mod(dh, stride) # wh padding elif scaleFill: # stretch dw, dh = 0.0, 0.0 new_unpad = (new_shape[1], new_shape[0]) ratio = new_shape[1] / shape[1], new_shape[0] / shape[0] # width, height ratios dw /= 2 # divide padding into 2 sides dh /= 2 if shape[::-1] != new_unpad: # resize img = cv2.resize(img, new_unpad, interpolation=cv2.INTER_LINEAR) top, bottom = int(round(dh - 0.1)), int(round(dh + 0.1)) left, right = int(round(dw - 0.1)), int(round(dw + 0.1)) img = cv2.copyMakeBorder(img, top, bottom, left, right, cv2.BORDER_CONSTANT, value=color) # add border return img, ratio, (dw, dh) device = select_device('0') half = device.type != 'cpu' # half precision only supported on CUDA # Load model weights = './best.pt' model = attempt_load(weights, map_location=device) # load FP32 model stride = int(model.stride.max()) # model stride imgsz = check_img_size(640, s=stride) # check img_size if half: model.half() # to FP16 def detect(source, conf_thres=0.25, iou_thres=0.45, classes=None, agnostic_nms=False): # img0 = cv2.imread(source) # BGR img0 = source # Padded resize img = letterbox(img0, imgsz, stride=stride)[0] # Convert img = img[:, :, ::-1].transpose(2, 0, 1) # BGR to RGB, to 3x416x416 img = np.ascontiguousarray(img) # Get names and colors---获取classnamelist names = model.module.names if hasattr(model, 'module') else model.names # Run inference if device.type != 'cpu': model(torch.zeros(1, 3, imgsz, imgsz).to(device).type_as(next(model.parameters()))) # run once t0 = time.time() pred_list = [] # print(img) img = torch.from_numpy(img).to(device) img = img.half() if half else img.float() # uint8 to fp16/32 img /= 255.0 # 0 - 255 to 0.0 - 1.0 if img.ndimension() == 3: img = img.unsqueeze(0) # Inference pred = model(img, augment=False)[0] # Apply NMS pred = non_max_suppression(pred, conf_thres, iou_thres, classes=classes, agnostic=agnostic_nms) # print(pred) if pred: for p in pred: pred_list.append(p.tolist()) # print(f'Done. ({time.time() - t0:.3f}s)') return pred_list def base64_to_image(base64_code): # base64解码 img_data = base64.b64decode(base64_code) # 转换为np数组 img_array = np.fromstring(img_data, np.uint8) # 转换成opencv可用格式 img = cv2.imdecode(img_array, cv2.COLOR_RGB2BGR) return img class Image(BaseModel): img: str @app.post('/detect') def detect_fun(image: Image): img = base64_to_image(image.img) # img = cv2.cvtColor(img, cv2.COLOR_RGB2BGR) pred_list = detect(source=img) # print(pred_list) # cv2.imwrite('xxx.jpg', img) return {'state': 'success', 'answer': pred_list} if __name__ == '__main__': # fast:app 中的 fast=运行的文件名,如果修改了记得这里别忘记改 uvicorn.run("interface_of_model:app", host="0.0.0.0", port=8000, reload=True) # pred_list = detect(weights='./runs/train/exp8/weights/best.pt', source="/home/zk/git_projects/hand_pose/hand_pose_yolov5_5.0/hand_pose/images/four_fingers10.jpg") # print(pred_list) 客户端代码如下: import requests import cv2 import base64 import json import random import serial import struct # Init serial port Usart = serial.Serial( port="COM3", # '/dev/ttyUSB0', # 串口 baudrate=115200, # 波特率 timeout=0.001) # 判断串口是否打开成功 if Usart.isOpen(): print("serial port open success") else: print("serial port open failed") def run(): cap = cv2.VideoCapture(0) cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640) cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480) while True: ret, frame = cap.read() if not ret: print('cap error,system exit') break # 反转图像 frame = cv2.flip(frame, 1) # 检测手位置 hands_pos = detect(frame) # 在复制的图层进行表情绘制 draw_frame = frame.copy() # 如果检测的人脸位置不为空才进来预测表情 if len(hands_pos) != 0: # 预测人脸的表情 draw_hands(draw_frame, hands_pos) # 展示视频画面 cv2.imshow('video capture', draw_frame) key = cv2.waitKey(10) & 0xff if key == 27: # 等待按下 ESC 键退出 cap.release() cv2.destroyAllWindows() Usart.close() break def image_to_base64(image_np): image = cv2.imencode('.jpg', image_np)[1] image_code = str(base64.b64encode(image))[2:-1] return image_code def detect(frame): files = {'img': image_to_base64(frame)} # print(files) response = requests.post('http://127.0.0.1:8000/detect', json.dumps(files)) # print(response.json()) return response.json()['answer'][0] def draw_hands(draw_frame, hands_pos): classes = ["four", "five", "one", "little", "ok", "zero", "hand", "horns", "three", "thumbup", "two"] # print(hands_pos) send_bufer = [170,0,0,0] for hand_pos in hands_pos: plot_one_box(hand_pos[:4], draw_frame, [255,0,0], label=classes[int(hand_pos[-1])], confidence=hand_pos[-2]) if (hand_pos[-2] > 0.55): print(hand_pos) x_p = 16 * hand_pos[0] / (640 - (hand_pos[2] - hand_pos[0])) # 映射x->16 x_p = x_p if x_p < 16 else 15 y_p = 16 * hand_pos[1] / (480 - (hand_pos[3] - hand_pos[1])) # 映射y->16 y_p = y_p if y_p < 16 else 15 y_p = 15 - y_p send_bufer[0] = 170 # header = 0xAA send_bufer[1] = int(hand_pos[-1]) send_bufer[2] = int(x_p) send_bufer[3] = int(y_p) print(send_bufer) send_data = struct.pack("%dB" % (len(send_bufer)), *send_bufer) # 解析成16进制 Usart.write(send_data) # 发送 # x是 预测结果前4个 def plot_one_box(x, img, color=None, label=None, line_thickness=3, confidence=0): # Plots one bounding box on image img tl = line_thickness or round(0.002 * (img.shape[0] + img.shape[1]) / 2) + 1 # line/font thickness color = color or [random.randint(0, 255) for _ in range(3)] c1, c2 = (int(x[0]), int(x[1])), (int(x[2]), int(x[3])) cv2.rectangle(img, c1, c2, color, thickness=tl, lineType=cv2.LINE_AA) if label: tf = max(tl - 1, 1) # font thickness t_size = cv2.getTextSize(label, 0, fontScale=tl / 3, thickness=tf)[0] c2 = c1[0] + t_size[0], c1[1] - t_size[1] - 3 cv2.rectangle(img, c1, c2, color, -1, cv2.LINE_AA) # filled cv2.putText(img, label+'{}'.format(format(confidence, '.3f')), (c1[0], c1[1] - 2), 0, tl / 3, [0, 255, 0], thickness=tf, lineType=cv2.LINE_AA) if __name__ == '__main__': run() 像素屏使用一块基于ESP32的Arduino小板驱动,主要实现把手势在屏幕窗口中的坐标映射到16x16像素的点上,不同的手势显示不同颜色,具体代码如下: Arduino代码: #define FASTLED_ALL_PINS_HARDWARE_SPI #include <FastLED.h> #define LED_PIN 23 #define COLOR_ORDER GRB #define CHIPSET WS2812B // // Mark's xy coordinate mapping code. See the XYMatrix for more information on it. // // Params for width and height const uint8_t kMatrixWidth = 16; const uint8_t kMatrixHeight = 16; #define MAX_DIMENSION ((kMatrixWidth>kMatrixHeight) ? kMatrixWidth : kMatrixHeight) #define NUM_LEDS (kMatrixWidth * kMatrixHeight) // Param for different pixel layouts const bool kMatrixSerpentineLayout = true; uint8_t key_p = 0; uint8_t key_x = 0; uint8_t key_y = 0; uint8_t key_flag = 0; uint16_t XY( uint8_t x, uint8_t y) { uint16_t i; if( kMatrixSerpentineLayout == false) { i = (y * kMatrixWidth) + x; } if( kMatrixSerpentineLayout == true) { if( y & 0x01) { // Odd rows run backwards uint8_t reverseX = (kMatrixWidth - 1) - x; i = (y * kMatrixWidth) + reverseX; } else { // Even rows run forwards i = (y * kMatrixWidth) + x; } } return i; } // The leds CRGB leds[kMatrixWidth * kMatrixHeight]; void setup() { // uncomment the following lines if you want to see FPS count information Serial.begin(115200); Serial.println("resetting!"); delay(3000); FastLED.addLeds<CHIPSET, LED_PIN, COLOR_ORDER>(leds, NUM_LEDS).setCorrection(TypicalSMD5050); FastLED.setBrightness(96); } void Serial_recive() { uint8_t rcv_buff[4]; uint8_t rcv_flag = 0,rcv_num = 0; while(Serial.available()>0){ if(rcv_flag == 0) { rcv_buff[0] = Serial.read(); if(rcv_buff[0] == 0xAA) { rcv_flag = 1; rcv_num = 1; } } else { rcv_buff[rcv_num] = Serial.read(); rcv_num++; if(rcv_num > 3) { rcv_flag = 0; key_p = rcv_buff[1]; key_x = rcv_buff[2]; key_y = rcv_buff[3]; key_flag = 1; } } } } void loop() { uint8_t ihue=0; char buff[50]; Serial_recive(); if(key_flag == 1) { key_flag = 0; ihue = key_p*22; FastLED.clearData(); leds[XY(key_x,key_y)] = CHSV(ihue,255,250); FastLED.show(); sprintf(buff, "h=%d",ihue); Serial.println(buff); } //delay(10); } 整体运行起来,演示视频如下: [localvideo]d4e03008dbe75d7b868da39687f26233[/localvideo]   3、总结 由于本人水平有限,目前只能通过“曲线救国”方式实现手势识别功能。等官方或论坛里的实例更多一些,我再尝试把运算部分移植到开发板上。

  • 回复了主题帖: 【玄铁杯第三届RISC-V应用创新大赛】LicheePi 4A+003打印外壳

    wangerxian 发表于 2023-10-30 16:06 还有螺栓?这3D打印机的精度得挺高得吧,是外面3D打印出来的?
    最普通的就行,对精度要求不高

  • 回复了主题帖: 【玄铁杯第三届RISC-V应用创新大赛】LicheePi 4A+003打印外壳

    nmg 发表于 2023-10-30 15:38 喷漆的手艺都用上啦
    施展浑身解数搞手工

  • 2023-10-30
  • 发表了主题帖: 【玄铁杯第三届RISC-V应用创新大赛】LicheePi 4A+003打印外壳

      本篇介绍基3D打印外壳的相关工作。主要介绍两部分:LicheePi 4A外壳效果和像素屏外壳加工制作过程。 1、LicheePi 4A外壳 在论坛里大佬提供了LicheePi 4A开发板的3D文件,我只需弄到3D打印机里面等着就行了,这个比较简单,打印完的效果如下图。 组装过程如下图: 组装完成后如下图: 大佬提供的图纸还是很靠谱的,打印完的外壳只需简单刮一下毛边,基本上就能严丝合缝的扣合在一起。 有了这个外壳做保护,就不用担心调试时不小心误触造成损坏了。 2、像素屏外壳 我的作品规划是用像素屏做显示。我从淘宝买了一块16x16点阵的像素软屏,采用WS2812B灯珠,如下图。 这个像素屏直接用来显示,效果很差,不同灯光照射下表现差异很大。如下图。 为了做出比较好的效果,就得做一些结构件进行处理。参考网上到大佬们的做法,主要从以下方面进行制作。 2.1、结构设计 为了保证不串光,需要设计一个隔光栅格,然后还得做一个支架固定像素屏;为了光线均匀,还需要一个均光片,为了保护均光片和防止漏光还需要一层护屏,然后最外面套一层外壳。这里我直接采用了网上现成的方案,直接打印。其中均光片用A4纸裁剪合适尺寸实现。如下图: 2.2、隔光栅格处理 其中的隔光栅格费了好大功夫。因为刚打印出来的栅格是半透光的,效果很不好。隔光栅格如下图: 为了挡光,我把它喷漆成黑色的,然后发现黑的又特别吸光,导致看起来不够亮,没办法又在表面喷了一层白漆,最后才能起到隔光反光的综合效果,处理完的实物如下图。 2.3、像素屏总装和测试 各个部分都弄好后,一层层装起来,就构成一件成品,如下图所示。 上电测试,跑一个测试程序,整体效果还不错。下图是开灯和关灯效果。 3、总结 通过以上操作,基本把两大部分的外壳制作完成,经过简单测试都能正常工作,为下一步任务打好基础。

统计信息

已有131人来访过

  • 芯积分:1170
  • 好友:2
  • 主题:66
  • 回复:150
  • 课时:11
  • 资源:5

留言

你需要登录后才可以留言 登录 | 注册


现在还没有留言