2022湖北省大学生电子设计竞赛E题(TI杯)

关于作者:本科大三在读,电气专业。基础不好,不喜勿喷,感谢访问本网站,如果能给你提供新思路就是我最大的荣幸!

点击此处获取2022年TI杯大学生电子设计竞赛参考赛题

关于题目

​ 设计制作一个声源定位跟踪系统,能够实时显示及指示声源的位置,当声源移动时能够用激光笔动态跟踪声源。 声源检测系统测量区域分布俯视如图 1 所示。

图1 系统测量区域俯视图

要求

  1. 设计并制作声音发生装置——“声源”,装置能独立工作,声音音量手动可调,装置最大边长或直径不超过 10cm,装置可用支架安装,并可在地面移动;声源中心点 B 用红色或其他醒目颜色标识,并在 B 点所在的平面以 B 点为圆心,直径为 5cm 画圆圈,用醒目线条标识,该平面面向检测指示装置(图中 A 点)。(4 分)
  2. 设计并制作一个声源定位检测装置,传感器安装在图 1 的 C 区范围内,高度不超过 1m,系统采用的拾音器 或麦克风传感器数量不超过 10 个;在装置上标记测试参考点 A,作为位置坐标的原点;装置上有显示电路,实时显 示 D 区域内声源的位置,显示 A、B 两点直线距离 γ 和以 A 点为原点,AB 在地面的投影与图 1 中心线的夹角 θ, 测量时间不超过 5s,距离 γ 和角度 θ 的测值误差越小越好。(36 分)
  3. 设计并制作一个声源指示控制装置,此装置和上述声源定位检测装置可以合为一体。也放置在图 1 的 C 区, 安装有激光笔和二维电动云台,能控制激光笔指向声源,定位计算过程中时,激光笔关闭,定位运算完成时激光笔 开启。定位指示声源时,动作反应时间不超过 10s,光点与 B 点偏差越小越好。(30 分)
  4. 声源移动动态追踪:当声源摆放在地面,用细绳牵引,以 0.2m/s 左右的速度在 D 区移动时,激光笔光点指 向 B 点,光点与 B 点偏差越小好,跟踪反应时间越短越好。(20 分)

解题过程

听说Sipeed的MIC阵列模块可以秒杀E题?解题过程?不存在的

MicArray 麦克风阵列 - Sipeed Wiki

对就是这个😧

包括官方还给了一个2022年电赛E题声源定位跟踪系统做好的链接,直接套用代码接线就可以。。。所以我直接照着完善了一下代码框架,接上线后由于阵列模块非常容易受干扰(当然可以通过选择音源提高效果),所以需要进行一些处理。

from Maix import MIC_ARRAY as mic
import lcd, image
import time
import math
from Maix import GPIO
from fpioa_manager import fm
from machine import Timer, PWM

# MIC-IO
mic.init(i2s_d0=32, i2s_d1=33, i2s_d2=34, i2s_d3=35, i2s_ws=9, i2s_sclk=10, sk9822_dat=11, sk9822_clk=15)
# Laser-IO
fm.register(7, fm.fpioa.GPIO0)
Laser = GPIO(GPIO.GPIO0, GPIO.OUT)
# KEY-IO
fm.register(26, fm.fpioa.GPIO1)
KG0 = GPIO(GPIO.GPIO1, GPIO.IN, GPIO.PULL_DOWN)
fm.register(27, fm.fpioa.GPIO2)
KG1 = GPIO(GPIO.GPIO2, GPIO.IN, GPIO.PULL_DOWN)
# SERVO-IO
tim = Timer(Timer.TIMER0, Timer.CHANNEL0, mode=Timer.MODE_PWM)
Servo_laser = PWM(tim, freq=50, duty=0, pin=17)
# LCD-INIT-320*240 and MAP-INIT
lcd.init()
lcd.fill_rectangle(46, 5, 230, 4, (255, 0, 0))  # 上边线
lcd.fill_rectangle(46, 5, 4, 230, (255, 0, 0))  # 左边线
lcd.fill_rectangle(46, 235, 230, 4, (255, 0, 0))  # 下边线
lcd.fill_rectangle(276, 5, 4, 234, (255, 0, 0))  # 右边线
lcd.fill_rectangle(237, 5, 4, 234, (255, 0, 0))  # 右边线2
lcd.fill_rectangle(0, 77, 47, 4, (255, 0, 0))  # 左区域上边线
lcd.fill_rectangle(0, 154, 47, 4, (255, 0, 0))  # 左区域下边线
lcd.fill_rectangle(0, 77, 4, 77, (255, 0, 0))  # 左区域左边线


