摘要:效果展示装甲板检测在参考了众多的优秀开源代码后,我们也构思出了一套稳定的识别装甲板的方案。在得到的最终的扇叶中,再次寻找所有轮廓,寻找装甲板的中心。其中的条件为长宽比,矩匹配,面积筛选。圆心是通过象限以及给定的半径进行确定。
还在苦恼RM视觉开源代码怎么看?带你解读最近深大的视觉开源源码
本文解说的深大开源代码百度网盘压缩包如下: https://pan.baidu.com/s/1Fl76ClJJ4Kr6ol4WVzPxpw
提取码:dxlw
ss
本套代码完成了对 RoboMaster2019 赛场上大小符以及机器人装甲板的识别,通过自定的通讯协议将视觉处理后的信息发送给下位机,由嵌入式单片机处理视觉信息并控制云台的运动。
我们选用的是 Dahua A5131CU210 工业相机,与下位机通讯选用 TTL-USB 串口模块,mini PC 选用的是 Intel NUC8i5BEH 。若想运行代码,只需给 PC 插上一个 TTL-USB 串口模块和一个 USB cam , 再稍微改动一下代码就可。
在参考了众多的优秀开源代码后,我们也构思出了一套稳定的识别装甲板的方案。在 6m 内的识别准确率几乎 100% ,加上 ROI 操作和多线程,处理一张图片的时间在 1ms 以内,整个程序跑下来也只需要 4-6ms ,时延非常低,方便更好地做移动预测。并且预处理对不同曝光也很适应,适用曝光范围在 3000-6000(各工业相机参数不同,这里仅针对 Dahua 此型号相机)。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qypJPOnK-1633583044645)(https://github.com/yarkable/RP_Infantry_Plus/blob/master/assets/autoaim.gif)]
大小符采用的是深度学习和传统方法结合的方法。可以做到实时预测旋转之后的位置,加上roi和多线程操作之后,处理一张图片的时间在2ms之内,加上读图也只需要4-6ms。大小符所使用的摄像头跟自瞄相同。
附上效果图:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TUIlVv2c-1633583044649)(https://github.com/yarkable/RP_Infantry_Plus/blob/master/assets/rune_desc.jpg)]
运行环境:
RP_Infantry_Plus/├── depends(系统动态链接库)├── extraFile│ ├── AimXMl│ │ ├── calib_no_4_1280.yml│ │ └── param_config.yml│ ├── caffemodel│ │ ├── armornet_iter_200000.caffemodel│ │ ├── deploy.prototxt│ │ ├── lenet_iter_200000.caffemodel│ │ └── lenet_train_test_deploy.prototxt│ └── Rune│ ├── lenet│ │ ├── deploy.prototxt│ │ ├── lenet_iter_80000.caffemodel│ │ ├── lenet_iter_80000加了负样本.caffemodel│ │ └── lenet_iter_80000_旋转后.caffemodel│ ├── xml│ │ └── Mono_out.xml│ └── yolo│ ├── tiny-yolov2-trial3-noBatch_235000.weights│ ├── tiny-yolov2-trial3-noBatch_80000.weights│ ├── tiny-yolov2-trial3-noBatch.cfg│ └── 单通道│ ├── tiny-yolov2-trial3-noBatch_80000.weights│ └── tiny-yolov2-trial3-noBatch.cfg├── include│ ├── Aim│ │ ├── AngleSolver.hpp│ │ ├── ArmorDetector.hpp│ │ ├── separate.h│ │ └── Settings.hpp│ ├── Camera│ │ └── video.h│ ├── header.h│ ├── ImageConsProd.hpp│ ├── Rune│ │ ├── Detect.h│ └── SerialPort│ ├── CRC_Check.h│ └── serialport.h├── includes (相机 SDK 包含头文件)├── main.cpp├── RP_Infantry_Plus.pro├── RP_Infantry_Plus.pro.user└── src ├── Aim │ ├── AngleSolver.cpp │ └── ArmorDetector.cpp ├── Camera │ └── video.cpp ├── ImageConsProd.cpp ├── Rune │ ├── Detect.cpp └── SerialPort ├── CRC_Check.cpp └── serialport.cpp
文件名 | 包含类 | 用途 |
---|---|---|
AngleSolver.cpp | RectPnPSolver / AngleSolver /AngleSolverFactory | 对目标进行角度解算(DJI开源) |
ArmorDetector.cpp | ArmorDetector | 进行装甲板检测 |
video.cpp | Video | 将工业相机 SDK 封装成类 |
ImageConsProd.cpp | ImageConsProd | 处理多线程以及数据传输 |
Detect.cpp | Detect | 进行大小符装甲检测 |
serialport.cpp | SerialPort | 自定义与下位机通讯的通讯协议 |
自瞄系统的逻辑流程很简单,通过面向对象思想将整一套流程实现,配合多线程使用,整体通俗易懂,以下是自瞄流程图:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FoNb4GNz-1633583044652)(https://github.com/yarkable/RP_Infantry_Plus/blob/master/assets/peocess.png)]
如果 last_result
为 true
,则检测区域在上一帧的附近,也就是用到了 ROI 方法,如果超过一定帧数未检测到目标,则逐渐扩大搜索范围,直到 33 帧仍然未检测到目标则全图搜索,此时将与 ROI 有关的变量全部清零。
对检测区域内的图进行二值化,也就是预处理,思路就是先将图片用灰度阈值进行二值化,这样子可以将图片中发光的物体给提取出来(装甲板灯条以及日光灯等等),然后再用某种方法将图片中红色或蓝色的区域提取出来,之后再膨胀一下,跟灰度二值化图片逻辑与,最后得到的就是一张只有红/蓝灯条的二值图,在这上面进行接下去的操作。
在当前二值图内找到所有的轮廓点,用最小旋转矩形将他们包围,此时得到一个个多带带的旋转矩形,根据装甲板灯条的几何特征首先筛除掉一些旋转矩形
bool if1 = (fabs(rrect.angle) < 45.0 && rrect.size.height > rrect.size.width); // 往左 bool if2 = (fabs(rrect.angle) > 60.0 && rrect.size.width > rrect.size.height); // 往右 bool if3 = max_rrect_len > _para.min_light_height; // 灯条的最小长度+ bool if4 = (max_rrect_len / min_rrect_len >= 1.1) && (max_rrect_len / min_rrect_len < 15); // 灯条的长宽比
将这些灯条两两再次组成一个大的旋转矩形(也就是候选装甲板),根据一些限制条件筛除掉不符合条件的装甲板,将剩下的待选装甲板放入一个向量中。(其中利用灯条的角度差信息 angleabs 为大连交通大学开源)
bool condition1 = delta_h > _para.min_light_delta_h && delta_h < _para.max_light_delta_h; bool condition2 = MAX(leni, lenj) >= 113 ? abs(yi - yj) < 166/ && abs(yi - yj) < 1.66 * MAX(leni, lenj) : abs(yi - yj) < _para.max_light_delta_v/ && abs(yi - yj) < 1.2 * MAX(leni, lenj); bool condition3 = lr_rate < _para.max_lr_rate; bool condition4 = sentry_mode ? angleabs < 25 : angleabs < 15 - 5; // 给大点防止运动时掉帧
如果向量中没有元素,则说明没有找到目标,只有一个的话则这就是最终选择的装甲板,有两个以上元素的话就得进行接下去的比较,最容易误识别的地方为装甲板灯条和它两边的两根灯条,经过分析,我们发现这种情况下如果用到了 ROI 操作,包围真正装甲板的那个旋转矩形的角度是最小的,我们可以根据这个特征进行筛选,具体思路请看代码。
找到装甲板之后对其进行 pnp 解算,得到云台转至其中间所需要的 pitch 和 yaw 值,角度解算请参考 DJI 开源的算法,距离信息我们实际测量得到了灯条的长度和实际距离的函数关系,直接代入即可求得距离,误差在 20cm 之内。
哨兵比较特殊,我们对其进行了特殊处理,如果接收到电控传来哨兵模式,我们会对其装甲板匹配条件进行宽松处理,并且对其用上多分类识别装甲板贴纸上的 ID ,在10帧之内如果未找到目标依旧给电控发送找到目标的标志位,直到超过上述帧数才发送未识别到目标,这样子防止了灯条被打灭就失去目标导致云台突然停一下的问题,打灭了之后云台依旧会向前运动,可以快速推掉哨兵。
include/Detect.h
中的 sParam.use_yolo
变为 1,则使用 yolo 来检测没有打过的扇叶,若为 0 则根据 lastData
是否有效来进行 ROI 区域的检测。src/Detect.h
可以选择二值化的方法 enum binaryMode{ BGR = 1, HSV = 2, BGR_useG = 3, OTSU = 4, GRAY = 5, YCrCb = 6, LUV = 7, };
bool condition1 = whrio < param.flabellum_whrio_max && whrio > param.flabellum_whrio_min;// 长宽比 bool condition2 = area > param.flabellum_area_min;// 最小面积
如果得到的扇叶个数大于 1,可以使用 lenet 或者选择面积最小的得到最终的未打过的扇叶。
在 inlcude/Detect.h
里面的 sParam.use_lenet
可以控制是否使用 lenet
进行分类。
在得到的最终的扇叶中,再次寻找所有轮廓,寻找装甲板的中心。其中的条件为长宽比,矩匹配,面积筛选。
bool condition1 = whrio < param.armor_whrio_max && whrio > param.armor_whrio_min;// 长宽比 bool condition2 = rev < param.armor_rev_thres;// 矩匹配阈值 bool condition3 = area > param.armor_area_min;// 最小面积
如果为小符,那么就可以不用进行预测,直接将数据发送给下位机,但如果是大符的话,就需要进行圆周预测。预测采用的方法为先预测到切线然后进行修正。根据旋转矩形的角度先预测到切线的位置,然后再绕着装甲点旋转一定角度,近似到圆周上,由于预测角度小,所以预测点和真实打击点基本吻合。
对得到的装甲板的数据进行保存和发送。其中装甲板的象限数据是通过箭头坐标以及旋转矩形的角度进行确定。圆心是通过象限以及给定的半径进行确定。
我们自定的通讯协议一共有 30 个字节,除去校验和预留的数据位,还有 12 个字节的 float 型数据 和 6 个字节的标志位可供使用,能够完成大部分数据的传输。
Byte0 | Byte1 | Byte2 | Byte3 | Byte4 | Byte5 | Byte6 | Byte7 | Byte8 | Byte9 |
---|---|---|---|---|---|---|---|---|---|
0xA5 | cmdID | CRC8_Check | pitch_data | pitch_data | pitch_data | pitch_data | yaw_data | yaw_data | yaw_data |
Byte10 | Byte11 | Byte12 | Byte13 | Byte14 | Byte15 | Byte16 | Byte17 | Byte18 | Byte19 |
yaw_data | dist_data | dist_data | dist_data | dist_data | flag1 | flag2 | flag3 | flag4 | flag5 |
- 0xA5 -帧头
- cmdID : 8 bit int - 命令模式(0 不处理,1 为红色自瞄,2 为蓝色自瞄, 3 4 5 6 7 8 为大小符)
- pitch_data : 32 bit float - 接收视觉解算出来的云台 pitch 值
- yaw_data : 32 bit float - 接收视觉解算出来的云台 yaw 值
- dist_data : 32 bit flaot - 接收视觉解算目标到相机的距离值
- flag1 : 8 bit int - 是否瞄准到中心(大小符用) / 哨兵模式 (stm32 -> PC)
- flag2 : 8 bit int - 是否找到目标(自瞄)/ 吊基地模式(stm32 -> PC)
- flag3 : 8 bit int - 是否识别到大小符
- flag4 : 8 bit int - 是否击打过大小符
- flag5 : 8 bit int - 装甲板是否贴脸(已弃用)
此份代码除了相机可能与其他学校不同,就只需要修改一下路径即可运行,涉及到的路径有以下:
main.cpp
中的 config_file_name
src/Aim/ArmorDetector.hpp
中的 net
include/header.h
中的 camera_yaml
yolo_model_file
yolo_txt_file
lenet_model_file
lenet_txt_file
extraFile/AimXMl/param_config.yml
中的 intrinsic_file_720
刚开始的配置参数是集合在一个 yaml 文件中,在实际调车过程中,发现还是直接修改程序中的代码来的更快捷方便一些,因此后续几乎没有用过 yaml 进行配置,当然,为了程序规范,建议还是用外部配置文件来更改参数,以下是我们之前用的 yaml 配置文件(已弃用),有需要可以在文件中添加配置或者删除配置。
%YAML:1.0---# For Debug Imageshow_image: 1save_result: 0# Parameter for Armor Detection Systemmin_light_height: 10light_slope_offset: 30max_light_delta_h: 720min_light_delta_h: 20max_light_delta_v: 100max_light_delta_angle: 30near_face_v: 100max_lr_rate: 1.99max_wh_ratio: 5.12min_wh_ratio: 1.03target_max_angle: 20small_armor_wh_threshold: 3.33binary_classfication_threshold: 166# Parameter for Cameraintrinsic_file_720: "/home/nuc/kevin/RP_Infantry_Plus/extraFile/AimXMl/calib_no_4_1280.yml"
在赛场上,只有三分钟准备时间,因此我们需要让程序在 PC 开机之后自启动,这就需要写一个自启动脚本,并且,为了防止程序异常中断,我们还用了看门狗来保护程序,原理就是不断检测程序是否在运行,若没有运行,则立刻运行,如果在运行,就隔一段时间再去检测。同样,只需要修改一下路径就可以运行。(我们发现如果在 PC 意外断电的时候,会有几率将二进制文件给损坏,导致程序无法运行,因此为了保险起见,在开机时重新 make 一下程序),为了防止极端情况,在程序意外中断 10 次之后我们直接重启 PC (一般不会遇到这种情况)。
#!/bin/bashsec=1cnt=0name=RP_Infantry_PlusThread=`ps -ef | grep $name | grep -v "grep"`cd /home/s305-nuc5/kevin/build-$name-Desktop-Debug/make clean && make -jwhile [ 1 ]docount=`ps -ef | grep $name | grep -v "grep" | wc -l`echo "Thread count: $count"echo "Expection count: $cnt"if [ $count -gt 1 ]; then echo "The $name is still alive!" sleep $secelse echo "Starting $name..." cd ~ && ./ttyUSB.sh cd /home/s305-nuc5/kevin/build-$name-Desktop-Debug/ gnome-terminal -x bash -c "./$name;exec bash;" echo "$name has started!" sleep $sec ((cnt=cnt+1)) if [ $cnt -gt 9 ]; then reboot fifidone
其中的 ttyUSB.sh
是对 TTL-USB 串口赋权限用的,内容如下,默认串口为名为 /dev/ttyUSB0
,因为串口松动可能会变成 /dev/ttyUSB1
,因此我们对两个串口都赋予权限(有一个串口会不存在,不过不妨碍程序运行)
#!/bin/bashecho ubuntu|sudo -S sudo chmod +777 /dev/ttyUSB0echo ubuntu|sudo -S sudo chmod +777 /dev/ttyUSB1
只需将 watchDog.sh
添加到 ubuntu 系统的 StartUp Applications
中就可以实现开机自启动程序。
由于调试时需要知道所处环境对曝光的适应程度,并且要输出一些信息来帮助我们判断是否有误识别,因此在 src/Aim/ArmorDetector.cpp
和 src/ImageConsProd.cpp
中定义了多个宏定义开关方便调试,在正式比赛时要将这些开关关闭,否则会影响算法的实时性。
src/Aim/ArmorDetector.cpp
中的 SHOW_DEBUG_IMG
宏定义,展示代码每一步的图片处理细节src/Aim/ArmorDetector.cpp
中的 COUT_LOG
宏定义,输出匹配灯条的信息src/Aim/ArmorDetector.cpp
中的 CLASSIFICATION
宏定义,使用多分类识别装甲板(已弃用)src/ImageConsProd.cpp
中的 SHOW_IMAGE
宏定义,展示程序最终选择的目标装甲板图src/ImageConsProd.cpp
中的 COUT
宏定义,输出读一张图以及处理一张图所耗费的时间src/ImageConsProd.cpp
中的 SERIAL_DEBUG
宏定义,强制选择自瞄或大小符模式,正式比赛务必关闭这个开关src/ImageConsProd.cpp
中的 CAP_VIDEO
宏定义,在程序运行的同时将采取到的图片帧保存为视频存入本地由于在赛场的时候,灯光等场地因素对我们的图像处理有影响,因此我们也需要通过一些信息来判断程序是否成功识别成功,因此在 include/Detect.h
里面有个调试的开关。同时,在开启这个开关之后,对算法的实时性也有影响。
include/Detect.h
中的 sParam.debug
变成1,将会输出二值化后的图,以及程序处理过程中的细节以及得到的结果。src/ImageConsprod.cpp
中的 RUNE_CAP_VIDEO
宏定义,在程序运行的同时将采取到的图片帧保存为视频存入本地。本套代码实现了装甲板识别和大小符的识别这两个功能,自瞄视觉处理后直接将云台到目标的转角输出给下位机,大小符发送目标在相机中的像素值坐标,甚至不用标定相机,一次标定,一劳永逸,只需要修改角度偏移即可,无需过多调试,有较强的可移植性和鲁棒性。
装甲板识别
大小符
虽然本套代码将该实现的功能都实现了,但是还是有做的不足的地方。
希望未来可以做到自适应性曝光,这样就不用在适应性训练中修改曝光值调整参数了,并且解 pnp 得到的距离也不准,应该是算法上有些问题,希望下一年可以解决这个问题,直接用 pnp 算法求得敌方装甲板到自己的距离。
并且之后可以尝试视觉直接用二阶卡尔曼滤波器做移动预测,目前是电控在做移动预测,但是偶尔会出现一些问题,如果是视觉在做预测的话可以更加方便的发现问题所在,不用双方都进行 debug 。
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/121990.html
摘要:月日,阿里巴巴手机淘宝技术团队携手华为安卓绿色联盟,在杭州西溪园区成功举办走进阿里开发者沙龙,来自互联网和移动应用开发领域的上百位技术开发者和爱好者欢聚一堂,共同交流探讨了的未来,以及作为开发者需要做哪些技术储备和转型。 showImg(https://segmentfault.com/img/bVPsYF?w=1270&h=800); 6 月 17 日,阿里巴巴手机淘宝技术团队 MT...
摘要:本文分析主要把文件转为动画属性动画入门实践掘金前言说道动画肯定要先介绍一下,逐帧动画和补间动画。安卓动画详解一官方文档掘金概览安卓为元素和自己绘制图形提供了一系列的,这篇文章介绍了这些的主要用法。 那些年收藏的 Android 开源库集合 (UI 效果) - Android - 掘金文章内容定期更新该专题其他文章:那些年收藏的Android开源库集合(控件)那些年收藏的Android开...
摘要:掘金非官方的知乎日报,一款基于的。项目实战跟小一起做菜鸟音乐高仿网易云音乐今日力推厨客趣刻掘金一厨客简介,厨客,是一款查询搜索分类收藏菜谱功能的。 Android 教你打造独一无二的刷新加载框架 - Android - 掘金其实早在去年七月,群里小伙伴就有让我共享这个。但我当时绝的技术不纯熟。代码有bug什么的。没有写出来。现在感觉整理的差不多了。就写出来让大家看看,有问题一起讨论解决...
摘要:周末是时隔两月的家人团聚,而每次内容的准备平均需要我集中精力工作小时,所以第期的内容今早才准备好,对不住大家了。下面是本周精选内容,请享用。本文作者王仕军,商业转载请联系作者获得授权,非商业转载请注明出处。 showImg(https://segmentfault.com/img/remote/1460000009742537?w=1240&h=509); 周末是时隔两月的家人团聚,而...
摘要:一本适合基础入门的中文翻译书掘金中文翻译版本书是对所写一书的中文翻译版本,仅供交流学习使用,严禁商业用途。我们在中使用监测事件掘金源码解析掘金看看调用的代码又是什么抱着一贯的好奇,点进去看看。 一本适合 RxJava 基础入门的中文翻译书 - Android - 掘金RxJava Essentials 中文翻译版 本书是对Ivan.Morgillo所写一书的中文翻译版本,仅供交流学习使...
阅读 1554·2023-04-25 19:37
阅读 972·2021-11-16 11:45
阅读 2469·2021-10-18 13:30
阅读 2898·2021-10-09 09:44
阅读 2473·2021-09-29 09:34
阅读 1335·2019-08-30 15:55
阅读 3015·2019-08-30 11:10
阅读 1707·2019-08-29 16:52