实际情况
目前无论基于电信号还是光信号的通信,都无法避免的有一定概率会发生“丢包‘’,这是因为光或者电信号传输都依赖“媒介”,而"媒介"的实际状态我们无法把控。比如CAN总线通信,是在一对导线上传递的差分电压信号。2.4G的无线,是在空间上传递的电磁波信号。而"媒介"的状态,比如CAN通信导线上的特征阻抗或许我们认为还能控制,但是导线周围的电磁环境呢?2.4G无线传输就更不必说了,空间上的一堵墙,一块铁板,就能明显的影响无线传输。
摆在我们面前的,就是我们如何证明我们的通信,是稳定的呢?
你的想法或许是这样:我们需要将每一次通信的“发起端”发起的时间和"结束端"接收的时间都记录下来,一一对应即可。这种想法没有问题,我们可以将发起端设备和结束端设备将记录时间的Log都统一输出到一个地方做记录统计,但是这种方法成本比较高。所以我们可以针对具体的产品,去做这个测试。
我手上的设备,通信的发起端设备可以通过通信的协议控制结束端设备上灯的亮灭,于是我们写一个测试程序,让发起端设备按一定次数控制终端亮灭(举个例子一万次),我们则根据接收端实际亮灭的的次数来判断通信的丢包率。而这种测试方式,需要一个能实现灯光检测的图像识别软件(判断灯亮灭的状态)。用Python3来利用笔记本上的摄像头来实现起来代码不超过150行。
Python3代码
找个带有摄像头的笔记本,安装PyCharm,你可以理解它为Python的IDE。为了实现光电的图像识别,我们需要导入CV2以及numpy两个Package。打开当前的工程的设置,点开Project Interpreter,点击右侧的加号,增加这两个库。
代码将摄像头捕捉的图像输出到一个窗口上,我们重写窗口有鼠标点下的事件函数,记录当前鼠标的坐标信息。
我们在捕捉到图像的时候,如果有记录的坐标信息,则在坐标周围画一个黑色圆圈,表示我们监控这个圆圈内的灯的亮灭。然后我们这个圆圈内部的RGB值的信息来判断是否亮灯,并输出记录为.CSV文件,具体代码如下。(代码写的C风格比较重,如果熟悉Python的人应该能写的更短更好看)
import cv2
import time
import numpy
from numpy import *
import datetime
import numpy as np
CirclLightNumber=12
MousClickNumber=0
MousClickTable=numpy.zeros(shape=(CirclLightNumber,2)) #第一个参数为点的
LightState=[0,0,0,0,0,0,0,0,0,0,0,0]
LightUpNumber=[0,0,0,0,0,0,0,0,0,0,0,0]
LightDownNumber=[0,0,0,0,0,0,0,0,0,0,0,0]
AverageLight=[0,0,0,0,0,0,0,0,0,0,0,0]
ALightUpNumber=0
ALightDownNumber=0
ALightState=0
ALightArrSta=[[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,0],[0,0,0],[0,0,0]]
ALightUpN=0
ALightDownN=0
WhLoopIndex=0
def on_EVENT_LBUTTONDOWN(event, x, y, flags, param):
if event == cv2.EVENT_LBUTTONDOWN:
global MousClickNumber
MousClickTable[MousClickNumber] =[y, x]
MousClickNumber=MousClickNumber+1
if MousClickNumber>(CirclLightNumber-1):#循环头
MousClickNumber=0
cap = cv2.VideoCapture(0)
cv2.namedWindow("image")
cv2.setMouseCallback("image", on_EVENT_LBUTTONDOWN)
time_creat = datetime.datetime.now().strftime('%H%M%S.csv')
fout = open(time_creat,encoding='utf8',mode='w')
content = "%s,%s,%s\n" % ("Index","State", "Time,")
fout.write(content)
while(1):
# get a frame and show
ret, frame = cap.read()
WhLoopIndex=WhLoopIndex+1
if WhLoopIndex >2:
WhLoopIndex=0
for index in range(MousClickNumber):
kin=int(MousClickTable[index][0])
lin=int(MousClickTable[index][1])
'''
xy = "%d,%d,%d" % (frame[kin, lin, 0], frame[kin,lin , 1], frame[kin, lin, 2])
cv2.putText(frame, xy, (lin, kin), cv2.FONT_HERSHEY_PLAIN,
1.0, (0, 0, 0), thickness=1)
'''
#获取采样点周围5个点的亮度值
xy = "%d|" % (index)
AverageLight[0]=(int(frame[kin, lin, 0])+int(frame[kin, lin, 1])+int(frame[kin, lin, 2]))/3
AverageLight[1]= (int(frame[(kin+1), lin, 0]) + int(frame[(kin+1), lin, 1]) + int(frame[(kin+1), lin, 2])) / 3
AverageLight[2]= (int(frame[(kin), (lin+1), 0]) + int(frame[(kin), (lin+1), 1]) + int(frame[(kin), (lin+1), 2])) / 3
AverageLight[3]= (int(frame[(kin-1), lin, 0]) + int(frame[(kin-1), lin, 1]) + int(frame[(kin-1), lin, 2])) / 3
AverageLight[4] = (int(frame[(kin), (lin-1), 0]) +int(frame[(kin), (lin-1), 1]) + int(frame[(kin), (lin-1), 2])) / 3
ALightUpNumber=0;
ALightDownNumber=0;
ALightUpN=0;
ALightDownN=0;
#5个点中 >220为灯亮 <=则为灯灭
for indexIn in range(5):
if AverageLight[indexIn] > 220:
ALightUpNumber=ALightUpNumber+1
else:
ALightDownNumber=ALightDownNumber+1
#灯亮的次数为3~5,则记录此次循环的状态为灯亮
if ALightUpNumber>2:
ALightArrSta[index][WhLoopIndex]=250
else:
ALightArrSta[index][WhLoopIndex]=150
# 针对这个LightBow,3个历史记录的点中 >220为灯亮 <=则为灯灭
for indexIn in range(3):
if ALightArrSta[index][indexIn] > 220:
ALightUpN =ALightUpN+1
else:
ALightDownN = ALightDownN + 1
# 在历史中,有两次即为灯亮
if ALightUpN>1:
ALightState=250
else:
ALightState=150
if ALightState>220 and LightState[index]==0:#检测到灯亮
#xyz = "%d,%d,%d" % (frame[kin, lin, 0], frame[kin, lin, 1], frame[kin, lin, 2])
#print(xyz)
LightState[index]=1
time_now = datetime.datetime.now().strftime('%H:%M:%S.%f')
print("%d,%d,%s" % (index,1,time_now))
content = "%d,%d,%s\n" % (index, 1, time_now)
fout.write(content)
LightUpNumber[index]=LightUpNumber[index]+1
if ALightState <220 and LightState[index] == 1:#检测到灯灭
#xyz = "%d,%d,%d" % (frame[kin, lin, 0], frame[kin, lin, 1], frame[kin, lin, 2])
#print(xyz)
LightState[index] = 0
time_now = datetime.datetime.now().strftime('%H:%M:%S.%f')
#print("%d,%d,%s" % (index,0,time_now))
content="%d,%d,%s\n" % (index, 0, time_now)
fout.write(content)
LightDownNumber[index] = LightDownNumber[index] + 1
#print(LightDownNumber[index] )
cv2.circle(frame, (lin, kin), 10, (50, 50, 50), thickness=2)
cv2.putText(frame, xy, (lin,(kin-10)), cv2.FONT_HERSHEY_PLAIN,
1.0, (0, 0, 0), thickness=2)
cv2.imshow("image", frame)
#h, w, _ = frame.shape # 返回height,width,以及通道数,不用所以省略掉
#print('行数%d,列数%d' % (h, w))
time.sleep(0.1)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
cap.release()
fout.close()
fout = open("Result"+time_creat,encoding='utf8',mode='w')
#fout.seek(15,0)
fout.write("Index,LightUpUmber,LightDownUmber\n")
for index in range(MousClickNumber):
ReContent = "%d,%d,%d\n" % ((index),LightUpNumber[index], LightDownNumber[index])
fout.write(ReContent)
fout.close()
cv2.destroyAllWindows()