01 调制激光检测


1.裁判系统要求

测试ESP32S基本模块的功能,并验证是否可以应用在AI智能车竞赛检测激光信号中 测试了基于 ESP32 模块来检测 全国大学生智能车竞赛 中的 室内AI视觉组 的车模信号。其中包括两类信号:

  • 第一类是车模想目标靶发送的 调制信号(125Hz)
  • 第二类型号是车模运行在目标靶位前后位置检测信号;原来定义为光电检测信号。现在考虑将其改成 传统的比赛系统所使用的感应线圈。

▲ 赛道上的AprilTag和路边 的目标靶位

2.原来算法存在的问题

(1)存在的问题

测试ESP32S基本模块的功能,并验证是否可以应用在AI智能车竞赛检测激光信号中给定的Python程序检测算法存在以下问题:

  • 计算时间偏长;计算一个通道的信号大约需要40ms时间。这样两个通道就需要80ms。
  • 所占用的内存偏高。原来的计算所使用的数据类型都是浮点数。cos,sin表格对应的内存过大。

(2)拟解决方案

将原来的计算都修改为整形数据的运算,同时解决前面的计算实际上以及所需要存储空间大的问题。

02 程序优化验证实验


1.改进测试论证

(1)测试浮点数和整形数所占空间

【Ⅰ.测试内存大小方法】

Python 获得对象内存占用内存大小 sys.getsizeof 给出了测试python对象在内存中的大小方法。下面测试一下是否在MicroPython也可以使用。

  • 通过测试,发现在MicroPython中,对应的sys没有对应的 getsizeof()的函数。

Getting the size of an object in MicroPython - MicroPython Forum 给出了使用以下两个函数来获得对象所占用的内存的大小方法:

  • gc.collect()
  • gc.mem_free()
  • 测量浮点list内存大小
    结果:[1.24 for _ in range(512)] : 2096
from machine                import Pin
import usys
gc.collect()
mem1 = gc.mem_free()
a = [1.24 for _ in range(512)]
gc.collect()
mem2 = gc.mem_free()
print(mem1, mem2, mem1-mem2)
  • 测量整形list大小:
    结果:[0 for _ in range(512)] : 2096

居然,在MicroPython中浮点数与整形数所占的内存是相同的。都是4个字节。

【II.整形数最大值】

根据 MicroPython usys.maxsize 给出测定当前平台上可以表达的最大整形数:32bit。

from machine                import Pin
import usys
print('%x'%usys.maxsize)

7fffffff

因此,通过上面测试可以看到,使用整数和浮点数对于内存占用,在MicroPython中是无法改变的。

(2)测试浮点数和整形数乘加运算时间

【Ⅰ.测试代码】
from machine                import Pin
import usys
import time
gc.collect()
mem1 = gc.mem_free()
a = [0.1 * i for i in range(512)]
b = [0.2 * i for i in range(512)]
startms = time.ticks_ms()
sum = 0
for i in range(512):sum += (a[i] * b[i])
totalms = time.ticks_diff(time.ticks_ms(), startms)
print(totalms)
【Ⅱ.测试结果】
浮点数消耗时间:
运行时间:16ms
整形数消耗时间:
运行时间:12ms

可以看使用整形数进行计算,的确可以将计算机时间提高,但只是提高大约25%,并不明显。

2.提高系统时钟频率

(1)测量系统工作频率

from machine                import Pin
import usys
import time
import machine
print(machine.freq())
machine.freq(240000000)
print(machine.freq())

运行结果:

160000000
240000000

这说明模块原来的工作频率就已经有160MHz,所以提高的240MHz可以提高50%的运行速度。

(2)重新测试运行时间

【Ⅰ.测试代码】
from machine                import Pin
import usys
import time
import machine
print(machine.freq())
machine.freq(240000000)
print(machine.freq())
a = [0.1*i for i in range(512)]
b = [0.2*i for i in range(512)]
startms = time.ticks_ms()
sum = 0
for i in range(512):sum += (a[i] * b[i])
totalms = time.ticks_diff(time.ticks_ms(), startms)
print(totalms)
【Ⅱ.测试结果】

