feat: Генерация диаграммы сварочной точки по аналитичиским идеальным данным

This commit is contained in:
Andrew 2024-10-30 18:42:23 +03:00
commit 983c2759ba
12 changed files with 3156 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
/__pycache__/
/tck_venv/

2000
2024_10_28-17_03_34.csv Normal file

File diff suppressed because it is too large Load Diff

55
DataParsinator.py Normal file
View File

@ -0,0 +1,55 @@
import pandas as pd
import numpy as np
class DataParser:
def __init__(self, scaler = 1):
self.data = pd.DataFrame({})
self.scaler = scaler
def setData(self, path):
self.data = pd.read_csv(path)
def getBoolDict (self):
boolDict = {}
for signalName in self.data.columns:
if type (self.data[signalName].iloc[0]) == np.bool:
boolDict[signalName] = self._getBoolChanges(signalName)
return boolDict
def getFloatDict (self):
floatDict = {}
for signalName in self.data.columns:
if type (self.data[signalName].iloc[0]) == np.float64:
floatDict[signalName] = self._getFloatChanges(signalName)
return floatDict
def _getBoolChanges(self, signalName):
timeCode = self.data['time']
signal_values = self.data[signalName]
changes = []
if len(signal_values) > 0:
changes.append([float(timeCode.iloc[0]), 'high' if bool(signal_values.iloc[0]) else 'low'])
for i in range(1, len(signal_values)):
if signal_values.iloc[i] != signal_values.iloc[i - 1]:
changes.append([float(timeCode.iloc[i])*self.scaler, 'high' if bool(signal_values.iloc[i]) else 'low'])
return changes
def _getFloatChanges(self, signalName):
timeCode = self.data['time']
signal_values = self.data[signalName]
changes = []
if len(signal_values) > 0:
for i in range (len(signal_values)):
changes.append([float(timeCode.iloc[i]), float(signal_values.iloc[i])])
return changes

223
OptAlgorithm.py Normal file
View File

