Compare commits
92 Commits
45d4dc1dde
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 5bc9206bc0 | |||
| e010d1de02 | |||
| 5a08029bad | |||
| 3e410de064 | |||
| dcc9ef6801 | |||
| ee7194d439 | |||
| 1bc44a8dc8 | |||
| aca293eb6a | |||
| 88ba988c1e | |||
| 705dcaa9a9 | |||
| d4533ea54c | |||
| 2563626def | |||
| bff7daff82 | |||
| d1f32f1719 | |||
| 2afb5fa266 | |||
| 75a1f05dc8 | |||
| 7a077b4879 | |||
| 3fe31f6bf0 | |||
| 4df758ccf1 | |||
| b12a416110 | |||
| 897bb59e01 | |||
| 5685eebe2c | |||
| f2ed9c9724 | |||
| db17fbc1d9 | |||
| 451cd9bc68 | |||
| f8d788d92e | |||
| b1ceb7c913 | |||
| ac2180b041 | |||
| 729bf18aec | |||
| 6013acc226 | |||
| 945e088667 | |||
| 8269b5112f | |||
| 582bdec6c5 | |||
| 74c7746142 | |||
| 1a1f228b69 | |||
| a099481b3e | |||
| ffce232d47 | |||
| 87d012086b | |||
| 7456e7f02a | |||
| 9dbe3e1f5d | |||
| c8d8118d98 | |||
| 4b093e86de | |||
| 083767f626 | |||
| b081c33748 | |||
| eae3d4bae1 | |||
| 5531952667 | |||
| 48d347fb06 | |||
| be2a4a8c24 | |||
| 8efc607e03 | |||
| 6e717027c6 | |||
| 8686a8ef19 | |||
| deb7f54bfb | |||
| 678357b838 | |||
| bcb45f3174 | |||
| b31b5cd6c4 | |||
| 4a2692c52c | |||
| 145cb418ab | |||
| 4aea4432c3 | |||
| 33fff0baf8 | |||
| 31a0393c16 | |||
| d53b4d3080 | |||
| eeedb5505a | |||
| 1117fa90a6 | |||
| 04760db19e | |||
| 250d82d470 | |||
| 08ca590e92 | |||
| 960ed0ba67 | |||
| bfe8d4c2a7 | |||
| a7955bc775 | |||
| 0c4926f0d5 | |||
| e0d1fc976a | |||
| 6e015d30a1 | |||
| a92f04cfde | |||
| 5cfb0c7847 | |||
| 31d7dc0396 | |||
| d716fe0a6f | |||
| 716563f3e0 | |||
| 1a02111482 | |||
| f6574b75c5 | |||
| 7913195a38 | |||
| 8b91c9b4fa | |||
| 5868cd6b15 | |||
| dacd6b6329 | |||
| 294747cadb | |||
| 1e31b0f3fb | |||
| f8923844bb | |||
| 58044aac0b | |||
| 4969ad3b14 | |||
| 40bd342181 | |||
| 376aaa040a | |||
| 622ac95912 | |||
| 12a9070e51 |
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
.venv/
|
||||||
|
*.csv
|
||||||
91
Beispiel Kennlinien/01_sinus.txt
Normal file
91
Beispiel Kennlinien/01_sinus.txt
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
0;10.0
|
||||||
|
1;10.7
|
||||||
|
2;11.4
|
||||||
|
3;12.0
|
||||||
|
4;12.6
|
||||||
|
5;13.2
|
||||||
|
6;13.7
|
||||||
|
7;14.1
|
||||||
|
8;14.5
|
||||||
|
9;14.8
|
||||||
|
10;14.9
|
||||||
|
11;15.0
|
||||||
|
12;15.0
|
||||||
|
13;14.9
|
||||||
|
14;14.6
|
||||||
|
15;14.3
|
||||||
|
16;13.9
|
||||||
|
17;13.5
|
||||||
|
18;12.9
|
||||||
|
19;12.3
|
||||||
|
20;11.7
|
||||||
|
21;11.0
|
||||||
|
22;10.3
|
||||||
|
23;9.7
|
||||||
|
24;9.0
|
||||||
|
25;8.3
|
||||||
|
26;7.7
|
||||||
|
27;7.1
|
||||||
|
28;6.5
|
||||||
|
29;6.1
|
||||||
|
30;5.7
|
||||||
|
31;5.4
|
||||||
|
32;5.1
|
||||||
|
33;5.0
|
||||||
|
34;5.0
|
||||||
|
35;5.1
|
||||||
|
36;5.2
|
||||||
|
37;5.5
|
||||||
|
38;5.9
|
||||||
|
39;6.3
|
||||||
|
40;6.8
|
||||||
|
41;7.4
|
||||||
|
42;8.0
|
||||||
|
43;8.6
|
||||||
|
44;9.3
|
||||||
|
45;10.0
|
||||||
|
46;10.7
|
||||||
|
47;11.4
|
||||||
|
48;12.0
|
||||||
|
49;12.6
|
||||||
|
50;13.2
|
||||||
|
51;13.7
|
||||||
|
52;14.1
|
||||||
|
53;14.5
|
||||||
|
54;14.8
|
||||||
|
55;14.9
|
||||||
|
56;15.0
|
||||||
|
57;15.0
|
||||||
|
58;14.9
|
||||||
|
59;14.6
|
||||||
|
60;14.3
|
||||||
|
61;13.9
|
||||||
|
62;13.5
|
||||||
|
63;12.9
|
||||||
|
64;12.3
|
||||||
|
65;11.7
|
||||||
|
66;11.0
|
||||||
|
67;10.3
|
||||||
|
68;9.7
|
||||||
|
69;9.0
|
||||||
|
70;8.3
|
||||||
|
71;7.7
|
||||||
|
72;7.1
|
||||||
|
73;6.5
|
||||||
|
74;6.1
|
||||||
|
75;5.7
|
||||||
|
76;5.4
|
||||||
|
77;5.1
|
||||||
|
78;5.0
|
||||||
|
79;5.0
|
||||||
|
80;5.1
|
||||||
|
81;5.2
|
||||||
|
82;5.5
|
||||||
|
83;5.9
|
||||||
|
84;6.3
|
||||||
|
85;6.8
|
||||||
|
86;7.4
|
||||||
|
87;8.0
|
||||||
|
88;8.6
|
||||||
|
89;9.3
|
||||||
|
90;10.0
|
||||||
30
Beispiel Kennlinien/02_sprungantworten.txt
Normal file
30
Beispiel Kennlinien/02_sprungantworten.txt
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
0;0
|
||||||
|
4.9;0
|
||||||
|
5;5
|
||||||
|
9.9;5
|
||||||
|
10;7
|
||||||
|
14.9;7
|
||||||
|
15;10
|
||||||
|
19.9;10
|
||||||
|
20;12
|
||||||
|
24.9;12
|
||||||
|
25;15
|
||||||
|
29.9;15
|
||||||
|
30;17
|
||||||
|
34.9;17
|
||||||
|
35;15
|
||||||
|
39.9;15
|
||||||
|
40;12
|
||||||
|
44.9;12
|
||||||
|
45;10
|
||||||
|
49.9;10
|
||||||
|
50;7
|
||||||
|
54.9;7
|
||||||
|
55;5
|
||||||
|
59.9;5
|
||||||
|
60;0
|
||||||
|
69.9;0
|
||||||
|
70;15
|
||||||
|
79.9;15
|
||||||
|
80;0
|
||||||
|
90;0
|
||||||
@@ -1 +1,5 @@
|
|||||||
# GUI zur T2000
|
# GUI zur T2000
|
||||||
|
## Installation unter Linux
|
||||||
|
~~~
|
||||||
|
curl -s https://gitea.msb24.duckdns.org/musabe24/t2000_gui/raw/branch/main/installer.sh | bash
|
||||||
|
~~~
|
||||||
10
conf.txt
Normal file
10
conf.txt
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
227C-024-15{
|
||||||
|
Kp:0.11
|
||||||
|
Ki:0.09
|
||||||
|
Kd:0.00
|
||||||
|
}
|
||||||
|
328CS-024-10{
|
||||||
|
Kp:0.3
|
||||||
|
Ki:1.0
|
||||||
|
Kd:0.00
|
||||||
|
}
|
||||||
7
installer.sh
Normal file
7
installer.sh
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
sudo apt-get install python3-tk -y
|
||||||
|
cd ~
|
||||||
|
git clone https://gitea.msb24.duckdns.org/musabe24/t2000_gui.git
|
||||||
|
cd t2000_gui
|
||||||
|
python3 -m venv .venv
|
||||||
|
.venv/bin/pip install -r requirements.txt
|
||||||
572
main.py
572
main.py
@@ -1,87 +1,531 @@
|
|||||||
import tkinter as tk
|
import tkinter as tk
|
||||||
from tkinter import messagebox
|
from tkinter import ttk, messagebox, PhotoImage, simpledialog
|
||||||
|
import customtkinter
|
||||||
|
customtkinter.set_appearance_mode("light")
|
||||||
|
customtkinter.set_default_color_theme("blue")
|
||||||
|
import serial
|
||||||
|
import serial.tools.list_ports
|
||||||
import matplotlib.pyplot as plt
|
import matplotlib.pyplot as plt
|
||||||
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
|
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
|
||||||
from matplotlib.figure import Figure
|
import threading
|
||||||
|
import time
|
||||||
|
import numpy as np
|
||||||
|
import csv
|
||||||
|
import os
|
||||||
|
from PIL import Image, ImageTk
|
||||||
|
|
||||||
class T2000ErdemGUI:
|
class ArduinoGUI:
|
||||||
def __init__(self, root):
|
def __init__(self, root):
|
||||||
|
self.my_font = customtkinter.CTkFont(family="Calibri", size=15)
|
||||||
self.root = root
|
self.root = root
|
||||||
self.root.title("T2000 Erdem")
|
self.root.title("PRG 342 GUI")
|
||||||
self.root.geometry("1024x600")
|
self.root.attributes('-fullscreen', True) # Setzt das Fenster in den Vollbildmodus
|
||||||
|
|
||||||
# P- und I-Anteil Input Felder
|
self.ser = None
|
||||||
tk.Label(self.root, text="P-Anteil:").place(x=10, y=10)
|
|
||||||
self.p_value = tk.DoubleVar()
|
|
||||||
tk.Entry(self.root, textvariable=self.p_value).place(x=100, y=10)
|
|
||||||
|
|
||||||
tk.Label(self.root, text="I-Anteil:").place(x=10, y=50)
|
self.arduino = None
|
||||||
self.i_value = tk.DoubleVar()
|
self.ports = list(serial.tools.list_ports.comports())
|
||||||
tk.Entry(self.root, textvariable=self.i_value).place(x=100, y=50)
|
self.running = True
|
||||||
|
self.current_point = None
|
||||||
|
self.setpoints = []
|
||||||
|
|
||||||
# Eingabefelder für Koordinaten
|
# Flags for user actions
|
||||||
tk.Label(self.root, text="Koordinaten der Drehmoment-Drehwinkel-Kennlinie:").place(x=10, y=90)
|
self.setpoint_flag = False
|
||||||
self.coordinates_text = tk.Text(self.root, height=5, width=20)
|
self.multi_setpoints_flag = False
|
||||||
self.coordinates_text.place(x=20, y=110)
|
self.tare_flag = False
|
||||||
|
self.demag_flag = False
|
||||||
|
self.input_source_flag = False
|
||||||
|
self.pid_flag = False # Flag for sending PID parameters
|
||||||
|
|
||||||
# Eingabefeld für direkte Drehmomentvorgabe
|
self.pid_params = {}
|
||||||
tk.Label(self.root, text="Soll-Drehmoment in Nm:").place(x=10, y=220)
|
self.selected_pid = None
|
||||||
self.torque_value = tk.DoubleVar()
|
|
||||||
tk.Entry(self.root, textvariable=self.torque_value).place(x=200, y=220)
|
|
||||||
|
|
||||||
# Anzeige für aktuellen Drehwinkel und Drehmoment
|
self.create_widgets()
|
||||||
tk.Label(self.root, text="Aktueller Drehwinkel:").place(x=10, y=260)
|
|
||||||
self.current_angle = tk.Label(self.root, text="0" + "°")
|
|
||||||
self.current_angle.place(x=200, y=260)
|
|
||||||
|
|
||||||
tk.Label(self.root, text="Aktuelles Drehmoment:").place(x=10, y=300)
|
self.load_configurations()
|
||||||
self.current_torque = tk.Label(self.root, text="0" + " Nm")
|
|
||||||
self.current_torque.place(x=200, y=300)
|
|
||||||
|
|
||||||
# Button zum Schließen der Anwendung
|
# Start the communication thread
|
||||||
self.close_button = tk.Button(self.root, text="Beenden", command=self.close_application)
|
self.communication_thread = threading.Thread(target=self.communication_loop)
|
||||||
self.close_button.place(x=300, y=560)
|
self.communication_thread.start()
|
||||||
|
|
||||||
# Button zur Erstellung des Diagramms
|
self.logging = False
|
||||||
self.plot_button = tk.Button(self.root, text="Eingaben übernehmen", command=self.create_diagram)
|
self.start_time = None
|
||||||
self.plot_button.place(x=10, y=560)
|
self.log_file = None
|
||||||
|
self.csv_writer = None
|
||||||
|
|
||||||
# Diagramm
|
def toggle_datalogger(self):
|
||||||
self.figure = Figure(figsize=(6,6), dpi=100)
|
if not self.logging:
|
||||||
self.plot = self.figure.add_subplot(111)
|
self.start_logging()
|
||||||
self.plot.set_xlim(0, 90)
|
else:
|
||||||
self.plot.set_ylim(ymin=0)
|
self.stop_logging()
|
||||||
self.plot.set_xlabel('Drehwinkel in Grad')
|
|
||||||
self.plot.set_ylabel('Drehmoment in Nm')
|
def start_logging(self):
|
||||||
|
self.logging = True
|
||||||
|
self.start_time = time.time()
|
||||||
|
self.log_file = open("Data.csv", mode='w', newline='', encoding='utf-8')
|
||||||
|
self.csv_writer = csv.writer(self.log_file, delimiter='\t')
|
||||||
|
|
||||||
|
# PID parameters under each other
|
||||||
|
self.csv_writer.writerow(["Kp", f"{self.pid_params[self.selected_pid]['Kp']:.3f}".replace('.', ',')])
|
||||||
|
self.csv_writer.writerow(["Ki", f"{self.pid_params[self.selected_pid]['Ki']:.3f}".replace('.', ',')])
|
||||||
|
self.csv_writer.writerow(["Kd", f"{self.pid_params[self.selected_pid]['Kd']:.3f}".replace('.', ',')])
|
||||||
|
|
||||||
|
self.csv_writer.writerow(["Zeit in s", "Winkel in Grad", "Sollwert in Nm", "Istwert in Nm", "Erregerspannung in V"])
|
||||||
|
messagebox.showinfo("Info", "Datalogging gestartet")
|
||||||
|
|
||||||
|
|
||||||
|
def stop_logging(self):
|
||||||
|
self.logging = False
|
||||||
|
if self.log_file:
|
||||||
|
self.log_file.close()
|
||||||
|
messagebox.showinfo("Info", "Datalogging gestoppt")
|
||||||
|
|
||||||
|
def create_widgets(self):
|
||||||
|
|
||||||
|
# Laden und Skalieren des Logos
|
||||||
|
original_image = Image.open("src/logo1.png")
|
||||||
|
resized_image = original_image.resize((500, 121), Image.LANCZOS) # Passen Sie die Größe (200, 100) nach Bedarf an
|
||||||
|
self.logo_image = ImageTk.PhotoImage(resized_image)
|
||||||
|
# Logo anstelle des Platzhalters
|
||||||
|
self.logo_label = customtkinter.CTkLabel(self.root, image=self.logo_image, text="")
|
||||||
|
self.logo_label.image = self.logo_image # Referenz behalten, um Garbage Collection zu verhindern
|
||||||
|
self.logo_label.grid(row=0, column=0, columnspan=3, padx=(10, 5), pady=(10, 0), sticky="w")
|
||||||
|
|
||||||
|
# Placeholer
|
||||||
|
self.placeholer = customtkinter.CTkLabel(self.root, text="")
|
||||||
|
self.placeholer.grid(row=1, column=0, padx=(10, 5), pady=(10, 0), sticky="w")
|
||||||
|
|
||||||
|
# Labels and ComboBox for Stellantriebstyp
|
||||||
|
self.stellantriebstyp_label = customtkinter.CTkLabel(self.root, text="Stellantriebstyp:", font=self.my_font)
|
||||||
|
self.stellantriebstyp_label.grid(row=3, column=0, padx=(10, 5), pady=(10, 0), sticky="w")
|
||||||
|
|
||||||
|
self.config_combobox_value = customtkinter.StringVar()
|
||||||
|
self.config_combobox = customtkinter.CTkComboBox(self.root, variable=self.config_combobox_value, state="readonly", font=self.my_font)
|
||||||
|
self.config_combobox.grid(row=3, column=1, columnspan=1, padx=(0, 10), pady=(10, 0), sticky="ew")
|
||||||
|
#self.config_combobox.bind("<<ComboboxSelected>>", self.on_config_selected)
|
||||||
|
|
||||||
|
# Setzen Button für Stellantriebstyp
|
||||||
|
self.set_pid_button = customtkinter.CTkButton(self.root, text="Setzen", command=self.set_pid_parameters, font=self.my_font)
|
||||||
|
self.set_pid_button.grid(row=3, column=2, padx=(0, 10), pady=(10, 0), sticky="ew")
|
||||||
|
|
||||||
|
# Labels and ComboBox for COM Port
|
||||||
|
self.com_label = customtkinter.CTkLabel(self.root, text="COM Port:", font=self.my_font)
|
||||||
|
self.com_label.grid(row=2, column=0, padx=(10, 5), pady=(10, 0), sticky="w")
|
||||||
|
|
||||||
|
self.combobox_value = customtkinter.StringVar()
|
||||||
|
self.combobox = customtkinter.CTkComboBox(self.root, variable=self.combobox_value, font=self.my_font)
|
||||||
|
self.combobox.configure(values=[port.device for port in self.ports])
|
||||||
|
self.combobox.grid(row=2, column=1, padx=(0, 10), pady=(10, 0), sticky="ew")
|
||||||
|
|
||||||
|
self.connect_button = customtkinter.CTkButton(self.root, text="Verbinden", command=self.connect_arduino, font=self.my_font)
|
||||||
|
self.connect_button.grid(row=2, column=2, padx=(0, 10), pady=(10, 0), sticky="ew")
|
||||||
|
|
||||||
|
# Setpoint entry
|
||||||
|
self.setpoint_label = customtkinter.CTkLabel(self.root, text="Sollwerteingabe in Nm:", font=self.my_font)
|
||||||
|
self.setpoint_label.grid(row=4, column=0, padx=(10, 5), pady=(10, 0), sticky="w")
|
||||||
|
|
||||||
|
self.setpoint_entry = customtkinter.CTkEntry(self.root, font=self.my_font)
|
||||||
|
self.setpoint_entry.grid(row=4, column=1, padx=(0, 10), pady=(10, 0), sticky="ew")
|
||||||
|
|
||||||
|
self.set_setpoint_button = customtkinter.CTkButton(self.root, text="Setzen", command=self.set_setpoint, font=self.my_font)
|
||||||
|
self.set_setpoint_button.grid(row=4, column=2, padx=(0, 10), pady=(10, 0), sticky="ew")
|
||||||
|
|
||||||
|
# Multi Setpoints Textbox and Button
|
||||||
|
self.multi_setpoints_label = customtkinter.CTkLabel(self.root, text="Sollwerteeingabe\n(Winkel;Drehmoment):", font=self.my_font)
|
||||||
|
self.multi_setpoints_label.grid(row=5, column=0, padx=(10, 5), pady=(10, 0), sticky="w")
|
||||||
|
|
||||||
|
self.multi_setpoints_text = customtkinter.CTkTextbox(self.root, font=self.my_font)
|
||||||
|
self.multi_setpoints_text.grid(row=5, column=1, padx=(0, 10), pady=(10, 0), sticky="ew")
|
||||||
|
|
||||||
|
self.send_multi_setpoints_button = customtkinter.CTkButton(self.root, text="Sollwerte senden", command=self.send_multi_setpoints, font=self.my_font)
|
||||||
|
self.send_multi_setpoints_button.grid(row=5, column=2, padx=(0, 10), pady=(10, 0), sticky="ew")
|
||||||
|
|
||||||
|
# Analog Control CheckBox
|
||||||
|
self.checkbox_var = customtkinter.IntVar()
|
||||||
|
self.checkbox = customtkinter.CTkCheckBox(self.root, text="Analogsteuerung", variable=self.checkbox_var, command=self.input_source_Switch, font=self.my_font)
|
||||||
|
self.checkbox.grid(row=6, column=0, columnspan=3, padx=(10, 10), pady=(10, 0), sticky="w")
|
||||||
|
|
||||||
|
# Tare and Demagnetize Buttons
|
||||||
|
self.tare_button = customtkinter.CTkButton(self.root, text="Drehwinkel tarieren", command=self.tare_angle, font=self.my_font)
|
||||||
|
self.tare_button.grid(row=7, column=0, padx=(10, 5), pady=(10, 0), sticky="sew")
|
||||||
|
|
||||||
|
self.demag_button = customtkinter.CTkButton(self.root, text="Manuelle Entmagnetisierung", command=self.demagnetize, font=self.my_font)
|
||||||
|
self.demag_button.grid(row=7, column=1, padx=(0, 5), pady=(10, 0), sticky="sew")
|
||||||
|
|
||||||
|
# Exit Button
|
||||||
|
self.exit_button = customtkinter.CTkButton(self.root, text="Beenden", command=self.on_closing, font=self.my_font)
|
||||||
|
self.exit_button.grid(row=7, column=2, padx=(0, 10), pady=(10, 0), sticky="sew")
|
||||||
|
|
||||||
|
#Datalogger
|
||||||
|
self.datalogger_button = customtkinter.CTkButton(self.root, text="Datenlogger", command=self.toggle_datalogger, font=self.my_font)
|
||||||
|
self.datalogger_button.grid(row=6, column=2, padx=(0, 10), pady=(10, 0), sticky="sew")
|
||||||
|
|
||||||
|
# Plot
|
||||||
|
self.figure, self.ax = plt.subplots()
|
||||||
self.canvas = FigureCanvasTkAgg(self.figure, self.root)
|
self.canvas = FigureCanvasTkAgg(self.figure, self.root)
|
||||||
self.canvas.get_tk_widget().place(x=424, y=0)
|
self.canvas.get_tk_widget().grid(row=0, column=3, rowspan=8, columnspan=4, padx=(10, 10), pady=(10, 0), sticky="nsew")
|
||||||
|
|
||||||
def close_application(self):
|
self.ax.set_title("Drehmoment-Drehwinkel-Kennlinie")
|
||||||
if messagebox.askokcancel("Beenden", "Möchten Sie die Anwendung wirklich beenden?"):
|
self.ax.set_xlabel("Winkel (°)")
|
||||||
self.root.quit()
|
self.ax.set_ylabel("Drehmoment (Nm)")
|
||||||
|
self.ax.set_xlim(0, 90)
|
||||||
|
self.ax.set_ylim(0, 50)
|
||||||
|
|
||||||
def create_diagram(self):
|
# Current readings labels
|
||||||
self.plot.clear()
|
self.current_angle = 0
|
||||||
self.plot.set_xlim(0, 90)
|
self.current_torque = 0
|
||||||
self.plot.set_xlabel('Drehwinkel in Grad')
|
self.analogInput = 0
|
||||||
self.plot.set_ylabel('Drehmoment in Nm')
|
self.currentSetpoint = 0
|
||||||
|
self.currentOutput = 0
|
||||||
|
|
||||||
coordinates = self.coordinates_text.get("1.0", tk.END).strip().split("\n")
|
self.angle_label_var = customtkinter.StringVar()
|
||||||
x_coords, y_coords = [], []
|
self.angle_label_var.set("Drehwinkel: 0")
|
||||||
for coord in coordinates:
|
self.angle_label = customtkinter.CTkLabel(self.root, textvariable=self.angle_label_var, font=self.my_font)
|
||||||
x, y = map(float, coord.split(','))
|
self.angle_label.grid(row=8, column=3, padx=(10, 5), pady=(10, 0), sticky="sw")
|
||||||
x_coords.append(x)
|
|
||||||
y_coords.append(y)
|
|
||||||
|
|
||||||
self.plot.plot(x_coords, y_coords, marker='o', linestyle='-')
|
self.torque_label_var = customtkinter.StringVar()
|
||||||
self.plot.set_ylim(bottom=0)
|
self.torque_label_var.set("Istwert: 0")
|
||||||
|
self.torque_label = customtkinter.CTkLabel(self.root, textvariable=self.torque_label_var, font=self.my_font)
|
||||||
|
self.torque_label.grid(row=8, column=4, padx=(10, 5), pady=(10, 0), sticky="sw")
|
||||||
|
|
||||||
|
self.analogread_label_var = customtkinter.StringVar()
|
||||||
|
self.analogread_label_var.set("Analogeingang: 0")
|
||||||
|
self.analogread_label = customtkinter.CTkLabel(self.root, textvariable=self.analogread_label_var, font=self.my_font)
|
||||||
|
self.analogread_label.grid(row=8, column=5, padx=(10, 5), pady=(10, 0), sticky="sw")
|
||||||
|
|
||||||
|
self.currentSetpoint_label_var = customtkinter.StringVar()
|
||||||
|
self.currentSetpoint_label_var.set("Aktueller Sollwert: 0")
|
||||||
|
self.currentSetpoint_label = customtkinter.CTkLabel(self.root, textvariable=self.currentSetpoint_label_var, font=self.my_font)
|
||||||
|
self.currentSetpoint_label.grid(row=8, column=6, padx=(10, 5), pady=(10, 0), sticky="sw")
|
||||||
|
|
||||||
|
self.root.grid_columnconfigure(0, weight=0)
|
||||||
|
self.root.grid_columnconfigure(1, weight=0)
|
||||||
|
self.root.grid_columnconfigure(2, weight=0)
|
||||||
|
self.root.grid_columnconfigure(3, weight=1)
|
||||||
|
self.root.grid_columnconfigure(4, weight=1)
|
||||||
|
self.root.grid_columnconfigure(5, weight=1)
|
||||||
|
self.root.grid_columnconfigure(6, weight=1)
|
||||||
|
self.root.grid_rowconfigure(0, weight=0)
|
||||||
|
self.root.grid_rowconfigure(1, weight=0)
|
||||||
|
self.root.grid_rowconfigure(2, weight=0)
|
||||||
|
self.root.grid_rowconfigure(3, weight=0)
|
||||||
|
self.root.grid_rowconfigure(4, weight=0)
|
||||||
|
self.root.grid_rowconfigure(5, weight=0)
|
||||||
|
self.root.grid_rowconfigure(6, weight=0)
|
||||||
|
self.root.grid_rowconfigure(7, weight=1)
|
||||||
|
self.root.grid_rowconfigure(8, weight=0)
|
||||||
|
|
||||||
|
|
||||||
|
def set_pid_parameters(self):
|
||||||
|
if self.arduino:
|
||||||
|
self.selected_pid = self.config_combobox.get()
|
||||||
|
if self.selected_pid in self.pid_params:
|
||||||
|
self.pid_flag = True
|
||||||
|
else:
|
||||||
|
messagebox.showerror("Fehler", "Ausgewählte PID Konfiguration nicht gefunden.")
|
||||||
|
else:
|
||||||
|
messagebox.showwarning("Warnung", "Keine Verbindung zum Arduino hergestellt.")
|
||||||
|
|
||||||
|
def load_configurations(self):
|
||||||
|
try:
|
||||||
|
with open("conf.txt", "r") as file:
|
||||||
|
config_name = None
|
||||||
|
for line in file:
|
||||||
|
line = line.strip()
|
||||||
|
if line.startswith("#") or not line:
|
||||||
|
continue
|
||||||
|
if "{" in line:
|
||||||
|
config_name = line.split("{")[0].strip()
|
||||||
|
self.pid_params[config_name] = {"Kp": 0.0, "Ki": 0.0, "Kd": 0.0}
|
||||||
|
elif "}" in line:
|
||||||
|
config_name = None
|
||||||
|
else:
|
||||||
|
if config_name:
|
||||||
|
key, value = line.split(":")
|
||||||
|
key = key.strip()
|
||||||
|
value = round(float(value.strip()), 3)
|
||||||
|
self.pid_params[config_name][key] = value
|
||||||
|
|
||||||
|
self.config_combobox.configure(values=list(self.pid_params.keys()))
|
||||||
|
if self.pid_params:
|
||||||
|
self.config_combobox.set(list(self.pid_params.keys())[0])
|
||||||
|
self.selected_pid = list(self.pid_params.keys())[0]
|
||||||
|
except Exception as e:
|
||||||
|
messagebox.showerror("Fehler", f"Fehler beim Laden der Konfigurationen: {e}")
|
||||||
|
|
||||||
|
def connect_arduino(self):
|
||||||
|
com_port = self.combobox_value.get()
|
||||||
|
if com_port:
|
||||||
|
try:
|
||||||
|
self.arduino = serial.Serial(com_port, 19200, timeout=1)
|
||||||
|
messagebox.showinfo("Info", "Arduino verbunden an " + com_port)
|
||||||
|
except Exception as e:
|
||||||
|
messagebox.showerror("Fehler", str(e))
|
||||||
|
else:
|
||||||
|
messagebox.showwarning("Warnung", "Bitte einen COM-Port auswählen")
|
||||||
|
|
||||||
|
def connect_lsp(self):
|
||||||
|
try:
|
||||||
|
ports = serial.tools.list_ports.comports()
|
||||||
|
used_ports = [port.device for port in ports]
|
||||||
|
|
||||||
|
serPort = None
|
||||||
|
|
||||||
|
if os.name == 'nt':
|
||||||
|
for i in range(1, 257):
|
||||||
|
port = f'(COM{i}'
|
||||||
|
if (port not in used_ports) and (serPort == None):
|
||||||
|
serPort = port
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
for i in range(256):
|
||||||
|
port = f'/dev/ttyS{i}'
|
||||||
|
if (port not in used_ports) and (serPort == None):
|
||||||
|
serPort = port
|
||||||
|
pass
|
||||||
|
for i in range(256):
|
||||||
|
port = f'/dev/ttyUSB{i}'
|
||||||
|
if (port not in used_ports) and (serPort == None):
|
||||||
|
serPort = port
|
||||||
|
pass
|
||||||
|
|
||||||
|
if serPort != None:
|
||||||
|
self.ser = serial.Serial(serPort, 9600, timeout=1)
|
||||||
|
print(F"Slave Port bereit an {serPort}")
|
||||||
|
except Exception as e:
|
||||||
|
print(F"Fehler beim Öffnen des Slave Ports an {serPort}")
|
||||||
|
pass
|
||||||
|
|
||||||
|
def set_setpoint(self):
|
||||||
|
if self.arduino:
|
||||||
|
try:
|
||||||
|
self.setpoint = float(self.setpoint_entry.get()) * 1000
|
||||||
|
self.setpoint_flag = True
|
||||||
|
except ValueError:
|
||||||
|
messagebox.showerror("Fehler", "Ungültiger Setpoint.")
|
||||||
|
else:
|
||||||
|
messagebox.showwarning("Warnung", "Keine Verbindung zum Arduino hergestellt")
|
||||||
|
|
||||||
|
def send_multi_setpoints(self):
|
||||||
|
if self.arduino:
|
||||||
|
setpoints = self.multi_setpoints_text.get("1.0", tk.END).strip().split("\n")
|
||||||
|
coordinates = []
|
||||||
|
for point in setpoints:
|
||||||
|
try:
|
||||||
|
angle, torque = map(float, point.split(";"))
|
||||||
|
coordinates.append((angle, torque))
|
||||||
|
except ValueError:
|
||||||
|
messagebox.showerror("Fehler", f"Ungültige Eingabe: {point}")
|
||||||
|
return
|
||||||
|
|
||||||
|
coordinates.sort()
|
||||||
|
self.setpoints = coordinates
|
||||||
|
self.plot_coordinates(coordinates)
|
||||||
|
self.multi_setpoints_flag = True
|
||||||
|
else:
|
||||||
|
messagebox.showwarning("Warnung", "Keine Verbindung zum Arduino hergestellt")
|
||||||
|
|
||||||
|
def plot_coordinates(self, coordinates):
|
||||||
|
angles, torques = zip(*coordinates)
|
||||||
|
self.ax.clear()
|
||||||
|
self.ax.plot(angles, torques, 'bo-', label="Setpoints")
|
||||||
|
self.ax.set_title("Drehmoment-Drehwinkel-Kennlinie")
|
||||||
|
self.ax.set_xlabel("Winkel in °")
|
||||||
|
self.ax.set_ylabel("Drehmoment in Nm")
|
||||||
|
self.ax.set_xlim(0, 90)
|
||||||
|
self.ax.set_ylim(0, 50)
|
||||||
|
self.ax.legend()
|
||||||
self.canvas.draw()
|
self.canvas.draw()
|
||||||
|
|
||||||
def update_current_values(self, angle, torque):
|
def interpolate_coordinates(self, coordinates):
|
||||||
self.current_angle.config(text=str(angle))
|
interpolated_points = []
|
||||||
self.current_torque.config(text=str(torque))
|
for i in range(len(coordinates) - 1):
|
||||||
|
start_angle, start_torque = coordinates[i]
|
||||||
|
end_angle, end_torque = coordinates[i + 1]
|
||||||
|
num_points = int((end_angle - start_angle) * 2)
|
||||||
|
angles = np.linspace(start_angle, end_angle, num_points + 1)
|
||||||
|
torques = np.linspace(start_torque, end_torque, num_points + 1)
|
||||||
|
interpolated_points.extend(zip(angles, torques))
|
||||||
|
|
||||||
# Anwendung starten
|
full_interpolated_points = [(angle, 0) for angle in np.arange(0, 90.5, 0.5)]
|
||||||
root = tk.Tk()
|
for angle, torque in interpolated_points:
|
||||||
app = T2000ErdemGUI(root)
|
index = int(angle * 2)
|
||||||
root.mainloop()
|
full_interpolated_points[index] = (angle, torque)
|
||||||
|
|
||||||
|
return full_interpolated_points
|
||||||
|
|
||||||
|
def send_interpolated_points(self):
|
||||||
|
if self.arduino:
|
||||||
|
setpoints_command = "u"
|
||||||
|
for angle, torque in self.setpoints:
|
||||||
|
torque_mnm = torque * 1000
|
||||||
|
setpoints_command += f"{int(angle * 2)},{torque_mnm:.1f};"
|
||||||
|
setpoints_command = setpoints_command.rstrip(';') + "u\n"
|
||||||
|
self.arduino.write(setpoints_command.encode())
|
||||||
|
|
||||||
|
timeout_ctr = time.time()
|
||||||
|
while(self.arduino.in_waiting == 0 and time.time() - timeout_ctr < 2):
|
||||||
|
pass
|
||||||
|
|
||||||
|
try:
|
||||||
|
print(self.arduino.readline())
|
||||||
|
#print(self.arduino.read_all())
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(e)
|
||||||
|
|
||||||
|
def tare_angle(self):
|
||||||
|
if self.arduino:
|
||||||
|
self.tare_flag = True
|
||||||
|
else:
|
||||||
|
messagebox.showwarning("Warnung", "Keine Verbindung zum Arduino hergestellt")
|
||||||
|
|
||||||
|
def demagnetize(self):
|
||||||
|
if self.arduino:
|
||||||
|
self.demag_flag = True
|
||||||
|
else:
|
||||||
|
messagebox.showwarning("Warnung", "Keine Verbindung zum Arduino hergestellt")
|
||||||
|
|
||||||
|
def input_source_Switch(self):
|
||||||
|
if self.arduino:
|
||||||
|
self.input_source_flag = True
|
||||||
|
else:
|
||||||
|
messagebox.showwarning("Warnung", "Keine Verbindung zum Arduino hergestellt")
|
||||||
|
|
||||||
|
def communication_loop(self):
|
||||||
|
while self.running:
|
||||||
|
if self.arduino:
|
||||||
|
self.arduino.read_all()
|
||||||
|
if self.setpoint_flag:
|
||||||
|
self.arduino.write(f"s{self.setpoint:.0f},0\n".encode())
|
||||||
|
self.setpoint_flag = False
|
||||||
|
while self.arduino.out_waiting:
|
||||||
|
pass
|
||||||
|
if self.multi_setpoints_flag:
|
||||||
|
self.send_interpolated_points()
|
||||||
|
self.multi_setpoints_flag = False
|
||||||
|
while self.arduino.out_waiting:
|
||||||
|
pass
|
||||||
|
if self.tare_flag:
|
||||||
|
self.arduino.write(b'w\n')
|
||||||
|
self.tare_flag = False
|
||||||
|
while self.arduino.out_waiting:
|
||||||
|
pass
|
||||||
|
if self.demag_flag:
|
||||||
|
self.arduino.write(b'e\n')
|
||||||
|
self.demag_flag = False
|
||||||
|
while self.arduino.out_waiting:
|
||||||
|
pass
|
||||||
|
if self.input_source_flag:
|
||||||
|
self.arduino.write(b'S\n')
|
||||||
|
self.input_source_flag = False
|
||||||
|
while self.arduino.out_waiting:
|
||||||
|
pass
|
||||||
|
if self.pid_flag:
|
||||||
|
self.send_pid_parameters()
|
||||||
|
self.pid_flag = False
|
||||||
|
|
||||||
|
self.arduino.write(b'a\n')
|
||||||
|
timeout_ctr = time.time()
|
||||||
|
while(self.arduino.in_waiting == 0 and time.time() - timeout_ctr < 2):
|
||||||
|
pass
|
||||||
|
|
||||||
|
try:
|
||||||
|
data = self.arduino.readline().decode(errors='ignore').strip().split(";")
|
||||||
|
if len(data) >= 4 and all(data): # Ensure all fields are not empty
|
||||||
|
self.current_angle = float(data[0]) / 1000 if data[0] else 0
|
||||||
|
self.current_torque = float(data[1]) / 1000 if data[1] else 0
|
||||||
|
self.analogInput = float(data[2]) / 1000 if data[2] else 0
|
||||||
|
self.currentSetpoint = float(data[3]) / 1000 if data[3] else 0
|
||||||
|
self.currentOutput = float(data[4]) * 0.006349206 if data[4] else 0
|
||||||
|
|
||||||
|
self.angle_label_var.set(f"Drehwinkel: {self.current_angle:.1f} °")
|
||||||
|
self.torque_label_var.set(f"Istwert: {self.current_torque:.1f} Nm")
|
||||||
|
self.analogread_label_var.set(f"Analogeingang: {self.analogInput:.1f} V")
|
||||||
|
self.currentSetpoint_label_var.set(f"Aktueller Sollwert: {self.currentSetpoint:.1f} Nm")
|
||||||
|
|
||||||
|
if self.current_point is not None:
|
||||||
|
self.current_point.remove()
|
||||||
|
self.current_point, = self.ax.plot([self.current_angle], [self.current_torque], 'ro')
|
||||||
|
self.canvas.draw()
|
||||||
|
|
||||||
|
if self.logging:
|
||||||
|
elapsed_time = time.time() - self.start_time
|
||||||
|
formatted_time = f"{elapsed_time:.3f}".replace('.', ',')
|
||||||
|
formatted_angle = f"{self.current_angle:.3f}".replace('.', ',')
|
||||||
|
formatted_setpoint = f"{self.currentSetpoint:.3f}".replace('.', ',')
|
||||||
|
formatted_torque = f"{self.current_torque:.3f}".replace('.', ',')
|
||||||
|
formatted_output = f"{self.currentOutput:.3f}".replace('.', ',')
|
||||||
|
|
||||||
|
self.csv_writer.writerow([formatted_time, formatted_angle, formatted_setpoint, formatted_torque, formatted_output])
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(e)
|
||||||
|
|
||||||
|
time.sleep(0.01)
|
||||||
|
|
||||||
|
|
||||||
|
if self.ser:
|
||||||
|
if self.ser.in_waiting:
|
||||||
|
try:
|
||||||
|
command = ""
|
||||||
|
while self.ser.in_waiting:
|
||||||
|
command += self.ser.read(1).decode(errors='ignore').strip()
|
||||||
|
if command[0] == 'a':
|
||||||
|
message = f"{self.current_angle};{self.current_torque};{self.analogInput};{self.currentSetpoint};{self.currentOutput}\n"
|
||||||
|
self.ser.write(message.encode())
|
||||||
|
if command[0] == 'w':
|
||||||
|
self.tare_angle()
|
||||||
|
if command[0] == 'u':
|
||||||
|
outCommand = F"{command}\n"
|
||||||
|
self.arduino.write(outCommand.encode())
|
||||||
|
print(self.arduino.readline())
|
||||||
|
time.sleep(0.1)
|
||||||
|
|
||||||
|
self.ser.read()
|
||||||
|
except Exception as e:
|
||||||
|
print(e)
|
||||||
|
#self.ser.write(b'Hallo\n')
|
||||||
|
else :
|
||||||
|
try:
|
||||||
|
pass
|
||||||
|
self.connect_lsp()
|
||||||
|
except Exception as e:
|
||||||
|
print(e)
|
||||||
|
|
||||||
|
def send_pid_parameters(self):
|
||||||
|
if self.selected_pid in self.pid_params:
|
||||||
|
pid_values = self.pid_params[self.selected_pid]
|
||||||
|
kp = f"{pid_values['Kp']:.3f}"
|
||||||
|
ki = f"{pid_values['Ki']:.3f}"
|
||||||
|
kd = f"{pid_values['Kd']:.3f}"
|
||||||
|
self.arduino.write(f"p{kp}\n".encode())
|
||||||
|
time.sleep(0.1)
|
||||||
|
self.arduino.write(f"i{ki}\n".encode())
|
||||||
|
time.sleep(0.1)
|
||||||
|
self.arduino.write(f"d{kd}\n".encode())
|
||||||
|
messagebox.showinfo("Info", f"PID-Parameter gesendet: Kp={kp}, Ki={ki}, Kd={kd}")
|
||||||
|
else:
|
||||||
|
messagebox.showerror("Fehler", "Ausgewählte PID Konfiguration nicht gefunden.")
|
||||||
|
|
||||||
|
def on_closing(self):
|
||||||
|
userInput = simpledialog.askstring("Masterpasswort eingeben", "Zum Ausführen dieses Befehls ist das Masterpasswort notwendig.\n")
|
||||||
|
if userInput == "976638":
|
||||||
|
self.running = False
|
||||||
|
if self.arduino:
|
||||||
|
self.arduino.close()
|
||||||
|
self.communication_thread = None
|
||||||
|
if self.ser:
|
||||||
|
self.ser.close()
|
||||||
|
self.root.quit()
|
||||||
|
self.root.destroy()
|
||||||
|
elif userInput == None:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
messagebox.showerror("Fehler", "Es wurde ein falsches Passwort eingegeben.")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
root = customtkinter.CTk()
|
||||||
|
app = ArduinoGUI(root)
|
||||||
|
root.protocol("WM_DELETE_WINDOW", app.on_closing)
|
||||||
|
root.mainloop()
|
||||||
|
|||||||
6
requirements.txt
Normal file
6
requirements.txt
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
tk==0.1.0
|
||||||
|
customtkinter==5.2.2
|
||||||
|
pyserial==3.5
|
||||||
|
matplotlib==3.9.0
|
||||||
|
numpy==2.0.1
|
||||||
|
Pillow==10.4.0
|
||||||
BIN
src/logo1.png
Normal file
BIN
src/logo1.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 162 KiB |
2
start_linux.sh
Executable file
2
start_linux.sh
Executable file
@@ -0,0 +1,2 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
.venv/bin/python main.py
|
||||||
3
start_windows.bat
Normal file
3
start_windows.bat
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
@ECHO OFF
|
||||||
|
call ./.venv/Scripts/activate.bat
|
||||||
|
python main.py
|
||||||
Reference in New Issue
Block a user