本篇文章介绍使用Python实现网口发送从站控制报文,并通过OpenCV来检测从站通信灯光电亮度实现通信成功率的闭环测试。可以根据大量的数据来验证硬件控制系统对上位机发送控制报文执行的成功率,可以作为硬件产品的老化手段。
文章目录
为什么需要这个测试上位机
目前无论基于电信号还是光信号的通信,都无法避免的有一定概率会发生“丢包‘’的问题,这是因为光或者电信号传输都依赖“媒介”,而"媒介"的实际状态我们无法把控。比如CAN总线通信,是在一对导线上传递的差分电压信号。2.4G的无线,是在空间上传递的电磁波信号。而"媒介"的状态,比如CAN通信导线上的特征阻抗或许我们认为还能控制,但是导线周围的电磁环境呢?2.4G无线传输就更不必说了,空间上的一堵墙,一块铁板,就能明显的影响无线传输。
摆在我们面前的,就是我们如何证明我们的通信,是稳定的呢?
你的想法或许是这样:我们需要将每一次通信的“发起端”发起的时间和"结束端"接收的时间都记录下来,一一对应即可。这种想法没有问题,我们可以将发起端设备和结束端设备将记录时间的Log都统一输出到一个地方做记录统计,但是这种方法成本比较高。所以我们可以针对具体的产品,去做这个测试。
我目前在研发的设备,通信的发起端设备可以通过通信的协议控制结束端设备上灯的亮灭,于是我们写一个测试程序,让发起端设备按一定次数控制终端亮灭(举个例子一万次),我们则根据接收端实际亮灭的的次数来判断通信的丢包率。而这种测试方式,需要一个能实现灯光检测的图像识别软件(判断灯亮灭的状态)。用Python3来利用笔记本上的摄像头来实现起来代码不超过150行。
实现代码详解
我首先找到一个带有摄像头的笔记本,安装PyCharm,你可以理解它为Python的IDE。为了实现光电的图像识别,我们需要导入CV2以及numpy两个Package。打开当前的工程的设置,点开Project Interpreter,点击右侧的加号,增加这两个库。

代码将摄像头捕捉的图像输出到一个窗口上,我们重写窗口有鼠标点下的事件函数,记录当前鼠标的坐标信息。
我们在捕捉到图像的时候,如果有记录的坐标信息,则在坐标周围画一个黑色圆圈,表示我们监控这个圆圈内的灯的亮灭。然后我们这个圆圈内部的RGB值的信息来判断是否亮灯,并输出记录为.CSV文件,具体代码如下:
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()
我们选择如下如所示10个智能终端来进行灯光检测,代码运行的结果如下所示:

根据最终的实际终端的亮灭效果以及协议发送情况,判断终端对控制协议的实际成功率,最终对整个系统的稳定性做出一个闭环的评估。
十六宿舍 原创作品,转载必须标注原文链接。
©2023 Yang Li. All rights reserved.
欢迎关注『十六宿舍』,大家喜欢的话,给个👍,更多关于嵌入式相关技术的内容持续更新中。
本文介绍了一种使用Python和OpenCV进行通信成功率闭环测试的方法。通过上位机发送控制报文控制从站设备的灯光变化,并利用摄像头捕获图像判断灯光状态,从而评估通信的稳定性和成功率。
1327

被折叠的 条评论
为什么被折叠?