@ -0,0 +1,223 @@
from OptTimeCalculator import OptTimeCalculator
from Params import Params
from numpy import cos, sin, sqrt, cbrt, arcsin, linspace
class OptAlgorithm():
paramsPath = "."
paramsFile = "params.json"
def __init__(self, useMins = True, s1 = 0, s2 = 0):
self.p = Params(OptAlgorithm.paramsPath, OptAlgorithm.paramsFile)
calc = OptTimeCalculator(self.p)
if useMins:
s1 = self.p.smin1t
s2 = self.p.smin2t
calc.T(s = s1, s2 = s2)
self.Ts = calc.Ts
self.TStartOpen = sqrt(self.p.flon / self.p.k * 2 / self.p.a)
self.T2 = calc.calcSecond(Tstart = self.TStartOpen, s2 = s2)
self.x1Contact = s1 + self.p.x10
self.x2Contact = s2 + self.p.x20
def V1Close(self, t: float):
if t < self.Ts["t1"]:
return self.p.a * t
else:
return self.p.a * self.Ts["t1"]
def X1Close(self, t: float):
t1 = min(t, self.Ts["t1"])
x0 = self.p.a * t1 * t1 / 2
t2 = max(t - self.Ts["t1"], 0)
x1 = self.p.a * self.Ts["t1"] * t2
return x0 + x1 + self.p.x10
def V2Close(self, t: float):
if t < self.T2["t1"]:
return self.p.a2 * t
t -= self.T2["t1"]
if t < self.T2["t2"]:
return self.p.a2 * self.T2["t1"]
t -= self.T2["t2"]
return self.p.a2 * self.T2["t1"] - self.p.a2 * t
def X2Close(self, t: float):
t1 = min(t, self.T2["t1"])
x0 = self.p.a2 * t1 * t1 / 2
t2 = max(min(t - self.T2["t1"], self.T2["t2"]), 0)
x1 = self.p.a2 * self.T2["t1"] * t2
t3 = max(min(t - self.T2["t2"]- self.T2["t1"], self.T2["t1"]), 0)
x2 = self.p.a2 * self.T2["t1"] * t3 - self.p.a2 * t3 * t3 / 2
return x0 + x1 + x2 + self.p.x20
def FClose(self, t: float):
return 0
def V1Grow(self, t: float):
F = self.FGrow(t)
dF0 = self.p.a * self.Ts["t1"] * self.p.k
if t < self.Ts["tm"]:
dF = sqrt(self.p.k / self.p.m) * self.p.umax / self.p.l * sin(sqrt(self.p.k / self.p.m) * t) + dF0 * cos(sqrt(self.p.k / self.p.m) * t)
return dF / self.p.k
if t < self.Ts["tend"]:
return sqrt(self.p.k/self.p.m)*sqrt((self.p.Fd-self.p.Fprop)**2 - F**2) / self.p.k
v0 = sqrt(self.p.k/self.p.m)*sqrt((self.p.Fd-self.p.Fprop)**2 - self.p.Fq**2)
b = (1/3 * v0 / cbrt((self.p.Fd - self.p.Fq))**2)**3
dF = 3.0*b*cbrt((F -self.p.Fd)/b)**2
return dF / self.p.k
def X1Grow(self, t: float):
F = self.FGrow(t)
x = F / self.p.k
return x + self.x1Contact
def V2Grow(self, t: float):
"""Считается, что верхний электрод не влияет на набор усилия, функция не реализована!, возвращает 0. Устанавливайте kturn = 0
"""
return 0
def X2Grow(self, t: float):
"""Считается, что верхний электрод не влияет на набор усилия, функция не реализована!, возвращает 0. Устанавливайте kturn = 0
"""
return self.x2Contact
def FGrow(self, t: float):
v0 = self.p.a * self.Ts["t1"]
dF0 = self.p.a * self.Ts["t1"] * self.p.k
Fm = (self.p.Ftg**2 - self.p.m/self.p.k*dF0**2)/(2 * self.p.awork * self.p.m)
if t < self.Ts["tm"]:
return - self.p.umax / self.p.l * cos(sqrt(self.p.k / self.p.m) * t) + self.p.umax / self.p.l + sqrt(self.p.m / self.p.k) * dF0 * sin(sqrt(self.p.k / self.p.m) * t)
t -= self.Ts["tm"]
if t < self.Ts["tend"]:
tm = arcsin(Fm/(self.p.Ftg)) * sqrt(self.p.m/self.p.k)
return self.p.Ftg * sin(sqrt(self.p.k / self.p.m)*(t + tm))
t -= self.Ts["tend"]
a = self.p.Fd
v0 = sqrt(self.p.k/self.p.m)*sqrt((self.p.Fd-self.p.Fprop)**2 - self.p.Fq**2)
b = (1/3 * v0 / cbrt((self.p.Fd - self.p.Fq))**2)**3
q = self.p.Fq
return 3 * t**2 * cbrt(b*b*(q-a)) + 3 * t * cbrt(q-a)**2 * cbrt(b) + b * t**3 + q
def V1Open(self, t: float):
if t < self.Ts["to1"]:
return -self.p.a * t
t -= self.Ts["to1"]
if t < self.Ts["to2"]:
return -self.p.a * self.Ts["to1"]
t -= self.Ts["to2"]
if t < self.Ts["to1"]:
return -self.p.a * self.Ts["to1"] + self.p.a * t
return 0
def X1Open(self, t: float):
xm = self.p.Fd / self.p.k
t1 = min(t, self.Ts["to1"])
x0 = -self.p.a * t1 * t1 / 2
t2 = max(min(t - self.Ts["to1"], self.Ts["to2"]), 0)
x1 = -self.p.a * self.Ts["to1"] * t2
t3 = max(min(t - self.Ts["to2"]- self.Ts["to1"], self.Ts["to1"]), 0)
x2 = -self.p.a * self.Ts["to1"] * t3 + self.p.a * t3 * t3 / 2
return xm + x0 + x1 + x2 + self.x1Contact
def V2Open(self, t: float):
t = max(t-self.TStartOpen , 0)
if t < self.T2["to1"]:
return -self.p.a2 * t
t -= self.T2["to1"]
if t < self.T2["to2"]:
return -self.p.a2 * self.T2["to1"]
t -= self.T2["to2"]
if t < self.T2["to1"]:
return -self.p.a2 * self.T2["to1"] + self.p.a2 * t
return 0
def X2Open(self, t: float):
t = max(t-self.TStartOpen , 0)
t1 = min(t, self.T2["to1"])
x0 = -self.p.a2 * t1 * t1 / 2
t2 = max(min(t - self.T2["to1"], self.T2["to2"]), 0)
x1 = -self.p.a2 * self.T2["to1"] * t2
t3 = max(min(t - self.T2["to2"]- self.T2["to1"], self.T2["to1"]), 0)
x2 = -self.p.a2 * self.T2["to1"] * t3 + self.p.a2 * t3 * t3 / 2
return x0 + x1 + x2 + self.x2Contact
def FOpen(self, t: float):
x1 = self.X1Open(t)
x2 = self.X2Open(t)
F = self.p.k * max(0, (x1 + x2 - self.x1Contact - self.x2Contact))
return F
def calcPhaseClose(self, t: float):
return self.X1Close(t), self.X2Close(t), self.V1Close(t), self.V2Close(t), self.FClose(t)
def calcPhaseGrow(self, t: float):
return self.X1Grow(t), self.X2Grow(t), self.V1Grow(t), self.V2Grow(t), self.FGrow(t)
def calcPhaseOpen(self, t: float):
return self.X1Open(t), self.X2Open(t), self.V1Open(t), self.V2Open(t), self.FOpen(t)
def getSpecific(self, param : str, phase : str, t : float):
"""Получить значение величины в определенную фазу в момент времени t (с начала фазы)
Args:
param (str): Значение из списка X1 | X2 | V1 | V2 | F
phase (str): Значение из списка: Close | Grow | Open
t (float): Время
Returns:
Значение величины
"""
funcName = param + phase
try:
func = getattr(self, funcName)
except:
print("Wrong param or phase name")
return 0
return func(t)
def getVar(self, param : str, t : float):
if t < self.Ts["tclose"]:
return self.getSpecific(param, "Close", t)
t -= self.Ts["tclose"]
if t < self.Ts["tgrow"] :
return self.getSpecific(param, "Grow", t)
t -= self.Ts["tgrow"]
return self.getSpecific(param, "Open", t)
if __name__ == "__main__":
opt = OptAlgorithm()
import matplotlib.pyplot as plt
from matplotlib import use
print(opt.Ts)
print(opt.calcPhaseClose(opt.Ts["tclose"]))
print(opt.calcPhaseGrow(opt.Ts["tgrow"]))
print(opt.calcPhaseOpen(opt.Ts["topen"]))
print(opt.getSpecific("X1", "Close", 0))
use("GTK4Agg", force = True)
t = linspace(0, 0.5, 50000)
V1 = [opt.getVar("V1", tt) for tt in t]
V2 = [opt.getVar("V2", tt) for tt in t]
plt.plot(t, V2)
plt.plot(t, V1)
#plt.legend()
plt.show()