运行时间: 11ms

通过测试结果来看,时间从16ms缩短到11ms,这与系统频率的提升(160MHz 提升到240MHz)是相对应的。

frequency must be 20MHz, 40MHz, 80Mhz, 160MHz or 240MHz

3. 优化运算结构

根据前面的测试,如果能够综合结合将计算类型修改成整型,并提高系统工作频率,这样就可以大约能够将速度提高1倍左右。

通过实际测试,修改成整形运算之后,在240Mhz运行,实际计算的时间只有8ms左右。

(1)修改程序结果

在前面的计算过程,由于使用了循环,这可能会浪费时间。如果将循环去掉,直接使用MicroPython内置的sum, zip函数,可以节省程序流程。比如:

sumd = sum([aa*bb for aa,bb in zip(a,b)])

此时,测试程序执行的时间只有:4ms! 到此为止,程序执行速度提高了整整 4倍

03 优化信号检测程序


1.构建整型的加窗sin.cos系数

(1)代码

def angle1(n):return n*2*3.1415926*SAMPLE_PERIOD / 1000.0 * FREQUENCY_MODdef wval(w):if w < SAMPLE_NUM / 2:return w * 2 / SAMPLE_NUMelse: return (SAMPLE_NUM - w) * 2 / SAMPLE_NUMwdim = [wval(w) for w in range(SAMPLE_NUM)]
cosdim = [int(math.sin(angle1(a)) * wdim[a] * 0x7ff) for a in range(SAMPLE_NUM)]
sindim = [int(math.cos(angle1(a)) * wdim[a] * 0x7ff) for a in range(SAMPLE_NUM)]

(2)系数波形

▲ W数组

▲ sindim, cosdim 对应的数组

对于宽度为τ\tauτ,高度为E的等腰三角信号f(t)f\left( t \right)f(t)对应的频谱为:

Ftriangle(ω)=Eτ2Sa2(ωt4)F_{triangle} \left( \omega \right) = {{E\tau } \over 2}Sa^2 \left( {{{\omega t} \over 4}} \right)Ftriangle​(ω)=2Eτ​Sa2(4ωt​)

2.修改计算程序

(1)修改后的代码

def sample_amp(s):global cosdim,sindim,sample_pointcos_sam = 0sin_sam = 0scopy = s.copy()if sample_point > 0:scopy = scopy[sample_point:] + scopy[:sample_point]cos_sum = sum([s*w for s,w in zip(scopy,cosdim)]) / SAMPLE_NUMsin_sum = sum([s*w for s,w in zip(scopy,sindim)]) / SAMPLE_NUMreturn math.sqrt(cos_sum**2 + sin_sum**2)

(2)测试运行时间

计算出一个系数的时间:11ms。速度提高了大约 4倍

3.测试检测性能

(1)频率响应曲线

测试所使用的程序:
ESP32 程序:参见附件1
测试程序:参见附件2:

使用 DG1062 产生峰峰值1V正弦波,频率从110Hz变化到140Hz,输入的ESP32AD通道。计算检测信号的输出如下图所示:

▲ 对于不同的频率检测到的125Hz的输出数值

这个波形符合理论上三角加窗函数的频谱值。第一个过零点分别位于121Hz与129Hz。是采样时间(0.5s)对应频率的两倍。

因此,经过改动之后,使用整型数所得到的结果与浮点数是相同的。

(2)检测检测时间

这是用来测量当光照在传感器上,到程序给出相应之间的时间差。

  • 所使用的程序: 参见附件3

测试方案,是将上面的程序烧制在ESP32内部的boot.py。当检测到调制激光之后,便将GPIO15提高。外部使用ESP8266来测试对应的时间差。

测试时间差使用 ESP-12F模块转接板测试版调试说明,下载MicroPython程序。ESP8266-12F 模块。

▲ 测试响应时间

