Skip to content

Commit 05625e9

Browse files
committed
Added editable text fields for PID gains
1 parent 3bdec39 commit 05625e9

1 file changed

Lines changed: 92 additions & 92 deletions

File tree

autotune/autotune.py

Lines changed: 92 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737
"""
3838

3939
import sys
40-
from PyQt5.QtWidgets import QDialog, QApplication, QLabel, QRadioButton, QSlider, QPushButton, QVBoxLayout, QHBoxLayout, QFormLayout, QFileDialog, QLineEdit, QSpinBox, QDoubleSpinBox, QMessageBox, QCheckBox, QTableWidget, QTableWidgetItem, QHeaderView, QGroupBox, QComboBox, QTabWidget, QWidget, QGridLayout
40+
from PyQt5.QtWidgets import QDialog, QApplication, QLabel, QRadioButton, QSlider, QPushButton, QVBoxLayout, QHBoxLayout, QFormLayout, QFileDialog, QLineEdit, QSpinBox, QDoubleSpinBox, QMessageBox, QCheckBox, QTableWidget, QTableWidgetItem, QHeaderView, QGroupBox, QComboBox, QTabWidget, QWidget, QGridLayout, QSizePolicy
4141
from PyQt5.QtCore import Qt
4242

4343
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
@@ -53,6 +53,13 @@
5353

5454
from data_selection_window import DataSelectionWindow
5555

56+
def isNumber(value):
57+
try:
58+
float(value)
59+
return True
60+
except ValueError:
61+
return False
62+
5663
class Window(QDialog):
5764
def __init__(self, parent=None):
5865
super(Window, self).__init__(parent)
@@ -70,10 +77,12 @@ def __init__(self, parent=None):
7077
self.rise_time = 0.13
7178
self.damping_index = 0.0
7279
self.detune_coeff = 0.5
73-
self.kc = 0.01
74-
self.ki = 0.0
75-
self.kd = 0.0
76-
self.kff = 0.0
80+
self.gains = {
81+
"P": 0.01,
82+
"I": 0.0,
83+
"D": 0.0,
84+
"FF": 0.0
85+
}
7786
self.figure = plt.figure(1)
7887
self.figure.subplots_adjust(hspace=0.5, wspace=1.0)
7988
self.num = []
@@ -287,6 +296,22 @@ def printImproperTfError(self):
287296
msg.exec_()
288297

289298
def createPidLayout(self):
299+
self.gain_line_edit = {}
300+
self.gain_slider = {}
301+
self.parallel_gain_lbl = {}
302+
slider_props = {
303+
"P": {"min": 0.001, "max": 4.0, "step": 0.001},
304+
"I": {"min": 0.0, "max": 20.0, "step": 0.1},
305+
"D": {"min": 0.0, "max": 0.2, "step": 0.001},
306+
"FF": {"min": 0.0, "max": 1.0, "step": 0.001}
307+
}
308+
309+
def make_slider_callback(gain):
310+
return lambda: self.updateGainFromSlider(gain)
311+
312+
def make_line_edit_callback(gain):
313+
return lambda: self.updateGainFromLineEdit(gain)
314+
290315
layout_pid = QGridLayout()
291316

292317
layout_options = QHBoxLayout()
@@ -304,87 +329,62 @@ def createPidLayout(self):
304329
layout_pid.addWidget(QLabel("Ideal/Standard\nKp * [1 + Ki + Kd]"), 0, 2)
305330
layout_pid.addWidget(QLabel("Parallel\nKp + Ki + Kd"), 0, 3)
306331

307-
layout_pid.addWidget(QLabel("K"), 1, 0)
308-
self.slider_k = DoubleSlider(Qt.Horizontal)
309-
self.slider_k.setMinimum(0.001)
310-
self.slider_k.setMaximum(4.0)
311-
self.slider_k.setInterval(0.001)
312-
self.slider_k.valueChanged.connect(self.updateLabelK)
313-
layout_pid.addWidget(self.slider_k, 1, 1)
314-
self.lbl_k_standard = QLabel("{:.3f}".format(self.kc))
315-
layout_pid.addWidget(self.lbl_k_standard, 1, 2)
316-
self.lbl_k_parallel = QLabel("{:.3f}".format(self.kc))
317-
layout_pid.addWidget(self.lbl_k_parallel, 1, 3)
318-
319-
layout_pid.addWidget(QLabel("I"), 2, 0)
320-
self.slider_i = DoubleSlider(Qt.Horizontal)
321-
self.slider_i.setMinimum(0.0)
322-
self.slider_i.setMaximum(20.0)
323-
self.slider_i.setInterval(0.1)
324-
self.slider_i.valueChanged.connect(self.updateLabelI)
325-
layout_pid.addWidget(self.slider_i, 2, 1)
326-
self.lbl_i_standard = QLabel("{:.2f}".format(self.ki))
327-
layout_pid.addWidget(self.lbl_i_standard, 2, 2)
328-
self.lbl_i_parallel = QLabel("{:.2f}".format(self.kc * self.ki))
329-
layout_pid.addWidget(self.lbl_i_parallel, 2, 3)
330-
331-
layout_pid.addWidget(QLabel("D"), 3, 0)
332-
self.slider_d = DoubleSlider(Qt.Horizontal)
333-
self.slider_d.setMinimum(0.0)
334-
self.slider_d.setMaximum(0.2)
335-
self.slider_d.setInterval(0.001)
336-
self.slider_d.valueChanged.connect(self.updateLabelD)
337-
layout_pid.addWidget(self.slider_d, 3, 1)
338-
self.lbl_d_standard = QLabel("{:.3f}".format(self.kd))
339-
layout_pid.addWidget(self.lbl_d_standard, 3, 2)
340-
self.lbl_d_parallel = QLabel("{:.4f}".format(self.kc * self.kd))
341-
layout_pid.addWidget(self.lbl_d_parallel, 3, 3)
342-
343-
layout_pid.addWidget(QLabel("FF"), 4, 0)
344-
self.slider_ff = DoubleSlider(Qt.Horizontal)
345-
self.slider_ff.setMinimum(0.0)
346-
self.slider_ff.setMaximum(5.0)
347-
self.slider_ff.setInterval(0.01)
348-
self.slider_ff.valueChanged.connect(self.updateLabelFF)
349-
layout_pid.addWidget(self.slider_ff, 4, 1)
350-
self.lbl_ff_standard = QLabel("{:.3f}".format(self.kff))
351-
layout_pid.addWidget(self.lbl_ff_standard, 4, 2)
352-
self.lbl_ff_parallel = QLabel("{:.3f}".format(self.kff))
353-
layout_pid.addWidget(self.lbl_ff_parallel, 4, 3)
332+
row = 1
333+
for gain in self.gains.keys():
334+
if gain == "FF":
335+
layout_pid.addWidget(QLabel("{}".format(gain)), row, 0)
336+
else:
337+
layout_pid.addWidget(QLabel("K{}".format(gain.lower())), row, 0)
338+
339+
self.gain_slider[gain] = DoubleSlider(Qt.Horizontal)
340+
self.gain_slider[gain].setMinimum(slider_props[gain]["min"])
341+
self.gain_slider[gain].setMaximum(slider_props[gain]["max"])
342+
self.gain_slider[gain].setInterval(slider_props[gain]["step"])
343+
self.gain_slider[gain].valueChanged.connect(make_slider_callback(gain))
344+
layout_pid.addWidget(self.gain_slider[gain], row, 1)
345+
346+
self.gain_line_edit[gain] = QLineEdit("{:.3f}".format(self.gains[gain]))
347+
self.gain_line_edit[gain].setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed)
348+
self.gain_line_edit[gain].setMinimumWidth(0)
349+
self.gain_line_edit[gain].setMinimumSize(0, 0)
350+
self.gain_line_edit[gain].setAlignment(Qt.AlignCenter)
351+
self.gain_line_edit[gain].textChanged.connect(make_line_edit_callback(gain))
352+
layout_pid.addWidget(self.gain_line_edit[gain], row, 2)
353+
354+
if gain == "P" or gain == "FF":
355+
self.parallel_gain_lbl[gain] = QLabel("{:.4f}".format(self.gains[gain]))
356+
else:
357+
self.parallel_gain_lbl[gain] = QLabel("{:.4f}".format(self.gains["P"] * self.gains[gain]))
358+
self.parallel_gain_lbl[gain].setAlignment(Qt.AlignCenter)
359+
layout_pid.addWidget(self.parallel_gain_lbl[gain], row, 3)
360+
361+
row += 1
354362

355363
return layout_pid
356364

357-
def updateLabelK(self):
358-
self.kc = self.slider_k.value()
359-
self.lbl_k_standard.setText("{:.3f}".format(self.kc))
360-
self.lbl_k_parallel.setText("{:.3f}".format(self.kc))
361-
362-
# Kc also modifies the Ki and Kd gains of the parallel form
363-
self.lbl_i_parallel.setText("{:.2f}".format(self.kc * self.ki))
364-
self.lbl_d_parallel.setText("{:.4f}".format(self.kc * self.kd))
365-
if self.slider_k.isSliderDown():
366-
self.updateClosedLoop()
367-
368-
def updateLabelI(self):
369-
self.ki = self.slider_i.value()
370-
self.lbl_i_standard.setText("{:.2f}".format(self.ki))
371-
self.lbl_i_parallel.setText("{:.2f}".format(self.kc * self.ki))
372-
if self.slider_i.isSliderDown():
365+
def updateGainFromSlider(self, gain : str):
366+
if self.gain_slider[gain].hasFocus():
367+
self.gains[gain] = self.gain_slider[gain].value()
368+
self.gain_line_edit[gain].setText("{:.3f}".format(self.gains[gain]))
369+
self.updateGainLabels(gain)
370+
if self.gain_slider[gain].isSliderDown():
371+
self.updateClosedLoop()
372+
373+
def updateGainFromLineEdit(self, gain : str):
374+
if isNumber(self.gain_line_edit[gain].text()) and self.gain_line_edit[gain].hasFocus():
375+
self.gains[gain] = float(self.gain_line_edit[gain].text())
376+
self.gain_slider[gain].setValue(self.gains[gain])
377+
self.updateGainLabels(gain)
373378
self.updateClosedLoop()
374379

375-
def updateLabelD(self):
376-
self.kd = self.slider_d.value()
377-
self.lbl_d_standard.setText("{:.3f}".format(self.kd))
378-
self.lbl_d_parallel.setText("{:.4f}".format(self.kc * self.kd))
379-
if self.slider_d.isSliderDown():
380-
self.updateClosedLoop()
381-
382-
def updateLabelFF(self):
383-
self.kff = self.slider_ff.value()
384-
self.lbl_ff_standard.setText("{:.3f}".format(self.kff))
385-
self.lbl_ff_parallel.setText("{:.3f}".format(self.kff))
386-
if self.slider_ff.isSliderDown():
387-
self.updateClosedLoop()
380+
def updateGainLabels(self, gain : str):
381+
if gain == "FF":
382+
self.parallel_gain_lbl[gain].setText("{:.4f}".format(self.gains[gain]))
383+
else:
384+
# Kp also modifies the Ki and Kd gains of the parallel form
385+
self.parallel_gain_lbl["P"].setText("{:.4f}".format(self.gains["P"]))
386+
self.parallel_gain_lbl["I"].setText("{:.4f}".format(self.gains["P"] * self.gains["I"]))
387+
self.parallel_gain_lbl["D"].setText("{:.4f}".format(self.gains["P"] * self.gains["D"]))
388388

389389
def createGmvcLayout(self):
390390
layout_gmvc = QFormLayout()
@@ -553,20 +553,20 @@ def computeController(self):
553553
sigma = self.rise_time # rise time
554554
delta = self.damping_index # damping property, set between 0 and 2 (1 for Butterworth)
555555
lbda = self.detune_coeff
556-
(self.kc, self.ki, self.kd) = computePidGmvc(self.num, self.den, self.dt, sigma, delta, lbda)
556+
(self.gains["P"], self.gains["I"], self.gains["D"]) = computePidGmvc(self.num, self.den, self.dt, sigma, delta, lbda)
557557
#TODO:find a better solution
558-
self.ki /= 5.0
558+
self.gains["I"] /= 5.0
559559
static_gain = sum(self.num) / sum(self.den)
560-
self.kff = max(1 / static_gain, 0.0)
560+
self.gains["FF"] = max(1 / static_gain, 0.0)
561561

562562
self.updateKIDSliders()
563563
self.updateClosedLoop()
564564

565565
def updateKIDSliders(self):
566-
self.slider_k.setValue(self.kc)
567-
self.slider_i.setValue(self.ki)
568-
self.slider_d.setValue(self.kd)
569-
self.slider_ff.setValue(self.kff)
566+
for gain in self.gains.keys():
567+
self.gain_line_edit[gain].setText("{:.3f}".format(self.gains[gain]))
568+
self.gain_slider[gain].setValue(self.gains[gain])
569+
self.updateGainLabels(gain)
570570

571571
def updateClosedLoop(self):
572572
if not self.is_system_identified:
@@ -575,10 +575,10 @@ def updateClosedLoop(self):
575575
num = self.num
576576
den = self.den
577577
dt = self.dt
578-
kc = self.kc
579-
ki = self.ki
580-
kd = self.kd
581-
kff = self.kff
578+
kc = self.gains["P"]
579+
ki = self.gains["I"]
580+
kd = self.gains["D"]
581+
kff = self.gains["FF"]
582582

583583
delays = ctrl.TransferFunction([1], np.append([1], np.zeros(self.sys_id_delays)), dt, inputs='r', outputs='rd')
584584
plant = ctrl.TransferFunction(num, den, dt, inputs='u', outputs='plant_out')

0 commit comments

Comments
 (0)