Skip to content

Commit

Permalink
Dynamically adjust the charge current to avoid over-voltage
Browse files Browse the repository at this point in the history
  • Loading branch information
bjpirt committed Dec 2, 2023
1 parent 763c272 commit 0802846
Show file tree
Hide file tree
Showing 4 changed files with 95 additions and 17 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,8 @@ Which will produce `build/out/tesla-bms-emulator.uf2`. You can drag and drop thi

## To Do

- [ ] Dynamically scale back the charge current to avoid going over voltage
- [ ] Add support for heating the battery pack in cold weather
- [x] Dynamically scale back the charge current to avoid going over voltage
- [x] Add hysteresis on over voltage faults
- [x] Simulator for integration testing over socat virtual serial port
- [x] Cell balancing
Expand Down
7 changes: 0 additions & 7 deletions battery/battery_pack.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,13 +122,6 @@ def cell_voltage_difference(self) -> float:
return max((module.cell_voltage_difference for module in self.modules))
return 0.0

def should_charge(self, current_state: bool) -> bool:
if current_state is True:
return self.high_cell_voltage < self._config.cell_high_voltage_setpoint
else:
return self.high_cell_voltage < (
self._config.cell_high_voltage_setpoint - self._config.charge_hysteresis_voltage)

def should_discharge(self, current_state: bool) -> bool:
if current_state is True:
return self.low_cell_voltage > self._config.cell_low_voltage_setpoint
Expand Down
36 changes: 27 additions & 9 deletions bms/bms.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,10 @@ def __init__(self, battery_pack: BatteryPack, config: Config, current_sensor: Op
self.__interval = get_interval()
self.__interval.set(self.__poll_interval)
self.__led = Led(self.__config.led_pin)
self.__charging_enabled = True
self.__discharging_enabled = True
self.__charging_timer = get_interval()
self.__charging_timer.set(self.__config.charge_hysteresis_time)
self.__charge_current = self.__config.max_charge_current
self.__discharging_timer = get_interval()
self.__discharging_timer.set(self.__config.charge_hysteresis_time)
self.__wdt: Optional[WDT] = None
Expand Down Expand Up @@ -73,17 +73,35 @@ def current(self) -> float:

@property
def charge_current_setpoint(self) -> float:
self.__charging_enabled = self.battery_pack.should_charge(self.__charging_enabled)
if self.__charging_enabled is True:
if self.__charging_timer.ready:
return self.__config.max_charge_current
else:
self.__charging_timer.reset()
return 0
voltage_difference = self.battery_pack.high_cell_voltage - \
self.__config.cell_high_voltage_setpoint
if voltage_difference > 0:
scale_factor = 0.5
# Scale back the charge current in proportion to how much the voltage is over the threshold
# If the voltage difference is > 0.25V then set the charge current to 50% of actual
max_difference = 0.25
most_reduction = 0.5
# If the voltage is not much over (0.025V), then set the charge current to 90% of actual
min_difference = 0.025
least_reduction = 0.9
if voltage_difference >= max_difference:
scale_factor = most_reduction
elif voltage_difference < min_difference:
scale_factor = least_reduction
else:
scale_factor = most_reduction + ((voltage_difference - min_difference) /
(max_difference - min_difference)) * (least_reduction - most_reduction)

self.__charge_current = self.current * scale_factor
elif voltage_difference <= -0.02:
self.__charge_current = min(
self.__charge_current * 1.1, self.__config.max_charge_current)
return self.__charge_current

@property
def discharge_current_setpoint(self) -> float:
self.__discharging_enabled = self.battery_pack.should_discharge(self.__discharging_enabled)
self.__discharging_enabled = self.battery_pack.should_discharge(
self.__discharging_enabled)
if self.__discharging_enabled is True:
if self.__discharging_timer.ready:
return self.__config.max_discharge_current
Expand Down
66 changes: 66 additions & 0 deletions test/bms/test_Bms.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import unittest
from battery.battery_pack import BatteryPack # type: ignore
from bms import Config
from bms.bms import Bms
from bms.current_sensor.current_sensor import CurrentSensor


class FakeCurrentSensor(CurrentSensor):
def __init__(self) -> None:
self.fake_current = 10.0

@property
def current(self):
return self.fake_current


class FakePack(BatteryPack):
def __init__(self):
self.fake_high_cell_voltage = 3.9
self.ready = True

@property
def high_cell_voltage(self):
return self.fake_high_cell_voltage


class BmsTestCase(unittest.TestCase):
def setUp(self):
self.config = Config("config.default.json")
self.pack = FakePack()
self.current_sensor = FakeCurrentSensor()
self.bms = Bms(self.pack, self.config, self.current_sensor)
return super().setUp()

def test_charge_current_setpoint_under_voltage(self):
self.assertEqual(self.bms.charge_current_setpoint, 100)

def test_charge_current_setpoint_over_voltage(self):
self.pack.fake_high_cell_voltage = 4.35
self.assertAlmostEqual(self.bms.charge_current_setpoint, 5)
self.pack.fake_high_cell_voltage = 4.2
self.current_sensor.fake_current = 5
self.assertAlmostEqual(self.bms.charge_current_setpoint, 3.16666667)
self.pack.fake_high_cell_voltage = 4.12
self.current_sensor.fake_current = 3.16
self.assertAlmostEqual(self.bms.charge_current_setpoint, 2.844)
self.pack.fake_high_cell_voltage = 4.09
self.current_sensor.fake_current = 2.844
self.assertAlmostEqual(self.bms.charge_current_setpoint, 2.844)
self.pack.fake_high_cell_voltage = 4.1
self.current_sensor.fake_current = 2.844
self.assertAlmostEqual(self.bms.charge_current_setpoint, 2.844)
self.pack.fake_high_cell_voltage = 4.11
self.current_sensor.fake_current = 2.844
self.assertAlmostEqual(self.bms.charge_current_setpoint, 2.5596)
self.pack.fake_high_cell_voltage = 4.1
self.current_sensor.fake_current = 2.5596
self.assertAlmostEqual(self.bms.charge_current_setpoint, 2.5596)
self.pack.fake_high_cell_voltage = 4.04
self.assertAlmostEqual(self.bms.charge_current_setpoint, 2.81556)
self.pack.fake_high_cell_voltage = 4.05
self.current_sensor.fake_current = 2.81556
self.assertAlmostEqual(self.bms.charge_current_setpoint, 3.097116)
self.pack.fake_high_cell_voltage = 4.09
self.current_sensor.fake_current = 2.81556
self.assertAlmostEqual(self.bms.charge_current_setpoint, 3.097116)

0 comments on commit 0802846

Please sign in to comment.