148
OptTimeCalculator.py Normal file
View File

@ -0,0 +1,148 @@
from numpy import sqrt, arcsin, arccos, cos, sin
from scipy.optimize import minimize
import numpy
from Params import Params
class OptTimeCalculator():
def __init__(self, params : Params):
self.Ts = {}
self.params = params
self.Tmax = 1
def tGrow(self, F):
return arcsin(F/(self.params.Ftg)) * sqrt(self.params.m/self.params.k)
def T(self, s, s2 = 0, debug = False):
if hasattr(s, "__len__"):
s = s[0]
v0q = min(sqrt(2 * self.params.a * s), self.params.v1limit)
v0 = min(v0q, sqrt(1/(self.params.k*self.params.m))* self.params.Ftg)
t1 = v0 / self.params.a
#test second
t21 = sqrt(s2/self.params.a2)
t21 = min(self.params.v2limit/self.params.a2, t21)
t22 = max(0, (s2 - (self.params.a2 * t21 * t21)) / self.params.v2limit)
T2 = t22 + 2 * t21
t2t = max(0, (s - (self.params.a * t1 * t1 /2)) / v0q)
#if debug:
#print("!", t1, t2t, "|", t21, t22, "|", t1+t2t, 2*t21+t22, t2t + t1 < T2)
if t2t + t1 < T2:
if debug:
print("bad time")
return 2 * self.Tmax
#print("t2", t2t)
s3 = s+((self.params.Fd - self.params.fl)/2 + self.params.fl)/self.params.k
t3 = sqrt(s3 / self.params.a)
v3max = min(t3 * self.params.a, self.params.v1limit)
t3 = v3max / self.params.a
t3q = max(0, (s3 - t3*t3*self.params.a)/v3max)
v0 = v0 * self.params.k
l = sqrt(self.params.awork**2 * self.params.m**2 + self.params.m/self.params.k * v0**2)
#print("v0", v0, v0/k)
Fmeet = (self.params.Ftg**2 - self.params.m/self.params.k*v0**2)/(2 * self.params.awork * self.params.m)
print(Fmeet)
#if debug:
#print(Fmeet)
Fq = self.params.Fq
if Fmeet > Fq:
if debug:
print("cant reach", Fmeet)
return self.Tmax
#print(Ftg - Fmeet)
if Fmeet - self.params.awork*self.params.m > l:
if debug:
print("wut")
return 3*self.Tmax
beta = self.params.awork * self.params.m
t2 = sqrt(self.params.m/self.params.k) * (arcsin((Fmeet - beta)/(l)) + arccos(sqrt(self.params.m/self.params.k) * v0/l))
if debug:
qtq = sqrt(self.params.k/self.params.m)
Fqq = -self.params.awork*self.params.m*cos(qtq*t2) + self.params.awork*self.params.m + v0 / qtq * sin(qtq*t2)
print("angles", (Fmeet / (2*beta) -beta)/(l), (sqrt(self.params.m/self.params.k) * v0/l), Fqq, Fmeet)
tend = self.tGrow(Fq) - self.tGrow(Fmeet)
vp = 1/sqrt(self.params.k * self.params.m) * sqrt(self.params.Ftg**2 - self.params.Fq**2)
ap = Fq / self.params.m
tprop = 2*vp / ap
#print(s, t1, t2, Fmeet, tend)
T = t1 + t2t + t2 + 2*t3 + t3q + tprop + tend
self.Ts = {"t1":t1, "t2":t2t, "tm":t2, "tend":tend, "t4":tprop, "to1":t3, "to2": t3q, "T":T,
"tclose":t1+t2t, "tgrow":t2+tend+tprop, "topen": 2*t3+t3q}
if debug:
print("phases", t1, t2t, "|", t2, tend, tprop, "|", 2*t3+t3q, "|", T)
print("tclose: ",t1+t2t, "tgrow:", t2+tend+tprop, "topen:", 2*t3+t3q, "all:", T)
return T
def calcSecond(self, Tstart, s2):
T1 = self.Ts["tclose"]
t1 = T1 / 2 - sqrt(T1**2 - 4 * s2 / self.params.a2) / 2
t2 = sqrt(T1 * T1 - 4 * s2 / self.params.a2)
T2 = self.Ts["topen"] - Tstart
to1 = T2 / 2 - sqrt(T2**2 - 4 * s2 / self.params.a2) / 2
to2 = sqrt(T2 * T2 - 4 * s2 / self.params.a2)
Ts = {"t1":t1, "t2":t2, "to1":to1, "to2": to2}
self.T2 = Ts
return self.T2
def calcOpt(self):
s0 = [0.04]
bnds = [(0.00001, None)]
res = minimize(self.T, s0, method='Nelder-Mead', tol=1e-7, bounds=bnds)
nonbound = res.x
bound = res.x
if nonbound < self.params.smin1:
#print("IS SMIN")
bound = max(nonbound, self.params.smin1)
#T(q, debug = True)
return nonbound, bound
def showPlot(self):
N = 50000
ss = numpy.linspace(0.0001, 0.030, N)
Ts = numpy.array([self.T(si) for si in ss])
import matplotlib.pyplot as plt
from matplotlib import use
q, qq = self.calcOpt()
q = 1000*q
qq = 1000 * qq
use("GTK4Agg", force = True)
plt.plot(ss*1000, Ts*1000, "red", label = "T(S)")
plt.plot(numpy.ones(N) * qq, Ts*1000, label = "factOpt")
plt.plot(numpy.ones(N) * q, Ts*1000, "black", label = "trueOpt")
plt.title("T(s)")
plt.xlabel("s, мм")
plt.ylabel("T, мс")
plt.legend()
plt.savefig("model.png")
plt.show()
def getTimes(self, s):
self.T(s, debug=False)
return self.Ts
if __name__ == "__main__":
opt = OptTimeCalculator()
nonbound, bound = opt.calcOpt()
print("nonbound", nonbound*1000, "мм")
print(opt.getTimes(nonbound))
print("bound", bound*1000, "мм")
print(opt.getTimes(bound))

