neraly complete Model of Driving done, but needs tweaking

This commit is contained in:
2025-09-05 14:54:29 +02:00
parent 0276a3fb3c
commit 6108413d7e
12 changed files with 1469 additions and 726 deletions

View File

@@ -5,180 +5,182 @@
from __future__ import annotations
import tkinter as tk
from tkinter import ttk
from typing import Dict, Any
# Wichtig: Defaults aus dem Modul importieren
from app.simulation.modules.engine import ENGINE_DEFAULTS
from app.simulation.ui import UITab
from app.simulation.ui import UITab
class EngineTab(UITab):
NAME = "engine"
TITLE = "Motor"
PRIO = 10
def __init__(self, parent, sim):
self.sim = sim
self.frame = ttk.Frame(parent, padding=8)
self.frame.columnconfigure(1, weight=1)
for c in (0,1,2,3): self.frame.columnconfigure(c, weight=1)
# ------------- Widgets anlegen (OHNE Defaultwerte eintragen) --------------
row = 0
ttk.Label(self.frame, text="Leerlauf [RPM]").grid(row=row, column=0, sticky="w"); row+=1
self.idle_var = tk.IntVar(); ttk.Entry(self.frame, textvariable=self.idle_var, width=10)\
.grid(row=row-1, column=1, sticky="w")
# ---------- Linke Spalte ----------
rowL = 0
def L(lbl, var, w=12, kind="entry", values=None):
nonlocal rowL
ttk.Label(self.frame, text=lbl).grid(row=rowL, column=0, sticky="w")
if kind == "entry":
ttk.Entry(self.frame, textvariable=var, width=w).grid(row=rowL, column=1, sticky="w")
elif kind == "combo":
ttk.Combobox(self.frame, textvariable=var, state="readonly",
values=values or [], width=w).grid(row=rowL, column=1, sticky="w")
rowL += 1
ttk.Label(self.frame, text="Max RPM").grid(row=row, column=0, sticky="w"); row+=1
self.maxrpm_var = tk.IntVar(); ttk.Entry(self.frame, textvariable=self.maxrpm_var, width=10)\
.grid(row=row-1, column=1, sticky="w")
self.idle = tk.IntVar(); L("Leerlauf [RPM]", self.idle)
self.maxrpm = tk.IntVar(); L("Max RPM", self.maxrpm)
self.rise = tk.IntVar(); L("Anstieg [RPM/s]", self.rise)
self.fall = tk.IntVar(); L("Abfall [RPM/s]", self.fall)
self.curve = tk.StringVar(); L("Gaspedal-Kennlinie", self.curve, kind="combo",
values=["linear","progressive","aggressive"])
ttk.Label(self.frame, text="Anstieg [RPM/s]").grid(row=row, column=0, sticky="w"); row+=1
self.rise_var = tk.IntVar(); ttk.Entry(self.frame, textvariable=self.rise_var, width=10)\
.grid(row=row-1, column=1, sticky="w")
ttk.Separator(self.frame).grid(row=rowL, column=0, columnspan=2, sticky="ew", pady=(8,6)); rowL += 1
ttk.Label(self.frame, text="Abfall [RPM/s]").grid(row=row, column=0, sticky="w"); row+=1
self.fall_var = tk.IntVar(); ttk.Entry(self.frame, textvariable=self.fall_var, width=10)\
.grid(row=row-1, column=1, sticky="w")
self.power = tk.DoubleVar(); L("Motorleistung [kW]", self.power)
self.tqpeak = tk.DoubleVar(); L("Drehmoment-Peak [RPM]", self.tqpeak)
ttk.Label(self.frame, text="Gaspedal-Kennlinie").grid(row=row, column=0, sticky="w"); row+=1
self.thr_curve = tk.StringVar()
ttk.Combobox(self.frame, textvariable=self.thr_curve, state="readonly",
values=["linear","progressive","aggressive"])\
.grid(row=row-1, column=1, sticky="w")
ttk.Separator(self.frame).grid(row=rowL, column=0, columnspan=2, sticky="ew", pady=(8,6)); rowL += 1
ttk.Separator(self.frame).grid(row=row, column=0, columnspan=2, sticky="ew", pady=(8,6)); row+=1
self.st_nom = tk.DoubleVar(); L("Starter Nenn-RPM", self.st_nom)
self.st_vmin= tk.DoubleVar(); L("Starter min. Spannung [V]", self.st_vmin)
self.st_thr = tk.DoubleVar(); L("Start-Schwelle [RPM]", self.st_thr)
self.stall = tk.DoubleVar(); L("Stall-Grenze [RPM]", self.stall)
# Leistung
ttk.Label(self.frame, text="Motorleistung [kW]").grid(row=row, column=0, sticky="w"); row+=1
self.power_kw = tk.DoubleVar(); ttk.Entry(self.frame, textvariable=self.power_kw, width=10)\
.grid(row=row-1, column=1, sticky="w")
ttk.Separator(self.frame).grid(row=rowL, column=0, columnspan=2, sticky="ew", pady=(8,6)); rowL += 1
ttk.Label(self.frame, text="Drehmoment-Peak [RPM]").grid(row=row, column=0, sticky="w"); row+=1
self.peak_rpm = tk.DoubleVar(); ttk.Entry(self.frame, textvariable=self.peak_rpm, width=10)\
.grid(row=row-1, column=1, sticky="w")
self.o_idle = tk.DoubleVar(); L("Öldruck Leerlauf [bar]", self.o_idle)
self.o_slope= tk.DoubleVar(); L("Öldruck Steigung [bar/krpm]", self.o_slope)
self.o_floor= tk.DoubleVar(); L("Öldruck Boden [bar]", self.o_floor)
ttk.Separator(self.frame).grid(row=row, column=0, columnspan=2, sticky="ew", pady=(8,6)); row+=1
# ---------- Rechte Spalte ----------
rowR = 0
def R(lbl, var, w=12, kind="entry"):
nonlocal rowR
ttk.Label(self.frame, text=lbl).grid(row=rowR, column=2, sticky="w")
if kind == "entry":
ttk.Entry(self.frame, textvariable=var, width=w).grid(row=rowR, column=3, sticky="w")
elif kind == "label":
ttk.Label(self.frame, textvariable=var).grid(row=rowR, column=3, sticky="w")
elif kind == "scale":
s = ttk.Scale(self.frame, from_=0.0, to=100.0, variable=var,
command=lambda _=None: self._on_pedal_change())
s.grid(row=rowR, column=3, sticky="ew")
rowR += 1
# Starter
ttk.Label(self.frame, text="Starter Nenn-RPM").grid(row=row, column=0, sticky="w"); row+=1
self.starter_nom = tk.DoubleVar(); ttk.Entry(self.frame, textvariable=self.starter_nom, width=10)\
.grid(row=row-1, column=1, sticky="w")
self.dk_idle = tk.DoubleVar(); R("DK min Leerlauf [%]", self.dk_idle)
self.dk_over = tk.DoubleVar(); R("DK Schub [%]", self.dk_over)
self.dk_tau = tk.DoubleVar(); R("DK Zeitkonstante [s]", self.dk_tau)
self.tq_kp = tk.DoubleVar(); R("Torque-Kp", self.tq_kp)
self.tq_ki = tk.DoubleVar(); R("Torque-Ki", self.tq_ki)
ttk.Label(self.frame, text="Starter min. Spannung [V]").grid(row=row, column=0, sticky="w"); row+=1
self.starter_vmin = tk.DoubleVar(); ttk.Entry(self.frame, textvariable=self.starter_vmin, width=10)\
.grid(row=row-1, column=1, sticky="w")
ttk.Separator(self.frame).grid(row=rowR, column=2, columnspan=2, sticky="ew", pady=(8,6)); rowR += 1
ttk.Label(self.frame, text="Start-Schwelle [RPM]").grid(row=row, column=0, sticky="w"); row+=1
self.start_th = tk.DoubleVar(); ttk.Entry(self.frame, textvariable=self.start_th, width=10)\
.grid(row=row-1, column=1, sticky="w")
self.jit_idle= tk.DoubleVar(); R("Jitter Leerlauf [±RPM]", self.jit_idle)
self.jit_high= tk.DoubleVar(); R("Jitter hoch [±RPM]", self.jit_high)
self.jit_tau = tk.DoubleVar(); R("Jitter-Zeitkonstante [s]", self.jit_tau)
self.jit_off = tk.DoubleVar(); R("Jitter aus unter [RPM]", self.jit_off)
ttk.Label(self.frame, text="Stall-Grenze [RPM]").grid(row=row, column=0, sticky="w"); row+=1
self.stall_rpm = tk.DoubleVar(); ttk.Entry(self.frame, textvariable=self.stall_rpm, width=10)\
.grid(row=row-1, column=1, sticky="w")
ttk.Separator(self.frame).grid(row=rowR, column=2, columnspan=2, sticky="ew", pady=(8,6)); rowR += 1
ttk.Separator(self.frame).grid(row=row, column=0, columnspan=2, sticky="ew", pady=(8,6)); row+=1
self.amb_c = tk.DoubleVar(); R("Umgebung [°C]", self.amb_c)
self.cold_k = tk.DoubleVar(); R("Kalt-Leerlauf +/°C [RPM/°C]", self.cold_k)
self.cold_max=tk.DoubleVar(); R("Kalt-Leerlauf max [RPM]", self.cold_max)
# Thermik (analog Variablen ohne Defaults anlegen) ...
self.amb_c = tk.DoubleVar(); self.c_warm = tk.DoubleVar(); self.c_cool = tk.DoubleVar()
self.o_warm = tk.DoubleVar(); self.o_cool = tk.DoubleVar()
self.cold_gain = tk.DoubleVar(); self.cold_gain_max = tk.DoubleVar()
# (Labels/Entries spar ich hier ab wie gehabt weiterführen)
ttk.Separator(self.frame).grid(row=rowR, column=2, columnspan=2, sticky="ew", pady=(8,6)); rowR += 1
# Öl, DBW, Jitter, Pedal
self.o_idle = tk.DoubleVar(); self.o_slope = tk.DoubleVar(); self.o_floor = tk.DoubleVar()
self.plate_idle_min = tk.DoubleVar(); self.plate_overrun = tk.DoubleVar(); self.plate_tau = tk.DoubleVar()
self.torque_kp = tk.DoubleVar(); self.torque_ki = tk.DoubleVar()
self.jitter_idle = tk.DoubleVar(); self.jitter_high = tk.DoubleVar()
self.jitter_tau = tk.DoubleVar(); self.jitter_off = tk.DoubleVar()
self.pedal = tk.DoubleVar(); R("Gaspedal [%]", self.pedal, kind="scale")
ttk.Label(self.frame, text="Gaspedal [%]").grid(row=row, column=0, sticky="w"); row+=1
self.pedal_var = tk.DoubleVar()
self.pedal_scale = ttk.Scale(self.frame, from_=0.0, to=100.0, variable=self.pedal_var)
self.pedal_scale.grid(row=row-1, column=1, sticky="ew")
# ---------- Buttons ----------
rowBtns = max(rowL, rowR) + 1
btn = ttk.Frame(self.frame); btn.grid(row=rowBtns, column=0, columnspan=4, sticky="w", pady=(8,0))
ttk.Button(btn, text="Aktualisieren", command=self.refresh).pack(side="left")
ttk.Button(btn, text="Anwenden", command=self.apply).pack(side="left", padx=(8,0))
# Buttons
row += 1
btnrow = ttk.Frame(self.frame); btnrow.grid(row=row, column=0, columnspan=2, sticky="w", pady=(8,0))
ttk.Button(btnrow, text="Aktualisieren", command=self.refresh).pack(side="left")
ttk.Button(btnrow, text="Anwenden", command=self.apply).pack(side="left", padx=(8,0))
# Zum Start einmal „live“ laden:
self.refresh()
# liest IMMER effektiv: config.get(key, ENGINE_DEFAULTS[key])
def _on_pedal_change(self):
try: self.sim.v.set("throttle_pedal_pct", float(self.pedal.get()))
except: pass
def refresh(self):
e = dict(ENGINE_DEFAULTS)
e.update(self.sim.v.config.get("engine", {})) # Config über default mergen
e = dict(ENGINE_DEFAULTS); e.update(self.sim.v.config.get("engine", {}))
self.idle_var.set(e["idle_rpm"])
self.maxrpm_var.set(e["max_rpm"])
self.rise_var.set(e["rpm_rise_per_s"])
self.fall_var.set(e["rpm_fall_per_s"])
self.thr_curve.set(e["throttle_curve"])
self.power_kw.set(e["engine_power_kw"])
self.peak_rpm.set(e["torque_peak_rpm"])
# links
self.idle.set(e["idle_rpm"])
self.maxrpm.set(e["max_rpm"])
self.rise.set(e["rpm_rise_per_s"])
self.fall.set(e["rpm_fall_per_s"])
self.curve.set(e["throttle_curve"])
self.starter_nom.set(e["starter_rpm_nominal"])
self.starter_vmin.set(e["starter_voltage_min"])
self.start_th.set(e["start_rpm_threshold"])
self.stall_rpm.set(e["stall_rpm"])
self.power.set(e["engine_power_kw"])
self.tqpeak.set(e["torque_peak_rpm"])
self.amb_c.set(e["coolant_ambient_c"])
self.c_warm.set(e["coolant_warm_rate_c_per_s"])
self.c_cool.set(e["coolant_cool_rate_c_per_s"])
self.o_warm.set(e["oil_warm_rate_c_per_s"])
self.o_cool.set(e["oil_cool_rate_c_per_s"])
self.cold_gain.set(e["idle_cold_gain_per_deg"])
self.cold_gain_max.set(e["idle_cold_gain_max"])
self.st_nom.set(e["starter_rpm_nominal"])
self.st_vmin.set(e["starter_voltage_min"])
self.st_thr.set(e["start_rpm_threshold"])
self.stall.set(e["stall_rpm"])
self.o_idle.set(e["oil_pressure_idle_bar"])
self.o_slope.set(e["oil_pressure_slope_bar_per_krpm"])
self.o_floor.set(e["oil_pressure_off_floor_bar"])
self.plate_idle_min.set(e["throttle_plate_idle_min_pct"])
self.plate_overrun.set(e["throttle_plate_overrun_pct"])
self.plate_tau.set(e["throttle_plate_tau_s"])
self.torque_kp.set(e["torque_ctrl_kp"])
self.torque_ki.set(e["torque_ctrl_ki"])
# rechts
self.dk_idle.set(e["throttle_plate_idle_min_pct"])
self.dk_over.set(e["throttle_plate_overrun_pct"])
self.dk_tau.set(e["throttle_plate_tau_s"])
self.tq_kp.set(e["torque_ctrl_kp"])
self.tq_ki.set(e["torque_ctrl_ki"])
self.jitter_idle.set(e["rpm_jitter_idle_amp_rpm"])
self.jitter_high.set(e["rpm_jitter_high_amp_rpm"])
self.jitter_tau.set(e["rpm_jitter_tau_s"])
self.jitter_off.set(e["rpm_jitter_off_threshold_rpm"])
self.jit_idle.set(e["rpm_jitter_idle_amp_rpm"])
self.jit_high.set(e["rpm_jitter_high_amp_rpm"])
self.jit_tau.set(e["rpm_jitter_tau_s"])
self.jit_off.set(e["rpm_jitter_off_threshold_rpm"])
self.pedal_var.set(e["throttle_pedal_pct"])
self.amb_c.set(e["coolant_ambient_c"])
self.cold_k.set(e["idle_cold_gain_per_deg"])
self.cold_max.set(e["idle_cold_gain_max"])
self.pedal.set(e["throttle_pedal_pct"])
self._on_pedal_change()
def apply(self):
# Nur hier wird geschrieben
cfg = {"engine": {
"idle_rpm": int(self.idle_var.get()),
"max_rpm": int(self.maxrpm_var.get()),
"rpm_rise_per_s": int(self.rise_var.get()),
"rpm_fall_per_s": int(self.fall_var.get()),
"throttle_curve": self.thr_curve.get(),
"engine_power_kw": float(self.power_kw.get()),
"torque_peak_rpm": float(self.peak_rpm.get()),
"starter_rpm_nominal": float(self.starter_nom.get()),
"starter_voltage_min": float(self.starter_vmin.get()),
"start_rpm_threshold": float(self.start_th.get()),
"stall_rpm": float(self.stall_rpm.get()),
"coolant_ambient_c": float(self.amb_c.get()),
"coolant_warm_rate_c_per_s": float(self.c_warm.get()),
"coolant_cool_rate_c_per_s": float(self.c_cool.get()),
"oil_warm_rate_c_per_s": float(self.o_warm.get()),
"oil_cool_rate_c_per_s": float(self.o_cool.get()),
"idle_cold_gain_per_deg": float(self.cold_gain.get()),
"idle_cold_gain_max": float(self.cold_gain_max.get()),
"idle_rpm": int(self.idle.get()),
"max_rpm": int(self.maxrpm.get()),
"rpm_rise_per_s": int(self.rise.get()),
"rpm_fall_per_s": int(self.fall.get()),
"throttle_curve": self.curve.get(),
"engine_power_kw": float(self.power.get()),
"torque_peak_rpm": float(self.tqpeak.get()),
"starter_rpm_nominal": float(self.st_nom.get()),
"starter_voltage_min": float(self.st_vmin.get()),
"start_rpm_threshold": float(self.st_thr.get()),
"stall_rpm": float(self.stall.get()),
"oil_pressure_idle_bar": float(self.o_idle.get()),
"oil_pressure_slope_bar_per_krpm": float(self.o_slope.get()),
"oil_pressure_off_floor_bar": float(self.o_floor.get()),
"throttle_plate_idle_min_pct": float(self.plate_idle_min.get()),
"throttle_plate_overrun_pct": float(self.plate_overrun.get()),
"throttle_plate_tau_s": float(self.plate_tau.get()),
"torque_ctrl_kp": float(self.torque_kp.get()),
"torque_ctrl_ki": float(self.torque_ki.get()),
"rpm_jitter_idle_amp_rpm": float(self.jitter_idle.get()),
"rpm_jitter_high_amp_rpm": float(self.jitter_high.get()),
"rpm_jitter_tau_s": float(self.jitter_tau.get()),
"rpm_jitter_off_threshold_rpm": float(self.jitter_off.get()),
"throttle_pedal_pct": float(self.pedal_var.get()),
"throttle_plate_idle_min_pct": float(self.dk_idle.get()),
"throttle_plate_overrun_pct": float(self.dk_over.get()),
"throttle_plate_tau_s": float(self.dk_tau.get()),
"torque_ctrl_kp": float(self.tq_kp.get()),
"torque_ctrl_ki": float(self.tq_ki.get()),
"rpm_jitter_idle_amp_rpm": float(self.jit_idle.get()),
"rpm_jitter_high_amp_rpm": float(self.jit_high.get()),
"rpm_jitter_tau_s": float(self.jit_tau.get()),
"rpm_jitter_off_threshold_rpm": float(self.jit_off.get()),
"coolant_ambient_c": float(self.amb_c.get()),
"idle_cold_gain_per_deg": float(self.cold_k.get()),
"idle_cold_gain_max": float(self.cold_max.get()),
"throttle_pedal_pct": float(self.pedal.get()),
}}
self.sim.load_config(cfg)