From 08b02a5a5d42286e5c2f439508b1acd7f59f8e9a Mon Sep 17 00:00:00 2001 From: jazzpi Date: Fri, 12 Aug 2022 13:01:44 +0200 Subject: [PATCH] SoC for stacks --- charger-display.py | 90 ++++++++++++++++++++++------------------------ 1 file changed, 43 insertions(+), 47 deletions(-) diff --git a/charger-display.py b/charger-display.py index ed2691a..806d6af 100755 --- a/charger-display.py +++ b/charger-display.py @@ -54,6 +54,22 @@ MASTER_THRESH_OT = 1 MASTER_THRESH_UV = 2 MASTER_THRESH_OV = 3 +INTERNAL_RESISTANCE_CURVE_X = [2.0, 4.12] +INTERNAL_RESISTANCE_CURVE_Y = [0.0528, 0.0294] +SOC_OCV_X = [2.1, 2.9, 3.2, 3.3, 3.4, 3.5, 3.68, 4.0, 4.15, 4.2] +SOC_OCV_Y = [0, 0.023, 0.06, 0.08, 0.119, 0.227, 0.541, 0.856, 0.985, 1.0] + + +def estimate_soc(voltage: float) -> float: + r_i = np.interp( + [voltage], + INTERNAL_RESISTANCE_CURVE_X, + INTERNAL_RESISTANCE_CURVE_Y, + )[0] + i = data.current / PARALLEL_CELLS + ocv = voltage - i * r_i + return np.interp([ocv], SOC_OCV_X, SOC_OCV_Y)[0] * 100 + class SlaveData: cell_voltages: list[float] @@ -135,63 +151,58 @@ class AccumulatorData: class StackGuiElement: title: str stack_id: int - min_voltage_label: QLabel - max_voltage_label: QLabel - min_temp_label: QLabel - max_temp_label: QLabel + voltage_label: QLabel + soc_label: QLabel + temp_label: QLabel groupBox: QGroupBox detail_popup: QWidget def __init__(self, title: str, stack_id: int): self.title = title self.stack_id = stack_id - self.min_voltage_label = QLabel() - self.max_voltage_label = QLabel() - self.min_temp_label = QLabel() - self.max_temp_label = QLabel() + self.voltage_label = QLabel() + self.soc_label = QLabel() + self.temp_label = QLabel() self.groupBox = QGroupBox() self.detail_popup = None self.__post__init__() def __post__init__(self): - l1 = QLabel("Min Voltage [V]") - l2 = QLabel("Max Voltage [V]") - l3 = QLabel("Min Temperature [°C]") - l4 = QLabel("Max Temperature [°C]") + l1 = QLabel("Voltage [V]") + l2 = QLabel("SoC [%]") + l3 = QLabel("Temperature [°C]") popup_btn = QPushButton("Details") popup_btn.clicked.connect(self.show_popup) l1.setAlignment(Qt.AlignLeft) l2.setAlignment(Qt.AlignLeft) l3.setAlignment(Qt.AlignLeft) - l4.setAlignment(Qt.AlignLeft) - self.min_voltage_label.setAlignment(Qt.AlignLeft) - self.max_voltage_label.setAlignment(Qt.AlignLeft) - self.min_temp_label.setAlignment(Qt.AlignLeft) - self.max_temp_label.setAlignment(Qt.AlignLeft) + self.voltage_label.setAlignment(Qt.AlignLeft) + self.soc_label.setAlignment(Qt.AlignLeft) + self.temp_label.setAlignment(Qt.AlignLeft) grid = QGridLayout() grid.addWidget(l1, 0, 0) grid.addWidget(l2, 1, 0) grid.addWidget(l3, 2, 0) - grid.addWidget(l4, 3, 0) - grid.addWidget(self.min_voltage_label, 0, 1) - grid.addWidget(self.max_voltage_label, 1, 1) - grid.addWidget(self.min_temp_label, 2, 1) - grid.addWidget(self.max_temp_label, 3, 1) + grid.addWidget(self.voltage_label, 0, 1) + grid.addWidget(self.soc_label, 1, 1) + grid.addWidget(self.temp_label, 2, 1) - grid.addWidget(popup_btn, 0, 2) + grid.addWidget(popup_btn, 3, 0) self.groupBox.setTitle(self.title) self.groupBox.setLayout(grid) def update_data_from_slave(self, slave: SlaveData): - self.min_voltage_label.setText(f"{min(slave.cell_voltages):.02f}") - self.max_voltage_label.setText(f"{max(slave.cell_voltages):.02f}") - self.min_temp_label.setText(f"{min(slave.cell_temps):.02f}") - self.max_temp_label.setText(f"{max(slave.cell_temps):.02f}") + min_v, max_v = min(slave.cell_voltages), max(slave.cell_voltages) + self.voltage_label.setText(f"[{min_v:.02f}, {max_v:.02f}]") + min_soc, max_soc = estimate_soc(min_v), estimate_soc(max_v) + self.soc_label.setText(f"[{min_soc:02.0f}, {max_soc:02.0f}]") + min_t, max_t = min(slave.cell_temps), max(slave.cell_temps) + self.temp_label.setText(f"[{min_t:.02f}, {max_t:.02f}]") def show_popup(self): if self.detail_popup is None: @@ -265,6 +276,7 @@ class StackPopup(QWidget): groupbox.setLayout(grid) layout.addWidget(groupbox) self.setLayout(layout) + self.setWindowTitle(f"FT22 Charger Display - Stack {stack_id} Details") self.update_data() timer.timeout.connect(self.update_data) @@ -470,8 +482,8 @@ class Window(QWidget): # Accumulator self.l_min_voltage.setText(f"{data.min_voltage:.02f}") self.l_max_voltage.setText(f"{data.max_voltage:.02f}") - self.l_min_soc.setText(f"{data.min_soc:.01f}") - self.l_max_soc.setText(f"{data.max_soc:.01f}") + self.l_min_soc.setText(f"{data.min_soc:02.0f}") + self.l_max_soc.setText(f"{data.max_soc:02.0f}") self.l_min_temp.setText(f"{data.min_temp:.02f}") self.l_max_temp.setText(f"{data.max_temp:.02f}") self.l_current.setText(f"{data.current:.02f}") @@ -612,8 +624,8 @@ class Worker(QObject): ] data.min_voltage = min(voltages) data.max_voltage = max(voltages) - data.min_soc = self.calculate_soc(data.min_voltage) - data.max_soc = self.calculate_soc(data.max_voltage) + data.min_soc = estimate_soc(data.min_voltage) + data.max_soc = estimate_soc(data.max_voltage) data.min_temp = min(temps) data.max_temp = max(temps) data.time_since_last_frame = time.time() - data.last_frame @@ -678,22 +690,6 @@ class Worker(QObject): data.ts_state = TSState(state) data.panic = data.ts_state == TSState.TS_ERROR - INTERNAL_RESISTANCE_CURVE_X = [2.0, 4.12] - INTERNAL_RESISTANCE_CURVE_Y = [0.0528, 0.0294] - SOC_OCV_X = [2.1, 2.9, 3.2, 3.3, 3.4, 3.5, 3.68, 4.0, 4.15, 4.2] - SOC_OCV_Y = [0, 0.023, 0.06, 0.08, 0.119, 0.227, 0.541, 0.856, 0.985, 1.0] - - def calculate_soc(self, voltage: float): - r_i = np.interp( - [voltage], - self.INTERNAL_RESISTANCE_CURVE_X, - self.INTERNAL_RESISTANCE_CURVE_Y, - )[0] - # i = data.current / PARALLEL_CELLS - i = 3 / PARALLEL_CELLS - ocv = voltage - i * r_i - return np.interp([ocv], self.SOC_OCV_X, self.SOC_OCV_Y)[0] * 100 - def parse_cell_temps(self, slave: int): temps = list( filter(lambda t: t > 0 and t < 60, data.slaves[slave].cell_temps[:16])