Toggle navigation
首页
问答
文章
积分商城
专家
专区
更多专区...
文档中心
返回主站
搜索
提问
会员
中心
登录
注册
OPENMV
RA8-M85-vision-board
【RA8D1-Vision Board】利用手势交互操作的USB HID鼠标的设计与实现
发布于 2024-06-15 19:20:23 浏览:346
订阅该版
[tocm] 本文利用vision board openmv实现一个利用手势交互的usb hid鼠标设备。项目开始时以为只要手势识别后获得坐标偏移并利用openmv的HID支持上报HID事件就行了,也就是注重项目本身的开发,但实现过程中碰了不少坑,有60%的时间其实是花在从坑里爬出来的努力上,绝大部分原因是对openmv、micropython不熟,笔者觉得把实现过程中碰到的坑和解决办法记录下来可能比平铺直述讲实现原理更重要些,也算是一份开发笔记吧。 # 第一个坑:openmv各大移植实现的功能完整性有区别 这和RT-Thread对各MCU和开发板支持完整性有区别是一个道理。项目之初网上搜索openmv HID搜到一大堆资料,其实都只适用于功能移植完备的stm32版本(但当时不知道这个坑),其它开发板比如vision board或rpi pico并不适用。比如usb device默认是cdc+msc,可以写个boot.py改成cdc+hid云云。群里咨询后得知vision board openmv RTT移植版的usb device只支持cdc。。。然后Rb大佬提交了hid_example这个proj,笔者试了试这个proj这个例子可以正常工作。那问题来了:怎么集成进openmv或者说micropython?阅读microptyhon代码后发现走micropython标准通路其实水很深的,github上有人提了rpi pico的HID支持可以去看看,如果也按标准方式来做vision board的hid支持,那代码量就太大了。再次群里咨询,这次获得的启示是按micropython调用c module实现。一顿操作猛如虎: 1.删除重复的tinyusb 因为libraries/components/tinyusb/是有一份tinyusb源码的 ```bash git rm -rf projects/vision_board_openmv/packages/tinyusb/ ``` 2.并修改projects/vision_board_openmv/SConstruct ```c --- a/projects/vision_board_openmv/SConstruct +++ b/projects/vision_board_openmv/SConstruct @@ -85,6 +85,13 @@ def ourspawn(sh, escape, cmd, args, e): if platform.system() == 'Windows': env['SPAWN'] = ourspawn +if os.path.exists(os.path.join(os.getcwd(), "libraries")): + # tinyusb package + objs.extend(SConscript(os.path.join('libraries/components/tinyusb', '', 'SConscript') )) +else: + # tinyusb package + objs.extend(SConscript(os.path.join('../../../libraries/components/tinyusb', '', 'SConscript'))) + # include drivers objs.extend(SConscript(os.path.join(libraries_path_prefix, 'HAL_Drivers', 'SConscript'))) ``` 3.修改hid_example.c 把hid鼠标和键盘上报事件的函数抽出 沿着粗糙猛的路子,直接用hid_example.c,先跑起来再说 4.修改projects/vision_board_openmv/packages/micropython-v1.13.0/port/modules/modrtthread.c 粗糙猛,先改它吧后面再考虑放哪 ```c index 47e23ebf..2a11fa89 100644 --- a/projects/vision_board_openmv/packages/micropython-v1.13.0/port/modules/modrtthread.c +++ b/projects/vision_board_openmv/packages/micropython-v1.13.0/port/modules/modrtthread.c @@ -32,6 +32,28 @@ #include "py/runtime.h" +extern void hid_key(uint8_t modifier, uint8_t keycode); +extern void hid_mouse(uint8_t buttons, int8_t x, int8_t y); + +STATIC mp_obj_t mod_hid_key(mp_obj_t modifier_obj, mp_obj_t keycode_obj) { + int modifier = mp_obj_get_int(modifier_obj); + int keycode = mp_obj_get_int(keycode_obj); + + hid_key(modifier, keycode); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(mod_hid_key_obj, mod_hid_key); + +STATIC mp_obj_t mod_hid_mouse(mp_obj_t btn_obj, mp_obj_t x_obj, mp_obj_t y_obj) { + int btn = mp_obj_get_int(btn_obj); + int x = mp_obj_get_int(x_obj); + int y = mp_obj_get_int(y_obj); + + hid_mouse(btn, x, y); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_3(mod_hid_mouse_obj, mod_hid_mouse); + rt_bool_t rt_is_preempt_thread(void) { if (rt_interrupt_get_nest() || rt_critical_level()) { return RT_FALSE; @@ -94,6 +116,8 @@ STATIC const mp_rom_map_elem_t mp_module_rtthread_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_current_tid), MP_ROM_PTR(&mod_current_tid_obj) }, { MP_ROM_QSTR(MP_QSTR_stacks_analyze), MP_ROM_PTR(&mod_stacks_analyze_obj) }, { MP_ROM_QSTR(MP_QSTR_list_device), MP_ROM_PTR(&mod_list_device_obj) }, + { MP_ROM_QSTR(MP_QSTR_hid_key), MP_ROM_PTR(&mod_hid_key_obj) }, + { MP_ROM_QSTR(MP_QSTR_hid_mouse), MP_ROM_PTR(&mod_hid_mouse_obj) }, }; STATIC MP_DEFINE_CONST_DICT(mp_module_rtthread_globals, mp_module_rtthread_globals_table); ``` # 第二个坑:micropython的RTT移植版的特殊之处 这时就会掉进第二个坑, qstrdefs.generated.h这个文件这个文件网上搜索和对比micropython主线感觉应该是编译过程中自动生成的,git log却提示这个文件在RTT版本中是源码仓库中自带非编译时自动生成的,那micropython的RTT版移植时是如何生成这个文件的呢?笔者未找到RTT的micropython移植版本中的生成方法。这次还是要追求粗糙猛,研读源码之后发现这个文件其实是有规律的,很多都可以手动填,但是形如(const byte*)"\x3e\x17"不知道如何生成。文件名是qstr,以qstr为关键字找阿找终于找到了port/genhdr/gen_qstr.py,运行之后就能获得上面这个类似(const byte*)"\x3e\x17"组合,终于把添加的两个c module函数填进qstrdefs.generated.h ```c --- a/projects/vision_board_openmv/packages/micropython-v1.13.0/port/genhdr/qstrdefs.generated.h +++ b/projects/vision_board_openmv/packages/micropython-v1.13.0/port/genhdr/qstrdefs.generated.h @@ -1097,6 +1097,8 @@ QDEF(MP_QSTR_help, (const byte*)"\x94\x04" "help") QDEF(MP_QSTR_hex, (const byte*)"\x70\x03" "hex") QDEF(MP_QSTR_hexlify, (const byte*)"\x2a\x07" "hexlify") QDEF(MP_QSTR_hid, (const byte*)"\xe0\x03" "hid") +QDEF(MP_QSTR_hid_key, (const byte*)"\x48\x07" "hid_key") +QDEF(MP_QSTR_hid_mouse, (const byte*)"\x3e\x09" "hid_mouse") QDEF(MP_QSTR_high, (const byte*)"\x2b\x04" "high") QDEF(MP_QSTR_hint, (const byte*)"\x5e\x04" "hint") QDEF(MP_QSTR_histeq, (const byte*)"\x57\x06" "histeq") ``` 现在终于到了激动人心的时刻,可以在micropython的REPL中输入如下代码完成和hid_example一样的鼠标画圆圈功能 ```python import rtthread,math last_x = 168 last_y = 0 for i in range(0, 360, 1): x = 168*math.cos(math.radians(i)) y = 168*math.sin(math.radians(i)) rtthread.hid_mouse(0, (int)(x-last_x), (int)(y-last_y)) last_x = x last_y = y ``` 坑终于都填完了,接下来实现最后项目的代码。 # AI模型 VS AprilTag 笔者一开始看中了edgeimpulse所谓的FOMO(Faster Objects, More Objects)模型,并照着edgeimpulse的howto实现了手在图中的识别也能获得手的(x,y)坐标,但最后发现一个问题,手靠近/远离摄像头没办法识别出来,其实就是z坐标了,那实现鼠标点击用什么手势呢?笔者原来设计是手推向摄像头再退回就是一次鼠标点击手势。网上调研一番后感觉还是AprilTag更合适,它可以快速精确地获得(x, y, z)坐标,这对利用做手势交互操作输入设备非常重要。 # AprilTag图片的获取 直接去官网[https://github.com/AprilRobotics/apriltag-imgs.git](https://github.com/AprilRobotics/apriltag-imgs.git) clone下来后发现其实那图片不能用的太小啦,README.md提示可用类似下面的命令转换(covert工具在imagemagick包中,apt-get install imagemagick即可) ```bash convert tag36h11/tag36_11_00001.png -scale 2000% 1.png ``` # 设计思路之鼠标移动事件 AprilTag识别后可以通过cx()与cy()函数获得质心的(x, y)坐标,两次位置之间的差分(x2-x1, y2-y1)可作为hid_mouse输入量,从而实现鼠标的移动。 # 设计思路之鼠标左键点击事件 AprilTag识别之后通过z_translation()获得相对于摄像头的距离,此距离数值的单位不详,但项目不在乎单位只需要知道有相对摄像头的推近和远离动作,即可模拟鼠标的左键。 # 最后python源码如下: ```python import sensor, image, rtthread sensor.reset() sensor.set_pixformat(sensor.RGB565) sensor.set_framesize(sensor.QQVGA) # we run out of memory if the resolution is much bigger... sensor.skip_frames(time = 2000) sensor.set_auto_gain(False) # must turn this off to prevent image washout... sensor.set_auto_whitebal(False) # must turn this off to prevent image washout... scalex = int(1366/160) scaley = -int(768/120) flag = 0 while(True): img = sensor.snapshot() for tag in img.find_apriltags(): x = tag.cx() y = tag.cy() z = tag.z_translation() print_args = (x, y, z) print("(%d, %d) Tz %f " % print_args) if (flag): if ((z-last_z) > 1): rtthread.hid_mouse(1, 0, 0) else: rtthread.hid_mouse(0, (x-last_x)*scalex, (y-last_y)*scaley) else: flag = 1 last_x = x last_y = y last_z = z ``` 演示视频: [https://www.bilibili.com/video/BV1w3Vve8E7x/](https://www.bilibili.com/video/BV1w3Vve8E7x/) 视频解读: 视频中python代码是拷贝进micropython的REPL运行的;tmux左屏是REPL,测试过程中会打印(x,y, z_translation)三维组合;tmux右屏运行Linux操作系统下的getevent程序,此程序在任何/dev/input/eventN设备节点有input事件时都会读出数据并解析成人类可读形式,比如 ```bash /dev/input/event21: EV_REL REL_X fffffff8 /dev/input/event21: EV_REL REL_Y fffffffa /dev/input/event21: EV_SYN SYN_REPORT 00000000 ``` 就是一次典型的鼠标移动事件。而形如 ```bash /dev/input/event21: EV_KEY BTN_MOUSE DOWN /dev/input/event21: EV_SYN SYN_REPORT 00000000 /dev/input/event21: EV_MSC MSC_SCAN 00090001 /dev/input/event21: EV_KEY BTN_MOUSE UP ``` 就是一个完整的鼠标左键点击过程:包含鼠标左键按下事件和释放事件 视频中也能看到手势控制的鼠标的移动包括画圆和点击。 # 总结: 本次活动学会了micropython的修改、如何给micropython添加c module、AprilTag的使用、edgeimpulse的FOMO模型训练和推理使用,最终实现了一个利用手势交互操作的USB HID鼠标。 # 将来的工作: 鼠标去抖 鼠标右键的手势操作
0
条评论
默认排序
按发布时间排序
登录
注册新账号
关于作者
rvcore
这家伙很懒,什么也没写!
文章
10
回答
9
被采纳
1
关注TA
发私信
相关文章
1
OPENMV软件包怎么使用
2
OPENMV STM32H7 编译失败
3
openMV+micropy与RT1064的一个尝试
4
Vision Board连接不上OpenMV IDE
5
Vision Board使用openmv无法下载脚本
6
在visonboard开发中尝试在openmv中加载个人训练的YOLOv5模型,报错,超出内存
7
Vision Board 的openmv如何部署socket 模块
8
Vison Board 如何在openmv 、main.py脚本中实现和其它线程通信?
9
那个vision board 在 openmv ide 设置波特率的时候,是不是有一个小bug ,设置成0的时候就是115200?
10
Vision Broad连接OTG口后openMV显示错误:系统找不到指定的文件,Renesas Flash也显示连接不上
推荐文章
1
RT-Thread应用项目汇总
2
玩转RT-Thread系列教程
3
国产MCU移植系列教程汇总,欢迎查看!
4
机器人操作系统 (ROS2) 和 RT-Thread 通信
5
五分钟玩转RT-Thread新社区
6
【技术三千问】之《玩转ART-Pi》,看这篇就够了!干货汇总
7
关于STM32H7开发板上使用SDIO接口驱动SD卡挂载文件系统的问题总结
8
STM32的“GPU”——DMA2D实例详解
9
RT-Thread隐藏的宝藏之completion
10
【ART-PI】RT-Thread 开启RTC 与 Alarm组件
热门标签
RT-Thread Studio
串口
Env
LWIP
SPI
AT
Bootloader
Hardfault
CAN总线
FinSH
ART-Pi
USB
DMA
文件系统
RT-Thread
SCons
RT-Thread Nano
线程
MQTT
STM32
RTC
FAL
rt-smart
ESP8266
I2C_IIC
UART
WIZnet_W5500
ota在线升级
freemodbus
PWM
flash
cubemx
packages_软件包
BSP
潘多拉开发板_Pandora
定时器
ADC
flashDB
GD32
socket
中断
编译报错
Debug
SFUD
rt_mq_消息队列_msg_queue
msh
keil_MDK
ulog
C++_cpp
MicroPython
本月问答贡献
a1012112796
10
个答案
1
次被采纳
踩姑娘的小蘑菇
4
个答案
1
次被采纳
红枫
4
个答案
1
次被采纳
张世争
4
个答案
1
次被采纳
Ryan_CW
4
个答案
1
次被采纳
本月文章贡献
catcatbing
3
篇文章
5
次点赞
YZRD
2
篇文章
5
次点赞
qq1078249029
2
篇文章
2
次点赞
xnosky
2
篇文章
1
次点赞
Woshizhapuren
1
篇文章
5
次点赞
回到
顶部
发布
问题
投诉
建议
回到
底部