在 OpenCV 中通过视频从 ESP32 获取数据 - Python => 冻结视频

Get data from ESP32 over video in OpenCV - Python => freezing video

我在一个项目中工作,我需要通过视频实时显示外部数据(来自带有 BME280 传感器的 ESP32)。我在 Python 中使用 opencv 从我的网络摄像头获取视频。 视频工作正常,直到我将用于获取传感器数据的代码放在视频上,它变得非常慢并且每 1/2 秒冻结一次(与 ESP32 代码的延迟有关)。 我也发现超时(在 python 代码中)起着非常重要的作用...如果我将其设置为 0,视频流会很好,但数据无法正常显示。 所以我知道我的代码中的 readline() 有一个阻塞行为,如果它没有接收到该行的最后一个字符(“\n”),它就不能在“while”循环中继续。 问题是,我怎样才能重新定义我的代码来避免这种情况??

Python代码:

import serial
import cv2 as cv
import datetime

cap = cv.VideoCapture(0)
if not cap.isOpened():
    print("Camera cannot open")
    exit()
    
    
arduino = serial.Serial('COM7', baudrate=115200, timeout = 0)

while True:

    ret , frame= cap.read()
    
    if not ret:
        print("void frame")
        break
    
    #reading line from ESP32

    rawString = str(arduino.readline())
    print(rawString)
    # replace "\r" and "\n" for ""

    rawString = rawString.replace("\r\n'","")
    rawString = rawString.replace("b'","")
    
    #Add data to video (real time and sensor)
    hora = str(datetime.datetime.now())
    print(type(hora))
    font = cv.FONT_HERSHEY_SIMPLEX
    cv.putText(frame, hora,(10,50), font, 0.8,(255,0,0),2,cv.LINE_AA)
    cv.putText(frame, rawString,(10,80), font, 0.8,(255,0,0),2,cv.LINE_AA)

    #show the video
    cv.imshow("Video", frame)
    
    if cv.waitKey(1) == ord('q'):
        break

arduino.close()
cap.release()
cv.destroyAllWindows()

ESP32代码:

#include <Wire.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BME280.h>



Adafruit_BME280 bme; // I2C

unsigned long delayTime;

void setup() {
  Serial.begin(115200);
  Serial.println(F("BME280 test"));

  bool status;

  // default settings
  // (you can also pass in a Wire library object like &Wire2)
  status = bme.begin(0x77);  
  if (!status) {
    Serial.println("Could not find a valid BME280 sensor, check wiring!");
    while (1);
  }

  Serial.println("-- Default Test --");
  delayTime = 500;

}


void loop() { 
  printValues();
  delay(delayTime);
}

void printValues() {

  Serial.println(bme.readTemperature());

}

我可以立即想到三种方法来解决你的问题。

一个简单的解决方案是以非阻塞方式从ESP32读取温度。作为一个有用的技巧,你可以在创建 pyserial 端口时指定一个非常小的超时值——比如 1 毫秒。如果 ESP32 的串行缓冲区中没有数据,那么 readline() 将 return 什么都没有。当发生这种情况时,只需跳过更新变量 rawString。见 notes on readline()

arduino = serial.Serial('COM7', baudrate=115200, timeout = 0.001)
...
    serialData = arduino.readline()
    if serialData:
        rawString = str(serialData)
...

请注意,正确实现的非阻塞读取需要将 pyserial 超时参数设置为 0(如您所愿),然后读取 ESP32 从 pyserial 的缓冲区字节发送的任何内容字节使用 read()。您必须自己进行字符串处理才能检测传感器数据行。不难,自己动手吧。

一个高级解决方案是线程。 readline()是一个阻塞动作,这样的事情应该运行在一个单独的线程中,以避免阻塞你的视频处理。因此,请随意启动一个新线程并 运行 那里与 ESP32 的所有通信。要将温度传播到主线程,只需使用共享的全局变量或类似的东西(无论是 Python 中具有原子更新的最简单的数字类型)。

一个糟糕的解决方案是更新您的 ESP32 以发送与视频帧速率相同的频率的温度更新。我会避免这种情况:)