基于ESP32的竞赛裁判系统功能调试-计时线圈功能

Posted 卓晴

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了基于ESP32的竞赛裁判系统功能调试-计时线圈功能相关的知识,希望对你有一定的参考价值。

简 介: 对于智能车竞赛比赛系统中的电磁检测功能进行了调试,设置了程序运行的主要框架。

关键词 智能车竞赛比赛系统计时系统ESP32电磁检测

 

§01 能车竞赛裁判系统


  间间隔了一段时间,在昨天完成了 对于ESP32智能车竞赛比赛系统硬件的初步调试 。智能车竞赛系统的功能在 基于ESP32模块利用检测线圈检测车模停止时间,室内视觉AI裁判系统 进行了定义,根据现在已经进行的 福建省智能车竞赛四川省智能车竞赛 来看,在即将到来的 暑期第十六届智能车竞赛 中,由于赛题的需求,对于比赛系统的更新势在必行。

  下面是福建省赛华侨大学唐懋老师发送过来的关于比赛系统功能改进的建议:

卓老师,我是华侨大学唐懋,今年福建省省赛承办人。根据您的要求,我主要对计时系统在比赛中的问题跟您描述一下:

  1. 智能视觉组目前无全国统一的计时系统,这个必须尽快推出和推广,否则影响面非常大。我们省赛是开会大家决定只出现数字在三叉路口,地面有二维码,其他动物水果就没有采用了。计时还是采用龙邱的传统计时系统。智能视觉组的确需要尽快推广符合比赛要求并且性能可靠稳定,性价比合理的计时系统。
  2. 龙邱的传统计时系统不适合单车拉力组,因为单车拉力组的电动机离地面较高,正常比赛难以触发,若贴上多个磁标,则会影响车体重心导致无法完赛这也是个需要面对的问题。而且单车要过计时线圈(贴在地面)那个坎,有的队伍会翻车。
  3. 龙邱的传统计时系统还是很不稳定,频频出现问题,我买六个计时系统,开赛没多久当场坏掉两个(一直滴滴响无法计时),干扰问题也非常严重,建议采取线圈和红外结合的方式,特别是单车组。

  今天(2021-06-10)对于智能车竞赛系统的动态功能进行完善。

1、主要测试功能

  • 三种传感器检测: (1)电磁线圈传感器;(2)光带传感器;(3)激光信号检测传感器;
  • 两种计算机连接方式:(1)通过WiFi与计算机联网;(2)通过USB与计算机联网;
  • 与比赛系统功能整合: 通过上述两种与计算机联网方式,将检测功能整合在原来的比赛系统中;

2、硬件与软件修改

  在这里,将下面在调试过程中出现的问题,涉及到对于第一版本的硬件和软件修改进行记录。在前期硬件调试出现的修改意见记录在博文 基于ESP32智能车竞赛比赛系统硬件初步调试-5-6 中。

 

§02 线圈检测功能


  于线圈检测车模上带有的永磁铁磁标是传统 信标裁判系统 所使用的基本原理。特别是在室外光线变化比较大的情况下,它还是具有一定的优势。在 基于ESP32模块利用检测线圈检测车模停止时间,室内视觉AI裁判系统 已经进行了对采集到的线圈信号进行滤波处理,完成了对于磁标的检测试验。

1、数据采集与滤波

线圈信号采集端口:
OUT1:Pin34
Out2:Pin35

(1)数据采集

#!/usr/local/bin/python
# -*- coding: gbk -*-
#============================================================
# TEST1.PY                     -- by Dr. ZhuoQing 2021-06-10
#
# Note:
#============================================================

from machine                import ADC,Timer,Pin

#------------------------------------------------------------
led = Pin(5, Pin.OUT)

#------------------------------------------------------------
adc1 = ADC(Pin(36))
adc2 = ADC(Pin(39))
adc3 = ADC(Pin(34))
adc4 = ADC(Pin(35))

adc1.atten(ADC.ATTN_6DB)
adc2.atten(ADC.ATTN_6DB)
adc3.atten(ADC.ATTN_6DB)
adc4.atten(ADC.ATTN_6DB)

SAMPLE_NUM = const(500)
AVERAGE_NUM = 16
ad1dim = [0] * SAMPLE_NUM
ad2dim = [0] * SAMPLE_NUM
ad3dim = [0] * SAMPLE_NUM
ad4dim = [0] * SAMPLE_NUM

sample_point = 0
stop_flag = 0

def ADC4Sample(_):
    global ad1dim,ad2dim,ad3dim,ad4dim
    global sample_point
    global adc1,adc2,adc3
    global stop_flag

    led.on()
    ad1dim[sample_point] = adc1.read()
    ad2dim[sample_point] = adc2.read()
    ad3dim[sample_point] = adc3.read()
    ad4dim[sample_point] = adc4.read()

    sample_point += 1
    if sample_point >= SAMPLE_NUM:
        sample_point = 0

    led.off()