28
Params.py Normal file
View File

@ -0,0 +1,28 @@
import json
from numpy import sqrt
class Params():
def __init__(self, path, file):
config = self.json_load(path, file)
for key, value in config.items():
setattr(self, key, value)
self.smin1t = self.smin1 - self.dblock / 2
self.smin2t = self.smin2 - self.dblock / 2
self.awork = self.umax / (self.l * self.m)
self.fl = self.Fd * (1-self.kturn)
self.flon = self.Fd * self.kturn
self.Fprop = self.kprop * self.Fd
self.Ftg = self.Fd * (1 - self.kprop)
self.Fq = 3 * self.Fd / 2 - 1/2 * sqrt(self.Fd*self.Fd + 16 * self.Fd * self.Fprop - 8 * self.Fprop**2)
def json_load(self, path : str, filename : str):
with open("/".join([path, filename]), "r") as f:
config = json.load(f)
return config

94
Requestinator.py Normal file
View File

@ -0,0 +1,94 @@
from plantuml import PlantUML
from os.path import abspath
class Request:
def __init__(self):
self.server = PlantUML(url='http://www.plantuml.com/plantuml/svg/')
self.clear()
def _startUML(self):
self.reqArr.append('@startuml')
def _endUML(self):
self.reqArr.append('@enduml')
def clear(self):
self.timestamps = {}
self.reqArr = []
self.variables = []
self.lineStyles = {}
self.stringUML = ''
self._startUML()
def addAnalog(self, name, string):
self.variables.append(f'analog "{string}" as {name}')
def addBinary(self, name, string, style = ''):
if style: name = name + '<<' + style + '>>'
self.variables.append(f'binary "{string}" as {name}')
def addClock(self, name, string):
self.variables.append(f'clock "{string}" as {name}')
def addConcise(self, name, string):
self.variables.append(f'concise "{string}" as {name}')
def addRobust(self, name, string):
self.variables.append(f'robust "{string}" as {name}')
def appendStr(self, string = ''):
self.variables.append(string)
def addLineStyle(self, name, color = 'green', thicknes = 1):
self.lineStyles[name] = [f'LineColor {color}', f'LineThickness {thicknes}']
def generateSVG(self):
self._compileUML()
filename = abspath('UML.txt')
self.server.processes_file(filename, outfile='UML.svg')
#result = self.server.processes(self.stringUML)
#return result
def setTimestamps(self, name, input):
for time, state in input:
try:
self.timestamps[f'@{time}'].append(f'{name} is {state}')
except KeyError:
self.timestamps[f'@{time}'] = [f'{name} is {state}']
def _addTimestamp(self, timecode, vars):
self.reqArr.append(timecode)
for var in vars:
self.reqArr.append(var)
def _addHeader(self):
self.reqArr.append('<style>')
if self.lineStyles:
self.reqArr.append('timingDiagram {')
for name, style in self.lineStyles.items():
self.reqArr.append(f' .{name} ' + '{')
for param in style: self.reqArr.append(' ' + str(param))
self.reqArr.append(' }')
self.reqArr.append('}')
self.reqArr.append('</style>')
def _addVariables(self):
for var in self.variables: self.reqArr.append(str(var))
def _compileUML(self):
self._addHeader()
self._addVariables()
if self.timestamps:
for key, item in self.timestamps.items():
self._addTimestamp(key, item)
self._endUML()
self.stringUML = [line + '\n' for line in self.reqArr]
with open('UML.txt', 'w', encoding='utf-8') as file:
file.writelines(self.stringUML)

