赞
踩
博主已经大三,想着暑假参加电赛,于是将21电赛的控制题尝试做了一下,由于成本有限,想着能不能仅使用一块k210完成这个项目,但是看网上查找资料都没人这样做,于是博主就按照自己的想法实现了这个,为了让更多和博主一样的小白能够简单制作这个项目,博主决定写一下这个项目,顺便记录一下自己的学习过程,顺便希望暑假电赛一切顺利。
因为代码还没有注释整理好,所以博主暂时只分享出数字识别模型(当初博主想找网上现成的模型,由于营销号都限制收费获取,博主一怒之下暴训500张图,实现了0.97的识别率)
博主的车还是很慢的
小车的结构照片和实现功能视频博主还没拍,之后添加链接补上~
题目基础部分要求使用一辆小车实现近端,中端,和远端的送药过程
并且给出了红线用于循迹,那么比赛的需求就是重点考察以下几个功能
首先对于红线循迹,由于寻常传感器大多是寻黑线,大多对于红线识别效果不好,市面大多采用多路灰度传感器循迹,但是由于成本较高,有些朋友会望而却步,经济实力足够的朋友采用这种方法循迹效果自然更好。同时,也具备另一种解决方法摄像头循迹,本人采用的就是这个方法;另一个功能数字识别也有两种常用解决方法:1、使用opemmv的模板匹配;2、使用K210训练识别模型,本人采用的也是第二个选项。(博主也是刚入门的菜鸟,如果还有博主没想到的实现方法,请大佬指点)
那么这里很明显,要想实现低成本制作,我们可以把循迹和数字识别结合在一起解决,这样成本当然最低,同时opemmv价格上是大于K210的,所以博主决定使用K210实现这两个功能
ps:由于赛题为直线,有的大佬不采用循迹,而是使用编码器和电机搭配位置环控制,做到行走固定路线,实在是很牛逼的想法啊。
这种灰度传感器就不需要使用了,本人亲测排雷,还浪费了我一个礼拜,贪小便宜吃大亏。
我的做法是将k210的需求分解成4个任务
至于什么时候执行对应的任务,则由stm32通过串口给出指令。这里有个细节就是K210并没有串口中断,但是你又不可能定时接收,这样做即浪费及时间和资源,实时性也没有保障,所以我选择使用stm32和k210连接一条外部中断线,这样stm32发送数据前将那条线拉低或者拉高实现K210在中断里面执行。实现伪串口中断,当时博主也是灵光一闪想到的,还是挺机智的。
解决了通信问题,接下来就是K210的重点功能实现。
ROIS={ 'down': (0, 164, 220, 20), 'middle': (0, 144, 220, 20), 'up': (0, 124, 220, 20) } state_1=(0,120,224,10) #判断是什么状态的循迹 2是识别十字路口 3是识别终点 def track(): global route_num global code_flag m=0#记录识别的数量 n=0#记录路程 img = sensor.snapshot()#.binary([(61, 19, 18, 91, 62, -21)]) #img.draw_rectangle(10,20,100,60,color = (255, 0, 0))(100, 0, 18, 91, 62, -21) #img.draw_rectangle(170, 50 , 30, 50,color = (255, 0, 0)) # img =img.binary([(0, 25)]) if(code_flag==2): #路口循迹还是终点循 #for area in state_1:#路口识别 a=img.find_blobs([(22, 100, 36, 100, -8, 67)],roi=state_1,merge=True,pixels_area=100) tmp=find_max(a) if(tmp): #m=m+1 if(tmp[2]>60): uart_A.write(send_data_packet(0,1,0))#检测到了路口 print('ok1') code_flag=0 return None ''' if(code_flag==3): #终点循迹 a=img.find_blobs([(22, 100, 36, 100, -8, 67)],roi=[0, 164, 220, 20],merge=True,pixels_area=60)#识别红线 if (a==None): # for b in a: # m=m+1 #统计检测到的黑色色块的数量 # if(m>6): #统计到五块以上 uart_A.write(send_data_packet(0,1,0))#检测到了终点 print('ok2') print(m) code_flag=0 return None ''' m=0 for area in ROIS: a=img.find_blobs([(22, 100, 36, 100, -8, 67)],roi=ROIS[area],merge=True,pixels_area=25) tmp=find_max(a) if(tmp): img.draw_rectangle(tmp[0:4]) img.draw_cross(tmp[5], tmp[6]) n=n+tmp[5] m=m+1 print(tmp[5]) if((m==0)and (code_flag==3)): uart_A.write(send_data_packet(0,1,0))#检测到了终点 print('ok2') code_flag=0 return None #print(m) code_flag=0 uart_A.write(send_data_packet(((n/m-115+200)*0.16-12),0,0)) print(n/m) print(send_data_packet(((n/m-115+200)*0.15-10),0,0)) lcd.display(img)
以上是识别路口+循迹+识别终点模块代码。
另外接下贴出K210串口识别指令和数据帧格式
#数据帧格式 | x-循迹偏移量 |y-检测成功 成功1,不成功0 | z-作为判断识别到的数字 或者 作为左右转向的0左,1右,2直行 def send_data_packet(x, y,z): data='ba'+str('%03d'%x)+str(y)+str(z)+'\r\n' return data #串口中断 判断下一次是否发送数据 def fun(uart_flag): global code_flag text=uart_A.read(2) #读取数据 #print(1) if text: #如果读取到了数据 text=text.decode('utf-8') if(text[0]=='b'): if(text[1]=='1'): #数字识别请求 code_flag=1 elif(text[1]=='2'): #循迹+路口识别请求 code_flag=2 elif(text[1]=='3'): #循迹+终点识别请求 code_flag=3 elif(text[1]=='4'): #数字匹配请求 code_flag=4 elif(text[1]=='0'): #停止运行,等待指令 code_flag=0 print(text) uart_flag.irq(fun, GPIO.IRQ_BOTH)#使能外部中断,执行串口处理
至此实现了四个任务中的两个;
还剩下两个功能,由于都涉及数字识别,所以博主把他们放一起。
首先对于k210如果你想要长时间跑模型还想使用找色块等部分库函数,你就需要使用内存略微小点的固件,我使用的是这个固件,具体怎么烧录固件和接下来的识别模型教程不懂得大家可以去看别的博主的文章或者b站视频。
下载网站,和模型都放在文章结尾处,有需要的自取。
使用这个模型就可以实现保留部分找色块这种的基本图像功能,也能较长时间运行模型。识别部分反而更加简单,训练出一个好模型,基本就实现了功能,具体思路就是匹配数字,同时发送识别到的数字给单片机,单片机判断是否为近端,如果为近端,就不需要功能4了,如果为非近端,就让单片机发送对应的指令就i行了。任务4的实现就是简单将识别的数字和我们开机识别的数字比对,匹配成功则判断坐标是坐标还是右边,将结果发送给单片机处理。
def recog(task): global labels global anchors global num_goal global code_flag img = sensor.snapshot() objects = kpu.run_yolo2(task, img) if objects: if(code_flag==1): max_value=objects[0] for obj in objects: pos = obj.rect() img.draw_rectangle(pos) img.draw_string(pos[0], pos[1], "%s : %.2f" %(labels[obj.classid()], obj.value()), scale=2, color=(255, 0, 0)) if(max_value.value()<obj.value()): max_value=obj num_goal=labels[max_value.classid()] uart_A.write(send_data_packet(0,0,int(num_goal))) print(int(int(num_goal))) code_flag=0 if(code_flag==4): for obj in objects: pos = obj.rect() img.draw_rectangle(pos) img.draw_string(pos[0], pos[1], "%s : %.2f" %(labels[obj.classid()], obj.value()), scale=2, color=(255, 0, 0)) if(num_goal==labels[obj.classid()]): if(pos[0]>80): uart_A.write(send_data_packet(0,1,1))#检测到了右行 elif(pos[0]<80): uart_A.write(send_data_packet(0,1,0))#检测到了左行 else: uart_A.write(send_data_packet(0,0,2))#未检测到,直行 code_flag=0 lcd.display(img)
代码稀碎,感兴趣的我之后把整理好的代码放到文件末。
这里要处理的比较简单,具体需要的功能如下
这里有两段功能
#include"fun.h" #include"OLED.h" #include"UART.h" extern char num_goal;//需要检测的目标数字 extern uint8_t recog_flag;//检测是否成功 extern uint8_t turn_err;//转向误差 extern uint8_t K210_state;//设置K210的状态 extern uint8_t turn;//转向 extern uint8_t turn_stack[4];//转向栈 extern uint8_t top;//栈顶指针 extern uint8_t send_flag; extern uint8_t start_flag;//开始标志 extern uint8_t recog_count; /* *功能:将信息发送给K210 *参数:K210_state 选择要设置的K210的状态 *返回值:无 */ void usart_pack(uint8_t K210_state) { GPIO_WriteBit(GPIOC,GPIO_Pin_13,!GPIO_ReadOutputDataBit(GPIOC,GPIO_Pin_13)); Send_Byte(USART1,'b');//发送帧头数据 switch(K210_state) { case 0:Send_Byte(USART1,'0');break; case 1:Send_Byte(USART1,'1');break; case 2:Send_Byte(USART1,'2');break; case 3:Send_Byte(USART1,'3');break; case 4:Send_Byte(USART1,'4');break; } } /* *功能:解析接受的数据 *参数:需要解析的数据 *返回值:无 */ void data_deal(char *buffer) { if(buffer[0]=='b'&&buffer[1]=='a')//接受的数据无误 { send_flag=1; if(K210_state==1) num_goal=buffer[6]; else if(K210_state==2) { if(buffer[5]=='1') //检测成功 recog_flag=1,turn_err=-1; else recog_flag=0,turn_err=(buffer[2]-0x30)*100+(buffer[3]-0x30)*10+(buffer[4]-0x30); } else if(K210_state==3) { if(buffer[5]=='1') //检测成功 recog_flag=1,turn_err=-1; else recog_flag=0,turn_err=(buffer[2]-0x30)*100+(buffer[3]-0x30)*10+(buffer[4]-0x30); } else if(K210_state==4) { if(buffer[5]=='1')//识别成功数字 { recog_flag=1; turn=buffer[6]-0x30; } else { if(recog_count==9) { turn=0; } recog_count++; } } else if(K210_state==0) { start_flag=1; } } }
小车的运动其实无非两点
对于这部分,我都是在pid速度环的基础上实现的,另外,这里我的转向环其实也是可以作为速度环的参数,其实也算是串级pid了吧。我也不太清楚,但是总的来说使用了速度环和转向环两种
ps:对于速度环的调参,也没什么经验,网上大佬都有,这里只是建议大家可以使用串口做到上位机显示波形的模式,这里博主使用的是vofa,很多大佬是使用匿名上位机,当然也是可以的,博主的速度环和转向环其实效果还是属于比较差的,因为调参太麻烦了,很是消磨耐心,没有队友帮忙调实在是太累了。至于这部分代码就不贴了,以来还是可读性不强,另一方面这部分代码网上应该有很多了,没必要贴出来丢人。
给大家瞅瞅我速度环波形
低速还是挺好的,抖动不大,所以博主也是让车的速度是低速走,是在没耐心调参了。相信各位的效果比我强。
其他也没什么好说的,毕竟小白一枚,做出这个项目纯属侥幸。希望各位的项目能比我的更稳,更快!
[数字识别模型及openmv固件]
链接: https://pan.baidu.com/s/1APhRB1pNWLSt-NwvMtKmZQ 提取码: faiz
代码
链接:https://pan.baidu.com/s/1f7gOiSKLh-ZcFvK_8mshAA
提取码:faiz
剩余资料等博主整理完上传github仓库和百度网盘~
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。