在 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 以发送与视频帧速率相同的频率的温度更新。我会避免这种情况:)
我在一个项目中工作,我需要通过视频实时显示外部数据(来自带有 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 以发送与视频帧速率相同的频率的温度更新。我会避免这种情况:)