1
UML.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 17 KiB

76
UML.txt Normal file
View File

@ -0,0 +1,76 @@
@startuml
<style>
timingDiagram {
.biba {
LineColor red
LineThickness 2
}
}
</style>
binary "Electrode Closing Algorithm Done" as bool_0<<biba>>
binary "Electrode Closing Algorithm Execute" as bool_1<<biba>>
binary "STEP 1: ME Move to S1" as bool_2<<biba>>
binary "STEP 1: ME Move to S1 Complete" as bool_3<<biba>>
binary "STEP 2: ME Move to P2" as bool_4<<biba>>
binary "STEP 2: ME Move to P2 Complete" as bool_5<<biba>>
binary "STEP 3: ME Hold P2" as bool_6<<biba>>
binary "STEP 3: ME Hold P2 AND Condition Start Force Control" as bool_7<<biba>>
binary "STEP 4: ME Force Control" as bool_8<<biba>>
concise "Real data" as RD
concise "Client data" as CD
concise "Ideal data" as ID
@0.0
bool_0 is low
bool_1 is high
bool_2 is high
bool_3 is low
bool_4 is low
bool_5 is low
bool_6 is low
bool_7 is low
bool_8 is low
RD is closure #green
CD is closure
ID is closure #yellow
@191.0000090720132
bool_0 is high
bool_1 is low
RD is {-}
@106.5000050584785
bool_2 is low
bool_3 is low
bool_4 is high
@106.0000050347298
bool_3 is high
@157.5000074808485
bool_4 is low
bool_6 is high
@165.0000078370794
bool_6 is low
bool_7 is low
bool_8 is high
RD is {-}
@164.5000078133307
bool_7 is high
RD is compression #green
@165.0
CD is {-}
@166.0
CD is compression
@176.0
CD is {-}
@210.0
CD is opening
@300.0
CD is {-}
@18.32885128339674
ID is {-}
@18.42885128339674
ID is compression #yellow
@44.99013343875682
ID is {-}
@45.09013343875683
ID is opening #yellow
@91.75944699185315
ID is {-}
@enduml

254
main.py Normal file
View File