class Argument:
    """Some Argus"""

    def __init__(self):
        super(Argument, self).__init__()
        # Normal
        self.num = 0
        self.num2 = 0
        self.Angle_LB = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
        self.t = 0
        self.t1 = 0
        self.t2 = 0
        self.maxnum = 0
        self.minnum = 0
        self.jiaodu = 0
        self.Angle_last = 0
        self.b = [3, 1, 2, 5, 6]
        self.i = 100
        self.a = []
        self.times = 0
        self.pid = 0
        self.err = 0
        self.JD = 0
        self.output = 0

        # Kalman Filter
        self.KF_lastP = 0.1  # 上次的协方差
        self.KF_nowP = 0  # 本次的协方差
        self.KF_x_hat = 0  # 卡尔曼滤波的计算值,即为后验最优值
        self.KF_Kg = 0  # 卡尔曼增益系数
        self.KF_Q = 0  # 过程噪声
        self.KF_R = 0.01  # 测量噪声


arguments = Argument()


def kalman_filter(argus, value):
    argus.output = 0  # output为卡尔曼滤波计算值
    x_t = argus.KF_x_hat  # 当前先验预测值 = 上一次最优值
    argus.KF_nowP = argus.KF_lastP + argus.KF_Q  # 本次的协方差矩阵
    argus.KF_Kg = argus.KF_nowP / (argus.KF_nowP + argus.KF_R)  # 卡尔曼增益系数计算
    argus.output = x_t + argus.KF_Kg * (value - x_t)  # 当前最优值
    argus.KF_x_hat = argus.output  # 更新最优值
    argus.KF_lastP = (1 - argus.KF_Kg) * argus.KF_nowP  # 更新协方差矩阵


def servo(servo_inter, angle):
    servo_inter.duty((angle + 90) / 180 * 10 + 2.5)


while True:
    # Inner Params: [AngleX, AngleY, AngleR, Angle, AngleAddPi]
    angle_params = [0, 0, 0, 0, 0]
    # MIC-Detecting
    img_a = mic.get_map()
    img_b = mic.get_dir(img_a)
    mic.set_led(img_b, (10, 10, 0))
    # If Ques 3
    if KG0.value() == 1 and KG1.value() == 0:
        arguments.times += 1
        if arguments.times == 1:
            time.sleep(1)
            Laser.value(0)
    # Angle
    for i in range(len(img_b)):
        if img_b[i] >= 0:
            angle_params[0] += img_b[i] * math.sin(i * math.pi / 6)
            angle_params[1] += img_b[i] * math.cos(i * math.pi / 6)
    angle_params[0] = round(angle_params[0], 6)
    angle_params[1] = round(angle_params[1], 6)
    if angle_params[1] < 0:
        angle_params[4] = 180
    if angle_params[0] < 0 and angle_params[1] > 0:
        angle_params[4] = 360
    if angle_params[0] != 0 or angle_params[1] != 0:
        if angle_params[1] == 0:
            angle_params[3] = 90 if angle_params[0] > 0 else 270
        else:
            angle_params[3] = angle_params[4] + round(math.degrees(math.atan(angle_params[0] / angle_params[1])), 4)
        # Determine the scope
        if 90 < angle_params[3] < 270:
            angle_params[3] = arguments.Angle_last * 0.1 + angle_params[3] * 0.9
            kalman_filter(arguments, angle_params[3])
            arguments.Angle_last = angle_params[3]
            show_angle = 180 - angle_params[3]

            # Check Ques 3
            if KG0.value() == 1 and KG1.value() == 0:
                Laser.value(1)
                servo(Servo_laser, show_angle)

            lcd.draw_string(60, 200, "Angle: " + str(show_angle), lcd.BLUE, lcd.BLACK)
            lcd.draw_string(60, 180, "Distance: " + str(275 / math.cos(-show_angle * math.pi / 180)), lcd.BLUE,
                            lcd.BLACK)
            lcd.fill_rectangle(251, 10, 25, 225, (0, 0, 0))
            location = int(108 + math.tan(-show_angle * math.pi / 180) * 48)
            print(show_angle, location)
            if 0 < location < 200:
                lcd.fill_rectangle(251, location, 15, 15, (0, 255, 200))

            # Check Ques 4
            if KG0.value() == 1 and KG1.value() == 1:
                arguments.JD = angle_params[3]
                Laser.value(1)
                error = arguments.JD - arguments.pid
                arguments.pid += error * 0.1
                arguments.output = -arguments.pid
                if -270 < arguments.output < -90:
                    servo(Servo_laser, 180 + arguments.output)

反思总结

  1. 音源很重要
  2. 可以滤波
  3. 可以PID控制舵机提高稳定性

  目录