實際情況
目前無論基於電信號還是光信號的通信,都無法避免的有一定概率會發生“丟包‘’,這是因爲光或者電信號傳輸都依賴“媒介”,而"媒介"的實際狀態我們無法把控。比如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()