@ -0,0 +1,254 @@
from Requestinator import Request
from DataParsinator import DataParser
import pyqtgraph as pg
from pyqtgraph.Qt import QtCore, QtGui
import numpy as np
#нижний fe x1
import qt_settings as qts
from OptAlgorithm import OptAlgorithm
class Application:
def __init__(self):
pg.setConfigOptions(antialias=True)
self.opt = OptAlgorithm()
self.scaler = 1000
self.parser = DataParser(self.scaler)
self.r = Request()
self._getIdealTimings()
self._init_app()
self.updateUML()
self.WeldTime = 0.5 #[sec]
self.alpha = 100 #[0-255 прозрачность фона]
def _init_app(self):
self.app = pg.mkQApp("Plotting")
self.win = pg.GraphicsLayoutWidget(show=True, title="")
self.win.resize(1000,600)
self.win.setWindowTitle('')
self.p11, self.l11 = self._init_graph('Electrode force, closure', 'Force', 'N', 'Time', 'ms')
#self.p21, _ = self._init_graph('Electrode force, compression', 'Force', 'N', 'Time', 'ms')
#self.p31, _ = self._init_graph('Electrode force, compression', 'Force', 'N', 'Time', 'ms')
self.win.nextRow()
self.p12, self.l12 = self._init_graph('Rotor Position, closure', 'Posicion', 'mm', 'Time', 'ms')
#self.p22, _ = self._init_graph('Rotor Position, compression', 'Posicion', 'mm', 'Time', 'ms')
#self.p32, _ = self._init_graph('Rotor Position, compression', 'Posicion', 'mm', 'Time', 'ms')
self.win.nextRow()
self.p13, self.l13 = self._init_graph('Rotor Speed, closure', 'Speed', 'mm/s', 'Time', 'ms')
#self.p23, _ = self._init_graph('Rotor Speed, compression', 'Speed', 'mm/s', 'Time', 'ms')
#self.p33, _ = self._init_graph('Rotor Speed, compression', 'Speed', 'mm/s', 'Time', 'ms')
self.win.nextRow()
self.p12.setXLink(self.p11)
self.p13.setXLink(self.p11)
self.p11.setAutoVisible(x=False, y=True)
self.p12.setAutoVisible(x=False, y=True)
self.p13.setAutoVisible(x=False, y=True)
def _init_graph(self, title, Yname, Yunits, Xname, Xunits):
p11 = self.win.addPlot(title = title)
p11.showGrid(x=True, y=True)
p11.setLabel('left', Yname, units=Yunits)
p11.setLabel('bottom', Xname, units=Xunits)
legend1 = pg.LegendItem((80,60), offset=(70,20))
legend1.setParentItem(p11)
return p11, legend1
def updateUML(self, path = '2024_10_28-17_03_34.csv'):
real, client, ideal, bool = self._form_UMLdata(path)
self._requestSVG(real, client, ideal, bool)
def updatePlots(self):
#self._plotRealData()
times = np.arange(20000)/10
self._plotIdealData(times)
def _requestSVG(self, real_data, client_data, ideal_data, bool_data):
try:
self.r.clear()
self.r.addLineStyle('biba', 'red', 2)
for i, [signal, changes] in enumerate(bool_data.items()):
name = 'bool_'+str(i)
self.r.addBinary(name, str(signal), 'biba')
self.r.setTimestamps(name, changes)
self.r.addConcise('RD', 'Real data')
self.r.setTimestamps('RD', real_data)
self.r.addConcise('CD', 'Client data')
self.r.setTimestamps('CD', client_data)
self.r.addConcise('ID', 'Ideal data')
self.r.setTimestamps('ID', ideal_data)
self.r.generateSVG()
except Exception as e:
print ('Ну, svg у нас нет')
def _form_UMLdata(self, path):
scaler = self.scaler
sig = [
'Electrode Closing Algorithm Execute', #Начало закрытия
'Electrode Closing Algorithm Done',
'STEP 3: ME Hold P2 AND Condition Start Force Control', #Конец закрытия и Начало набора усилия
'STEP 4: ME Force Control',
'Position Control ME', #Начало разъезда или 'Posision Control Activated FE'
'Position Control FE', #Начало разъезда
'Position Control Completed ME', #Конец разъезда
'Position Control Completed FE', #Конец разъезда
'STEP 4: ME Force Control Complete', #Конец набора усилия
]
self.parser.setData(path)
self.bool_dict = self.parser.getBoolDict()
self.float_dict = self.parser.getFloatDict()
closure = self.__getTime([sig[0], sig[1]])
compression = self.__getTime([sig[2], sig[3]])
#opening = self.__getTime([sig[4], sig[5], sig[6], sig[7]])
real_data = [
[closure[0], 'closure #green'],
[closure[1], '{-}'],
[compression[0], 'compression #green'],
[compression[1], '{-}'],
#[max(opening[0:2]), 'opening #green'],
#[max(opening[2:4]), '{-}'],
]
client_data = [
[0.0*scaler, 'closure'],
[0.165*scaler, '{-}'],
[0.166*scaler, 'compression'],
[0.176*scaler, '{-}'],
#[0.180*scaler, 'welding'],
#[0.200*scaler, '{-}'],
[0.210*scaler, 'opening'],
[0.300*scaler, '{-}'],
]
ideal_data = [
[0.0*scaler, 'closure #yellow'],
[self.idealTime[0]*scaler, '{-}'],
[(self.idealTime[0] + 0.0001)*scaler, 'compression #yellow'],
[(self.idealTime[0] + self.idealTime[1])*scaler, '{-}'],
#[0.*scaler, 'welding #yellow'],
#[0.*scaler, '{-}'],
[(self.idealTime[0] + self.idealTime[1] + 0.0001)*scaler, 'opening #yellow'],
[(self.idealTime[0] + self.idealTime[1] + self.idealTime[2])*scaler, '{-}'],
]
return real_data, client_data, ideal_data, self.bool_dict
def __getTime(self, signals = '', states = ''):
res = []
for i, sig in enumerate(signals):
if states: state = states[i]
else: state = 'high'
index1 = next ((i for i, _ in enumerate(self.bool_dict[sig]) if _[1]==state), -1)
time = self.bool_dict[sig][index1][0]
res.append(time)
return res
def _getIdealTimings(self):
data = self.opt.Ts
self.idealTime = [data['tclose'], data['tgrow'], data['topen']]
def _form_idealdatGraph(self, times):
self.idealPhase0 = (self.idealTime[0])*self.scaler #Подъезд
self.idealPhase1 = (self.idealTime[0]+ self.idealTime[1])*self.scaler #Сжатие
self.idealPhase2 = (self.idealTime[0]+ self.idealTime[1] + self.WeldTime)*self.scaler #Сварка
self.idealPhase3 = (self.idealTime[0]+ self.idealTime[1] + self.idealTime[2] + self.WeldTime)*self.scaler #Разъезд
data = []
for time in times:
if time <= self.idealPhase0:
x_fe, x_me, v_fe, v_me, f = self.opt.calcPhaseClose(time/self.scaler)
elif time <= self.idealPhase1:
x_fe, x_me, v_fe, v_me, f = self.opt.calcPhaseGrow(time/self.scaler-self.idealTime[0])
elif time <= self.idealPhase2:
x_fe, x_me, v_fe, v_me, f = data[-1]
elif time <= self.idealPhase3:
x_fe, x_me, v_fe, v_me, f = self.opt.calcPhaseOpen(time/self.scaler-self.idealTime[0]-self.idealTime[1]-self.WeldTime)
else:
x_fe, x_me, v_fe, v_me, f = 0, 0, 0, 0, 0
data.append([x_fe, x_me, v_fe, v_me, f])
data = np.array(data).T
return data
def _plotRealData(self):
for i, (key, dat) in enumerate(self.float_dict.items()):
dat = np.array(dat).T
dat[0] = dat[0]*self.scaler
curve = pg.PlotDataItem(dat[0], dat[1], pen=pg.mkPen(color=qts.colors[i], width=2), name=key, autoDownsample=True, downsample=True)
if 'Electrode Force' in key:
self.p11.addItem(curve)
self.l11.addItem(curve, key)
elif 'Rotor Position' in key:
self.p12.addItem(curve)
self.l12.addItem(curve, key)
elif 'Rotor Speed' in key:
self.p13.addItem(curve)
self.l13.addItem(curve, key)
return dat[0]
def _plotIdealData(self, times):
data = self._form_idealdatGraph(times)
x_fe = pg.PlotDataItem(times, data[0]*1000, pen=pg.mkPen(color=qts.colors[8], width=2), name='x_fe', autoDownsample=True, downsample=True)
x_me = pg.PlotDataItem(times, data[1]*1000, pen=pg.mkPen(color=qts.colors[9], width=2), name='x_me', autoDownsample=True, downsample=True)
v_fe = pg.PlotDataItem(times, data[2]*1000, pen=pg.mkPen(color=qts.colors[8], width=2), name='v_fe', autoDownsample=True, downsample=True)
v_me = pg.PlotDataItem(times, data[3]*1000, pen=pg.mkPen(color=qts.colors[9], width=2), name='v_me', autoDownsample=True, downsample=True)
f = pg.PlotDataItem(times, data[4], pen=pg.mkPen(color=qts.colors[8], width=2), name='f', autoDownsample=True, downsample=True)
self.p11.addItem(f)
self.l11.addItem(f, 'Ideal force')
self.p12.addItem(x_fe)
self.l12.addItem(x_fe, 'FE POS')
self.p12.addItem(x_me)
self.l12.addItem(x_me, 'ME POS')
self.p13.addItem(v_fe)
self.l13.addItem(v_fe, 'FE VEL')
self.p13.addItem(v_me)
self.l13.addItem(v_me, 'ME VEL')
self._addBackgroundSplitter()
def _addBackgroundSplitter(self):
alpha = self.alpha
x = [[0, self.idealPhase0],
[self.idealPhase0, self.idealPhase1],
[self.idealPhase1, self.idealPhase2],
[self.idealPhase2, self.idealPhase3]]
y01 = [100000000000, 100000000000]
y0_1 = [-100000000000, -100000000000]
for i, _ in enumerate(x):
a01 = pg.PlotDataItem(_, y01, pen=pg.mkPen(color=qts.colors[8], width=2), name=' ')
a0_1 = pg.PlotDataItem(_, y0_1, pen=pg.mkPen(color=qts.colors[8], width=2), name=' ')
bg1 = pg.FillBetweenItem(a01, a0_1, qts.RGBA[i]+(alpha,))
bg2 = pg.FillBetweenItem(a01, a0_1, qts.RGBA[i]+(alpha,))
bg3 = pg.FillBetweenItem(a01, a0_1, qts.RGBA[i]+(alpha,))
self.p11.addItem(bg1)
self.p12.addItem(bg2)
self.p13.addItem(bg3)
self.p11.setYRange(-1000, 5000)
self.p12.setYRange(-50, 250)
self.p13.setYRange(-400, 400)
if __name__ == '__main__':
app = Application()
app.updatePlots()
pg.exec()