#------------------------------------------------------------
SAMPLE_PERIOD = 1

time0 = Timer(0)
time0.init(period=SAMPLE_PERIOD, mode=Timer.PERIODIC, callback=ADC4Sample)

#------------------------------------------------------------
#        END OF FILE : test1.PY
#============================================================

(2)定时器数据采集

  在上面程序中,使用LED1(IO5)来指示在定时器中完成数据采集消耗的时间。测量LED的波形,可以获得实际数据采集频率和消耗的时间。

采集四路模拟信号:
采集频率:1000Hz
占用时间:400us/每次

▲ 图2.1 数据采集指示脉冲

▲ 图2.1 数据采集指示脉冲

(3)显示采集数据

#!/usr/local/bin/python
# -*- coding: gbk -*-
#============================================================
# DRAW.PY                      -- by Dr. ZhuoQing 2021-06-10
#
# Note:
#============================================================

from headm import *

def thonnycmd(cmd):
    tspsendwindowkey('Thonny', 's', alt=1, noreturn=1)
    tspsendwindowkey('Thonny', '%s'%cmd, noreturn=1)

def thonnyshs(cmd='', wait=0):
    tspsendwindowkey('Thonny', 's', alt=1, noreturn=1)
    if len(cmd) > 0:
        tspsendwindowkey('Thonny', '%s\\r'%cmd, noreturn=1)
    if wait > 0:
        time.sleep(wait)
    tspsendwindowkey('Thonny', 'ac', control=1, noreturn=1)
    tspfocuswindow('TEASOFT:1')
    return clipboard.paste()

thonnyshs('print(ad3dim,ad4dim)', 2)
pastestr = clipboard.paste()
data1 = [float(s) for s in pastestr.split('[')[-1].split(']')[0].split(',')]
data2 = [float(s) for s in pastestr.split('[')[-2].split(']')[0].split(',')]

plt.plot(data1)
plt.plot(data2)
plt.xlabel("Sample")
plt.ylabel("Value")
plt.grid(True)
plt.tight_layout()
plt.show()

#------------------------------------------------------------
#        END OF FILE : DRAW.PY
#============================================================

▲ 采集到的两路信号

▲ 采集到的两路信号

▲ 采集到有磁铁波动时的信号

▲ 采集到有磁铁波动时的信号

  从上面的波形来看,这两个线圈应该是是叠加一起,但是极性相反。将其中一个线圈的极性调整过来重新测量。

▲ 采集到ADC信号波形

▲ 采集到ADC信号波形

(4)数据滤波

#------------------------------------------------------------
SAMPLE_AVERAGE_LENGTH   = 40
def sampleaverage40ms(s):
    global sample_point

    scopy = s.copy()
    if sample_point > 0:
        scopy = scopy[sample_point:] + scopy[:sample_point]

    for i in range(len(s) - SAMPLE_AVERAGE_LENGTH):
        scopy[-i-1] = sum(scopy[-(i+SAMPLE_AVERAGE_LENGTH+1):-(i+1)]) / SAMPLE_AVERAGE_LENGTH

    return scopy

▲ 使用20ms窗口数据平滑滤波效果

▲ 使用20ms窗口数据平滑滤波效果

▲ 使用40ms窗口数据平滑滤波效果

▲ 使用40ms窗口数据平滑滤波效果

2、改进的数据滤波

(1)数据采集滤波代码

  通过全局变量 sample_mode来表示不同的采集模式:

两种ADC采集模式:
sample mode=0:采集adc3,adc4
sample mode=1:采集adc1,adc2
#!/usr/local/bin/python
# -*- coding: gbk -*-
#============================================================
# TEST1.PY                     -- by Dr. ZhuoQing 2021-06-10
#
# Note:
#============================================================

from machine                import ADC,Timer,Pin,freq
import time

#------------------------------------------------------------
led = Pin(5, Pin.OUT)
freq(240000000)

#------------------------------------------------------------
adc1 = ADC(Pin(36))
adc2 = ADC(Pin(39))
adc3 = ADC(Pin(34))
adc4 = ADC(Pin(35))

adc1.atten(ADC.ATTN_6DB)
adc2.atten(ADC.ATTN_6DB)
adc3.atten(ADC.ATTN_6DB)
adc4.atten(ADC.ATTN_6DB)

SAMPLE_NUM = const(500)
AVERAGE_NUM = 16
ad1dim = [0] * SAMPLE_NUM
ad2dim = [0] * SAMPLE_NUM
ad3dim = [0] * SAMPLE_NUM
ad4dim = [0] * SAMPLE_NUM

