Dateien nach "/" hochladen
init
This commit is contained in:
commit
7c73e07a09
189
Arduino_Code.ino
Normal file
189
Arduino_Code.ino
Normal file
|
@ -0,0 +1,189 @@
|
||||||
|
//Code für den Arduino, später nur den Pin 11 entfernen, da der für das selbstständige Triggern ohne Messrad benutzt wird
|
||||||
|
#include <EEPROM.h>
|
||||||
|
|
||||||
|
volatile unsigned int counter = 0;
|
||||||
|
unsigned long lastSignalMillis1 = 0;
|
||||||
|
unsigned long lastSignalMillis2 = 0;
|
||||||
|
unsigned long time200 = 0;
|
||||||
|
bool signalActive1 = false;
|
||||||
|
bool signalActive2 = false;
|
||||||
|
|
||||||
|
int trigger = 10;
|
||||||
|
int vortrigger = 8;
|
||||||
|
int belichtung = 3;
|
||||||
|
|
||||||
|
bool pwmAktiv = false;
|
||||||
|
bool light_reset = false;
|
||||||
|
|
||||||
|
volatile float pwmFrequency = 15000; // PWM-Frequenz in Hz
|
||||||
|
volatile float dutyCycle = 70; // Duty Cycle in Prozent
|
||||||
|
long tast = (16000000L / pwmFrequency) * (dutyCycle / 100);
|
||||||
|
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
Serial.begin(9600);
|
||||||
|
|
||||||
|
// Falls vorhanden, werden die letzten gespeicherten Werte aus dem EEPROM gelesen
|
||||||
|
EEPROM.get(0, trigger);
|
||||||
|
EEPROM.get(4, vortrigger);
|
||||||
|
EEPROM.get(8, belichtung);
|
||||||
|
EEPROM.get(12, pwmFrequency);
|
||||||
|
EEPROM.get(16, dutyCycle);
|
||||||
|
//EEPROM.get(20, pwmAktiv);
|
||||||
|
|
||||||
|
//Falls nicht vorhanden, werden vordefinierte Werte verwendet und in den EEPROM geschrieben
|
||||||
|
if ((trigger == 0 || trigger == -1) || (vortrigger == 0 || vortrigger == -1) || (belichtung == 0 || belichtung == -1) || (pwmFrequency == 0 || pwmFrequency == -1) || (dutyCycle == 0 || dutyCycle == -1))
|
||||||
|
{
|
||||||
|
trigger = 10;
|
||||||
|
vortrigger = 8;
|
||||||
|
belichtung = 3;
|
||||||
|
pwmFrequency = 15000;
|
||||||
|
dutyCycle = 10;
|
||||||
|
pwmAktiv = false;
|
||||||
|
|
||||||
|
|
||||||
|
EEPROM.put(0, trigger);
|
||||||
|
EEPROM.put(4, vortrigger);
|
||||||
|
EEPROM.put(8, belichtung);
|
||||||
|
EEPROM.put(12, pwmFrequency);
|
||||||
|
EEPROM.put(16, dutyCycle);
|
||||||
|
//EEPROM.put(20, pwmAktiv);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
updateTast(); //Tastverhältnis für die PWM Einstellung aktuallisieren
|
||||||
|
|
||||||
|
|
||||||
|
pinMode(2, INPUT); // Eingangspin
|
||||||
|
pinMode(13, OUTPUT); // Hauptausgang zum Triggern der Kameras
|
||||||
|
pinMode(12, OUTPUT); //Zweiter Hauptausgang zum Triggern der Kameras (Aufgrund von reduzierter Belastbarkeit des Stromteilers benötigt)
|
||||||
|
pinMode(9, OUTPUT); // PWM-Ausgang für Beleuchtung
|
||||||
|
pinMode(11, OUTPUT); // PWM-Ausgang für das selbstständige Triggern
|
||||||
|
|
||||||
|
// Timer 1 Konfiguration fast PWM an Pin 9
|
||||||
|
TCCR1A = 0;
|
||||||
|
TCCR1B = 0;
|
||||||
|
TCCR1A |= (1 << COM1A1) | (1 << WGM11);
|
||||||
|
TCCR1B |= (1 << WGM13) | (1 << WGM12) | (1 << CS10);
|
||||||
|
ICR1 = (16000000L / pwmFrequency) - 1;
|
||||||
|
|
||||||
|
|
||||||
|
//Pin 2 (Eingang Messrad) erzeugt einen Interrupt um Impulse sicher zu erfassen
|
||||||
|
attachInterrupt(digitalPinToInterrupt(2), countSignal, RISING);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
|
||||||
|
|
||||||
|
int cycleTime = 4000; // Mikrosekunden 3703 = 27 Hz (nur für selbstständiges Triggern benötigt)
|
||||||
|
int onTime = cycleTime * 0.5; // 50% Duty Cycle
|
||||||
|
int offTime = cycleTime - onTime;
|
||||||
|
|
||||||
|
digitalWrite(11, HIGH);
|
||||||
|
delayMicroseconds(onTime);
|
||||||
|
digitalWrite(11, LOW);
|
||||||
|
delayMicroseconds(offTime);
|
||||||
|
|
||||||
|
|
||||||
|
if (counter >= vortrigger) { //Beleuchtung X Impulse vor dem Kameratrigger einschalten
|
||||||
|
if (!signalActive2) {
|
||||||
|
signalActive2 = true;
|
||||||
|
OCR1A = tast;
|
||||||
|
}
|
||||||
|
lastSignalMillis2 = millis();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (counter >= trigger) { //Kameras triggern
|
||||||
|
time200 = millis();
|
||||||
|
if (!signalActive1) {
|
||||||
|
signalActive1 = true;
|
||||||
|
digitalWrite(13, HIGH);
|
||||||
|
digitalWrite(12, HIGH);
|
||||||
|
}
|
||||||
|
lastSignalMillis1 = millis();
|
||||||
|
counter -= trigger;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (signalActive2 && (millis() - time200 >= belichtung) && counter < vortrigger && pwmAktiv==false) { //Beleuchtung ausschalten, wenn Belichtungszeit (beginnend ab Trigger) vorbei ist und der Counter wieder zurückgesetzt wurde
|
||||||
|
signalActive2 = false;
|
||||||
|
OCR1A = 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (signalActive1 && (millis() - lastSignalMillis1 >= 1)) {
|
||||||
|
signalActive1 = false;
|
||||||
|
digitalWrite(13, LOW);
|
||||||
|
digitalWrite(12, LOW);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pwmAktiv == true) //Beleuchtung dauerhaft an wenn das über GUI angefordert wurde. (Noch to do: Diese Abfrage aus dem loop herausziehen und den Aufruf in recieveData packen)
|
||||||
|
{
|
||||||
|
OCR1A = tast;
|
||||||
|
}
|
||||||
|
if (light_reset == true) {
|
||||||
|
OCR1A = 0;
|
||||||
|
light_reset = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (Serial.available() > 0) {
|
||||||
|
receiveData();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void countSignal() {
|
||||||
|
counter++;
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateTast() {
|
||||||
|
tast = (16000000L / pwmFrequency) * (dutyCycle / 100);
|
||||||
|
ICR1 = (16000000L / pwmFrequency) - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Funktion zum Erhalten von Daten über den RPi
|
||||||
|
void receiveData() {
|
||||||
|
String data = Serial.readStringUntil('\n');
|
||||||
|
if (data.startsWith("SET")) {
|
||||||
|
String command = data.substring(4);
|
||||||
|
if (command.startsWith("trigger")) {
|
||||||
|
trigger = command.substring(8).toInt();
|
||||||
|
EEPROM.put(0, trigger);
|
||||||
|
} else if (command.startsWith("vortrigger")) {
|
||||||
|
int tempVortrigger = command.substring(11).toInt();
|
||||||
|
vortrigger = trigger - tempVortrigger;
|
||||||
|
EEPROM.put(4, vortrigger);
|
||||||
|
} else if (command.startsWith("belichtung")) {
|
||||||
|
belichtung = command.substring(11).toInt();
|
||||||
|
EEPROM.put(8, belichtung);
|
||||||
|
} else if (command.startsWith("pwmFrequency")) {
|
||||||
|
pwmFrequency = command.substring(13).toInt();
|
||||||
|
EEPROM.put(12, pwmFrequency);
|
||||||
|
updateTast();
|
||||||
|
} else if (command.startsWith("dutyCycle")) {
|
||||||
|
int dutyCycleInt = command.substring(10).toInt();
|
||||||
|
dutyCycle = dutyCycleInt / 1.0;
|
||||||
|
EEPROM.put(16, dutyCycle);
|
||||||
|
updateTast();
|
||||||
|
} else if (command.startsWith("pwm_aktiv")) {
|
||||||
|
pwmAktiv = command.substring(10).toInt();
|
||||||
|
EEPROM.put(20, pwmAktiv);
|
||||||
|
if (pwmAktiv == 0) {
|
||||||
|
light_reset = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (data.startsWith("GET")) {
|
||||||
|
sendData();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//Funktion zum Senden von Daten an den RPi
|
||||||
|
void sendData() {
|
||||||
|
String data = "trigger:" + String(trigger) + ";";
|
||||||
|
data += "vortrigger:" + String(trigger-vortrigger) + ";";
|
||||||
|
data += "belichtung:" + String(belichtung) + ";";
|
||||||
|
data += "pwmFrequency:" + String(pwmFrequency) + ";";
|
||||||
|
data += "dutyCycle:" + String(int(dutyCycle)) + ";";
|
||||||
|
data += "pwm_aktiv:" + String(pwmAktiv) + ";";
|
||||||
|
Serial.println(data);
|
||||||
|
}
|
353
Hauptprogramm.py
Normal file
353
Hauptprogramm.py
Normal file
|
@ -0,0 +1,353 @@
|
||||||
|
#Hauptprogramm; Nimmt kontinuierlich Bilder auf wenn externer Trigger ausgelöst wird und speichert diese bei Bedarf
|
||||||
|
|
||||||
|
import cv2
|
||||||
|
import av
|
||||||
|
import time
|
||||||
|
import subprocess
|
||||||
|
import multiprocessing
|
||||||
|
from multiprocessing import shared_memory
|
||||||
|
import sys
|
||||||
|
import signal
|
||||||
|
import RPi.GPIO as GPIO
|
||||||
|
import numpy as np
|
||||||
|
import psutil
|
||||||
|
import os
|
||||||
|
|
||||||
|
#Region of Interest definieren:
|
||||||
|
roi_start = 0 #min: 0
|
||||||
|
roi_end = 800 #max: 720
|
||||||
|
|
||||||
|
roi_breite = roi_end - roi_start
|
||||||
|
|
||||||
|
|
||||||
|
#Warnungen von av deaktivieren (Ansonsten kommt dauerhaft eine Warnung über ein mögliches falsches Pixelformat, ist aber iO)
|
||||||
|
av.logging.set_level(av.logging.ERROR)
|
||||||
|
|
||||||
|
|
||||||
|
#GPIO Pin für Speicherimpuls einstellen
|
||||||
|
ausloeser = 17
|
||||||
|
GPIO.setmode(GPIO.BCM)
|
||||||
|
GPIO.setup(ausloeser, GPIO.IN, pull_up_down = GPIO.PUD_UP)
|
||||||
|
|
||||||
|
|
||||||
|
#Kameraeinstellungen; Soll später aus einer Textdatei ausgelesen werden, die über ein GUI eingestellt wird (Fast fertig)
|
||||||
|
camera_ids = [0, 2, 4, 6]
|
||||||
|
|
||||||
|
triggermodus_on = "v4l2-ctl -d %d -c exposure_dynamic_framerate=1"
|
||||||
|
triggermodus_off = "v4l2-ctl -d %d -c exposure_dynamic_framerate=0"
|
||||||
|
belichtung = "v4l2-ctl -d %d -c exposure_time_absolute=1"
|
||||||
|
auto_exposure = "v4l2-ctl -d %d -c auto_exposure=1"
|
||||||
|
brightness = "v4l2-ctl -d %d -c brightness=64"
|
||||||
|
contrast = "v4l2-ctl -d %d -c contrast=64"
|
||||||
|
|
||||||
|
|
||||||
|
#Funktion um die Kameraeinstellungen an das Terminal zu senden
|
||||||
|
def run_cmd(cmd):
|
||||||
|
try:
|
||||||
|
subprocess.run(cmd, shell=True, check=True)
|
||||||
|
except subprocess.CalledProcessError as e:
|
||||||
|
print("Fehler", e)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#Ctrl+C zum Programmabbruch definieren
|
||||||
|
def signal_handler(sig, frame):
|
||||||
|
print('Abbruch')
|
||||||
|
sys.exit(0)
|
||||||
|
#Evtl. noch Kameras schließen, Python Prozesse beenden, Bildspeicher komplett leeren, usw..)
|
||||||
|
|
||||||
|
signal.signal(signal.SIGINT, signal_handler)
|
||||||
|
|
||||||
|
|
||||||
|
#Klasse definieren, um Bilder von den Kameras aufzunehmen
|
||||||
|
class CameraModule():
|
||||||
|
def run(self, camera_id, camera_values, shm_np_block, shm_camera_id_block, shm_k_block):
|
||||||
|
|
||||||
|
shm_np_array = np.ndarray(shm_shape, dtype='uint8', buffer=shm_np_block.buf)
|
||||||
|
shm_camera_id_array = np.frombuffer(shm_camera_id_block.buf, dtype='int32')
|
||||||
|
shm_k_array = np.frombuffer(shm_k_block.buf, dtype='int32')
|
||||||
|
shm_cam_id = np.frombuffer(camera_values.buf, dtype='int32')
|
||||||
|
|
||||||
|
ring_counter = 0
|
||||||
|
|
||||||
|
#Cv2-Stream öffnen (Ohne den Cv2 Warmup werden die Kameras komischerweise nicht korrekt gestartet, hab da vieles probiert..)
|
||||||
|
cap = cv2.VideoCapture(f"/dev/video{camera_id}", cv2.CAP_V4L2)
|
||||||
|
cap.set(cv2.CAP_PROP_FOURCC, cv2.VideoWriter_fourcc('M', 'J', 'P', 'G'))
|
||||||
|
cap.set(cv2.CAP_PROP_FRAME_WIDTH, 1280)
|
||||||
|
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 800)
|
||||||
|
|
||||||
|
for _ in range(50): #50 Bilder aufnehmen um Kamera zu stabilisieren
|
||||||
|
ret, frame = cap.read()
|
||||||
|
|
||||||
|
cap.release()
|
||||||
|
cv2.destroyAllWindows()
|
||||||
|
time.sleep(5)
|
||||||
|
|
||||||
|
#av-Kameraeinstellungen
|
||||||
|
options = {
|
||||||
|
'video_size': '1280x800',
|
||||||
|
'framerate': '60',
|
||||||
|
'input_format': 'mjpeg'
|
||||||
|
}
|
||||||
|
|
||||||
|
input_stream = av.open('/dev/video{}'.format(camera_id), format='video4linux2', options=options)
|
||||||
|
time.sleep(1)
|
||||||
|
video_stream = next(s for s in input_stream.streams if s.type == 'video')
|
||||||
|
framerate = video_stream.average_rate
|
||||||
|
print(f"Kamera {camera_id} Framerate: {framerate}") #Zur Kontrolle
|
||||||
|
|
||||||
|
#50 Bilder aufnehmen; Dient dazu, die Kamera zu stabilisieren (Weißabgleich usw.)
|
||||||
|
for _ in range(50):
|
||||||
|
frame = next(input_stream.decode(video_stream))
|
||||||
|
time.sleep(0.1)
|
||||||
|
|
||||||
|
#Triggermodus der Kamera auf externen Trigger umstellen
|
||||||
|
run_cmd(triggermodus_on % camera_id)
|
||||||
|
|
||||||
|
k = 1
|
||||||
|
frame = None
|
||||||
|
ring_counter = 0
|
||||||
|
|
||||||
|
#Buffer der Kameras (~5 Bilder) füllen und Trigger synchronisieren; Wird später auch noch automatisiert durch Verbindung mit dem Arduino
|
||||||
|
print("Trigger auslösen bis Freigabe kommt")
|
||||||
|
for _ in range(50):
|
||||||
|
frame = next(input_stream.decode(video_stream))
|
||||||
|
print("Freigabe erteilt")
|
||||||
|
time.sleep(5)
|
||||||
|
print("Bereit")
|
||||||
|
|
||||||
|
#Kontinuierlich Bilder aufnehmen und in die image_queue schreiben
|
||||||
|
while True:
|
||||||
|
|
||||||
|
slot = (camera_id // 2 + ring_counter * 4) % 200
|
||||||
|
frame = next(input_stream.decode(video_stream))
|
||||||
|
numpy_frame = frame.to_ndarray(format='bgr24')[roi_start:roi_end, :]
|
||||||
|
|
||||||
|
freigabe = shm_k_array[slot]
|
||||||
|
|
||||||
|
if freigabe == 0:
|
||||||
|
shm_np_array[slot] = numpy_frame
|
||||||
|
shm_camera_id_array[slot] = camera_id
|
||||||
|
shm_k_array[slot] = k
|
||||||
|
shm_cam_id[camera_id//2] = k
|
||||||
|
|
||||||
|
k += 1
|
||||||
|
ring_counter = (ring_counter + 1)
|
||||||
|
|
||||||
|
else:
|
||||||
|
print("Speicher wird überschrieben")
|
||||||
|
print(shm_k_array[slot])
|
||||||
|
print(slot)
|
||||||
|
print(camera_id)
|
||||||
|
|
||||||
|
#Noch eine Fehlererkennung einbauen, falls Frame nicht ausgelesen werden kann?
|
||||||
|
|
||||||
|
|
||||||
|
#Kameraworker definieren
|
||||||
|
def camera_worker(camera_id, camera_values, shm_np_block, shm_camera_id_block, shm_k_block, cpu_core):
|
||||||
|
p = psutil.Process(os.getpid())
|
||||||
|
p.cpu_affinity([cpu_core]) #Jede Instanz wird auf einem seperaten CPU Kern ausgeführt
|
||||||
|
camera_module = CameraModule()
|
||||||
|
camera_module.run(camera_id, camera_values, shm_np_block, shm_camera_id_block, shm_k_block)
|
||||||
|
|
||||||
|
|
||||||
|
#Klasse definieren, um GPIO/Speicherimpuls zu überwachen
|
||||||
|
class SaveHandler():
|
||||||
|
def __init__(self, camera_values, trigger_liste):
|
||||||
|
self.camera_values = camera_values
|
||||||
|
self.trigger_liste = trigger_liste
|
||||||
|
GPIO.add_event_detect(ausloeser, GPIO.FALLING, callback=self.ausloeser_callback, bouncetime=200)
|
||||||
|
|
||||||
|
def ausloeser_callback(self, channel):
|
||||||
|
shm_cam_id = np.frombuffer(camera_values.buf, dtype='int32')
|
||||||
|
|
||||||
|
while True:
|
||||||
|
if not GPIO.input(channel):
|
||||||
|
|
||||||
|
cam_value = shm_cam_id
|
||||||
|
self.trigger_liste.append(cam_value)
|
||||||
|
print("Trigger empfangen")
|
||||||
|
time.sleep(1) #Ist nur Testweise drin, damit nicht zu oft getriggert wird durch betätigen eines Tasters
|
||||||
|
|
||||||
|
|
||||||
|
def handler_worker(camera_values, trigger_liste):
|
||||||
|
handler = SaveHandler(camera_values, trigger_liste)
|
||||||
|
handler.ausloeser_callback(ausloeser)
|
||||||
|
|
||||||
|
|
||||||
|
#Klasse definieren, um Bilder von der image_queue zu holen und an den Saver zu senden, falls gewünscht
|
||||||
|
class SaveImageProcess():
|
||||||
|
def __init__(self, shm_np_block, shm_camera_id_block, shm_k_block, trigger_liste, shm_save_frame, shm_save_k, shm_save_camera_id, shm_save_ts, shm_save_t):
|
||||||
|
self.trigger_liste = trigger_liste
|
||||||
|
self.shm_np_block = shm_np_block
|
||||||
|
self.shm_camera_id_block = shm_camera_id_block
|
||||||
|
self.shm_k_block = shm_k_block
|
||||||
|
self.shm_save_frame = shm_save_frame
|
||||||
|
self.shm_save_k = shm_save_k
|
||||||
|
self.shm_save_camera_id = shm_save_camera_id
|
||||||
|
self.shm_save_ts = shm_save_ts
|
||||||
|
self.shm_save_t = shm_save_t
|
||||||
|
|
||||||
|
|
||||||
|
def run(self, shm_np_block, shm_camera_id_block, shm_k_block, trigger_liste, shm_save_frame, shm_save_k, shm_save_camera_id, shm_save_ts, shm_save_t):
|
||||||
|
|
||||||
|
shm_np_array = np.ndarray(shm_shape, dtype='uint8', buffer=shm_np_block.buf)
|
||||||
|
shm_camera_id_array = np.frombuffer(shm_camera_id_block.buf, dtype='int32')
|
||||||
|
shm_k_array = np.frombuffer(shm_k_block.buf, dtype='int32')
|
||||||
|
|
||||||
|
shm_save_frame_array = np.ndarray(save_shape, dtype='uint8', buffer=shm_save_frame.buf)
|
||||||
|
shm_save_k_array = np.frombuffer(shm_save_k.buf, dtype='int32')
|
||||||
|
shm_save_camera_id_array = np.frombuffer(shm_save_camera_id.buf, dtype='int32')
|
||||||
|
shm_save_ts_array = np.frombuffer(shm_save_ts.buf, dtype='int32')
|
||||||
|
shm_save_t_array = np.frombuffer(shm_save_t.buf, dtype='int32')
|
||||||
|
|
||||||
|
|
||||||
|
ring_counter = 0
|
||||||
|
save_counter = 0
|
||||||
|
|
||||||
|
j = 0
|
||||||
|
l = 0
|
||||||
|
print("SaveImageProcess gestartet")
|
||||||
|
|
||||||
|
while True:
|
||||||
|
|
||||||
|
non_zero_indices = [index for index, value in enumerate(shm_k_array[:]) if value != 0]
|
||||||
|
|
||||||
|
if len(non_zero_indices) > 20:
|
||||||
|
print(non_zero_indices)
|
||||||
|
for index in non_zero_indices:
|
||||||
|
|
||||||
|
camera_id = shm_camera_id_array[index]
|
||||||
|
k = shm_k_array[index]
|
||||||
|
shm_k_array[index] = 0
|
||||||
|
|
||||||
|
trigger_test = np.array(trigger_liste)
|
||||||
|
|
||||||
|
for i in range(j, trigger_test.shape[0]):
|
||||||
|
if k >= (trigger_test[i][camera_id//2]-5+1) and k <= (trigger_test[i][camera_id//2]+5+1):
|
||||||
|
shm_save_frame_array[save_counter] = shm_np_array[index]
|
||||||
|
shm_save_camera_id_array[save_counter] = camera_id
|
||||||
|
shm_save_k_array[save_counter] = k
|
||||||
|
shm_save_ts_array[save_counter] = trigger_test[i][camera_id//2]
|
||||||
|
shm_save_t_array[save_counter] = i
|
||||||
|
|
||||||
|
save_counter = (save_counter + 1) % 200
|
||||||
|
|
||||||
|
|
||||||
|
if k > (trigger_test[i][camera_id//2]+200):
|
||||||
|
j += 1
|
||||||
|
|
||||||
|
|
||||||
|
def save_worker(shm_np_block, shm_camera_id_block, shm_k_block, trigger_liste, shm_save_frame, shm_save_k, shm_save_camera_id, shm_save_ts, shm_save_t):
|
||||||
|
|
||||||
|
save_worker = SaveImageProcess(shm_np_block, shm_camera_id_block, shm_k_block, trigger_liste, shm_save_frame, shm_save_k, shm_save_camera_id, shm_save_ts, shm_save_t)
|
||||||
|
save_worker.run(shm_np_block, shm_camera_id_block, shm_k_block, trigger_liste, shm_save_frame, shm_save_k, shm_save_camera_id, shm_save_ts, shm_save_t)
|
||||||
|
|
||||||
|
#Klasse um die Bilder zu speichern (Ist aufgeteilt, da das Speichern der Bilder deutlich der Aufnahme der Bilder hinterherhängt und ohne diesen Frames verworfen werden)
|
||||||
|
class Saver():
|
||||||
|
def __init__(self, shm_save_frame, shm_save_k, shm_save_camera_id, shm_save_ts, shm_save_t):
|
||||||
|
self.shm_save_frame = shm_save_frame
|
||||||
|
self.shm_save_k = shm_save_k
|
||||||
|
self.shm_save_camera_id = shm_save_camera_id
|
||||||
|
self.shm_save_ts = shm_save_ts
|
||||||
|
self.shm_save_t = shm_save_t
|
||||||
|
|
||||||
|
def run(self, shm_save_frame, shm_save_k, shm_save_camera_id, shm_save_ts, shm_save_t):
|
||||||
|
|
||||||
|
shm_save_frame_array = np.ndarray(save_shape, dtype='uint8', buffer=shm_save_frame.buf)
|
||||||
|
shm_save_k_array = np.frombuffer(shm_save_k.buf, dtype='int32')
|
||||||
|
shm_save_camera_id_array = np.frombuffer(shm_save_camera_id.buf, dtype='int32')
|
||||||
|
shm_save_ts_array = np.frombuffer(shm_save_ts.buf, dtype='int32')
|
||||||
|
shm_save_t_array = np.frombuffer(shm_save_t.buf, dtype='int32')
|
||||||
|
|
||||||
|
ring_counter = 0
|
||||||
|
|
||||||
|
while True:
|
||||||
|
if shm_save_k_array[ring_counter] != 0:
|
||||||
|
|
||||||
|
filename = "/mnt/ramdisk/Cam{}_T{}_TS{}_B{}.jpg".format(shm_save_camera_id_array[ring_counter], shm_save_t_array[ring_counter], shm_save_ts_array[ring_counter], shm_save_k_array[ring_counter])
|
||||||
|
cv2.imwrite(filename, shm_save_frame_array[ring_counter])
|
||||||
|
|
||||||
|
shm_save_k_array[ring_counter] = 0
|
||||||
|
ring_counter = (ring_counter + 1) % 200
|
||||||
|
|
||||||
|
|
||||||
|
def saver_worker(shm_save_frame, shm_save_k, shm_save_camera_id, shm_save_ts, shm_save_t):
|
||||||
|
|
||||||
|
saver = Saver(shm_save_frame, shm_save_k, shm_save_camera_id, shm_save_ts, shm_save_t)
|
||||||
|
saver.run(shm_save_frame, shm_save_k, shm_save_camera_id, shm_save_ts, shm_save_t)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
|
||||||
|
#Einstellungen der Shared Memory Blöcke
|
||||||
|
shm_shape = (200, roi_breite, 1280, 3)
|
||||||
|
save_shape = (200, roi_breite, 1280, 3)
|
||||||
|
cv_shape = (1, 4, 1, 1)
|
||||||
|
shm_np_block = shared_memory.SharedMemory(create=True, size=shm_shape[0]*shm_shape[1]*shm_shape[2]*shm_shape[3]*np.dtype('uint8').itemsize)
|
||||||
|
shm_camera_id_block = shared_memory.SharedMemory(create=True, size=200 * np.dtype('int32').itemsize)
|
||||||
|
shm_k_block = shared_memory.SharedMemory(create=True, size=200 * np.dtype('int32').itemsize)
|
||||||
|
|
||||||
|
|
||||||
|
shm_save_frame = shared_memory.SharedMemory(create=True, size=save_shape[0]*save_shape[1]*save_shape[2]*save_shape[3]*np.dtype('uint8').itemsize)
|
||||||
|
shm_save_k = shared_memory.SharedMemory(create=True, size=200 * np.dtype('int32').itemsize)
|
||||||
|
shm_save_camera_id = shared_memory.SharedMemory(create=True, size=200 * np.dtype('int32').itemsize)
|
||||||
|
shm_save_ts = shared_memory.SharedMemory(create=True, size=200 * np.dtype('int32').itemsize)
|
||||||
|
shm_save_t = shared_memory.SharedMemory(create=True, size=200 * np.dtype('int32').itemsize)
|
||||||
|
|
||||||
|
camera_values = shared_memory.SharedMemory(create=True, size= 4*np.dtype('int32').itemsize)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#Kameraeinstellungen vornehmen/Wird später durch GUI ersetzt
|
||||||
|
print("Kameras einstellen")
|
||||||
|
for i in [0, 2, 4, 6]:
|
||||||
|
run_cmd(triggermodus_off % i)
|
||||||
|
time.sleep(0.5)
|
||||||
|
run_cmd(auto_exposure % i)
|
||||||
|
time.sleep(0.5)
|
||||||
|
run_cmd(belichtung % i)
|
||||||
|
time.sleep(0.5)
|
||||||
|
run_cmd(brightness % i)
|
||||||
|
time.sleep(0.5)
|
||||||
|
run_cmd(contrast % i)
|
||||||
|
time.sleep(0.5)
|
||||||
|
print("Kamera %d eingestellt" % i)
|
||||||
|
|
||||||
|
|
||||||
|
#Trigger_liste als Multiprocessing-Liste erstellen
|
||||||
|
manager = multiprocessing.Manager()
|
||||||
|
trigger_liste = manager.list()
|
||||||
|
|
||||||
|
|
||||||
|
processes = []
|
||||||
|
i = 0
|
||||||
|
cpu_cores = [0, 1, 2, 3]
|
||||||
|
|
||||||
|
for camera_id in camera_ids:
|
||||||
|
process = multiprocessing.Process(target=camera_worker, args=(camera_id, camera_values, shm_np_block, shm_camera_id_block, shm_k_block, cpu_cores[i]))
|
||||||
|
i += 1
|
||||||
|
process.start()
|
||||||
|
processes.append(process)
|
||||||
|
|
||||||
|
|
||||||
|
handler = SaveHandler(camera_values, trigger_liste)
|
||||||
|
|
||||||
|
save_process = multiprocessing.Process(target=save_worker, args=(shm_np_block, shm_camera_id_block, shm_k_block, trigger_liste, shm_save_frame, shm_save_k, shm_save_camera_id, shm_save_ts, shm_save_t))
|
||||||
|
processes.append(save_process)
|
||||||
|
save_process.start()
|
||||||
|
|
||||||
|
saver2 = Saver(shm_save_frame, shm_save_k, shm_save_camera_id, shm_save_ts, shm_save_t)
|
||||||
|
saver2.run(shm_save_frame, shm_save_k, shm_save_camera_id, shm_save_ts, shm_save_t)
|
||||||
|
|
||||||
|
process.join()
|
||||||
|
save_process.join()
|
||||||
|
saver2.join()
|
||||||
|
|
||||||
|
|
162
Steuerung_Arduino.py
Normal file
162
Steuerung_Arduino.py
Normal file
|
@ -0,0 +1,162 @@
|
||||||
|
#Erstellt die grafische Oberfläche und ermöglicht die serielle Kommunikation mit dem Arduino, um die Einstellungen für die Beleuchtung und Triggerhandhabung vorzunehmen
|
||||||
|
|
||||||
|
import tkinter as tk
|
||||||
|
from tkinter import messagebox
|
||||||
|
import serial
|
||||||
|
import os
|
||||||
|
|
||||||
|
class Entry:
|
||||||
|
def __init__(self, root):
|
||||||
|
self.root = root
|
||||||
|
root.title("Steuerung Arduino")
|
||||||
|
|
||||||
|
root.protocol("WM_DELETE_WINDOW", self.on_closing)
|
||||||
|
|
||||||
|
|
||||||
|
self.settings_file = "/home/pi/Arduino/einstellungen.txt"
|
||||||
|
settings_path = os.path.dirname(self.settings_file)
|
||||||
|
if not os.path.exists(settings_path):
|
||||||
|
os.makedirs(settings_path)
|
||||||
|
|
||||||
|
self.load_settings() #Einstellungen beim Start des Programms laden
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.ser = serial.Serial("/dev/ttyUSB0", 9600, timeout = 2) #Serielle Verbindung zum Arduino aufbauen mit 2 Sekunden Timeout
|
||||||
|
except serial.SerialException as e:
|
||||||
|
messagebox.showerror("Fehler in der Kommunikation mit dem Arduino", e)
|
||||||
|
return
|
||||||
|
|
||||||
|
self.create_widgets()
|
||||||
|
|
||||||
|
root.geometry("400x500")
|
||||||
|
|
||||||
|
#Widgets für die GUI erstellen (Checkboxen, Eingabefelder, Beschriftungen)
|
||||||
|
def create_widgets(self):
|
||||||
|
self.entries = []
|
||||||
|
|
||||||
|
names = ["trigger", "vortrigger", "belichtung", "pwmFrequency", "dutyCycle"]
|
||||||
|
description = ["Impulse zum Auslösen des Kameratriggers", "Start der Beleuchtung vor dem Kameratrigger", "Belichtungsdauer", "Frequenz PWM", "Duty Cycle PWM"]
|
||||||
|
units = ["Imp", "Imp", "ms", "Hz", "%"]
|
||||||
|
extra = ["[250 Hz - 60 kHz]", "[0 - 100 %]"]
|
||||||
|
|
||||||
|
i = 0
|
||||||
|
|
||||||
|
for name in names:
|
||||||
|
frame = tk.Frame(self.root)
|
||||||
|
frame.pack(fill=tk.X, pady=5)
|
||||||
|
tk.Label(frame, text = description[i]).pack()
|
||||||
|
|
||||||
|
entry_frame = tk.Frame(frame)
|
||||||
|
entry_frame.pack(side=tk.TOP)
|
||||||
|
entry = tk.Entry(entry_frame, width = 7)
|
||||||
|
entry.pack(side=tk.LEFT)
|
||||||
|
unit = tk.Label(entry_frame, text=units[i]).pack(side=tk.LEFT)
|
||||||
|
|
||||||
|
entry.insert(0, self.settings.get(name, ''))
|
||||||
|
|
||||||
|
self.entries.append((name, entry))
|
||||||
|
|
||||||
|
i += 1
|
||||||
|
|
||||||
|
self.pwm_aktiv = tk.IntVar(value = self.settings.get("pwm_aktiv", 0))
|
||||||
|
tk.Checkbutton(self.root, text = "Beleuchtung einschalten", variable=self.pwm_aktiv).pack(pady=5)
|
||||||
|
|
||||||
|
tk.Button(self.root, text = "Einstellungen an Arduino senden", command = self.send_arduino_settings).pack(pady=5)
|
||||||
|
tk.Button(self.root, text = "Einstellungen aus Arduino laden", command = self.load_arduino_settings).pack(pady=5)
|
||||||
|
tk.Button(self.root, text='Einstellungen speichern', command=self.save_settings).pack(pady=5)
|
||||||
|
tk.Button(self.root, text='Einstellungen laden', command=self.load_settings).pack(pady=5)
|
||||||
|
|
||||||
|
#Funktion um die Einstellungen in der Textdatei zu speichern
|
||||||
|
def save_settings(self):
|
||||||
|
settings = [f"{name}:{entry.get()}\n" for name, entry in self.entries]
|
||||||
|
with open(self.settings_file, "w") as f:
|
||||||
|
f.writelines(settings)
|
||||||
|
|
||||||
|
#Funktion um Einstellungen aus Textdatei zu lesen
|
||||||
|
def load_settings(self):
|
||||||
|
if os.path.exists(self.settings_file):
|
||||||
|
with open(self.settings_file, "r") as f:
|
||||||
|
self.settings = {line.split(':')[0]: line.split(':')[1].strip() for line in f}
|
||||||
|
else:
|
||||||
|
self.settings = {}
|
||||||
|
|
||||||
|
#Einstellungen über serielle Verbindung vom Arduino laden
|
||||||
|
def load_arduino_settings(self):
|
||||||
|
if not self.ser.is_open:
|
||||||
|
messagebox.showerror("Fehler", "Keine Verbindung zum Arduino")
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.ser.write(b'GET\n')
|
||||||
|
data = self.ser.readline().decode('utf-8').strip()
|
||||||
|
|
||||||
|
except serial.SerialException as e:
|
||||||
|
messagebox.showerror("Fehler in der Kommunikation mit dem Arduino", e)
|
||||||
|
return
|
||||||
|
|
||||||
|
settings = data.split(';')
|
||||||
|
for setting, (name, entry) in zip(settings, self.entries):
|
||||||
|
try:
|
||||||
|
value = setting.split(':')[1]
|
||||||
|
except IndexError:
|
||||||
|
messagebox.showerror("Fehler", "Unerwartete Daten vom Arduino erhalten, Kommunikation überprüfen")
|
||||||
|
return
|
||||||
|
if name == "pwm_aktiv":
|
||||||
|
self.pwm_aktiv.set(int(value))
|
||||||
|
|
||||||
|
if "." in value:
|
||||||
|
float_value = float(value)
|
||||||
|
if float_value.is_integer():
|
||||||
|
value = int(float_value)
|
||||||
|
|
||||||
|
entry.delete(0, tk.END) #Feld leeren
|
||||||
|
entry.insert(0, value) #Am Anfang des Feldes wird Value eingegeben
|
||||||
|
self.settings[name] = value #Werte in settings speichern
|
||||||
|
|
||||||
|
#Einstellungen über serielle Verbindung an Arduino senden
|
||||||
|
def send_arduino_settings(self):
|
||||||
|
if not self.ser.is_open:
|
||||||
|
messagebox.showerror("Fehler", "Keine Verbindung zum Arduino")
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
for name, entry in self.entries:
|
||||||
|
value = entry.get()
|
||||||
|
if not value.isdigit() and name != "pwm_aktiv":
|
||||||
|
messagebox.showerror("Fehler", f"Ungültiger Wert für {name}")
|
||||||
|
return
|
||||||
|
|
||||||
|
if name == "pwmFrequency" and not (250 <= float(value) <= 60000):
|
||||||
|
messagebox.showerror("Fehler", f"Ungültiger Wert für PWM Frequenz. Der Wert muss zwischen 250 und 60000 liegen")
|
||||||
|
return
|
||||||
|
if name == "dutyCycle" and not (0 <= int(value) <= 100):
|
||||||
|
messagebox.showerror("Fehler", f"Ungültiger Wert für Duty Cycle. Der Wert muss zwischen 0 und 100 liegen")
|
||||||
|
return
|
||||||
|
|
||||||
|
command = f"SET {name} {value}"
|
||||||
|
self.ser.write((command + "\n").encode("utf-8"))
|
||||||
|
|
||||||
|
command = f"SET pwm_aktiv {self.pwm_aktiv.get()}"
|
||||||
|
self.ser.write((command + "\n").encode("utf-8"))
|
||||||
|
|
||||||
|
except serial.SerialException as e:
|
||||||
|
messagebox.showerror("Fehler in der Kommunikation mit dem Arduino", e)
|
||||||
|
return
|
||||||
|
|
||||||
|
def on_closing(self):
|
||||||
|
if self.ser.is_open:
|
||||||
|
self.ser.close()
|
||||||
|
self.root.destroy()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
root = tk.Tk()
|
||||||
|
app = Entry(root)
|
||||||
|
root.mainloop()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user