19
params.json Normal file
View File

@ -0,0 +1,19 @@
{
"a" : 25.41,
"dblock" : 4.5e-3,
"v1limit" : 0.108,
"a2" : 35.81,
"v2limit" : 0.678,
"m" : 257,
"k" : 1759291,
"kprop" : 0.05,
"kturn" : 0.0,
"Fd" : 5000,
"umax" : 20,
"l" : 0.00125,
"smin1" : 0.004,
"smin2" : 0.004,
"tc" : 2,
"x10" : 0.080,
"x20" : 0.080
}

256
qt_settings.py Normal file
View File

@ -0,0 +1,256 @@
white_style = """
QMainWindow {
background-color: #ffffff;
font-family: "Segoe UI", sans-serif;
font-size: 14px;
}
QMessageBox {
background-color: #ffffff;
font-family: "Segoe UI", sans-serif;
font-size: 14px;
}
QPushButton {
background-color: #d3d3d3;
color: #000000;
padding: 10px 20px;
border: none;
border-radius: 4px;
font-family: "Segoe UI", sans-serif;
font-size: 14px;
}
QPushButton:hover:!disabled {
background-color: #b0b0b0;
}
QPushButton:disabled {
background-color: #a9a9a9;
color: #7f7f7f;
}
QCheckBox {
color: #000000;
font-size: 14px;
font-family: "Segoe UI", sans-serif;
}
QLineEdit {
background-color: #f0f0f0;
color: #000000;
padding: 5px;
border: 1px solid #a9a9a9;
border-radius: 4px;
font-family: "Segoe UI", sans-serif;
font-size: 14px;
}
QLabel {
color: #000000;
font-size: 16px;
font-family: "Segoe UI", sans-serif;
}
QGroupBox {
color: #000000;
font-family: "Segoe UI", sans-serif;
font-size: 14px;
}
QRadioButton {
color: #000000;
font-family: "Segoe UI", sans-serif;
font-size: 14px;
}
"""
dark_style = """
QMainWindow {
background-color: #0D1117; /* Тёмный, современный цвет для фона */
font-family: "Segoe UI", sans-serif;
font-size: 14px;
}
QMessageBox {
background-color: #161B22;
font-family: "Segoe UI", sans-serif;
font-size: 14px;
}
QPushButton {
background-color: #FFCC00; /* Яркий жёлтый цвет для акцента */
color: #0D1117; /* Темный цвет текста для контраста с желтым */
padding: 12px 25px;
border: 2px solid #E6B800;
border-radius: 8px;
font-family: "Segoe UI", sans-serif;
font-size: 16px;
font-weight: bold;
}
QPushButton:hover:!disabled {
background-color: #FFD700; /* Светлый желтый цвет для эффекта наведения */
}
QPushButton:disabled {
background-color: #555555;
color: #cccccc;
border: none;
}
QCheckBox {
color: #ffffff;
font-size: 14px;
font-family: "Segoe UI", sans-serif;
font-weight: bold;
}
QLineEdit {
background-color: #21262D;
color: #ffffff;
padding: 8px;
border: 2px solid #30363D;
border-radius: 6px;
font-family: "Segoe UI", sans-serif;
font-size: 16px;
font-weight: bold;
}
QLabel {
color: #ffffff;
font-size: 16px;
font-weight: bold;
font-family: "Segoe UI", sans-serif;
}
QGroupBox {
color: #ffffff;
font-family: "Segoe UI", sans-serif;
font-size: 16px;
font-weight: bold;
border: 1px solid #30363D;
border-radius: 6px;
margin-top: 10px;
}
QRadioButton {
color: #ffffff;
font-family: "Segoe UI", sans-serif;
font-size: 16px;
font-weight: bold;
}
QSpinBox {
font-family: "Segoe UI", sans-serif;
font-size: 16px;
font-weight: bold;
}
QDoubleSpinBox {
font-family: "Segoe UI", sans-serif;
font-size: 16px;
font-weight: bold;
}
"""
dis_robots = """
QPushButton {
background-color: #555555;
color: #cccccc;
padding: 12px 25px;
border: none;
border-radius: 8px;
font-family: "Segoe UI", sans-serif;
font-size: 16px;
}
QWidget#robot {
border: none;
padding: 5px;
background-color: rgba(33, 33, 33, 100);
}
"""
selected_robot = """
QWidget#robot {
border: 2px solid #E6B800;
border-radius: 10px;
padding: 5px;
}
"""
en_button = """
QPushButton {
background-color: #FFCC00;
color: #0D1117;
padding: 12px 25px;
border: 2px solid #E6B800;
border-radius: 8px;
font-family: "Segoe UI", sans-serif;
font-size: 16px;
font-weight: bold;
}
QPushButton:hover:!disabled {
background-color: #FFD700;
}
"""
dis_button = """
QPushButton {
background-color: #555555;
color: #cccccc;
padding: 12px 25px;
border: none;
border-radius: 8px;
font-family: "Segoe UI", sans-serif;
font-size: 16px;
}
"""
saved_button = """
QPushButton {
background-color: #28a745;
color: #ffffff;
padding: 12px 25px;
border: none;
border-radius: 8px;
font-family: "Segoe UI", sans-serif;
font-size: 16px;
font-weight: bold;
}
"""
start_button = """
QPushButton {
background-color: #FFCC00;
color: #0D1117;
padding: 20px 40px;
border: 2px solid #E6B800;
border-radius: 10px;
font-family: "Segoe UI", sans-serif;
font-size: 24px;
font-weight: bold;
background-image: linear-gradient(to bottom, #FFD700, #FFCC00);
}
QPushButton:hover:!disabled {
background-color: #FFD700;
}
QPushButton:pressed {
background-color: #E6B800;
padding-top: 22px;
padding-bottom: 18px;
}
QPushButton:disabled {
background-color: #555555;
color: #cccccc;
border: none;
}
"""
colors = [
'#FF6F61', # яркий коралловый
'#6B5B95', # приглушенный фиолетовый
'#88B04B', # яркий зеленый
'#F7CAC9', # светлый розовый
'#92A8D1', # светло-голубой
'#955251', # теплый терракотовый
'#B565A7', # лавандовый
'#009B77', # глубокий бирюзовый
'#DD4124', # ярко-красный
'#45B8AC' # мягкий мятный
]
RGBA = [(124, 252, 0),
(255, 215, 0),
(255, 69, 0),
(123, 104, 238)
]