测试资源:
使用GPIO5:产生PWM(125Hz)调制激光管; – PIN12
使用GPIO4:测量时间差。–PIN11
【Ⅰ.测量代码】
from machine                import Pin,PWM
import time
pin4 = Pin(4, Pin.IN, Pin.PULL_UP)
pwm = PWM(Pin(5))
pwm.freq(125)
pwm.duty(512)
def timedelay():pwm.duty(0)time.sleep_ms(500)startms = time.ticks_ms()pwm.duty(512)while True:if pin4.value() > 0:breaktotalms = time.ticks_diff(time.ticks_ms(), startms)return totalms
while True:print(timedelay())
【Ⅱ.测量结果】

将激光器距离光电管65厘米,测试一百次的检测结果,统计结果如下:

  • 平均值: 201.2ms
  • 方差:4.76
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
'%cmd, noreturn=1)if wait > 0:time.sleep(wait)tspsendwindowkey('Thonny', 'ac', control=1, noreturn=1)return clipboard.paste()
tspsendwindowkey('Thonny', 's', alt=1)
tspsendwindowkey('Thonny', 'ac', control=1)
pastestr = clipboard.paste()
v = [float(s) for s in pastestr.split('[')[-1].split(']')[0].split(',')]
printf(v)
printf(sum(v) / len(v))
printf(std(v))

如果将激光器靠近接收光电管,此时相应时间就会降低。如果远离,检测时间就会加长。这对应信号占整个采样时间比例不同。如果信号强,那么0.5秒中只需要200毫秒数据就可以检测到的阈值满足要求。如果信号强,这个时间可以短。如果信号弱,这个时间就会加长。时间变化从100ms到500ms。

▌实验总结


经过前面讨论,可以看到使用整形数来进行存储、计算激光信号。在MicroPython工作环境中,并没有减少对于内存的需求,但是通过提前构造已经加窗(三角窗口)的cos,sin整形数组,最终提高的监测计算速度。

经过测试可以看到计算一个通道的0.5秒(500个采样数据)对应的125Hz的频谱幅值,只需要11ms左右。检测两路则只需要22ms。这就大大提高的车模检测响应时间。


■ 相关文献链接:

  • 测试ESP32S基本模块的功能,并验证是否可以应用在AI智能车竞赛检测激光信号中
  • ESP32串口转WiFi双天线ESP32-S模组
  • 第十六届全国大学智能汽车竞赛竞速比赛规则
  • 第十六届全国大学生智能车竞赛竞速组-室内视觉组补充说明
  • 傅里叶帮我看看,谁在照射我?
  • 信标组裁判系统原理与实现
  • Python 获得对象内存占用内存大小 sys.getsizeof
  • Getting the size of an object in MicroPython - MicroPython Forum
  • MicroPython usys.maxsize
  • DG1062可编程信号源

▌附件


1.ESP32测试程序

