简 介: 对于新版的比赛裁判系统进行硬件测试,验证了新版的硬件满足比赛的要求。对于感光板的不同区域灵敏度不同的问题,最后验证是由于LED的分布电容所引起的时间常数不同造成了。对于单条串联的LED修改成三条之后,就使得感光板的灵敏度达到了一致了。
关键词
: 比赛裁判系统,硬件测试
§01 智能车竞赛比赛系统
在 基于ESP32智能车竞赛比赛系统硬件初步调试-5-6 中对于 智能车竞赛系统 进行了调试,并给出了 修改硬件 。
下面是LQ公司提供的修改后的主板以及目标板。本文后面对该硬件进行确认,并给出制作过程的流程。
▲ 图1.1 修改后的主板
▲ 图1.2 修改后的目标主板
1、主板硬件初步调试1
(1)焊接确认
下面是有LQ焊接后的主板,看到主板,确认将来由于ESP32 的天线在电路板的背面,它的证明敷铜,猜测可能会对WiFi信号的强度产生影响。
因此建议能够将ESP32旋转一个方向,使得WiFi的天线靠近电路板的边缘,并且对应的电路板上没有更多的敷铜和引线。
▲ 图1.3 由LQ焊接后的主板
▲ 图1.3.1 ESP32布局的建议
- 焊接ESP32下载程序接口:
▲ 图1.4 焊接VGRTNG端口
为了不使的主核心板的电压对于 ESP32,ESP8266下载板 电源影响,可以将VGRTNG的PIN1(3.3V)去掉。主核心板的电源有它自身的+5V接口,或者USB接口提供。
▲ 图1.4.1 VGRTNG的PIN1可以不用焊接
(2)上电静态测试
在“电池”端口施加+5V电源电压。
-
静态电压电流:
-
工作电流(mA)
:100左右
3.3V稳压(V)
:3.316
测量了两块待调试的核心板,它们的起始电流、电压都是相同的。
(3)下载ESP32 MicroPython
Ⅰ.下载固件: 从 MicroPython官方网站下载ESP32MicroPython固件 。下面是下载的固件的版本。
Ⅱ.下载程序: 根据 两款带有WiFI的MicroPython模块:ESP32,ESP8266 使用 Thonny IDE
下载ESP32的 MicroPython固件。
下载串口本使用了 利用CH340C制作MicroPython ESP8266,ESP32的下载器-改进型 对应的下载板。
▲ 图1.5 成功下载MicroPython固件的ESP32模块
这次两块ESP32核心板均可以正常下载MicroPython程序。
(4)初步测试IO1
【Ⅰ.端口设置】
SW1 | SW2 | SW3 | SW4 |
---|---|---|---|
GPIO15 | GPIO2 | GPIO19 | GPIO4 |
LED1 | LED2 | BZ1 |
---|---|---|
GPIO5 | GPIO18 | GPIO21 |
高电平点亮 绿色 | 高电平点亮 蓝色 | 高电平发出声音 |
注意:在 第一版本设计中 板上的蜂鸣器是无源的,驱动BZ1需要使用PWM。现在这个版本上焊接的BZ1是有源的蜂鸣器,则可以使用普通的IO来控制。
【Ⅱ.测试软件】
#!/usr/local/bin/python
# -*- coding: gbk -*-
#============================================================
# TESTIO.PY -- by Dr. ZhuoQing 2021-06-26
#
# Note:
#============================================================
from machine import Pin,PWM
import time
sw1 = Pin(15, Pin.IN, Pin.PULL_UP)
sw2 = Pin(2, Pin.IN, Pin.PULL_UP)
sw3 = Pin(19, Pin.IN, Pin.PULL_UP)
sw4 = Pin(4, Pin.IN, Pin.PULL_UP)
led1 = Pin(5, Pin.OUT)
led2 = Pin(18, Pin.OUT)
bz1 = Pin(21, Pin.OUT)
print('Test LED,BZ...')
while True:
led1.on()
led2.off()
bz1.on()
time.sleep_ms(1000)
led1.off()
led2.on()
bz1.off()
time.sleep_ms(250)
#------------------------------------------------------------
# END OF FILE : testio.PY
#============================================================
【Ⅲ.测试结果】
测试LED,BEEP的结果如下图所示。
▲ 图1.6 测试LED,BEEP的结果
2、目标板初步调试
(1)控制端口
PIN1 | PIN2 | PIN3 | PIN4 | PIN5 | PIN6 | PIN7 | PIN8 |
---|---|---|---|---|---|---|---|
+5V | GND | GPIO26 | GPIO25 | GPI33 | GPI32 | B- | A- |
+5V | GND | SPK | Green | Red | 复位按钮 | 边缘检测 | 中心检测 |
注:在当前版本中,PIN6(GPIO32)尚未设置按钮。
(2)测试LED,SPEAKER
【Ⅰ.测试程序】
#!/usr/local/bin/python
# -*- coding: gbk -*-
#============================================================
# TESTIO.PY -- by Dr. ZhuoQing 2021-06-26
#
# Note:
#============================================================
from machine import Pin,PWM
import time
sw1 = Pin(15, Pin.IN, Pin.PULL_UP)
sw2 = Pin(2, Pin.IN, Pin.PULL_UP)
sw3 = Pin(19, Pin.IN, Pin.PULL_UP)
sw4 = Pin(4, Pin.IN, Pin.PULL_UP)
led1 = Pin(5, Pin.OUT)
led2 = Pin(18, Pin.OUT)
led1.off()
led2.off()
bz1 = Pin(21, Pin.OUT)
bz1.off()
gled = Pin(25, Pin.OUT)
rled = Pin(33, Pin.OUT)
speaker = Pin(26, Pin.OUT)
print('Test LED,BZ...')
while True:
# swdim = [s.value() for s in (sw1, sw2, sw3, sw4)]
# print(swdim)
gled.on()
rled.off()
speaker.on()
time.sleep_ms(250)
gled.off()
rled.on()
speaker.off()
time.sleep_ms(250)
#------------------------------------------------------------
# END OF FILE : testio.PY
#============================================================
【Ⅱ.测试结果】
上述程序运行后,可以看到测试版上的LED与Speaker在闪烁。符合程序的功能要求。
▲ 图1.7 目标板上的LED,Speaker测试
§02 调试信号检测
下面开始测试信号各个信号检测功能。
1、光电检测
(1)工作点测量
测量U4的输出OUTA(PIN1),OUTB(PIN7)的电压。
-
● U4的工作点(将光电检测去掉):
-
OUTA
:0.567V
OUTB
:0.565V
VREF(PIN3,PIN5)
:0.574V
● U4的工作点(将光电检板连接):
-
OUTA
:3.3V
OUTB
:3.3V
VREF(PIN3,PIN5)
:0.574V
※ 错误: 将检测板加到核心板的时候U4的输出变成饱和了。
上面的饱和,主要原因来自于购买到的光电管的灵敏度太大了。
▲ 图2.1 光电检测管的极性
※ 修改方法:
- 修改U6,U8: 将R6,R8的阻值修改成10kΩ
- 使用热缩管将光电管包裹: 使用黑色热缩管将光电管的周围进行遮光。
- 只安装1,2,3位置上的检测管:这种安装只是为了能够防止车模缓慢扫描光电管作用。
▲ 图2.2 使用黑色热缩管对光电检测管周围进行遮光
▲ 图2.2.1 只安装1,2,3位置上的光电检测管
(2)检测测试
使用激光发射管透射在光电管上,测量运放输出的波形。
▲ 图2.2.2 测量U4的输出PIN1,PIN7的波形
▲ 图2.2.3 测量放大后的波形
2、线圈检测
(1)运放静态工作点
-
U3输出静态电压:
-
OUT1(Pin1)
:0.789V
OUT2(Pin7)
:0.782V
▲ 图2.4 电磁扫描引起的输出电压波动
3、光电检测条
测量三个光电检测条,它们可以直接通过三芯电缆连接电路板上的对应的三芯插座。
▲ 图2.5.1 检测光条板
通过测试也发现光条在不同的位置出现的灵敏度不同。越是靠近距离接口远端,光敏的越强。距离接口端敏感越小,具体原因不详。
通过进一步测试可以验证,不同的灵敏度应该与透射在其上的光强有关系。
▲ 图2.5 运放输出信号
§03 软件功能测试
1、检测软件
#!/usr/local/bin/python
# -*- coding: gbk -*-
#============================================================
# TESTALL.PY -- by Dr. ZhuoQing 2021-06-13
#
# Note:
#============================================================
from machine import UART,Pin,Timer,ADC
import time
import machine
#------------------------------------------------------------
machine.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)
ad1dim = [0] * SAMPLE_NUM
ad2dim = [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 = 500 #250
ad3checktime = 0
ad4checktime = 0
#------------------------------------------------------------
sample_mode = 0 # 0 : sample adc3, adc4
# 1 : sample adc1, adc2
sw1 = Pin(15, Pin.IN, Pin.PULL_UP)
sw2 = Pin(2, Pin.IN, Pin.PULL_UP)
sw3 = Pin(19, Pin.IN, Pin.PULL_UP)
sw4 = Pin(4, Pin.IN, Pin.PULL_UP)
led1 = Pin(5, Pin.OUT)
led2 = Pin(18, Pin.OUT)
led1.off()
led2.off()
bz1 = Pin(21, Pin.OUT)
bz1.off()
#------------------------------------------------------------
gled = Pin(25, Pin.OUT)
rled = Pin(33, Pin.OUT)
speaker = Pin(26, Pin.OUT)
#------------------------------------------------------------
sample_point = 0
stop_flag = 0
total_count = 0
#------------------------------------------------------------
uart1 = UART(2, baudrate=115200, rx=16, tx=17, timeout=10)
#------------------------------------------------------------
DSCMD_NONE = 0xff
DSCMD_HELLO = 0x00
DSCMD_INIT = 0x01
DSCMD_STARTSEND = 0x10
DSCMD_STOPSEND = 0x11
DSCMD_BEEP = 0x12
DSCMD_SENDSNAPSHOT = 0x13
DSCMD_TURNOFFLIGHT = 0x14
DSCMD_SETCONTINUECHECK = 0x15
DSCMD_SETBEACONSEQUENCY = 0x16
DSCMD_BEACONSTART = 0x17
DSCMD_BEACONSTOP = 0x18
DSCMD_GETBEACONSTATE = 0x19
DSCMD_BEACONINIT = 0x1a
DSRET_NCHECK = 0x80
DSRET_CHECK = 0x81
DSRET_LAST = 0x82
DSRET_HELLO = 0x90
DSRET_INIT = 0x91
SENDFLAG_LAST = 0x82
SENDFLAG_CHECK = 0x81
SENDFLAG_NOCHECK = 0x80
SENDFLAG_STOP = 0xff
SENDFLAG_SNAPSHOT = 0x83
#------------------------------------------------------------
def receCmd():
if uart1.any() == 0: return DSCMD_NONE,0
framebyte = uart1.read(4)
if len(framebyte) != 4: return DSCMD_NONE, 0
framelist = list(framebyte)
time = int.from_bytes(framebyte[1:3], 'big')
sumnum = sum(framelist[0:3]) & 0xff ^ 0xff
if sumnum != framelist[3]: return DSCMD_NONE, 0
return framelist[0], time
#------------------------------------------------------------
def sendCmd(cmd, time):
senddim = [0x55,cmd]
senddim.extend(list(time.to_bytes(4, 'big')))
sumnum = sum(senddim)&0xff^0xff
senddim.extend([sumnum])
sendbytes = bytes(senddim)
uart1.write(sendbytes)
#------------------------------------------------------------
def procCmd(cmd, time):
global count32,sendflag,delay3s,count32,sendenableflag
global initflag,lastcount32,snapshot32,speakercount,senddelay,sendcount
if cmd == DSCMD_NONE: return
if cmd == DSCMD_HELLO:
sendCmd(DSRET_HELLO, 0x0)
return
if cmd == DSCMD_INIT:
sendCmd(DSRET_INIT, 0x0)
count32 = 0x0
delay3s = time
count3s = time
initflag = 1
lastcount32 = 0
snapshot32 = 0
sendcount = 0
sendenableflag = 1
sendflag = SENDFLAG_STOP
return
if cmd == DSCMD_STARTSEND:
sendCmd(SENDFLAG_LAST, lastcount32)
count3s = delay3s
sendflag = 0x0
sendenableflag = 0x1
senddelay = time
sendcount = 0
return
if cmd == DSCMD_STOPSEND:
sendCmd(SENDFLAG_CHECK, count32)
sendenableflag = 0
sendflag = 0
sendcount = 0
return
if cmd == DSCMD_BEEP:
speakercount = 500
return
if cmd in (DSCMD_SETBEACONSEQUENCY,
DSCMD_BEACONSTART,
DSCMD_BEACONSTOP,
DSCMD_GETBEACONSTATE,
DSCMD_TURNOFFLIGHT,
DSCMD_BEACONINIT):
return
print(cmd, time)
#------------------------------------------------------------
speakercount = 500
def speaker1ms():
global speakercount
if speakercount > 0:
speakercount -= 1
speaker.on()
else:
speaker.off()
#------------------------------------------------------------
def sendTime():
global count32,snapshot32,lastcount32,sendflag
if sendflag == SENDFLAG_STOP: return
if initflag == 0: return
timesend = count32
if sendflag == SENDFLAG_LAST:
timesend = lastcount32
elif sendflag == SENDFLAG_CHECK or sendflag == SENDFLAG_NOCHECK:
timesend = count32
elif sendflag == SENDFLAG_SNAPSHOT:
timesend = snapshot32
sendCmd(SENDFLAG_SNAPSHOT, snapshot32)
sendflag = SENDFLAG_NOCHECK
sendCmd(sendflag, timesend)
sendflag = SENDFLAG_STOP
#------------------------------------------------------------
count32 = 0 # Global 1ms counter
sendflag = SENDFLAG_STOP # send flag
sendenableflag = 0 # Enable send
senddelay = 100 # Everay send period
sendcount = 0 # count for send delay
delay3s = 3000 # No check delay
count3s = 3000 # count for no check delay
initflag = 0
lastcount32 = 0
snapshot32 = 0
#------------------------------------------------------------
#------------------------------------------------------------
def ADC4Sample(_):
global count32,sendenableflag,sendflag,senddelay,sendcount
global delay3s,count3s,initflag,lastcount32,snapshot32
global speakercount
#--------------------------------------------------------
count32 += 1
speaker1ms()
#--------------------------------------------------------
if count32 & 0x100:
led1.on()
else: led1.off()
#--------------------------------------------------------
if sendenableflag != 0:
sendcount += 1
if sendcount >= senddelay:
sendcount = 0
if sendflag == SENDFLAG_STOP:
if count32 < delay3s:
sendflag = SENDFLAG_NOCHECK
else: sendflag = SENDFLAG_CHECK
else:
sendcount += 1
if sendcount >= senddelay:
sendcount = 0
if sendflag == SENDFLAG_STOP:
sendflag = SENDFLAG_CHECK
#--------------------------------------------------------
global ad1dim,ad2dim
global sample_point
global adc1,adc2,adc3, adc4
global ad3average, ad4average, ad34point, ad3sigma, ad4sigma
global ad3checktime, ad4checktime,total_count
global ad3baseline, ad4baseline
total_count += 1
#--------------------------------------------------------
detectflag = 0
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
#----------------------------------------------------
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
detectflag = 1
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
detectflag = 1
#--------------------------------------------------------
sample_point += 1
if sample_point >= SAMPLE_NUM:
sample_point = 0
#--------------------------------------------------------
if detectflag > 0:
speakercount = 200
if count3s >= delay3s:
if sendenableflag > 0:
if detectflag > 0:
count3s = 1
lastcount32 = count32
snapshot32 = 0x0
sendflag = SENDFLAG_LAST
else:
if detectflag > 0:
snapshot32 = count32
count3s = 1
else:
if detectflag > 0:
snapshot32 = count32
count3s += 1
#------------------------------------------------------------
time0 = Timer(0)
time0.init(period=1, mode=Timer.PERIODIC, callback=ADC4Sample)
#------------------------------------------------------------
print("TEST JUDGEMENT ALL...")
showcount = 0
#------------------------------------------------------------
while True:
#--------------------------------------------------------
showcount += 1
if showcount > 100:
showcount = 0
#--------------------------------------------------------
cmd,tm = receCmd()
if cmd != DSCMD_NONE:
led2.on()
procCmd(cmd, tm)
led2.off()
sendTime()
#--------------------------------------------------------
time.sleep_ms(2)
#------------------------------------------------------------
# END OF FILE : testtime.PY
#============================================================
§04 感光条的缺陷
在前面测试感光条的检测时,有一个现象就是感光片不同位置似乎检测的灵敏度不同。表现为使用车模轮胎模拟通过时,在靠近三极管基极部分比较灵敏,而远离三极管基极部分则表现迟缓。本来,这些光电管都是串联在一起,所引起的三极管基极电流变化的作用都应该是相通的。那么问题来了,究竟什么原因造成的这种不同呢。
1、不同位置响应时间不同
对于感光条检测不均匀的问题,现在终于有了一个比较好的解释,通过测试发现实际上对于不同位置上的感光所产生的输出波形的时间常数不一样。
▲ 图4.0 使用手分别遮挡感光板的两端
在靠近三极管基极的LED被遮光,输出响应时间很快。但是在远离三极管基极的LED被遮光的时候输出相应比较慢。这对于快速通过的车模显然没有来得及进行感应。
下面是分别测试是由手分别遮挡感光板的两端所产生的信号变化。可以看到,远离三极管的基极遮挡所产生的波形变化缓慢,而靠近三极管基极对应的波形变化迅速。
▲ 图4.1 原理三极管基极对应的相应
▲ 图4.2 在靠近三极管基极被遮挡是所产生的波形
2、原因分析
为了防止外部电磁场对于检测影响,实际上在感光板的背面有一层铺设的地线层,它有效降低了外部电磁场对于三极管的输出影响。这地线层与二极管以及相关引线之间分布着寄生电容。如下图所有红色电容所示。由此,可以看到不同的二极管的阻抗变化,反映到最后的输出电压上,所对应的电路时间常数是不同的(可以将二极管看成不同阻抗的电阻)。
▲ 图4.3 寄生电容(红色部分)影响了输出信号的时间常数
远离三极管基极部分的二极管阻抗变化对应的时间常数很大,而靠近三极管基极对应的二极管阻抗变化对于输出则缓冲较小。这也就解释了前面为什么不同位置光电板所检测的“灵敏度不同”的原因。
3、如何改进?
显然,如果需要改进,则需要将原来所有二极管串联的形式,改成若干并联的形式。可以有效降低分布电容所带来的影响。
下图给出了通过对于发光LED进行串并操作,来改善相应速度的电路图。
▲ 图4.4 将原来串联在一起的二极管进行适当的并联,减少分布电容所造成的响应缓慢
虽然上述方式可以有效减少串联LED的时间常数影响,但它是将电流变化分成若干支路,这样也会降低输出信号的动态幅值范围。因此串联的支路数量不能够太多。
下面是对原来的光电板进行改造后,将原来的一条串联支路改成三条之后的情况。经过测试之后,整条感光板的灵敏度基本一致了。
▲ 图4.5 经过改造后的测试电路板
※ 测试结论 ※
经过对于第二版本的裁判系统的测试,验证了原来的硬件都符合技术要求。下面对其进行最后的软件完善,满足实际比赛的要求。
■ 相关文献链接:
- 基于ESP32智能车竞赛比赛系统硬件初步调试-5-6
- AI视觉组基于ESP32的裁判系统第一版本设计要求
- 基于ESP32的竞赛裁判系统功能调试-硬件修改建议
- 利用CH340C制作MicroPython ESP8266,ESP32的下载器-改进型
- MicroPython官方网站下载ESP32MicroPython固件
- 两款带有WiFI的MicroPython模块:ESP32,ESP8266
- 寄生电容所带来的影响
● 相关图表链接:
- 图1.1 修改后的主板
- 图1.2 修改后的目标主板
- 图1.3 由LQ焊接后的主板
- 图1.3.1 ESP32布局的建议
- 图1.4 焊接VGRTNG端口
- 图1.4.1 VGRTNG的PIN1可以不用焊接
- 图1.5 成功下载MicroPython固件的ESP32模块
- 表1-1 Switch1234端口定义
- 表1-2 板载LED1,LED2,BZ1
- 图1.6 测试LED,BEEP的结果
- 表1-3 连接目标板的LL端口定义
- 图1.7 目标板上的LED,Speaker测试560
- 图2.1 光电检测管的极性
- 图2.2 使用黑色热缩管对光电检测管周围进行遮光
- 图2.2.1 只安装1,2,3位置上的光电检测管
- 图2.2.2 测量U4的输出PIN1,PIN7的波形
- 图2.2.3 测量放大后的波形
- 图2.4 电磁扫描引起的输出电压波动
- 图2.5.1 检测光条板
- 图2.5 运放输出信号
- 图4.0 使用手分别遮挡感光板的两端
- 图4.1 原理三极管基极对应的相应
- 图4.2 在靠近三极管基极被遮挡是所产生的波形
- 图4.3 寄生电容(红色部分)影响了输出信号的时间常数
- 图4.4 将原来串联在一起的二极管进行适当的并联,减少分布电容所造成的响应缓慢
- 图4.5 经过改造后的测试电路板