SAMPLE_AVERAGE_LENGTH   = 40
ad3average = [0] * SAMPLE_AVERAGE_LENGTH
ad4average = [0] * SAMPLE_AVERAGE_LENGTH
ad34point = 0
ad3sigma = 0
ad4sigma = 0

#------------------------------------------------------------
sample_point = 0
stop_flag = 0

sample_mode = 0                     # 0 : sample adc3, adc4
                                    # 1 : sample adc1, adc2
#------------------------------------------------------------

def ADC4Sample(_):
    global ad1dim,ad2dim,ad3dim,ad4dim
    global sample_point
    global adc1,adc2,adc3, adc4
    global ad3average, ad4average, ad34point, ad3sigma, ad4sigma

    led.on()

    if sample_mode == 1:
        ad1dim[sample_point] = adc1.read()
        ad2dim[sample_point] = adc2.read()

    if sample_mode == 0:
        adc = adc3.read()
        ad3sigma += adc
        ad3sigma -= ad3average[ad34point]
        ad3average[ad34point] = adc

        adc = adc4.read()
        ad4sigma += adc
        ad4sigma -= ad4average[ad34point]
        ad4average[ad34point] = adc
        ad34point += 1
        if ad34point >= SAMPLE_AVERAGE_LENGTH:
            ad34point = 0

        ad3dim[sample_point] = ad3sigma / SAMPLE_AVERAGE_LENGTH
        ad4dim[sample_point] = ad4sigma / SAMPLE_AVERAGE_LENGTH

    sample_point += 1
    if sample_point >= SAMPLE_NUM:
        sample_point = 0

    led.off()

#------------------------------------------------------------
SAMPLE_PERIOD = 1

time0 = Timer(0)
time0.init(period=SAMPLE_PERIOD, mode=Timer.PERIODIC, callback=ADC4Sample)

#------------------------------------------------------------
#        END OF FILE : test1.PY
#============================================================

  下面是强磁铁对应的采集信号:

▲ 滤波后采集到在有磁铁情况下的信号

▲ 滤波后采集到在有磁铁情况下的信号

  下面是小型磁铁对应的采集信号:

▲ 滤波后采集到在有磁铁情况下的信号

▲ 滤波后采集到在有磁铁情况下的信号

  从上图中可以看到采集数据位置 sample_point的附近引起的数据的突变情况。

 

§03 线圈触发检测


1、软件主程序

(1)程序功能

  • 在Timer采集的时候使用40ms数据滤波;
  • 建立两个通道的基准线;
  • 检查两个通道ADC值是否与基准线偏离超过给定的阈值。
程序参数:
AD34 BASE ALPHA :100-基准线滤波惯性系数
AD34 CHECK THRESHOLD :检查信号变化阈值

(2)程序代码

#!/usr/local/bin/python
# -*- coding: gbk -*-
#============================================================
# TEST1.PY                     -- by Dr. ZhuoQing 2021-06-10
#
# Note:
#============================================================

from machine                import ADC,Timer,Pin,freq
import time

#------------------------------------------------------------
led = Pin(5, Pin.OUT)
freq(240000000)

#------------------------------------------------------------
adc1 = ADC(Pin(36))
adc2 = ADC(Pin(39))
adc3 = ADC(Pin(34))
adc4 = ADC(Pin(35))

adc1.atten(ADC.ATTN_6DB)
adc2.atten(ADC.ATTN_6DB)
adc3.atten(ADC.ATTN_6DB)
adc4.atten(ADC.ATTN_6DB)

SAMPLE_NUM = const(500)
AVERAGE_NUM = 16
ad1dim = [0] * SAMPLE_NUM
ad2dim = [0] * SAMPLE_NUM
ad3dim = [0] * SAMPLE_NUM
ad4dim = [0] * SAMPLE_NUM

SAMPLE_AVERAGE_LENGTH   = 40
ad3average = [0] * SAMPLE_AVERAGE_LENGTH
ad4average = [0] * SAMPLE_AVERAGE_LENGTH
ad34point = 0
ad3sigma = 0
ad4sigma = 0

#------------------------------------------------------------
AD34_BASE_ALPHA         = 0.0005
ad3baseline = 0
ad4baseline = 0

AD34_CHECK_THRESHOLD    = 100
ad3checktime = 0
ad4checktime = 0

#------------------------------------------------------------
sample_point = 0
stop_flag = 0
sample_mode = 0                     # 0 : sample adc3, adc4
                                    # 1 : sample adc1, adc2

total_count = 0

