赞
踩
题记:这是一场因为规则审慎问题,导致取消成绩的失败方案,但并不是完全没有一些参考的意义和价值。以车会友,一直是智能车比赛的初衷,也是我们一直奋斗的目标。这个方案稳定度高,并且获得(470,500)的还算不错的成绩。
整个车成本约1800上下:这里选用了四个脉轮,以及2mm钢板作为底板,确保强度,亚克力/铝板的话可能强度不够,重就重些吧。
在家里打线上赛的大佬去美团打工,并在考虑edgeboard这个开发所需要时间和算力的情况下,我们选择让edgeboard,利用opencv,进行一个基础的识别,也就是这里,和卓老师个人公众号内容有所冲突,导致了最后的违规。
本次比赛主要就是巡线、抓物块、打靶子、放球,亮亮灯。
巡线这里不得不提到主办方精妙的设计,如果你利用OpenCV进行处理,他的黄色色块、宿营地、甚至地面上米黄色的城墙图,都会在不同亮度的情况下,对整个视觉产生巨大影响。
在这个场地中,如果摄像头不进行近处扫描,则四轮的脉轮会因为过早扫到前面的赛道进行漂移,如果扫的过近,则会V字弯道、U形弯道过不去。十字弯道如果盲目补线,则有可能打靶姿势不好纠正,连续双打靶,应该就是为了看你在丢失视野的情况下可不可以自己纠错,这篇需要好好写一下,将会交由我队友写的博客来进行详解。
见博客链接,代码均在此处
队友博客链接:(https://www.shinenet.cn/archives/198.html)
打靶子这块可以说十分简单,靶子高17,于是我们把笔尖固定在17cm高度,那么靶子就只需要做横向处理就行。抓住靶子特征,红色,原型。很好,openMV直接在靶心附近扫描红色圆心色块中心,并且设定一定的弧度、大小,来去除干扰,然后通过脉轮前后微调,使得这个圆心设定在屏幕中心,啪,打上去了。
这个采取的步进电机的方式,主控给设备命令,步进电机转动,把笔伸出去缩回来,又快又好,步进电机驱动可以购买,小步进电机也是(买个好的,防烧毁),然后openmv可以给个pwm波进行控制,搞定。
说实在举旗子这个方针,我们用了个三百六十度舵机,使他能够让三个旗子通过一个舵机来控制。
这里涉及比赛漏洞之一,既然大致顺序是定的,我们就只要识别出白色色块就行,唯一变化的城池顺序,是吧,写个菜单,能用按键按下去就能换代码中config顺序,然后config九个内容,每次识别到一个就+1,执行下一个任务,很好,底部识别没了,变成识别白色块了。打灯,搞个led灯光带,蒙张纸,然他漫反射,减少环境光源影响。openMV识别白色块,调调阈值,参数啥的,好了,又快又稳的底部识别完成了。到对应的位置,还可以通过色块中心点微调一下位置,稳稳当当的完成任务识别。
爪子大一些,两舵机,一个控制下抓角度,一个抓取就行
控制好距离、抓取角度、抓取位置,一抓即中。
利用edgeboard侧面识别赛道线相对距离,从而控制每次抓取时的固定位置。
同理,通过底部openmv大致校准后,用edgeboard的侧边距离控制好,角度锁死,结束。
驱动直接用iic与树莓派通信,就时不时掉线有点烦。直接pid写死在驱动板上,然后就靠iic给他传数值,然后他执行转速即可。自己画个驱动板就行,后续挣得队友同意会把pcb放上去。
分析到这里,整个比赛已经没什么难度了。
巡线部分由于赛道很有水平,等我队友睡醒了会详细写如何处理赛道中各种线各种情况,到时候会附上链接。
代码和3d建模,PCB,将会在征得队友同意后一起奉上。
希望这个思路,能给大家一定的参考。
感谢在比赛场上鼓励我的车友,以及和我一起靠兴趣做车的队友们。
利用openMV进行亮灯、打靶子、寻找靶心的程序
THRESHOLD = (24, 48, 28, 72, -8, 60) import sensor, image, time from ws2812 import WS2812 import utime import pyb from pyb import Pin,Timer from pyb import USB_VCP ring = WS2812(spi_bus=2, led_count=8) sensor.reset() sensor.set_vflip(True) sensor.set_hmirror(True) sensor.set_pixformat(sensor.RGB565) sensor.set_framesize(sensor.QQVGA) # 80x60 (4,800 pixels) - O(N^2) max = 2,3040,000. #sensor.set_windowing([0,20,80,40]) sensor.skip_frames(time = 2000) # WARNING: If you use QQVGA it may take seconds clock = time.clock() # to process a frame sometimes. #clock = time.clock() # to process a frame sometimes. usb = USB_VCP() count = 0 now_x = 0 temp = 0 data = [ (18, 6, 0), (15, 9, 0), (12, 12, 0), (9, 15, 0), (6, 18, 0), (3, 21, 0), (0, 24, 0), (8, 8, 8), ] # red data1 = [ (255, 0, 0), (255, 0, 0), (255, 0, 0), (255, 0, 0), (255, 0, 0), (255, 0, 0), (255, 0, 0), (255, 0, 0), ] # Green data2= [ (0, 255, 0), (0, 255, 0), (0, 255, 0), (0, 255, 0), (0, 255, 0), (0, 255, 0), (0, 255, 0), (0, 255, 0), ] # Blue data3= [ (0, 0, 255), (0, 0, 255), (0, 0, 255), (0, 0, 255), (0, 0, 255), (0, 0, 255), (0, 0, 255), (0, 0, 255), ] # 前进代码 def Gun_shoot_forward(): tim = Timer(4, freq=40000) # Frequency in Hz # p0 p = pyb.Pin("P8", pyb.Pin.OUT_PP) p.high() # 生成1kHZ方波,使用TIM4,channels 1 and 2分别是 50% 和 75% 占空比。 ch1 = tim.channel(1, Timer.PWM, pin=Pin("P7"), pulse_width_percent=50) # 旋转多少s之后,停止伸缩 time.sleep_ms(1000) # 关闭pwm波 ch1 = tim.channel(1, Timer.PWM, pin=Pin("P7"), pulse_width_percent=50) # 回退代码 def Gun_shoot_back(): tim = Timer(4, freq=40000) # Frequency in Hz # p0 p = pyb.Pin("P8", pyb.Pin.OUT_PP) p.low() # 生成1kHZ方波,使用TIM4,channels 1 and 2分别是 50% 和 75% 占空比。 ch1 = tim.channel(1, Timer.PWM, pin=Pin("P7"), pulse_width_percent=50) # 旋转多少s之后,停止伸缩 time.sleep_ms(1000) # 关闭pwm波 ch1 = tim.channel(1, Timer.PWM, pin=Pin("P7"), pulse_width_percent=50) # 扫描靶心 def Scan_target(): while True: clock.tick() img = sensor.snapshot().lens_corr(1.8) #.binary([THRESHOLD]).negate() thr = (0, 128) blobs = img.find_blobs([THRESHOLD],merge=True) flag = False output_str="P1" if not usb.debug_mode_enabled(): usb.write(output_str + "\n") for blob in blobs: if(blob.cx()>40 and blob.cx() < 120 and blob.cy()>20 and blob.cy() < 100 and blob.roundness() > 0.44): img.draw_cross(blob.cx(), blob.cy(), color = 127) temp = blob.cx() else: count = 0 now_x = 0 temp = 0 flag = True break if flag or len(blobs) == 0: continue if(count < 5): now_x = now_x + temp count = count + 1 else: now_x = now_x/5 if now_x/5 <= 81 and now_x >= 79: output_str="ST" if not usb.debug_mode_enabled(): usb.write(output_str + "\n") return else: output_str="[%d]" % (now_x) if not usb.debug_mode_enabled(): usb.write(output_str + "\n") count = 0 now_x = 0 continue def Led_show(status): if status == 1: # 红灯 ring.show(data1) sleep_ms(1000) ring.show(data) sleep_ms(500) ring.show(data1) sleep_ms(1000) ring.show(data) sleep_ms(500) ring.show(data1) elif status == 2: # 绿灯 ring.show(data2) sleep_ms(1000) ring.show(data) sleep_ms(500) ring.show(data2) sleep_ms(1000) ring.show(data) sleep_ms(500) ring.show(data2) elif status == 3: # 蓝灯 ring.show(data3) sleep_ms(1000) ring.show(data) sleep_ms(500) ring.show(data3) sleep_ms(1000) ring.show(data) sleep_ms(500) ring.show(data3) else : # 异常数据则回归正常白灯 ring.show(data) # 完成任务也是正常的白灯 ring.show(data) if __name__=='__main__': while True: # print("1: " + str(time.time())) if(usb.any( )): Status_type = usb.recv(1, timeout=5000) if not usb.debug_mode_enabled(): usb.send("Status") # 接收到状态机 if Status_type == b'R': Led_show(1) # 亮红灯 elif Status_type == b'G': Led_show(2) # 亮绿灯 elif Status_type == b'B': Led_show(3) # 亮蓝灯 elif Status_type == b'S': Scan_target() # 扫描靶心进行移动 elif Status_type == b'X': Gun_shoot_forward() # 当车停稳定后开始进行打靶 Gun_shoot_back() else: continue time.sleep_ms(10) # print("2: " + str(time.time()))
这部分用了一个亮灯的库,ws2812,可以简单搜一下,为了方便大家,这里我也粘贴上去。
import gc try: import pyb except ImportError: import machine as pyb class WS2812: """ Driver for WS2812 RGB LEDs. May be used for controlling single LED or chain of LEDs. Example of use: chain = WS2812(spi_bus=1, led_count=4) data = [ (255, 0, 0), # red (0, 255, 0), # green (0, 0, 255), # blue (85, 85, 85), # white ] chain.show(data) Version: 1.0 """ buf_bytes = (0x88, 0x8e, 0xe8, 0xee) def __init__(self, spi_bus=1, led_count=1, intensity=1): """ Params: * spi_bus = SPI bus ID (1 or 2) * led_count = count of LEDs * intensity = light intensity (float up to 1) """ self.led_count = led_count self.intensity = intensity # prepare SPI data buffer (4 bytes for each color) self.buf_length = self.led_count * 3 * 4 self.buf = bytearray(self.buf_length) # SPI init #self.spi = pyb.SPI(spi_bus, pyb.SPI.MASTER, baudrate=3200000, polarity=0, phase=1) self.spi = pyb.SPI(spi_bus, pyb.SPI.MASTER, baudrate=6400000, polarity=0, phase=1) # turn LEDs off self.show([]) def show(self, data): """ Show RGB data on LEDs. Expected data = [(R, G, B), ...] where R, G and B are intensities of colors in range from 0 to 255. One RGB tuple for each LED. Count of tuples may be less than count of connected LEDs. """ self.fill_buf(data) self.send_buf() def send_buf(self): """ Send buffer over SPI. """ self.spi.send(self.buf) gc.collect() def update_buf(self, data, start=0): """ Fill a part of the buffer with RGB data. Order of colors in buffer is changed from RGB to GRB because WS2812 LED has GRB order of colors. Each color is represented by 4 bytes in buffer (1 byte for each 2 bits). Returns the index of the first unfilled LED Note: If you find this function ugly, it's because speed optimisations beated purity of code. """ buf = self.buf buf_bytes = self.buf_bytes intensity = self.intensity mask = 0x03 index = start * 12 for red, green, blue in data: red = int(red * intensity) green = int(green * intensity) blue = int(blue * intensity) buf[index] = buf_bytes[green >> 6 & mask] buf[index+1] = buf_bytes[green >> 4 & mask] buf[index+2] = buf_bytes[green >> 2 & mask] buf[index+3] = buf_bytes[green & mask] buf[index+4] = buf_bytes[red >> 6 & mask] buf[index+5] = buf_bytes[red >> 4 & mask] buf[index+6] = buf_bytes[red >> 2 & mask] buf[index+7] = buf_bytes[red & mask] buf[index+8] = buf_bytes[blue >> 6 & mask] buf[index+9] = buf_bytes[blue >> 4 & mask] buf[index+10] = buf_bytes[blue >> 2 & mask] buf[index+11] = buf_bytes[blue & mask] index += 12 return index // 12 def fill_buf(self, data): """ Fill buffer with RGB data. All LEDs after the data are turned off. """ end = self.update_buf(data) # turn off the rest of the LEDs buf = self.buf off = self.buf_bytes[0] for index in range(end * 12, self.buf_length): buf[index] = off index += 1
底部白色色块扫描代码
# THRESHOLD = (68, 84, -7, 10, -19, 8) THRESHOLD = (68, 83, -11, 10, -6, 23) # Grayscale threshold for dark things... # THRESHOLD = (65, 83, -24, 9, -13, 29) # THRESHOLD = (29, 100, 45, 127, 5, 127) import sensor, image, time sensor.reset() sensor.set_vflip(True) sensor.set_hmirror(True) sensor.set_pixformat(sensor.RGB565) sensor.set_framesize(sensor.QQVGA) # 120x160 (4,800 pixels) - O(N^2) max = 2,3040,000. #sensor.set_windowing([0,20,80,40]) sensor.skip_frames(time = 2000) # WARNING: If you use QQVGA it may take seconds clock = time.clock() # to process a frame sometimes. from pyb import UART from pyb import USB_VCP usb = USB_VCP() count = 0 now_x = 0 now_y = 0 temp = 0 temp1 = 0 # uart = UART(3, 19200) output_str="openmv_basic" if not usb.debug_mode_enabled() and usb.isconnected(): usb.write(output_str) while True: clock.tick() img = sensor.snapshot().lens_corr(1.8) #.binary([THRESHOLD]).negate() thr = (0, 128) blobs = img.find_blobs([THRESHOLD],merge=True) # and blob.area()>4000 and blob.area() < 7500 SeeFlag = False if(usb.any( )): Ask = usb.recv(1, timeout=5000) if not usb.debug_mode_enabled(): if Ask == b'W': # 询问型号 answer = "openmv_basic" usb.write(answer) # 返回说明是底部openmv for blob in blobs: if(blob.cx()>10 and blob.cx() < 160 and blob.cy()>10 and blob.cy() < 110 and blob.area()>3500 and blob.area()<8500 and blob.density() > 0.35): # if(blob.cx()>10 and blob.cx() < 160 and blob.cy()>10 and blob.cy() < 110 and blob.area()>3500): # print(blob.density()) # print(blob.area()) # print(blob.density()) img.draw_cross(blob.cx(), blob.cy(), color = 127) temp = blob.cx() SeeFlag = True temp1 = blob.cy() if not SeeFlag: now_x = 0 now_y = 0 temp = 0 temp1 = 0 count = 0 continue # 将反应速度提到3 if(count < 3): now_x = now_x + temp now_y = now_y + temp1 count = count + 1 else: now_x = now_x/3 now_y = now_y/3 #print(now_x,now_y) #output_str="[%d,%d]" % (now_x,now_y) #uart.write(output_str) # 因为摄像头横过来了,将x,y呼唤,所以将中线x变成60,y中线变成80 output_str="[%d,%d]" % (now_y,now_x) if not usb.debug_mode_enabled() and usb.isconnected(): usb.write(output_str) else: print(output_str) count = 0 now_x = 0 now_y = 0 continue
这部分主要和openmv还有车速有关,车速越快,取均值次数就要少些,当然,如果有钱换openmv4,那么算力会大幅度提升。
因为用不起主办方的网盘,所以只能用csdn自带的了,点击下载
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。