diff --git a/.~lock.Messungen des Einzelteile.ods# b/.~lock.Messungen des Einzelteile.ods# new file mode 100644 index 0000000..82b0e94 --- /dev/null +++ b/.~lock.Messungen des Einzelteile.ods# @@ -0,0 +1 @@ +,clemens,cps-aio-001,01.07.2024 13:17,file:///home/clemens/.config/libreoffice/4; \ No newline at end of file diff --git a/Anleitung-Schnittstelle.pdf b/Anleitung-Schnittstelle.pdf new file mode 100644 index 0000000..ec946da Binary files /dev/null and b/Anleitung-Schnittstelle.pdf differ diff --git a/ESP32/esp32Serial/esp32Serial.ino b/ESP32/esp32Serial/esp32Serial.ino new file mode 100644 index 0000000..e79904a --- /dev/null +++ b/ESP32/esp32Serial/esp32Serial.ino @@ -0,0 +1,66 @@ +/* + * This ESP32 code is created by esp32io.com + * + * This ESP32 code is released in the public domain + * + * For more detail (instruction and wiring diagram), visit https://esp32io.com/tutorials/esp32-rs232 + */ + + +//######################## +char hexValues[] = "1B74"; + +// Länge des Hexadezimal-Arrays berechnen +int lengthHexArray = sizeof(hexValues) - 1; + +//es würde auch funktionieren, wenn man als Länge 2 einfügt, dann printed er in der Konsole nur ein N +//char asciiVal[lengthHexArray / 2 + 1] = {(char) 0x1B, (char) 0x70}; //70 ist Print - 74 ist tare +char asciiVal_read[3] = {(char) 0x1B, (char) 0x70}; //70 ist Print +char asciiVal_tare[3] = {(char) 0x1B, (char) 0x74}; //74 ist tare + +void setup() { + // start communication with baud rate 9600 + Serial.begin(9600); // Serial Monitor + Serial2.begin(9600); // RS232 + + // wait a moment to allow serial ports to initialize + delay(100); + + + // + // Beispiel-Hexadezimalwerte + + + + +} + +void loop() { + + //Serial.println(asciiVal); + + //char pcData = Serial.read(); + String pcData = Serial.readStringUntil('\n'); + + // abhängig davon, was vom PC empfangen wird, wird der entsprechende Befehl zur Waage geschickt + if(pcData == "tare"){ + Serial2.write(asciiVal_tare); + } else if(pcData == "getWeight"){ + Serial2.write(asciiVal_read); + } + pcData == ""; + + + //Serial2.write(asciiVal_read); + + // Check if there's data available on Serial + if (Serial2.available()) { + //char data = Serial2.read(); // read the received character + String data = Serial2.readStringUntil('\n'); // read the received character + data = data + "\n"; + Serial.write(data.c_str()); + } + + + +} diff --git a/Messungen des Einzelteile.ods b/Messungen des Einzelteile.ods new file mode 100644 index 0000000..f25cb09 Binary files /dev/null and b/Messungen des Einzelteile.ods differ diff --git a/SummeDerNormalverteilungen.pdf b/SummeDerNormalverteilungen.pdf new file mode 100644 index 0000000..31b98cb Binary files /dev/null and b/SummeDerNormalverteilungen.pdf differ diff --git a/readMe.txt b/readMe.txt new file mode 100644 index 0000000..fef9fa5 --- /dev/null +++ b/readMe.txt @@ -0,0 +1,92 @@ +https://pro.cps.unileoben.ac.at/index.php/login + +bei der cps-ProCould anmelden - FRITZE - CPS$123456789 +git anlegen +-> Mail 4.6 + +https://git.cps.unileoben.ac.at/ +unimail +Git/. +------------- + + + +------------- + +https://pyserial.readthedocs.io/en/latest/shortintro.html - Schnittstelle-Python-Serial + + + + +------------- +MariaDB +DBName: projectGeislinger +Tabellenname: Einzelteile +User: +BN: dbUser +PW: dbPassword + + +Einloggen als Root: sudo mariadb + +Adminaccount: +BN: admin +PW: admin + +zum Einloggen: +mysql -u [databaseUser] -p -> nach dem Enter das PW eingeben + +Erstellen einer neuen DB +create database [Datenbankname]; + +Erstellen eines neuen DBUsers: +create user '[username]'@'localhost' identified by '[Passwort]'; + +erstellen einer Tabelle: +create table [Datenbankname].[Tabellenname] (id int auto_increment not null primary key, Bezeichnung varchar(500), Gewicht FLOAT, BildPfad varchar(500)); +create table projectGeislinger.Einzelteile (id int auto_increment not null primary key, Bezeichnung varchar(500), GewichtMittelwert FLOAT, GewichtVarianz FLOAT, BildPfad varchar(500)); + +zum aktivieren des Autostarts: +sudo systemctl enable mariadb.service + +den Benutzer die Rechte auf die DB zuweisen: +GRANT ALL PRIVILEGES ON projectGeislinger.* TO "dbUser"@"localhost" identified by "dbPassword"; + +Insert: +insert into projectGeislinger.Einzelteile (Bezeichnung, GewichtMittelwert, GewichtVarianz) values ('25mm', 0.00415, 0.0000000087); + + + +id, Bezeichnung, Gewicht, imagePath + + + +------------------- +Bauteile: +Tellerfedern: +Durchmesser: +57mm - 12mal +45mm - 8mal +40mm - 10mal +31mm - 2mal +25mm - 16mal + +Messungen: +57mm: 0.0417; 0.0418; 0.0415; 0.0418; 0.0419; 0.0415; 0.0419; 0.0416; 0.0418; 0.0418; 0.0418; 0.0418 +45mm: 0.0219; 0.0220; 0.0219; 0.0219; 0.0219; 0.0219; 0.0219; 0.0219 +40mm: 0.0137; 0.0138; 0.0137; 0.0137; 0.0137; 0.0137; 0.0137; 0.0137; 0.0137; 0.0137 +31mm: 0.0070; 0.0069 +25mm: 0.0041; 0.0040; 0.0041; 0.0042; 0.0040; 0.0043; 0.0042; 0.0042; 0.0041; 0.0040; 0.0042; 0.0042; 0.0041; 0.0043; 0.0042; 0,0042 + +erledigte Schritte: +Wage mit Esp auslesen +Kommunikation zwischen dem ESP und Python am Rechner +Die Datenbank aufsetzen +Verbindung zwischen Python und DB aufsetzen +Die Bauteile "vermessen" -> ein Excelfile mit den Daten aufsetzen +-> Varianz und Mittelwert in die DB eintragen +Die Wahrscheinlichkeitsdichte einer Messung berechnen und damit den Bauteiltypen bestimmen + + + + diff --git a/readScale.py b/readScale.py new file mode 100644 index 0000000..49061f1 --- /dev/null +++ b/readScale.py @@ -0,0 +1,301 @@ +import serial +import binascii +import math +import mariadb +import copy + + +# aktuell ist es so programmiert, dass es annimmt, dass in kg gemessen wird (andere Einheiten liefern also flasche Ergebnisse) +# das was man noch dazuprogrammieren kann, ist wenn mehrere Teile auf der Waage liegen, ob dann die Typen noch immer erkannt werden + + + + +db_config = { + 'user': 'dbUser', + 'password': 'dbPassword', + 'host': 'localhost', + 'database': 'projectGeislinger', + 'port': 3306 # Standard port for MariaDB +} + +# Establishing the connection +conn = mariadb.connect(**db_config) +# Create a cursor to execute queries +cursor = conn.cursor() + +# Konfiguration der seriellen Schnittstelle +ser = serial.Serial('/dev/ttyUSB0', 9600) + +waageEingeschwungen = False + + + +def wahrscheinlichkeitsDichte(x,mue, var): + # in der Funktion wird der Wahrscheinlichkeitsdichtenwert der Variable x für eine bestimmte Normalverteilung berechnet + standardabweichung = var**0.5 + + result = 1/(standardabweichung * (2*math.pi)**0.5 ) * math.exp(-0.5 * ((x-mue)/standardabweichung)**2) + + return result + +def wahrscheinlichkeitsDichteMultipVar(waageMessung, numberObjectList, results): + # in der Funktion wird der Wahrscheinlichkeitsdichtenwert der Variable waageMessung für eine bestimmte Normalverteilung berechnet + + # Mathematische Sätze, auf welchen die Berechnung basiert: + # seien X1,..., Xn unabhängige Zufallsvariablen mit Dichten f1,..., fn, dann hat X = (X1,...,Xn) die gemeinsame Dichte f(x1,...,xn) = f1(x1) * ... + fn(xn) + # seien X1,..., Xn unabhängige Zufallsvariablen die N(mue_i, sigma_i^2) verteilt sind, dann ist X = X1+...+Xn - N(mue, sigma^2) verteilt mit mue=mue1+...+mue_n, sigma^2 = sigma_1^2+...+sigma_n^2 + + result = 0 + + # Berechnung vom Gesamtmittelwert + mueGes = 0 + for i in range(0, len(numberObjectList)): + mueGes = mueGes + numberObjectList[i] * results[i][2] + + # Berechnung von der Gesamtvarianz (var = sigma^2) + varGes = 0 + for i in range(0, len(numberObjectList)): + varGes = varGes + numberObjectList[i] * results[i][3] + + # Berechnung der Wahrscheinlichkeitsdichte + standardabweichungGes = varGes**0.5 + result = 1/(standardabweichungGes * (2*math.pi)**0.5 ) * math.exp(-0.5 * ((waageMessung-mueGes)/standardabweichungGes)**2) + + return result + + +# die Funktion kann man vermutlich rauslöschen +def numberSmallerObjects(numberBiggerObjects, messungWaage, mue1,mue2, sigma2): + for i in range(1,10000): + #damit wird geprüft, ob eh auch ein Objekt vom Typ1 auf der Waage liegt, oder nur ein Objekt vom Typ2 + # eigentlich müsste da 3*sigma drinnen stehen, um 99,7% der Fälle abzudecken, aber das ist nur bei einer gültigen Normalverteilung zutreffend (mit ausreichend Messungen) + if (messungWaage-numberBiggerObjects*(mue2+10*sigma2))/mue1 > 0: + if i > (messungWaage-numberBiggerObjects*mue2)/mue1: + return i + else: + return -1 + + +def sumMaxWeightObjects(objectType, numberObjectList, results): + sum = 0 + + # die Anzahl an Bauteilen, welche dem Objekttypen zugeordnet sind, sollen nicht mit dazugerechnet werden + for i in range(objectType+1, len(numberObjectList)): + # eigentlich müsste man nur mit 3 multiplizieren, um 99,7% der Bauteile im Bereich zu haben - das gilt aber nur für eine gültige Normalverteilung + #sum = sum + numberObjectList[i] * (results[i][2] + 3 * math.sqrt(results[i][3])) + sum = sum + numberObjectList[i] * (results[i][2]) + + return sum + + +def numberPossibleObjects(objectType, numberObjectList, results, messungWaage): + # der objectType definiert, welcher der Objekte das Anschauungsobjekt ist + # wenn nur ein Objekt darauf gelegt wird, welches nicht vom Typen des Anschauungsobjektes ist, dann wird 1 zurück gegeben + + #print(results[objectType][2]-10*math.sqrt(results[objectType][3])) + #print((messungWaage-sumMaxWeightObjects(objectType, numberObjectList, results))/results[objectType][2]) + + # wenn kein Gewicht auf der Waage liegt, dann soll -1 zurück gegeben werden. + if messungWaage <= 0: + return -1 + + #print(results[objectType][2]) + #print(((messungWaage-sumMaxWeightObjects(objectType, numberObjectList, results))/(results[objectType][2]+3*math.sqrt(results[objectType][3])))) + #print(((messungWaage-sumMaxWeightObjects(objectType, numberObjectList, results))/(results[objectType][2]-3*math.sqrt(results[objectType][3])))) + #zwischenSumme = ((messungWaage-sumMaxWeightObjects(objectType, numberObjectList, results))/(results[objectType][2]+3*math.sqrt(results[objectType][3]))) + zwischenSumme = ((messungWaage-sumMaxWeightObjects(objectType, numberObjectList, results))/(results[objectType][2])) + for i in range(1,10000): + if zwischenSumme <= 0: #in diesem Fall wird das gesamte Gewicht der Messung durch Objekte eines anderen Typen "aufgebraucht" + return 0 + if i > zwischenSumme: + return i # i entspricht beim returnen der Anzahl der möglichen Objekte + +def anzBauteileCountFunction(numberObjectList): + counter = 0 + + for i in range(0,len(numberObjectList)): + if numberObjectList[i] > 0: + counter = counter + 1 + + return counter + +""" + for i in range(1,10000): + #damit wird geprüft, ob eh auch ein Objekt vom Typ1 auf der Waage liegt, oder nur ein Objekt vom Typ2 + # eigentlich müsste da 3*sigma drinnen stehen, um 99,7% der Fälle abzudecken, aber das ist nur bei einer gültigen Normalverteilung zutreffend (mit ausreichend Messungen) + if (messungWaage-numberBiggerObjects*(mue2+10*sigma2))/mue1 > 0: + if i > (messungWaage-numberBiggerObjects*mue2)/mue1: + return i + else: + return -1 +""" + + +def main(): + + + print("connection is open: ", ser.is_open) + print("port to which it is connected: ", ser.portstr) + + + #an die Waage den Befehl senden, dass sie ausgelesen werden soll + ser.write(b'getWeight\n') + #ser.write(b'tare\n') + serialString = ser.readline().decode('utf-8').rstrip() #Auslesen des Serial-Strings/der Messung der Waage + + # wenn am Ende des Strings kg steht, dann ist die Waage eingeschwungen - das wird hiermit überprüft + lenString = len(serialString)-1 + if serialString[lenString] == "g" and serialString[lenString-1] == "k": + print("ist eingeschwungen") + waageEingeschwungen = True + else: + print("die Waage ist noch nicht eingeschwungen") + waageEingeschwungen = False + + #aus dem String werden alle Zeichen, welche nicht zur Darstellung der Zahl benötigt werden entfernt + intString = "" + for i in serialString: + if i=="-" or i=="0" or i=="." or i=="1" or i=="2" or i=="3" or i=="4" or i=="5" or i=="6" or i=="7" or i=="8" or i=="9": + intString = intString + i + print("Wert, welcher von der Waage ausgelesen wurde: " + intString + "kg") + + + # wenn die Waage nicht eingeschwungen ist, dann soll auch nichts weiter gemacht werden + if( waageEingeschwungen == True and float(intString)>0): + + + # Define the SQL query + sql_query = "SELECT * FROM projectGeislinger.Einzelteile" + # Execute the query + cursor.execute(sql_query) + # Fetch results + results = cursor.fetchall() + # Display data + print("Ausgabe der Daten der Datenbank:") + for row in results: + print(row) + + + anzBauteiltypen = len(results) + anzBauteile = anzBauteiltypen * [0] # erstellt eine Liste, in welcher die Anzahl der Bauteiltypen gespeichert ist, welche für die weiteren Schleifen verwendet wird + wahrscheinleichkeitsMatrix = [] + + # Berechnen der Anzahl an Objekten + for i in range(0, anzBauteiltypen): + #bauteilTypCounter = i+1 #zum hochzählen der Anzahlen der Bauteiltypen nach der k-Schleife + + while(True): + if i<(anzBauteiltypen-1): # wenn i die letzte Spalte der Bauteiltypenmatrix ist, dann soll der Teil übersprungen werden + for k in range(0, 10000): + anzBauteile[i+1] = k + nrObjects = numberPossibleObjects(i, anzBauteile, results, float(intString)) + if nrObjects == -1 or nrObjects == 0: + break + anzBauteile[i] = nrObjects + wahrscheinleichkeitsMatrix.append([wahrscheinlichkeitsDichteMultipVar(float(intString), anzBauteile[:], results), anzBauteile[:]]) + # da die Berechnete Anzahl an möglichen Messobjekten um 0 bis +1 um den "wahren" Wert schwankt, wird in der Folge auch die Wahrscheinlichkeit mit einer um 1 verringerten Bauteilanzahl berechnet (sollte diese größer als 1 sein) + if(anzBauteile[i] >= 1 and anzBauteileCountFunction(anzBauteile)>1): + anzBauteile = anzBauteile[:] + anzBauteile[i] = anzBauteile[i]-1 + wahrscheinleichkeitsMatrix.append([wahrscheinlichkeitsDichteMultipVar(float(intString), anzBauteile[:], results), anzBauteile[:]]) + + anzBauteile[i+1] = 0 + elif i == (anzBauteiltypen-1): + nrObjects = numberPossibleObjects(i, anzBauteile, results, float(intString)) + if nrObjects == -1 or nrObjects == 0: + break + anzBauteile[i] = nrObjects + wahrscheinleichkeitsMatrix.append([wahrscheinlichkeitsDichteMultipVar(float(intString), anzBauteile[:], results), anzBauteile[:]]) + if(anzBauteile[i] >= 1 and anzBauteileCountFunction(anzBauteile)>1): + anzBauteile = anzBauteile[:] + anzBauteile[i] = anzBauteile[i]-1 + wahrscheinleichkeitsMatrix.append([wahrscheinlichkeitsDichteMultipVar(float(intString), anzBauteile[:], results), anzBauteile[:]]) + + + if i >= (anzBauteiltypen-2): #wenn der Fall eintritt, dann wurden alle Fälle überprüft -> dadurch kann die i-Schleife in die letzte Runde gehen, kann das Programm sich ganz beenden + anzBauteile[i] = 0 + break + + + for m in range(i+2,anzBauteiltypen): + anzBauteile[m] = anzBauteile[m] + 1 + nrObjects = numberPossibleObjects(i, anzBauteile, results, float(intString)) + if nrObjects >= 1: + break + elif nrObjects < 1 and m == (anzBauteiltypen-1) : + break + else: + anzBauteile[m] = 0 + + + nrObjects = numberPossibleObjects(i, anzBauteile, results, float(intString)) + if (nrObjects == -1 or nrObjects == 0): + anzBauteile[anzBauteiltypen-1] = 0 + anzBauteile[i] = 0 + break + + + # Suchen der höchsten Wahrscheinlichkeitsdichte + countMaxPropDensity = 0 + maxPropDensity = 0 + #print(wahrscheinleichkeitsMatrix[0][0]) + for i in range(0, len(wahrscheinleichkeitsMatrix)): + if(wahrscheinleichkeitsMatrix[i][0] > maxPropDensity): + maxPropDensity = wahrscheinleichkeitsMatrix[i][0] + countMaxPropDensity = i + if wahrscheinleichkeitsMatrix[i][0] >0: + print(wahrscheinleichkeitsMatrix[i]) + print(wahrscheinleichkeitsMatrix[countMaxPropDensity][0]) + # die Bauteilkombination mit der höchsten Wahrscheinlichkeitsdichte ausgeben + for i in range(0, len(wahrscheinleichkeitsMatrix[0][1])): + print("Bauteiltyp: ", results[i][1]," Anzahl: ", wahrscheinleichkeitsMatrix[countMaxPropDensity][1][i]) + + #nrObjects = numberPossibleObjects(0, anzBauteile, results, float(intString)) + #print("number objects: ", nrObjects-1) + + """ + for i in range(3,0,-1): + numberOfSmallObjects = numberSmallerObjects(1, float(intString),results[4][2], results[3][2], math.sqrt(results[3][3])) + print("number of smaller objects: ", numberOfSmallObjects-1) + """ + + + """ + # in der Folge wird nun berechnet, welcher Normalverteilung die Messung am ehesten entspricht + dBId = 0 + maxPropDensity = 0 + for row in results: + propDens = wahrscheinlichkeitsDichte(float(intString) ,row[2], row[3]) + if propDens > maxPropDensity: + maxPropDensity = propDens + dBId = row[0] + + + #Ausgabe des Bauteiltypen, welchem die Messung am ehesten entspricht + for row in results: + if row[0] == dBId: + print(row[1]) + + #propDens = wahrscheinlichkeitsDichte(0.042 ,results[0][2], results[0][3]) + #print(propDens) + """ + + + + + + + ser.close() + print("connection is open: ", ser.is_open) + + + + + + +if __name__ == "__main__": + main() + + + diff --git a/readScale_Stand01.07.py b/readScale_Stand01.07.py new file mode 100644 index 0000000..b26afe9 --- /dev/null +++ b/readScale_Stand01.07.py @@ -0,0 +1,138 @@ +import serial +import binascii +import math +import mariadb + + +# aktuell ist es so programmiert, dass es annimmt, dass in kg gemessen wird (andere Einheiten liefern also flasche Ergebnisse) +# das was man noch dazuprogrammieren kann, ist wenn mehrere Teile auf der Waage liegen, ob dann die Typen noch immer erkannt werden + + + + +db_config = { + 'user': 'dbUser', + 'password': 'dbPassword', + 'host': 'localhost', + 'database': 'projectGeislinger', + 'port': 3306 # Standard port for MariaDB +} + +# Establishing the connection +conn = mariadb.connect(**db_config) +# Create a cursor to execute queries +cursor = conn.cursor() + +# Konfiguration der seriellen Schnittstelle +ser = serial.Serial('/dev/ttyUSB0', 9600) + +waageEingeschwungen = False + + + +def wahrscheinlichkeitsDichte(x,mue, var): + # in der Funktion wird der Wahrscheinlichkeitsdichtenwert der Variable x für eine bestimmte Normalverteilung berechnet + standardabweichung = var**0.5 + + result = 1/(standardabweichung * (2*math.pi)**0.5 ) * math.exp(-0.5 * ((x-mue)/standardabweichung)**2) + + return result + + + +def numberSmallerObjects(numberBiggerObjects, messungWaage, mue1,mue2, sigma2): + for i in range(1,10000): + #damit wird geprüft, ob eh auch ein Objekt vom Typ1 auf der Waage liegt, oder nur ein Objekt vom Typ2 + # eigentlich müsste da 3*sigma drinnen stehen, um 99,7% der Fälle abzudecken, aber das ist nur bei einer gültigen Normalverteilung zutreffend (mit ausreichend Messungen) + if (messungWaage-numberBiggerObjects*(mue2+10*sigma2))/mue1 > 0: + print((messungWaage-numberBiggerObjects*(mue2+10*sigma2))/mue1) + + if i > (messungWaage-numberBiggerObjects*mue2)/mue1: + return i + else: + return -1 + + + +def main(): + + + print("connection is open: ", ser.is_open) + print("port to which it is connected: ", ser.portstr) + + + #an die Waage den Befehl senden, dass sie ausgelesen werden soll + ser.write(b'getWeight\n') + #ser.write(b'tare\n') + serialString = ser.readline().decode('utf-8').rstrip() #Auslesen des Serial-Strings/der Messung der Waage + + # wenn am Ende des Strings kg steht, dann ist die Waage eingeschwungen - das wird hiermit überprüft + lenString = len(serialString)-1 + if serialString[lenString] == "g" and serialString[lenString-1] == "k": + print("ist eingeschwungen") + waageEingeschwungen = True + else: + print("die Waage ist noch nicht eingeschwungen") + waageEingeschwungen = False + + #aus dem String werden alle Zeichen, welche nicht zur Darstellung der Zahl benötigt werden entfernt + intString = "" + for i in serialString: + if i=="-" or i=="0" or i=="." or i=="1" or i=="2" or i=="3" or i=="4" or i=="5" or i=="6" or i=="7" or i=="8" or i=="9": + intString = intString + i + print("Wert, welcher von der Waage ausgelesen wurde: " + intString + "kg") + + + # wenn die Waage nicht eingeschwungen ist, dann soll auch nichts weiter gemacht werden + if( waageEingeschwungen == True): + + + # Define the SQL query + sql_query = "SELECT * FROM projectGeislinger.Einzelteile" + # Execute the query + cursor.execute(sql_query) + # Fetch results + results = cursor.fetchall() + # Display data + print("Ausgabe der Daten der Datenbank:") + for row in results: + print(row) + + + # in der Folge wird nun berechnet, welcher Normalverteilung die Messung am ehesten entspricht + dBId = 0 + maxPropDensity = 0 + for row in results: + propDens = wahrscheinlichkeitsDichte(float(intString) ,row[2], row[3]) + if propDens > maxPropDensity: + maxPropDensity = propDens + dBId = row[0] + + + #Ausgabe des Bauteiltypen, welchem die Messung am ehesten entspricht + for row in results: + if row[0] == dBId: + print(row[1]) + + #propDens = wahrscheinlichkeitsDichte(0.042 ,results[0][2], results[0][3]) + #print(propDens) + + + + + + + + ser.close() + print("connection is open: ", ser.is_open) + + + + + + +if __name__ == "__main__": + main() + + +