#!/usr/local/bin/python
# -*- coding: gbk -*-
#============================================================
# TEST11.PY                     -- by Dr. ZhuoQing 2021-04-11
#
# Note:
#============================================================from machine                import Pin,ADC,PWM
import time
from machine                import Timer
import math
import cmath
import machine#------------------------------------------------------------
machine.freq(240000000)#------------------------------------------------------------
pwm = PWM(Pin(2))
pwm.freq(125)
pwm.duty(512)led = Pin(18, Pin.OUT)#------------------------------------------------------------
adc1 = ADC(Pin(36))
adc2 = ADC(Pin(39))
adc3 = ADC(Pin(34))adc1.atten(ADC.ATTN_6DB)
adc2.atten(ADC.ATTN_6DB)
adc3.atten(ADC.ATTN_6DB)SAMPLE_NUM = const(500)
AVERAGE_NUM = 16
ad1dim = [0] * SAMPLE_NUM
ad2dim = [0] * SAMPLE_NUM
ad3dim = [0] * SAMPLE_NUMsample_point = 0
stop_flag = 0def ADC3Sample(_):global ad1dim,ad2dim,ad3dimglobal sample_pointglobal adc1,adc2,adc3global stop_flagad1dim[sample_point] = adc1.read()ad2dim[sample_point] = adc2.read()ad3dim[sample_point] = adc3.read()sample_point += 1if sample_point >= SAMPLE_NUM:sample_point = 0#------------------------------------------------------------
SAMPLE_PERIOD = 1time0 = Timer(0)
time0.init(period=SAMPLE_PERIOD, mode=Timer.PERIODIC, callback=ADC3Sample)#------------------------------------------------------------
FREQUENCY_MOD = 125def angle1(n):return n*2*3.1415926*SAMPLE_PERIOD / 1000.0 * FREQUENCY_MODdef wval(w):if w < SAMPLE_NUM / 2:return w * 2 / SAMPLE_NUMelse: return (SAMPLE_NUM - w) * 2 / SAMPLE_NUMwdim = [wval(w) for w in range(SAMPLE_NUM)]
cosdim = [int(math.sin(angle1(a)) * wdim[a] * 0x7ff) for a in range(SAMPLE_NUM)]
sindim = [int(math.cos(angle1(a)) * wdim[a] * 0x7ff) for a in range(SAMPLE_NUM)]#------------------------------------------------------------
def sample_amp(s):global cosdim,sindim,sample_pointscopy = s.copy()if sample_point > 0:scopy = scopy[sample_point:] + scopy[:sample_point]cos_sum = sum([s*w for s,w in zip(scopy,cosdim)]) / SAMPLE_NUMsin_sum = sum([s*w for s,w in zip(scopy,sindim)]) / SAMPLE_NUMreturn math.sqrt(cos_sum**2 + sin_sum**2)#------------------------------------------------------------time.sleep_ms(500)
startms = time.ticks_ms()
sample_amp(ad1dim)
totalms = time.ticks_diff(time.ticks_ms(), startms)
print(totalms)while True:ins = input('Input: ')if ins == 'q':breakprint((sample_amp(ad1dim), sample_amp(ad2dim), sample_amp(ad2dim)))time.sleep_ms(500)#------------------------------------------------------------
#        END OF FILE : test11.PY
#============================================================

2.测量不同频率下的检测输出

#!/usr/local/bin/python
# -*- coding: gbk -*-
#============================================================
# TEST2.PY                     -- by Dr. ZhuoQing 2021-04-11
#
# Note:
#============================================================from headm import *
from tsmodule.tsvisa        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'%cmd, noreturn=1)if wait > 0:time.sleep(wait)tspsendwindowkey('Thonny', 'ac', control=1, noreturn=1)return clipboard.paste()#------------------------------------------------------------
dg1062open()freqset = linspace(110, 140, 100)value = []for f in freqset:dg1062freq(1, f)time.sleep(1)cmdstr = thonnyshs('\r',0.1).split('(')[-1].split(',')[-3]value.append(float(cmdstr))printff(f, cmdstr)tspsave('Scan3', f=freqset, v=value)plt.plot(freqset, value)
plt.xlabel("Frequency(Hz)")
plt.ylabel("Value")
plt.grid(True)
plt.tight_layout()
plt.show()#------------------------------------------------------------
#        END OF FILE : TEST2.PY
#============================================================

3.测量响应时间测试程序

