文章之前12月发了一次,但是我后来申请的免毕设后,用到了一些文字,所以删了这篇文章,但是还是查重了,于是我把一些程序讲解先删了,等毕设结束后再编辑加上。
这次电赛我没有准备多少东西,只提前准备了图像识别和坐标计算一类的。在20年的省赛帮过雪地调过k210后,自己后来为了比赛,也买了一个。参加了省物联网和交通科技比赛。
由于这边学校对竞赛支持力度不大,经费不多,所以我就用了一块k210.用它作识别和巡线其实是完全够用的。只需要写个判断分开就好。
识别这块我起初自己配的aconda和pycharm环境,不过出现各种问题,没耐下心去做(这学期课程依旧很多,而且还有别的事)。直接用了mxyolo这款软件,一键配置环境库,再加上英伟达cuda加速就可以了。
我用了总共600张图片,花了20分钟大概(我的老笔记本gtx1050,没有独显直连所以也没有出过视频了。) 对象的1650大概在10分钟出头。标记是门体力活,好在有了不错的识别效果,图片样本拍摄是在不同光线和距离情况下,最终模型识别成功率大概在80%左右,速度很快。只是,7容易识别成1,5有时候会识别成2,这是在距离不定的情况下,我想是训练数据集不够。迭代在30,loss挺低了。
在摄像头悬空情况下,正好可以识别2-3个数字。
软件图后续想起来再补吧,我突然想写文章时候发现当时并没有截图。
再说一说软件部分吧,k210我是在Maixpy下进行编写的,调用外设库非常方便,只是没有花括号的缩进,我看着很难受。
初始化程序打开了各个外设以及定义了灰度值,灰度值需要在菜单栏里的——工具——机器视觉——阈值编辑器,选择帧缓冲图像,拖动滚动条,把红线与地面的背景分隔开。红线为白,其它为黑。(麻了,我又没存图,当时只留意存了十字线和巡线的。)
import sensor,image,lcd,time
import KPU as kpu
from Maix import FPIOA, GPIO
from fpioa_manager import fm
from board import board_info
from machine import UART
from Maix import GPIO
fm.register(17, fm.fpioa.GPIO0, force=True)
s_flag = GPIO(GPIO.GPIO0, GPIO.OUT)
flag=1
GRAYSCALE_THRESHOLD = [(87, 255)] #划分的灰度值
THRESHOLD =(10, 34, -41, 74, -109, 127) #摄像头垂直于地面
lcd.init(freq=15000000)
sensor.reset()
sensor.set_pixformat(sensor.RGB565)
sensor.set_framesize(sensor.QVGA)
#sensor.set_hmirror(0) #镜像
#sensor.set_vflip(1) #翻转
lcd.rotation(2) #旋转方向 参数是0~3, 分别代表顺时针旋转 0度 90度 180度 270度
sensor.set_windowing((224, 224))
#sensor.set_brightness(2)
sensor.run(1)
clock = time.clock()
fm.register(23,fm.fpioa.UART2_TX)
uart_A = UART(UART.UART2, 115200, 8, None, 1, timeout=1000, read_buf_len=4096)
task = kpu.load("/sd/tt.kmodel")
f=open("anchors.txt","r")
anchor_txt=f.read()
L=[]
for i in anchor_txt.split(","):
L.append(float(i))
anchor=tuple(L)
f.close()
a = kpu.init_yolo2(task, 0.6, 0.3, 5, anchor)
f=open("lable.txt","r")
labels_txt=f.read()
labels = labels_txt.split(",")
f.close()
死循环中我是通过标志位来切换两种模式的。首先上电先进入数字识别,第一次完成后进入巡线程序。后续切换是通过另一个主控发送数据改变的。
主控那边我简单说下吧,是通过闭环加入编码器计里程和十字线辅助判断每个路口,自加是第几个路口,停下来发信号给k210,在开始数字判断。
下面程序是老版本的,少写一些通信,数字在屏幕的左右正负偏差坐标即是数字在病房路口的位置,通过这个信息去PWM差速转向。
使用的PD进行转向控制,能精确锁住中线行驶。主控我认为用什么都可以,stc12啊,stc8都可以。之前自己做亚克力小车,加升压,在智能车赛道上一圈开环也能跑个16-18秒(没有加三岔路和圆环)。
这次比赛我用的是GD32的芯片,因为便宜,他们家封装的库还是挺好用的。
这题我选择后,第一天深夜我才确定用摄像头方案,以前没有试过巡线,凌晨3点从openmv那里速成,代码搬过来了。后续我想学学opencv。
发现这题坑真多,量很大,光靠我一个人肯定做不完,所以需要点取巧的方法。
开环直线行驶很稳定,但是到十字路口会被干扰,于是我加了判断,并且把左右两边的白色区域给忽略了,还是当作一条直线。(在主控上根据我经过的十字路口和数字进行综合判断的)
def c_line():
s_flag.value(1) #识别到数字后17口高电平s
img = sensor.snapshot().binary([THRESHOLD])
lcd.display(img)
roi0=(80, 0,30, 30) #给识别中线的位置标上兴趣框,并画下来,验证。
most_pixels=0
img.draw_rectangle(roi0, color =170)
blob0 = img.find_blobs(GRAYSCALE_THRESHOLD, roi=roi0,pixels_threshold=100, area_threshold=100, merge=True)
if blob0:
for n in range(len(blob0)):
#目标区域找到的颜色块(线段块)可能不止一个,找到最大的一个,作为本区域内的目标直线
if blob0[n].pixels() > most_pixels:
most_pixels = blob0[n].pixels()
#merged_blobs[i][4]是这个颜色块的像素总数,如果此颜色块像素总数大于
largest_blob = n
R = 1
else:
R = 0
roi1=(80, 200,30, 30)
img.draw_rectangle(roi1, color =170)
blob1 = img.find_blobs(GRAYSCALE_THRESHOLD, roi=roi1,pixels_threshold=100, area_threshold=100, merge=True)
if blob1:
for n in range(len(blob1)):
if blob1[n].pixels() > most_pixels:
most_pixels = blob1[n].pixels()
largest_blob = n
L = 1
else:
L = 0
uart_A.write('L')
uart_A.write(str(L))
uart_A.write('Z')
uart_A.write('R')
uart_A.write(str(R))
uart_A.write('X')
line=img.get_regression([(20, 250)],roi=(0,0,210,210),x_srtide=2,y_srtide=1,robust=False) #灰度 白黑
if(line):
rho_err = abs(line.rho())-img.width()/2 #piancha
#print(int(rho_err))
uart_A.write('P')
uart_A.write(str(int(rho_err)))
uart_A.write('S')
if line.theta()>90:
theta_err = line.theta()-180
else:
theta_err = line.theta()
img.draw_line(line.line(), color = 127)
print('R',R ,'L',L)
#print(rho_err,line.magnitude(),theta_err)
下面部分是识别数字的,比较简单,都是框架。因为比赛自己做自己用的,串口没有用到多严格的格式,只加了头尾,确保传的数正确就好。主控那边我加了语音播报病房号,也算个额外加分点,这个屡试不爽~
def c_num():
global flag
img = sensor.snapshot()
code = kpu.run_yolo2(task, img)
print(anchor)
a = lcd.display(img)
if code:
for i in code:
a=img.draw_rectangle(i.rect(),(0,255,0),2)
a = lcd.display(img)
for i in code:
lcd.draw_string(i.x()+45, i.y()-5, labels[i.classid()]+" "+'%.2f'%i.value(), lcd.WHITE,lcd.GREEN)
#print(labels[i.classid()])
#print('x',i.x(),y,'i.y()')
if(labels[i.classid()]=='1'):
#print('1')
uart_A.write('N')
uart_A.write('1')
uart_A.write('M')
if(labels[i.classid()]==' 2'):
uart_A.write('N')
uart_A.write('2')
uart_A.write('M')
if(labels[i.classid()]==' 3'):
uart_A.write('N')
uart_A.write('3')
uart_A.write('M')
if(labels[i.classid()]==' 4'):
uart_A.write('N')
uart_A.write('4')
uart_A.write('M')
if(labels[i.classid()]==' 5'):
uart_A.write('N')
uart_A.write('5')
uart_A.write('M')
if(labels[i.classid()]==' 6'):
uart_A.write('N')
uart_A.write('6')
uart_A.write('M')
if(labels[i.classid()]==' 7'):
uart_A.write('N')
uart_A.write('7')
uart_A.write('M')
if(labels[i.classid()]==' 8'):
uart_A.write('N')
uart_A.write('8')
uart_A.write('M')
flag=0
s_flag.value(0)
else:
a = lcd.display(img)
#uart_A.write('N')
#uart_A.write('0')
#uart_A.write('M')
Python我并没有好好学过,几乎没写过几次,都是临时抱佛脚,代码写的很烂。 这次没有什么详细讲解,代码直接甩出去,(不负责的我,这次写完就不想补充完善了)留给大家自己慢慢研究了。我感觉除了那个灰度图像阈值需要自己调整外,其他没有什么太大问题。
额,这次比完赛并没有完赛资料,就没有参加立创开源,少拿一份奖金,不过问题不大,我懒。
立创的奖金比学校的还要高,学校又要降奖金,因为今年拿奖的、参赛的人多了,我笑了……这学校各种恶心人的事,摆烂等我毕业答辩完再说。真不如我在一个好专科待遇好。
白嫖个便携式示波器挺好的。
继19年电赛在专科拿过一次国一后,没想到在本科这边又拿了一次。唯一一次遗憾的就是智能车了,但那个是外在因素,等完全毕业之后再谈。