#------------------------------------------------------------
def ADC4Sample(_):
    global ad1dim,ad2dim,ad3dim,ad4dim
    global sample_point
    global adc1,adc2,adc3, adc4
    global ad3average, ad4average, ad34point, ad3sigma, ad4sigma

    global ad3checktime, ad4checktime,total_count
    global ad3baseline, ad4baseline

    led.on()

    #--------------------------------------------------------
    total_count += 1

    #--------------------------------------------------------

    if sample_mode == 1:
        ad1dim[sample_point] = adc1.read()
        ad2dim[sample_point] = adc2.read()

    if sample_mode == 0:
        adc = adc3.read()
        ad3sigma += adc
        ad3sigma -= ad3average[ad34point]
        ad3average[ad34point] = adc

        adc = adc4.read()
        ad4sigma += adc
        ad4sigma -= ad4average[ad34point]
        ad4average[ad34point] = adc
        ad34point += 1
        if ad34point >= SAMPLE_AVERAGE_LENGTH:
            ad34point = 0

        ad3dim[sample_point] = ad3sigma / SAMPLE_AVERAGE_LENGTH
        ad4dim[sample_point] = ad4sigma / SAMPLE_AVERAGE_LENGTH

        #----------------------------------------------------
        value = ad3sigma / SAMPLE_AVERAGE_LENGTH
        if ad3baseline == 0:
            if ad34point == SAMPLE_AVERAGE_LENGTH - 1:
                ad3baseline = value
        else: ad3baseline = ad3baseline * (1 - AD34_BASE_ALPHA) +AD34_BASE_ALPHA * value

        if abs(value - ad3baseline) > AD34_CHECK_THRESHOLD:
            if ad3checktime == 0:
                ad3checktime = total_count

        value = ad4sigma / SAMPLE_AVERAGE_LENGTH
        if ad4baseline == 0:
            if ad34point == SAMPLE_AVERAGE_LENGTH - 1:
                ad4baseline = value
        else: ad4baseline = ad4baseline * (1 - AD34_BASE_ALPHA) + AD34_BASE_ALPHA * value

        if abs(value - ad4baseline) > AD34_CHECK_THRESHOLD:
            if ad4checktime == 0:
                ad4checktime = total_count

    #--------------------------------------------------------
    sample_point += 1
    if sample_point >= SAMPLE_NUM:
        sample_point = 0

    #--------------------------------------------------------
    led.off()

#------------------------------------------------------------
SAMPLE_PERIOD = 1

time0 = Timer(0)
time0.init(period=SAMPLE_PERIOD, mode=Timer.PERIODIC, callback=ADC4Sample)

#------------------------------------------------------------
while True:
    if ad3checktime != 0:
        print(ad3checktime)
        ad3checktime = 0

    if ad4checktime != 0:
        print(ad4checktime)
        ad4checktime = 0

    time.sleep_ms(100)

#------------------------------------------------------------
#        END OF FILE : test1.PY
#============================================================

2、程序功能测试

  手持小型磁铁在检测线圈晃动,会引起检测时间输出。

(1)测试电单车是否能够触发?

  怎么前面唐懋老师所提出的对于电单车,来自于LQ的电磁感应无法正常剪出,如果粘贴更多的磁铁则会引起电单车不稳定。下面就测试一下普通的电单车是否可以只使用一个永磁铁来触发线圈。

  下图是用于触发线圈的磁标。
▲ 图3.2.1 磁标

▲ 图3.2.1 磁标

永磁铁尺寸:
厚度:1.63mm
宽度:9.67mm
长度:19.52mm

  使用双面胶将磁标粘贴在电单车底部。

▲ 图3.2.2 使用双面胶将磁标粘贴在电单车底部

▲ 图3.2.2 使用双面胶将磁标粘贴在电单车底部

  通过实际检验可以知道,当电单车经过线圈是,可以比较可靠的触发感应线圈计时。

▲ 图3.2.3 电单车压过线圈会触发线圈计时

▲ 图3.2.3 电单车压过线圈会触发线圈计时

 

试总结 ※


  于计时线圈检测功能进行了调试。通过内部采集存储缓存区,可以观察到线圈采集信号的波形。这个存储区ad3dim, ad4dim在正式程序运行的时候是不需要的。 通过测试程序,验证了如下功能:

  • 通过对采集到的信号使用40ms的窗口平滑滤波,可以将外部50Hz的干扰信号降低的最低。
  • 利用一阶惯性平滑滤波获取两个通道的动态基线电平;
  • 设置全局计数变量 total_count,来标记检测电平变化的时间间隔;
  • 如果出现了电平变化,在全局变量 ad3checktime, ad4checktime记录下变化的时刻。

  通过实验验证,使用感应线圈可以比较精确检测到电单车经过时刻。如果希望点券不影响单车运行,可以考虑在其上加盖一层厚的纸张,来减少对于车模的影响。

  不过线圈存在所带来的颠簸也是仿真路面颠簸的最好形式。


■ 相关文献链接: