2023电赛E题思路+代码

本文分享了一名大一学生在电赛中的经验,使用OpenMV作为主控,构建了一个基于铝型材和角铁的硬件框架,实现屏幕显示和云台控制。通过PID算法追踪红色和绿色激光,虽然舵机精度不足,但整体方案展示了追踪和定位的能力。
摘要由CSDN通过智能技术生成

2023年的电赛结束了,我的大一生活也结束了
下面是我完成电赛E题的一些思路和我们组实际使用的代码
给大佬献丑了

  1. 根据题目给的任务要求我构建了这样的硬件环境
    1.openMV:直接做主控,当时说不让用步进电机索性直接拿它当主控了(后面确实后悔了)
    2.铝型材和角铁:分用了80和40的铝型材搭成的框架,稳的一批,甚至可以拿起来摇(狗头)
    3.屏幕:用的石灰板(楼下刚好在装修,而且好切)做的屏幕,在贴工图纸(够大)
    4.云台:最让我意难平的东西,选择的是SG90(实验室祖传的)精度和效果都让人很不满意
    主要就是这些了,其他外围电路没什么好说的没什么,基础部分的OpenMV用了配套的无畸变摄像头和OpenMV 舵机扩展板当时想着是把OpenMV 也放在一个单独的云台上矫正的(后面被要求禁止了),后面索性也没改,按键选择的是1*4的键盘(受限与OpenMV ),电源用的是220转12的学生电源,通过降压模块给舵机(这个确实是有必要的)和OpenMV 供电
  2. 基础部分1,2,3写死!!!没必要给自己找麻烦
  3. 基础4
import pyb
import sensor, image, time
from pid import PID
from servo import Servos
from machine import I2C, Pin
from pid import PID
from pyb import Servo
from pyb import Pin, ExtInt

pan_servo=Servo(1)
tilt_servo=Servo(2)

pan_pid = PID(p=0.04, i=0, imax=90) #脱机运行或者禁用图像传输,使用这个PID
tilt_pid = PID(p=0.04, i=0, imax=90) #脱机运行或者禁用图像传输,使用这个PID

# 初始化摄像头
sensor.reset()
sensor.set_pixformat(sensor.RGB565) # 设置图像色彩格式为RGB565格式
sensor.set_framesize(sensor.QQVGA)  # 设置图像大小为160*120
sensor.set_auto_whitebal(True)      # 设置自动白平衡
sensor.set_brightness(3000)         # 设置亮度为3000
sensor.skip_frames(time = 0)       # 跳过帧

clock = time.clock()

def function_c():					#对应按键选择基本部分4
    a = 80							#设置初始的x轴位置
    b = 80							#设置初始的y轴位置
    i = 0							#循环次数,判断是否跑了四个点
    w = 0
    h = 0
    global x, y

    clock.tick()  # 计时器开始

    img = sensor.snapshot()  # 获取图像

    print("RED", find_red(img))
    time.sleep_ms(500)  # 等待500毫秒
	# 在图像中寻找矩形
    for r in img.find_rects(threshold = 10000):
        # 判断矩形边长和颜色是否符合要求
        if r.w() > 20 and r.h() > 20:
            # 在屏幕上框出矩形
            img.draw_rectangle(r.rect(), color = (255, 0, 0), scale = 4)
            # 获取矩形角点位置
            corner = r.corners()

            # 在屏幕上圈出矩形角点

            img.draw_circle(corner[0][0], corner[0][1], 5, color = (0, 0, 255), thickness = 2, fill = False)
            img.draw_circle(corner[1][0], corner[1][1], 5, color = (0, 0, 255), thickness = 2, fill = False)
            img.draw_circle(corner[2][0], corner[2][1], 5, color = (0, 0, 255), thickness = 2, fill = False)
            img.draw_circle(corner[3][0], corner[3][1], 5, color = (0, 0, 255), thickness = 2, fill = False)

            # 打印四个角点坐标, 角点1的数组是corner[0], 坐标就是(corner[0][0],corner[0][1])
            # 角点检测输出的角点排序每次不一定一致,矩形左上的角点有可能是corner0,1,2,3其中一个

            # 记录四个角点的索引
            corners = [corner[0], corner[1], corner[2], corner[3]]
            curr_corner_index = 0  # 当前追踪的角点索引

            pan_servo.angle(80)  # 设置舵机水平角度
            tilt_servo.angle(77)  # 设置舵机垂直角度

            while True:
                jianpan()
                img.draw_rectangle(r.rect(), color = (255, 0, 0), scale = 4)
                img.draw_cross(corners[curr_corner_index][0], corners[curr_corner_index][1], color = (0, 0, 255), size = 1, thickness = 1)
                clock.tick()  # 计时器开始
                img = sensor.snapshot()  # 获取图像

                print("RED", find_red(img))
                time.sleep_ms(100)  # 等待



                pan_error = -corners[curr_corner_index][0] + x  # 计算水平方向的误差
                tilt_error = -corners[curr_corner_index][1] + y  # 计算垂直方向的误差
                print("error:", pan_error, tilt_error)

                pan_output = pan_pid.get_pid(pan_error,1)  # 使用PID控制器计算水平方向的输出值
                tilt_output = tilt_pid.get_pid(tilt_error, 1)/1  # 使用PID控制器计算垂直方向的输出值
                print("output", pan_output, tilt_output)

                a = a - pan_output # 调整水平舵机位置
                b = b + tilt_output  # 调整垂直舵机位置


                servo.position(1, a,300)  # 应用水平方向的输出值到舵机位置
                servo.position(0, b,300)  # 应用垂直方向的输出值到舵机位置
                print("angle", a, b)
                time.sleep_ms(150)  # 等待

                img = sensor.snapshot()  # 获取图像

                print("RED", find_red(img))
                time.sleep_ms(100)  # 等待500毫秒

                print(curr_corner_index,i)
                print("Laser Coordinate (a,b)", corners[curr_corner_index][0], corners[curr_corner_index][1])  # 打印当前角点的坐标
                # 判断pan_error和tilt_error的值是否小于5,如果小于5,认为角点已经找到,跳出循环
                if abs(pan_error) < 5 and abs(tilt_error) < 5:
                    curr_corner_index = (curr_corner_index + 1) % 4
                    if curr_corner_index == 0:
                        i += 1
                    time.sleep_ms(100)
                     #如果计时器超过了7秒
                if clock.avg() > 7000 :
                    curr_corner_index = (curr_corner_index + 1) % 4
                    # 重新开始计时
                    clock.tick()
                    time.sleep_ms(100)

                if curr_corner_index == 1 and i == 1  :
                    #退回到主函数
                    servo.position(0, 83)
                    servo.position(1, 77)
                    time.sleep_ms(1000)
                    break

    print("Executing Function C")
#寻找红色激光
def find_red(img):
    #调用全局变量x,y
    global x,y
    img = sensor.snapshot()
    clock.tick()
    red_td = [(68, 100, 6, 127, 2, 127)]
    # 根据阈值找到色块
    for b in img.find_blobs(red_td,pixels_threshold=2, area_threshold=15, merge=True,invert = 0):
            # 在屏幕上画出色块
            #img.draw_rectangle(b.rect(), color = (0, 255, 0), scale = 2, thickness = 2)
            x = b.cx()  # 获取激光中心的x坐标
            y = b.cy()  # 获取激光中心的y坐标
            img.draw_cross(x, y, color = (0, 255, 0), size = 1, thickness = 1)  # 在屏幕上画出激光中心
    #没有读取到激光返回0,0

    return x,y    

  1. 实际效果还行,就是舵机精度不够,过几天试试拿步进做一个看看效果

发挥部分

  1. 我感觉相对好做的,可以直接拿OpenMV 例程的追小球的云台改,效果还是相当不错的
    做一个红色激光的识别,做一个绿色激光的识别,整体跟基础部分没差多少

import pyb
import sensor, image, time

from pid import PID
from pyb import Servo

pan_servo=Servo(1)
tilt_servo=Servo(2)


x = 0
y = 0

w = 0
h = 0

pan_servo.calibration(500,2500,500)
tilt_servo.calibration(500,2500,500)

pan_servo.angle(45)
tilt_servo.angle(140)

# 初始化键盘行引脚和列引脚
row_pins = [
    pyb.Pin('P0', pyb.Pin.OUT_PP),  # 行引脚设置为输出
]
col_pins = [
    pyb.Pin('P1', pyb.Pin.IN, pyb.Pin.PULL_DOWN),  # 列引脚设置为输入,下拉电阻
]

# 1x4 键盘布局
keymap = [
    ['1'],
]


pan_pid = PID(p=0.07, i=0, imax=90) 
tilt_pid = PID(p=0.05, i=0, imax=90) 


sensor.reset() # Initialize the camera sensor.s
sensor.set_pixformat(sensor.RGB565) # use RGB565.
sensor.set_framesize(sensor.QQVGA) # use QQVGA for speed.
sensor.skip_frames(10) # Let new settings take affect.
sensor.set_auto_whitebal(False) # turn this off.
clock = time.clock() # Tracks FPS.

def find_red(img):
    #调用全局变量x,y
    global x,y
    img = sensor.snapshot()
    clock.tick()
    red_td = [(55, 74, 22, 127, -128, 127)]

    for b in img.find_blobs(red_td,pixels_threshold=2, area_threshold=15, merge=True,invert = 0):

            img.draw_rectangle(b.rect(), color = (0, 255, 0), scale = 2, thickness = 2)
            x = b.cx()  # 获取光中心的x坐标
            y = b.cy()  # 获取光中心的y坐标
            img.draw_cross(x, y, color = (255, 0, 0), size = 1, thickness = 1)  # 在屏幕上显示红色激光位置

    return x,y

def find_green(img):

    global w,h
    img = sensor.snapshot()
    clock.tick()
    red_td = [(81, 97, -128, -3, -128, 127)]

    for b in img.find_blobs(red_td,pixels_threshold=2, area_threshold=15, merge=True,invert = 0):
            img.draw_rectangle(b.rect(), color = (0, 255, 0), scale = 2, thickness = 2)
            w = b.cx()
            h = b.cy()
            img.draw_cross(w, h, color = (0, 255, 0), size = 1, thickness = 1)
    return w,h
    #在屏幕上显示绿色激光位置
def run():
    clock.tick() # Track elapsed milliseconds between snapshots().
    img = sensor.snapshot() # Take a picture and return the image.

    while True:
        print("RED ",find_red(img))
        print("GREEN ",find_green(img))
        pan_error  = w - x
        tilt_error = h - y
				#相对误差
        print("pan_error: ", pan_error)


        pan_output=pan_pid.get_pid(pan_error,1 )
        tilt_output=tilt_pid.get_pid(tilt_error,1)
        print("pan_output",pan_output)

        pan_servo.angle(pan_servo.angle()-pan_output+1)
        tilt_servo.angle(tilt_servo.angle()+tilt_output+1)
def jianpan():
    for i in range(len(keymap)):
    # 设置当前行为高电平,其他行为低电平
        for j in range(len(keymap)):
            row_pins[j].value(1 if i == j else 0)
        # 检查列引脚的状态
            for k in range(len(col_pins)):
                if col_pins[k].value() == 1:
                    key = keymap[i][k]  # 获取对应的按键字符

                # 根据按键执行相应的函数
                    if key == '1':
                        run()
                pyb.delay(10)  # 延时一段时间
while(True):
    clock.tick() # Track elapsed milliseconds between snapshots().
    img = sensor.snapshot() # Take a picture and return the image.
    clock.tick()
    find_red(img)
    find_green(img)
    jianpan()

有一点需要注意的是,红色激光和绿色激光完全重合是是无法判断相对误差的,但是此时error又刚好为0(本人代码能力不行,负负得正了),
红绿激光的读取部分,我将OpenMV(原摄像头)的对焦挑到最大,即使画面失真,由于红绿激光的亮度,在屏幕上能看到像素风格的红色和绿色光斑,计算的中心点和实际的红绿色激光的误差是相对小的(满足小于3cm的附加1要求),抗环境干扰能力是很强的(各种环境没测出识别不出来过)所以说我认为这才是附加题的正解

从理论部分来说,我这两个代码都能完成题目要求,但是实际的精度真的是让人大跌眼镜

我的E题啊


但是我基础3没写死啊!!!(我哭死)
吉林测试第一天晚上下雨啊!!!第二天测试环境真的不行啊啊啊
好了,看到这边给大一的小学弟点个赞不过分吧,求求了
图后面慢慢加吧,晚上先到这了

评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值