題記:這是一場因為規則審慎問題,導致取消成績的失敗方案,但并不是完全沒有一些參考的意義和價值,以車會友,一直是智能車比賽的初衷,也是我們一直奮斗的目標,這個方案穩定度高,并且獲得(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自帶的了,點擊下載
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/291137.html
標籤:其他
