Skip to content
Open
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
4ec925c
Add hysteresis model improvements
evajain02-cloud Feb 12, 2026
e5a41af
Merge branch '28-fs-3-histeresis-electrical-model' of https://github.…
goob10000 Feb 13, 2026
87f09b1
more histeresis analysis
goob10000 Mar 30, 2026
6efcc31
new model
evajain02-cloud Apr 7, 2026
b45c09d
stuff
evajain02-cloud Apr 7, 2026
e8f4330
slid voltage model around
goob10000 Apr 8, 2026
1ab83dc
Merge branch '28-fs-3-histeresis-electrical-model' into main-sim-main…
goob10000 Apr 8, 2026
3aa46da
in progress
goob10000 Apr 8, 2026
a6efbfd
validated electrical model and it works!
goob10000 Apr 9, 2026
bc55a6a
param changes and powertrain updates
goob10000 Apr 9, 2026
ba2a1a3
electrical model works!
goob10000 Apr 9, 2026
89775f8
Merge branch 'main' into main-sim-maintenence
goob10000 Apr 9, 2026
60f367b
trained kernel
goob10000 Apr 12, 2026
18d5140
soc prediction and beginnings of c program for it
goob10000 Apr 13, 2026
d5eb0ba
remove obsolete file
goob10000 May 30, 2026
c7f658b
ignore sim output so it doesn't get commited
goob10000 May 30, 2026
757390c
couple corrections to torque calculations
goob10000 May 30, 2026
d9e67ea
brake calipers act on both sides of disk
goob10000 May 30, 2026
b0089be
gear ratio error for rpm calculation
goob10000 May 30, 2026
bfb14d8
fix another gear ratio issue
goob10000 May 30, 2026
1ed02d1
fixed braking cooling calculations
goob10000 May 30, 2026
1bdce34
also some params for those fixes and testing
goob10000 May 30, 2026
7ffa1ec
fixed energy change order of magnitude issue
goob10000 May 30, 2026
bd03213
clean and mix max torque calculation
goob10000 May 30, 2026
ce42ff6
couple more fixes
goob10000 May 31, 2026
f82a578
Implemented regen and brake pedal travel
goob10000 May 31, 2026
78096eb
simulation analysis script
goob10000 Jun 5, 2026
95e8537
sim controls example
goob10000 Jun 5, 2026
76dd489
script for modding data into sim input
goob10000 Jun 5, 2026
503244f
little bit of sim stuff
goob10000 Jun 12, 2026
f03f3d7
little bit of sim stuff
goob10000 Jun 12, 2026
29baeac
some stuff
goob10000 Jun 19, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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)


Expand Down
131 changes: 74 additions & 57 deletions FullVehicleSim/Electrical/HisteresisCellModel/electrical_model_3.py
Original file line number Diff line number Diff line change
@@ -1,117 +1,134 @@
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 = []
soc_log = []
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)
Expand Down
Loading