After successfully opening and closing a ZED-X camera with the ZED SDK in Python (using a QThread from qt6), all subsequent attempts to open the camera (in new threads) fail. The SDK returns a camera open error, and dmesg shows ‘probe failed with error -1’ and ‘serializer initialization failed’ for the ZED-X sensor (MAX96712).
For each thread, I open the camera, take a picture and close the camera, but when I repeat the program break.
Even after deleting the camera object, doing explicit garbage collection, and waiting several seconds (busy-wait), the device cannot be re-initialized without rebooting the system.
But if I do it in the main thread (sequentially in a loop, using a for), the camera opens and closes successfully every time.
This test was created by ChatGPT but works to proof my point.
import sys
import time
import traceback
import pyzed.sl as sl
import gc
from PyQt6.QtCore import QThread, pyqtSignal, QObject, QCoreApplication
# --- CONFIG BLOCK ---
CONFIG = {
"max_cycles": 10, # How many times to open/close
"sleep_between": 2, # Seconds between close and next open
"resolution": sl.RESOLUTION.HD1080,
"fps": 15,
"depth_mode": sl.DEPTH_MODE.NEURAL,
"timeout_on_open": 8, # Not used - for future
"verbose": True
}
class CameraThread(QThread):
finished_signal = pyqtSignal(int, str) # ciclo, resultado
def __init__(self, cfg, cycle_id):
super().__init__()
self.cfg = cfg
self.cycle_id = cycle_id
def run(self):
reslog = ""
cam = None
try:
# --- OPEN ---
if self.cfg["verbose"]:
print(f"[Ciclo {self.cycle_id}] Opening ZED camera (QThread)...")
cam = sl.Camera()
init_params = sl.InitParameters(
camera_resolution=self.cfg["resolution"],
camera_fps=self.cfg["fps"],
depth_mode=self.cfg["depth_mode"],
coordinate_units=sl.UNIT.METER,
coordinate_system=sl.COORDINATE_SYSTEM.RIGHT_HANDED_Y_UP,
sdk_verbose=0
)
err = cam.open(init_params)
cam.enable_positional_tracking()
if err != sl.ERROR_CODE.SUCCESS:
print(f"[Ciclo {self.cycle_id}] ❌ Open error: {err}")
reslog = f"OPEN FAILED ({err})"
return
print(f"[Ciclo {self.cycle_id}] ✅ Camera opened!")
# --- GRAB ---
mat = sl.Mat()
status = cam.grab()
if status == sl.ERROR_CODE.SUCCESS:
cam.retrieve_image(mat, sl.VIEW.LEFT)
print(f"[Ciclo {self.cycle_id}] Image grab OK")
reslog = "SUCCESS"
else:
print(f"[Ciclo {self.cycle_id}] Grab failed: {status}")
reslog = f"GRAB FAILED ({status})"
# --- CLOSE ---
try:
cam.disable_positional_tracking()
except Exception:
print(f"[Ciclo {self.cycle_id}] disable_positional_tracking() failed (maybe not enabled)")
try:
cam.close()
print(f"[Ciclo {self.cycle_id}] Camera closed.")
except Exception as e:
print(f"[Ciclo {self.cycle_id}] Camera close error: {e}")
del cam
cam = None
gc.collect()
print(f"[Ciclo {self.cycle_id}] GC done.")
except Exception as e:
print(f"[Ciclo {self.cycle_id}] EXCEPTION: {e}")
print(traceback.format_exc())
reslog = f"EXCEPTION {e}"
finally:
# Emite señal de terminado para el ciclo
self.finished_signal.emit(self.cycle_id, reslog)
def run_camera_cycles_qt(config):
from PyQt6.QtWidgets import QApplication
app = QApplication(sys.argv)
logs = []
def on_cycle_finished(cycle_id, result):
print(f"=== Ciclo {cycle_id} terminado: {result} ===")
logs.append(f"Cycle {cycle_id}: {result}")
# Lanza el próximo ciclo o sale si ya son todos
run_next_cycle(cycle_id + 1)
def run_next_cycle(cycle):
if cycle > config["max_cycles"]:
print("==== TODOS LOS CICLOS HECHOS ====")
for log in logs:
print(log)
QCoreApplication.quit()
return
print(f"\n--- Lanzando ciclo {cycle} ---")
thread = CameraThread(config, cycle)
# Necesario: guardar referencia hasta terminar, sino el thread es recolectado antes de tiempo
threads.append(thread)
thread.finished_signal.connect(on_cycle_finished)
thread.start()
threads = []
run_next_cycle(1)
app.exec()
if __name__ == "__main__":
run_camera_cycles_qt(CONFIG)