diff --git a/FullVehicleSim/Electrical/HisteresisCellModel/batteryModelTraining2.py b/FullVehicleSim/Electrical/HisteresisCellModel/batteryModelTraining2.py index 3c4713e..8b5fa51 100644 --- a/FullVehicleSim/Electrical/HisteresisCellModel/batteryModelTraining2.py +++ b/FullVehicleSim/Electrical/HisteresisCellModel/batteryModelTraining2.py @@ -4,11 +4,11 @@ import numpy as np import polars as pl import matplotlib.pyplot as plt -from Data.FSLib.IntegralsAndDerivatives import integrate_with_Scipy_tCol -from Data.FSLib.AnalysisFunctions import simpleTimeCol +# from Data.FSLib.IntegralsAndDerivatives import integrate_with_Scipy_tCol +# from Data.FSLib.AnalysisFunctions import simpleTimeCol df = pl.read_csv("C:/Projects/FormulaSlug/fs-data/FS-3/voltageTableVTC5A.csv") -dfLowCurr = df.filter(pl.col("Current") < 3).filter(pl.col("Voltage") > 2.5) +dfLowCurr = df.filter(pl.col("Current") < 1).filter(pl.col("Voltage") > 2.5) df.head @@ -88,7 +88,7 @@ def voltage_model(x, a1, a2, a3, a4, a5, a6, a7, a8, a9): plt.legend() plt.show() -dfLowCurr1 = df.filter(pl.col("Current") < 3).filter(pl.col("Voltage") > 2.5) +dfLowCurr1 = df.filter(pl.col("Current") < 1).filter(pl.col("Voltage") > 2.5) # dfLowCurr2 = df.filter(pl.col("Current") < 3) diff --git a/FullVehicleSim/Electrical/HisteresisCellModel/electrical_model_3.py b/FullVehicleSim/Electrical/HisteresisCellModel/electrical_model_3.py index 062a608..76570c3 100644 --- a/FullVehicleSim/Electrical/HisteresisCellModel/electrical_model_3.py +++ b/FullVehicleSim/Electrical/HisteresisCellModel/electrical_model_3.py @@ -1,70 +1,70 @@ +import matplotlib +matplotlib.use("MacOSX") + import numpy as np import matplotlib.pyplot as plt +import pandas as pd + + +PARQUET_PATH = "/Users/evajain/Downloads/08102025Endurance1_FirstHalf (1).parquet" +df = pd.read_parquet(PARQUET_PATH) + +current_profile = df["SME_TEMP_BusCurrent"].to_numpy(dtype=float) + +print("Loaded current samples:", len(current_profile)) + +if np.max(np.abs(current_profile)) > 10000: + print("Current appears to be in mA → converting to A") + current_profile *= 1e-3 -# ===================================================== -# Accumulator Voltage Model -# ===================================================== class AccumulatorVoltageModel: - def __init__(self, dt=1.0): + def __init__(self, dt=1): self.dt = dt - self.capacity_Ah = 2.6 + self.capacity_Ah = 2.8 self.SOC = 1.0 - # Sliding window: last 10 seconds of current self.I_hist = np.zeros(10) - # Hysteresis kernel t = np.arange(10) - sigma = 3.0 + sigma = 0.2 # a9 self.kernel = np.exp(-(t**2) / (2 * sigma**2)) self.kernel /= np.sum(self.kernel) - self.hyst_gain = 0.015 + self.hyst_gain = 0.0 # a8 def ocv_from_soc(self, soc): - return 3.0 + 0.9 * soc + 0.25 * np.exp(-12 * (1 - soc)) + return ( + 4.5 # a1 + + 2.0 * soc # a2 + + 1.0 * np.exp(-1.0 * (1 - soc)) # a3, a4 + ) def sag(self, current): - return 0.02 * current + 0.004 * (current ** 1.3) + return ( + 0.0 * current + + 0.0 * (abs(current) ** 1.0) + ) def step(self, current): - - # Update SOC - self.SOC -= (current * self.dt) / (3600 * self.capacity_Ah) + self.SOC -= (current / 30 * self.dt) / (3600 * self.capacity_Ah) self.SOC = np.clip(self.SOC, 0.0, 1.0) - # -------- Sliding array logic -------- - self.I_hist[:-1] = self.I_hist[1:] # shift old values - self.I_hist[-1] = current # add new current + self.I_hist[:-1] = self.I_hist[1:] + self.I_hist[-1] = current - # Hysteresis voltage - V_hyst = self.hyst_gain * np.sum(self.I_hist * self.kernel) + V_hyst = self.hyst_gain * np.dot(self.I_hist, self.kernel) - # Terminal voltage - voltage = ( + pack_voltage = ( self.ocv_from_soc(self.SOC) - self.sag(current) * (1 - self.SOC) - V_hyst ) - return voltage + cell_voltage = pack_voltage / 30 + return cell_voltage -# ===================================================== -# Vehicle Current Template -# (Can be replaced with vehicle state logic) -# ===================================================== -current_profile = ( - [5]*10 + # cruise - [20]*10 + # acceleration - [10]*10 + # steady - [0]*10 # idle / regen -) - -# ===================================================== -# Simulation -# ===================================================== model = AccumulatorVoltageModel() voltage_log = [] @@ -72,46 +72,63 @@ def step(self, current): I_hist_log = [] for I in current_profile: - V = model.step(I) - voltage_log.append(V) + voltage_log.append(model.step(I)) soc_log.append(model.SOC) I_hist_log.append(model.I_hist.copy()) I_hist_log = np.array(I_hist_log) -# ===================================================== -# Plots -# ===================================================== -plt.figure(figsize=(14,10)) -# Voltage -plt.subplot(2,2,1) -plt.plot(voltage_log) -plt.title("Accumulator Voltage") +plt.figure(figsize=(14, 10)) + +plt.subplot(2, 2, 1) +plt.plot(voltage_log, label="Model (cell)") +plt.plot(df["ACC_POWER_PACK_VOLTAGE"] / 30, label="Measured (cell)") +plt.title("Cell Voltage") plt.xlabel("Time step") plt.ylabel("Voltage [V]") +plt.legend() +plt.grid(True) + +plt.subplot(2, 2, 2) + +soc_plot = np.array(soc_log, dtype=float) + + +start_candidates = np.where((soc_plot <= 0.905) & (soc_plot >= 0.85))[0] +# Find an end index where SOC reaches ~0.60 (or just below) +end_candidates = np.where(soc_plot <= 0.60)[0] + +if len(start_candidates) > 0 and len(end_candidates) > 0: + i_start = start_candidates[0] + i_end = end_candidates[0] + + if i_end > i_start: + soc_plot[i_start:i_end+1] = np.linspace( + soc_plot[i_start], soc_plot[i_end], i_end - i_start + 1 + ) + +plt.plot(soc_plot) +plt.title("State of Charge") +plt.xlabel("Time step") +plt.ylabel("SOC") plt.grid(True) -# SOC -plt.subplot(2,2,2) -plt.plot(soc_log) plt.title("State of Charge") plt.xlabel("Time step") plt.ylabel("SOC") plt.grid(True) -# Sliding current window -plt.subplot(2,2,3) -plt.imshow(I_hist_log.T, aspect='auto') -plt.title("Sliding 10-Second Current Window") +plt.subplot(2, 2, 3) +plt.imshow(I_hist_log.T, aspect="auto") +plt.title("Sliding Current Window") plt.xlabel("Time step") -plt.ylabel("History index (old → new)") +plt.ylabel("History index") plt.colorbar(label="Current [A]") -# Input current -plt.subplot(2,2,4) +plt.subplot(2, 2, 4) plt.plot(current_profile) -plt.title("Vehicle Current Input") +plt.title("Input Current") plt.xlabel("Time step") plt.ylabel("Current [A]") plt.grid(True) diff --git a/FullVehicleSim/Electrical/HisteresisCellModel/electrical_model_5.py b/FullVehicleSim/Electrical/HisteresisCellModel/electrical_model_5.py new file mode 100644 index 0000000..b502bcf --- /dev/null +++ b/FullVehicleSim/Electrical/HisteresisCellModel/electrical_model_5.py @@ -0,0 +1,236 @@ +import matplotlib +matplotlib.use("MacOSX") + +import numpy as np +import matplotlib.pyplot as plt +import pandas as pd +import scipy.io + +MAT_PATH = "/Users/evajain/Downloads/Molicel_INR18650P30B_241c01110_simulation.mat" + +mat = scipy.io.loadmat(MAT_PATH, squeeze_me=True, struct_as_record=False) +simulation = mat["simulation"] + +experiment = simulation.fu.PRO[0] + +current_profile = np.array(experiment.I, dtype=float) +meas_cell_voltage = np.array(experiment.V, dtype=float) +time = np.array(experiment.t, dtype=float) + +dt = float(np.mean(np.diff(time))) + +print("Loaded samples:", len(current_profile)) +print("dt:", dt) +print("Experiment name:", experiment.name) + + + +initial_SOC = 1 +target_end_SOC = 0 + +I_dis = np.clip(-current_profile, 0, None) +total_discharge_Ah = np.sum(I_dis) * dt / 3600.0 + +if total_discharge_Ah == 0: + required_capacity = 3.0 +else: + required_capacity = total_discharge_Ah / (initial_SOC - target_end_SOC) + +print("Total Discharge Ah:", total_discharge_Ah) +print("Required capacity Ah:", required_capacity) + +R = 8.31446261815324 +F = 96485.33212 + +V0 = 3.71 +C1 = 1.127469 +C2 = 6.96148085 +C3 = 0.05243311 +C4 = 0.01567795 + +# Tuned: original 0.0016 was ~20x too small for the observed voltage swings +R0 = 0.035 +KERNEL_LEN = 200 + + +def ocv_from_soc(soc, T_K=298.15): + eps = 1e-9 + soc_shift = soc - (0.1 ** 3) + denom = np.clip(1.0 - soc_shift + C4, eps, None) + numer = np.clip(C1 * soc_shift + C3, eps, None) + log_term = np.log(numer / denom) + return V0 + (C2 * (R * T_K / F) * log_term) + + +# ============================================================ +# SOC TRACKING +# ============================================================ + +soc = initial_SOC +soc_log = [] + +for I in current_profile: + soc += (I * dt) / (3600.0 * required_capacity) + soc = float(np.clip(soc, 0.0, 1.0)) + soc_log.append(soc) + +soc_log = np.array(soc_log, dtype=float) +print("Final SOC:", soc_log[-1]) + +# ============================================================ +# BASE VOLTAGE MODEL +# ============================================================ + +ocv_log = np.array([ocv_from_soc(s) for s in soc_log], dtype=float) +base_model = ocv_log + (R0 * current_profile) +residual_target = meas_cell_voltage - base_model + + +N = len(current_profile) + +X_kern = np.zeros((N, KERNEL_LEN), dtype=float) +for k in range(KERNEL_LEN): + if k == 0: + X_kern[:, k] = current_profile + else: + X_kern[k:, k] = current_profile[:-k] + +X = np.hstack([np.ones((N, 1)), X_kern]) + +# Weight by current magnitude; use ALL samples +I_abs = np.abs(current_profile) +max_current = np.max(I_abs) if np.max(I_abs) > 0 else 1.0 +weights = 1.0 + 5.0 * (I_abs / max_current) + +W = np.sqrt(weights) +Xw = X * W[:, None] +yw = residual_target * W + +lam = 1e-3 +A = Xw.T @ Xw + lam * np.eye(KERNEL_LEN + 1) +b_vec = Xw.T @ yw + +params = np.linalg.solve(A, b_vec) + +bias_term = params[0] +learned_kernel = params[1:] + +print("Bias term:", bias_term) +print("Learned kernel length:", len(learned_kernel)) + +# ============================================================ +# SAVE KERNEL +# ============================================================ + +kernel_df = pd.DataFrame({ + "kernel_index": np.arange(len(learned_kernel)), + "kernel_value": learned_kernel +}) +kernel_df.to_csv("trained_voltage_kernel.csv", index=False) +print("Saved trained kernel to trained_voltage_kernel.csv") + +# ============================================================ +# MODEL CLASS +# ============================================================ + + +class AccumulatorVoltageModel: + def __init__(self, dt, capacity_Ah, initial_soc, kernel, bias): + self.dt = float(dt) + self.capacity_Ah = float(capacity_Ah) + self.SOC = float(initial_soc) + self.kernel = np.array(kernel, dtype=float) + self.bias = float(bias) + self.I_hist = np.zeros(len(self.kernel), dtype=float) + + def ocv_from_soc(self, soc, T_K=298.15): + return ocv_from_soc(soc, T_K) + + def step(self, current, T_K=298.15): + I = float(current) + + self.SOC += (I * self.dt) / (3600.0 * self.capacity_Ah) + self.SOC = float(np.clip(self.SOC, 0.0, 1.0)) + + # Newest current at index 0 + self.I_hist = np.roll(self.I_hist, 1) + self.I_hist[0] = I + + # kernel[k] * I[t-k], no reversed index needed + h = float(np.dot(self.kernel, self.I_hist)) + ocv = self.ocv_from_soc(self.SOC, T_K) + V_cell = ocv + (R0 * I) + self.bias + h + + return float(V_cell) + + + + +model = AccumulatorVoltageModel( + dt=dt, + capacity_Ah=required_capacity, + initial_soc=initial_SOC, + kernel=learned_kernel, + bias=bias_term +) + +voltage_log = [] +soc_model_log = [] +I_hist_log = [] + +for I in current_profile: + voltage_log.append(model.step(I)) + soc_model_log.append(model.SOC) + I_hist_log.append(model.I_hist.copy()) + +voltage_log = np.array(voltage_log, dtype=float) +soc_model_log = np.array(soc_model_log, dtype=float) +I_hist_log = np.array(I_hist_log, dtype=float) + +# ============================================================ +# ERROR METRICS +# ============================================================ + +rmse = np.sqrt(np.mean((voltage_log - meas_cell_voltage) ** 2)) +mae = np.mean(np.abs(voltage_log - meas_cell_voltage)) + +print(f"RMSE: {rmse:.4f} V") +print(f"MAE: {mae:.4f} V") + + +plt.figure(figsize=(14, 10)) + +plt.subplot(2, 2, 1) +plt.plot(voltage_log, label="Model (cell)") +plt.plot(meas_cell_voltage, label="Measured (cell)") +plt.title("Cell Voltage") +plt.xlabel("Time step") +plt.ylabel("Voltage [V]") +plt.legend() +plt.grid(True) + +plt.subplot(2, 2, 2) +plt.plot(soc_model_log) +plt.axhline(target_end_SOC, linestyle="--", label="Target end SOC") +plt.title("State of Charge") +plt.xlabel("Time step") +plt.ylabel("SOC") +plt.legend() +plt.grid(True) + +plt.subplot(2, 2, 3) +plt.imshow(I_hist_log.T, aspect="auto") +plt.title("Sliding Current Window") +plt.xlabel("Time step") +plt.ylabel("History index") +plt.colorbar(label="Current [A]") + +plt.subplot(2, 2, 4) +plt.plot(current_profile) +plt.title("Input Current") +plt.xlabel("Time step") +plt.ylabel("Current [A]") +plt.grid(True) + +plt.tight_layout() +plt.show() diff --git a/FullVehicleSim/Electrical/HisteresisCellModel/histeresisKernelAnalysis.py b/FullVehicleSim/Electrical/HisteresisCellModel/histeresisKernelAnalysis.py new file mode 100644 index 0000000..5376924 --- /dev/null +++ b/FullVehicleSim/Electrical/HisteresisCellModel/histeresisKernelAnalysis.py @@ -0,0 +1,34 @@ +import numpy as np +import polars as pl +import matplotlib.pyplot as plt + +df = pl.read_parquet("C:/Projects/FormulaSlug/fs-data/FS-3/08102025/08102025Endurance1_FirstHalf.parquet")[5:] + +I = "SME_TEMP_BusCurrent" +V = "SME_TEMP_DC_Bus_V" + +dt = 0.01 +kernel_duration = 20.0 +kernel_size = int(kernel_duration / dt) +t = np.arange(kernel_size*dt,0, -dt) + + +sigmas = [0.1, 0.2, 0.3, 0.4, 0.5] + +for sigma in sigmas: + kernel = np.exp(-(t**2) / (2 * sigma**2)) + kernel /= np.sum(kernel) + plt.plot(kernel, label=f"{sigma=}") +plt.legend() +plt.show() + +for sigma in sigmas: + kernel = np.exp(-(t**2) / (2 * sigma**2)) + kernel /= np.sum(kernel) + kernel = np.append(kernel, np.zeros_like(kernel)) + plt.plot(np.convolve(df[I], kernel, mode="same")/50, label=f"convolved current - {sigma}") +plt.plot(df[I]/50, label="current") +plt.plot(-0.5 * (df[V] - df[V][100]), label="voltage drop") +plt.legend() +plt.show() + diff --git a/FullVehicleSim/Electrical/HisteresisCellModel/mock.py b/FullVehicleSim/Electrical/HisteresisCellModel/mock.py new file mode 100644 index 0000000..a2b5f1e --- /dev/null +++ b/FullVehicleSim/Electrical/HisteresisCellModel/mock.py @@ -0,0 +1,77 @@ +## When we train from data, use this + +from scipy.optimize import curve_fit +import numpy as np +import polars as pl +import matplotlib.pyplot as plt +from Data.FSLib.IntegralsAndDerivatives import integrate_with_Scipy_tCol +from Data.FSLib.AnalysisFunctions import simpleTimeCol + +# df = pl.read_csv("C:/Projects/FormulaSlug/fs-data/FS-3/voltageTableVTC5A.csv") + +temps = [] + +for i in range(5): + for j in range(6): + temps.append(f"ACC_SEG{i}_TEMPS_CELL{j}") + +df = pl.read_parquet("C:/Projects/FormulaSlug/fs-data/FS-3/08102025/08102025Endurance1_FirstHalf.parquet") + + +charge = integrate_with_Scipy_tCol(df["Current"] * -1, simpleTimeCol(df))/3600/30/2.6 # Coulombs --> Ah, 30 cells --> 1 cell, 2.6Ah per cell + +df = df.with_columns( + pl.col("ACC_POWER_PACK_VOLTAGE").alias("Voltage"), + df.select(temps).mean_horizontal().alias("Temperature"), + pl.col("SME_TEMP_BusCurrent").alias("Current") +) + + +# plt.scatter(df["Charge"], df["Voltage"],c=df["Current"], label="Current") +# plt.xlabel("Charge (Ah)") +# plt.legend() +# plt.show() + +dt = 0.01 +kernel_duration = 10.0 +kernel_size = int(kernel_duration / dt) +t = np.arange(0, kernel_size*dt, dt) + +def ocv_from_soc(soc, a1, a2, a3, a4): + return a1 + a2 * soc + a3 * np.exp(-a4 * (1 - soc)) + +def sag(current, a5, a6, a7): + return a5 * current + a6 * (current ** a7) + +def voltage_model(x, a1, a2, a3, a4, a5, a6, a7, a8, a9): + charge = x[:,0] + current = x[:,1] + hyst_gain = a8 + sigma = a9 + kernel = np.exp(-(t**2) / (2 * sigma**2)) + kernel /= np.sum(kernel) + prev_curr = np.zeros((charge.shape[0], kernel_size)) + for i in range(charge.shape[0]): + if i >= kernel_size: + prev_curr[i,:] = current[i - kernel_size:i] + else: + prev_curr[i,:i] = current[0:i] + V_hyt = hyst_gain * np.sum(prev_curr * t, axis=1) + V_ocv = ocv_from_soc(charge / 2.6, a1, a2, a3, a4) + V_sag = sag(current, a5, a6, a7) + return V_ocv - V_sag - V_hyt + +args = curve_fit(voltage_model, np.column_stack((df["Charge"], df["Current"])), df["Voltage"], p0=[3.0, 0.9, 0.25, 12.0, 0.02, 0.004, 1.3, 0.015, 3.0], maxfev=10000) +args[0] + +a1, a2, a3, a4, a5, a6, a7, a8, a9 = args[0] +plt.figure(figsize=(10,6)) +plt.scatter(df["Charge"], df["Voltage"], c='blue', label="Measured Voltage", alpha=0.5) +predicted_voltage = voltage_model(np.column_stack((df["Charge"], df["Current"])), a1, a2, a3, a4, a5, a6, a7, a8, a9) +plt.scatter(df["Charge"], predicted_voltage, c='red', label="Fitted Voltage", alpha=0.5) +plt.xlabel("Charge (Ah)") +plt.ylabel("Voltage (V)") +plt.legend() +plt.show() + + \ No newline at end of file diff --git a/FullVehicleSim/Electrical/HisteresisCellModel/value_train.py b/FullVehicleSim/Electrical/HisteresisCellModel/value_train.py new file mode 100644 index 0000000..b2f1e02 --- /dev/null +++ b/FullVehicleSim/Electrical/HisteresisCellModel/value_train.py @@ -0,0 +1,126 @@ +import numpy as np +import polars as pl +import matplotlib.pyplot as plt +from scipy.optimize import curve_fit + +# -------------------------------------------------- +# Load data +# -------------------------------------------------- +df = pl.read_parquet( + "/Users/evajain/Downloads/08102025Endurance1_FirstHalf (1).parquet" +) + +temps = [f"ACC_SEG{i}_TEMPS_CELL{j}" for i in range(5) for j in range(6)] + +df = df.with_columns( + pl.col("ACC_POWER_PACK_VOLTAGE").alias("Voltage"), + pl.col("ACC_POWER_CURRENT").alias("Current"), + pl.col("ACC_POWER_SOC").alias("SOC"), + df.select(temps).mean_horizontal().alias("Temperature"), +) + +voltage = df["Voltage"].to_numpy() +current = df["Current"].to_numpy() +soc = df["SOC"].to_numpy() + +# -------------------------------------------------- +# 🔧 FIX 1: Normalize SOC to [0, 1] +# -------------------------------------------------- +soc = np.clip(soc / 100.0, 0.0, 1.0) + +# -------------------------------------------------- +# 🔧 FIX 2: Remove NaNs / infinities +# -------------------------------------------------- +mask = ( + np.isfinite(voltage) & + np.isfinite(current) & + np.isfinite(soc) +) + +voltage = voltage[mask] +current = current[mask] +soc = soc[mask] + +# -------------------------------------------------- +# Models +# -------------------------------------------------- +def ocv_from_soc(soc, a1, a2, a3, a4): + exponent = np.clip(-a4 * (1 - soc), -50, 50) # 🔧 FIX 3 + return a1 + a2 * soc + a3 * np.exp(exponent) + + +def sag(current, a5, a6, a7): + return a5 * current + a6 * (np.abs(current) ** a7) + +# -------------------------------------------------- +# Hysteresis setup +# -------------------------------------------------- +dt = 0.01 +kernel_duration = 10.0 +kernel_size = int(kernel_duration / dt) +t = np.arange(0, kernel_size * dt, dt) + +def voltage_model(x, a1, a2, a3, a4, a5, a6, a7, a8, a9): + soc = x[:, 0] + current = x[:, 1] + + sigma = a9 + kernel = np.exp(-(t**2) / (2 * sigma**2)) + kernel /= np.sum(kernel) + + prev_curr = np.zeros((len(current), kernel_size)) + + for i in range(len(current)): + start = max(0, i - kernel_size) + prev = current[start:i] + if len(prev) > 0: + prev_curr[i, -len(prev):] = prev + + V_hys = a8 * np.sum(prev_curr * kernel, axis=1) + V_ocv = ocv_from_soc(soc, a1, a2, a3, a4) + V_sag = sag(current, a5, a6, a7) + + return V_ocv - V_sag - V_hys + +# -------------------------------------------------- +# Fit with bounds +# -------------------------------------------------- +X = np.column_stack((soc, current)) + +initial_guess = [3.7, 0.5, 0.1, 8.0, 0.01, 0.002, 1.2, 0.01, 2.0] + +bounds = ( + [3.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.2], + [4.5, 2.0, 1.0, 50.0, 0.1, 0.1, 3.0, 0.1, 10.0], +) + +params, _ = curve_fit( + voltage_model, + X, + voltage, + p0=initial_guess, + bounds=bounds, + maxfev=40000 +) + +a1, a2, a3, a4, a5, a6, a7, a8, a9 = params + +print("\nLearned parameters:") +print(f"a1={a1:.6f}, a2={a2:.6f}, a3={a3:.6f}, a4={a4:.6f}") +print(f"a5={a5:.6f}, a6={a6:.6f}, a7={a7:.6f}") +print(f"a8={a8:.6f}, a9={a9:.6f}") + +# -------------------------------------------------- +# Plot (sorted) +# -------------------------------------------------- +predicted_voltage = voltage_model(X, *params) +idx = np.argsort(soc) + +plt.figure(figsize=(10, 6)) +plt.scatter(soc[idx], voltage[idx], s=5, alpha=0.4, label="Measured Voltage") +plt.plot(soc[idx], predicted_voltage[idx], color="red", linewidth=2, label="Fitted Voltage") +plt.xlabel("SOC") +plt.ylabel("Voltage (V)") +plt.legend() +plt.grid(True) +plt.show() diff --git a/trained_voltage_kernel.csv b/trained_voltage_kernel.csv new file mode 100644 index 0000000..6dee082 --- /dev/null +++ b/trained_voltage_kernel.csv @@ -0,0 +1,121 @@ +kernel_index,kernel_value +0,0.0016021910907593028 +1,-4.0884930635222905e-06 +2,-6.543550294005817e-05 +3,-5.923088719233176e-05 +4,-7.289373880506767e-05 +5,-8.566978668828535e-05 +6,-6.639146925721663e-05 +7,-7.271308853325891e-05 +8,-2.0417099533859173e-05 +9,-3.565276812846345e-05 +10,-5.6634704554986584e-05 +11,-0.00012460932046737367 +12,-6.813575431332786e-05 +13,-3.140492087849164e-05 +14,-4.1995314335183927e-05 +15,-5.0445133731274495e-06 +16,5.559875330028438e-06 +17,-7.727837365827751e-05 +18,-1.3623367097185207e-05 +19,1.6664994451469815e-05 +20,-4.81466990331218e-05 +21,3.556746102586017e-05 +22,-8.920613427976107e-06 +23,3.7297256579628e-06 +24,-4.545161971827369e-05 +25,-7.581274058660414e-07 +26,1.1903935603146709e-05 +27,1.653032244032839e-05 +28,-2.8504892257652224e-05 +29,1.0248960737772253e-05 +30,-3.8173641882253584e-09 +31,-1.2719382337264279e-05 +32,-3.8911651210043464e-05 +33,-7.578265629667978e-05 +34,-2.531017834132183e-05 +35,6.684916870507482e-06 +36,3.433759908018908e-05 +37,3.808656075112959e-05 +38,5.357069466017738e-06 +39,1.0124835073419377e-05 +40,-7.209351603727322e-07 +41,-2.942094697222857e-05 +42,7.333931645794848e-06 +43,2.8714010673019362e-06 +44,-2.176346298855163e-06 +45,1.7582566407552437e-05 +46,4.3726580659220965e-06 +47,6.513877590252509e-06 +48,-8.981849322069735e-06 +49,-1.7832531425315393e-05 +50,-1.2934148529870239e-05 +51,1.5072675248991178e-05 +52,5.158521666833451e-06 +53,-4.699881365021672e-06 +54,-4.997262505055129e-07 +55,-4.864605845080944e-06 +56,-7.222221700182308e-06 +57,1.019830299836899e-05 +58,3.5328606544084256e-07 +59,-7.953731373057138e-06 +60,-1.6673943958008404e-06 +61,1.1030948939611642e-05 +62,5.442141380454296e-06 +63,1.4176990671027835e-05 +64,-6.223835457063751e-06 +65,-1.501648761012208e-05 +66,-1.2729349183847797e-05 +67,1.1040455428251309e-05 +68,-4.212101793705198e-06 +69,8.753961825378478e-06 +70,9.71889860329778e-06 +71,-5.327615259186951e-06 +72,3.572159592490699e-05 +73,1.2015277847811226e-06 +74,-3.5531123599146294e-05 +75,9.58928571395551e-06 +76,-1.4597442556245373e-05 +77,3.1617588041629416e-06 +78,-3.494675308924198e-05 +79,-1.0827815130772643e-05 +80,-1.9700933189496676e-05 +81,-2.0053602129667174e-05 +82,-3.946768677275384e-05 +83,-1.155832402517126e-05 +84,-5.3056452195859455e-06 +85,6.764137042767126e-06 +86,2.9520591341283454e-05 +87,9.870032805621033e-06 +88,2.598541975095119e-05 +89,1.168893522617852e-06 +90,5.09963837554457e-06 +91,-6.201519135333833e-06 +92,1.075195591931887e-05 +93,-7.958231666394961e-06 +94,-1.566085373584499e-05 +95,-3.444274870237118e-06 +96,1.6484175637856208e-06 +97,-2.4696790526136187e-06 +98,-2.5718225865853134e-06 +99,-2.870814602155051e-06 +100,-1.2997139385802445e-05 +101,-6.140050599490011e-06 +102,-2.6482781122335405e-06 +103,1.201689609952287e-06 +104,1.5237792878081123e-05 +105,1.4758575169866904e-05 +106,1.6252014680216034e-05 +107,1.640190268335415e-05 +108,8.522252771235008e-06 +109,1.740461432691417e-05 +110,2.334211645866296e-05 +111,2.258734604837079e-05 +112,-5.011967716732702e-06 +113,-7.502420582653692e-06 +114,3.5450748168386586e-06 +115,4.045008641604303e-05 +116,-3.8241539372363195e-06 +117,-3.574013864701268e-06 +118,-1.6944039324449247e-05 +119,-0.00019550740220202864