#!/usr/local/bin/python
# -*- coding: gbk -*-
#============================================================
# TEST11.PY                     -- by Dr. ZhuoQing 2021-04-11
#
# Note:
#============================================================from machine                import Pin,ADC,PWM
import time
from machine                import Timer
import math
import cmath
import machine#------------------------------------------------------------
machine.freq(240000000)#------------------------------------------------------------led1 = Pin(18, Pin.OUT)
led2 = Pin(5, Pin.OUT)
checkpin = Pin(2, Pin.IN, Pin.PULL_UP)
outpin = Pin(15, Pin.OUT)#------------------------------------------------------------
adc1 = ADC(Pin(36))
adc2 = ADC(Pin(39))
adc3 = ADC(Pin(34))adc1.atten(ADC.ATTN_6DB)
adc2.atten(ADC.ATTN_6DB)
adc3.atten(ADC.ATTN_6DB)SAMPLE_NUM = const(500)
AVERAGE_NUM = 16
ad1dim = [0] * SAMPLE_NUM
ad2dim = [0] * SAMPLE_NUM
ad3dim = [0] * SAMPLE_NUMsample_point = 0
stop_flag = 0def ADC3Sample(_):global ad1dim,ad2dim,ad3dimglobal sample_pointglobal adc1,adc2,adc3global stop_flagad1dim[sample_point] = adc1.read()ad2dim[sample_point] = adc2.read()ad3dim[sample_point] = adc3.read()sample_point += 1if sample_point >= SAMPLE_NUM:sample_point = 0#------------------------------------------------------------
SAMPLE_PERIOD = 1time0 = Timer(0)
time0.init(period=SAMPLE_PERIOD, mode=Timer.PERIODIC, callback=ADC3Sample)#------------------------------------------------------------
FREQUENCY_MOD = 125def angle1(n):return n*2*3.1415926*SAMPLE_PERIOD / 1000.0 * FREQUENCY_MODdef wval(w):if w < SAMPLE_NUM / 2:return w * 2 / SAMPLE_NUMelse: return (SAMPLE_NUM - w) * 2 / SAMPLE_NUMwdim = [wval(w) for w in range(SAMPLE_NUM)]
cosdim = [int(math.sin(angle1(a)) * wdim[a] * 0x7ff) for a in range(SAMPLE_NUM)]
sindim = [int(math.cos(angle1(a)) * wdim[a] * 0x7ff) for a in range(SAMPLE_NUM)]#------------------------------------------------------------
def sample_amp(s):global cosdim,sindim,sample_pointscopy = s.copy()if sample_point > 0:scopy = scopy[sample_point:] + scopy[:sample_point]cos_sum = sum([s*w for s,w in zip(scopy,cosdim)]) / SAMPLE_NUMsin_sum = sum([s*w for s,w in zip(scopy,sindim)]) / SAMPLE_NUMreturn math.sqrt(cos_sum**2 + sin_sum**2)#------------------------------------------------------------#time.sleep_ms(500)
#startms = time.ticks_ms()
#sample_amp(ad1dim)
#totalms = time.ticks_diff(time.ticks_ms(), startms)
#print(totalms)#------------------------------------------------------------print("Set pin2 low to return REPL..")loopcount = 0
THRESHOLD_VAL = const(50000)while True:check1 = sample_amp(ad1dim)check2 = sample_amp(ad2dim)flag = 0if check1 >= THRESHOLD_VAL:led1.on()flag = 1else: led1.off()if check2 >= THRESHOLD_VAL:led2.on()flag = 1else: led2.off()if flag == 0:outpin.off()else: outpin.on()if checkpin.value() == 0:breakprint("User stop.")#------------------------------------------------------------#while True:
#    ins = input('Input: ')#    if ins == 'q':
#        break#    print((sample_amp(ad1dim), sample_amp(ad2dim), sample_amp(ad2dim)))#    time.sleep_ms(500)#------------------------------------------------------------
#        END OF FILE : test11.PY
#============================================================

