第16届百度智能车比赛-方案制定与选择(技术报告)

题记:这是一场因为规则审慎问题,导致取消成绩的失败方案,但并不是完全没有一些参考的意义和价值。以车会友,一直是智能车比赛的初衷,也是我们一直奋斗的目标。这个方案稳定度高,并且获得(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自带的了,点击下载

github源码

edgeboard与树莓派源码

  • 12
    点赞
  • 26
    收藏
  • 打赏
    打赏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
©️2022 CSDN 皮肤主题:大白 设计师:CSDN官方博客 返回首页
评论 6

打赏作者

谷雨逝

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值