ESP32检测调制激光信号程序优化相关推荐

  1. 基于ESP32的竞赛裁判系统功能调试-激光信号调试

    简 介: 通过揣进测试了新版的基于ESP32的裁判系统对于激光信号的检测能力. 关键词: 智能车竞赛,125Hz,激光信号 §01 智能车竞赛裁判系统   对于 基于ESP32的智能车竞赛裁判系统 中 ...

  2. 测试ESP32S基本模块的功能,并验证是否可以应用在AI智能车竞赛检测激光信号中

    ▌01 ESP32测试模块 在 ESP32-S模块转接板设计与实现 给出了基于 利用CH340C制作MicroPython ESP8266,ESP32的下载器-改进型 对于ESP32进行开发的方式. ...

  3. 微信小程序如何检测接收iBeacon信号

    前话 微信小程序开发带着许多坑,最近就遇到了个需求,检测iBeacon来进行地点签到. (╯▔皿▔)╯ 微信小程序对于iBeacon的文档也写的十分精简,只简单介绍了每个接口的作用,这就导致我以为简单 ...

  4. 幅度响应怎么计算_四电平脉冲幅度调制(PAM4)信号的误码分析

    - PAM4 是一种高效利用带宽传输串行数据的方法,所需的通道带宽仅为 NRZ 所需带宽的一半. 用户需要具有即时数据访问能力的互联网络,这种不断增长的需求推动着以太网.64G光纤通道.CEI-56 ...

  5. 防止FPGA设计中综合后的信号被优化

    这不是一个新话题了,写这个也是当作自己的一个小小的笔记吧!觉得挺有用的. 一般在做前仿真(即功能仿真)时,不会考虑信号被优化的问题.最近做一个关于运算的小程序,前仿真的数据没有问题,但是实际出来的数据 ...

  6. 【读薄 CSAPP】贰 机器指令与程序优化

    [读薄 CSAPP]贰 机器指令与程序优化 文章目录 [读薄 CSAPP]贰 机器指令与程序优化 学习目标 基础知识 从 8086 到 Core i7 从 C 到机器代码 汇编入门 流程控制 条件代码 ...

  7. Epigentek染色质可及性检测试剂盒原则与程序

    艾美捷Epigentek EpiQuik™ 染色质可及性检测试剂盒是一套完整的优化试剂,旨在通过实时 PCR 对各种生物样品的核小体/转录因子定位进行染色质可及性的基因特异性分析.它是快速检测开放和封 ...

  8. 基于51单片机的火灾检测设计(仿真+程序+原理图+论文报告)

    本设计: 基于51单片机的火灾检测设计(仿真+程序+原理图+论文报告) Proteus仿真版本:proteus7.8 原理图:Altium Designer 程序编译器:keil 4 编程语言:C语言 ...

  9. Prepack详细介绍及微信小程序优化的新思路

    作者介绍:雪婧,美团点评点餐团队成员. 前言 Prepack前几个月刚出来的时候已经得到了前端界的大范围关注,而在不久之后又逐渐退出了人们的视线.此时这篇文章出来可能显得有些滞后,个人还是比较看好它未 ...

最新文章

  1. Oracle学习之段区块初步概念
  2. 《数学之美》第16章 信息指纹及其应用
  3. java sleep线程吗_JAVA的线程的sleep()方法是否占用CPU
  4. Polycarp Training
  5. 关于301跳转的多种办法
  6. 计算机软件大专证,在哪报名大专证怎么报考
  7. 成为大数据顶尖程序员,先过了这些Hadoop面试题!(附答案解析)
  8. html鼠标自动向下滑动,win10电脑中鼠标自动向下或向上滚动怎么解决
  9. 矿产资源勘查评价的新进展——GIS在矿产资源评价中的应用
  10. 撤销git reset
  11. 基于Quartz.net 的任务调度平台Weiz.TaskManager
  12. vrchat新手教程_VRChat入门指南| 最新电脑资讯
  13. 2022大学生免费(24元)申请个人软著专利(微信小程序)
  14. “天下文章一大抄”的时代已经过去
  15. struct timeval用法与时间溢出问题
  16. 完善跨境金融区块链服务平台,支持区域开放创新和特殊区域建设
  17. 小节点也能引爆活动!2021四月活动指导方案
  18. 十分钟手把手教你学会用HTML制作一个静态学术简历
  19. w10打游戏老是弹出计算机,Win10玩游戏频繁弹回桌面的怎么办?
  20. 7-1、Android 运行权限

热门文章

  1. web服务认证的实现
  2. 基于VMware vSphere 5.0的服务器虚拟化实践(9)
  3. 云计算时代的虚拟化安全
  4. [转]添加mysql索引的3条原则
  5. vault-使用kubernetes作为认证后端
  6. 商显行业高速发展,如何开启全新商务会议时代
  7. 为什么要选择Apache Pulsar(一)
  8. NB-IOT来了,物联网时代才真正来临
  9. js复制input 框中的值
  10. 新浪项目-------小知识点答疑解惑