hitl_tester.test_cases.bms.test_q_max
See: https://github.com/turnaroundfactor/battery-benchtop-rev1/issues/244
Fix issues with q_max accuracy being off.
The full test plan is below.
- Charge the pack completely, let it rest overnight.
- Discharge it and pause at all OCV intervals (95, 90, 80, 70...)
- During the pause set discharge current to 0 for 5-10 seconds
- Measure the voltage during this time
- Take notice of the voltage difference between when its charged and discharged
Used in these test plans:
- 2590_qmax_b ⠀⠀⠀(bms/2590_qmax_b.plan)
Example Command (warning: test plan may run other test cases):
./hitl_tester.py 2590_qmax_b -DMINUTE=60 -DHOUR=3600 -DMAX_CHARGE_TIME=43200 -DCHARGE_VOLTAGE=16.8 -DCHARGE_OV_PROTECTION=16.85 -DCHARGE_CURRENT_A=3 -DCHARGE_TERMINATION_CURRENT=0.1 -DCHARGE_SAMPLE_INTERVAL=1 -DMIN_READINGS=3 -DMAX_DISCHARGE_TIME=43200 -DMAX_DISCHARGE_STEP_TIME=3600 -DDISCHARGE_CURRENT_A=2 -DDISCHARGE_VOLTAGE=10 -DDISCHARGE_UV_PROTECTION=9.95 -DDISCHARGE_SAMPLE_INTERVAL=1 -DRESTING_TIME=30 -DRESTING_SAMPLE_INTERVAL=1 -DCAPACITY=11.485347
1""" 2See: https://github.com/turnaroundfactor/battery-benchtop-rev1/issues/244 3 4Fix issues with q_max accuracy being off. 5 6The full test plan is below. 7 81. Charge the pack completely, let it rest overnight. 92. Discharge it and pause at all OCV intervals (95, 90, 80, 70...) 103. During the pause set discharge current to 0 for 5-10 seconds 114. Measure the voltage during this time 125. Take notice of the voltage difference between when its charged and discharged 13""" 14 15import pytest 16 17from hitl_tester.modules.bms.bms_hw import BMSHardware, UserInterrupt 18from hitl_tester.modules.bms.plateset import Plateset 19from hitl_tester.modules.bms_types import DischargeType 20 21# Global Variables that can be shared between tests 22MINUTE = 60 23HOUR = 60 * MINUTE 24 25MAX_CHARGE_TIME = 12 * HOUR 26CHARGE_VOLTAGE = 16.8 27CHARGE_OV_PROTECTION = 16.85 28CHARGE_CURRENT_A = 3 29CHARGE_TERMINATION_CURRENT = 0.1 30CHARGE_SAMPLE_INTERVAL = 1 31MIN_READINGS = 3 # How many readings to take before passing (for erroneous readings). 32 33MAX_DISCHARGE_TIME = 12 * HOUR 34MAX_DISCHARGE_STEP_TIME = HOUR 35DISCHARGE_CURRENT_A = 2 36DISCHARGE_VOLTAGE = 10 37DISCHARGE_UV_PROTECTION = 9.95 38DISCHARGE_SAMPLE_INTERVAL = 1 39 40RESTING_TIME = 30 41RESTING_SAMPLE_INTERVAL = 1 42 43CAPACITY = 11.485347 # Set by the discharge cycle (value from last Q max test) 44 45bms_hardware = BMSHardware(pytest.flags) # type: ignore[arg-type] 46bms_hardware.init() 47plateset = Plateset() 48 49bms_hardware.csv.cycle = bms_hardware.csv.cycle_smbus 50bms_hardware.remaining_capacity_percentage = 100 51 52 53def standard_step_discharge( 54 percent_discharge: float, 55 total_ah: float, 56 max_time: float = MAX_DISCHARGE_STEP_TIME, 57 discharge_i: float = DISCHARGE_CURRENT_A, 58 discharge_sample_interval: float = DISCHARGE_SAMPLE_INTERVAL, 59 uv_protection: float = DISCHARGE_UV_PROTECTION, 60): 61 """Run a step discharge cycle.""" 62 bms_hardware.max_time = max_time 63 bms_hardware.current = discharge_i 64 bms_hardware.sample_interval = discharge_sample_interval 65 bms_hardware.uv_protection = uv_protection 66 bms_hardware.percent_discharge = percent_discharge 67 bms_hardware.total_ah = total_ah 68 69 bms_hardware.run_discharge_step_cycle() 70 71 72def standard_rest(max_time: float = RESTING_TIME, sample_rate: float = RESTING_SAMPLE_INTERVAL): 73 """Run a resting cycle.""" 74 bms_hardware.max_time = max_time 75 bms_hardware.sample_interval = sample_rate 76 bms_hardware.run_resting_cycle() 77 78 79def test_full_charge_cycle_1(): 80 """Completely charge cells.""" 81 bms_hardware.voltage = CHARGE_VOLTAGE 82 bms_hardware.ov_protection = CHARGE_OV_PROTECTION 83 bms_hardware.current = CHARGE_CURRENT_A 84 bms_hardware.termination_current = CHARGE_TERMINATION_CURRENT 85 bms_hardware.max_time = MAX_CHARGE_TIME 86 bms_hardware.sample_interval = CHARGE_SAMPLE_INTERVAL 87 bms_hardware.minimum_readings = MIN_READINGS 88 89 plateset.ce_switch = True 90 bms_hardware.run_li_charge_cycle() 91 plateset.ce_switch = False 92 93 94def test_full_discharge_cycle(): 95 """Completely discharge cells. Required to get capacity.""" 96 global CAPACITY 97 98 bms_hardware.max_time = MAX_DISCHARGE_TIME 99 bms_hardware.sample_interval = DISCHARGE_SAMPLE_INTERVAL 100 bms_hardware.discharge_type = DischargeType.CONSTANT_CURRENT 101 bms_hardware.current = DISCHARGE_CURRENT_A 102 bms_hardware.uv_protection = DISCHARGE_UV_PROTECTION 103 bms_hardware.voltage = DISCHARGE_VOLTAGE 104 105 CAPACITY = bms_hardware.run_discharge_cycle() 106 print(f"Ah = {CAPACITY}") 107 assert CAPACITY != 0 # make sure we didn't get an error 108 109 110def test_full_charge_cycle_2(): 111 """Completely charge cells.""" 112 bms_hardware.voltage = CHARGE_VOLTAGE 113 bms_hardware.ov_protection = CHARGE_OV_PROTECTION 114 bms_hardware.current = CHARGE_CURRENT_A 115 bms_hardware.termination_current = CHARGE_TERMINATION_CURRENT 116 bms_hardware.max_time = MAX_CHARGE_TIME 117 bms_hardware.sample_interval = CHARGE_SAMPLE_INTERVAL 118 bms_hardware.minimum_readings = MIN_READINGS 119 120 plateset.ce_switch = True 121 bms_hardware.run_li_charge_cycle() 122 plateset.ce_switch = False 123 UserInterrupt.force_pause() # Suspend the test 124 125 126def test_step_discharge(): 127 """Record the voltage at each state of charge.""" 128 soc = 100 129 ocv_steps = (100, 95, 90, 80, 70, 60, 50, 40, 30, 20, 10, 7.5, 5, 2.5, 0) 130 for new_soc in ocv_steps: 131 if new_soc != ocv_steps[0]: 132 if new_soc == ocv_steps[-1]: 133 bms_hardware.discharge_until_undervoltage = True 134 standard_step_discharge((soc - new_soc) / 100, CAPACITY) 135 standard_rest() 136 soc = new_soc
MINUTE =
60
HOUR =
3600
MAX_CHARGE_TIME =
43200
CHARGE_VOLTAGE =
16.8
CHARGE_OV_PROTECTION =
16.85
CHARGE_CURRENT_A =
3
CHARGE_TERMINATION_CURRENT =
0.1
CHARGE_SAMPLE_INTERVAL =
1
MIN_READINGS =
3
MAX_DISCHARGE_TIME =
43200
MAX_DISCHARGE_STEP_TIME =
3600
DISCHARGE_CURRENT_A =
2
DISCHARGE_VOLTAGE =
10
DISCHARGE_UV_PROTECTION =
9.95
DISCHARGE_SAMPLE_INTERVAL =
1
RESTING_TIME =
30
RESTING_SAMPLE_INTERVAL =
1
CAPACITY =
11.485347
bms_hardware =
<hitl_tester.modules.bms.bms_hw.BMSHardware object>
plateset =
<hitl_tester.modules.bms.plateset.Plateset object>
def
standard_step_discharge( percent_discharge: float, total_ah: float, max_time: float = 3600, discharge_i: float = 2, discharge_sample_interval: float = 1, uv_protection: float = 9.95):
54def standard_step_discharge( 55 percent_discharge: float, 56 total_ah: float, 57 max_time: float = MAX_DISCHARGE_STEP_TIME, 58 discharge_i: float = DISCHARGE_CURRENT_A, 59 discharge_sample_interval: float = DISCHARGE_SAMPLE_INTERVAL, 60 uv_protection: float = DISCHARGE_UV_PROTECTION, 61): 62 """Run a step discharge cycle.""" 63 bms_hardware.max_time = max_time 64 bms_hardware.current = discharge_i 65 bms_hardware.sample_interval = discharge_sample_interval 66 bms_hardware.uv_protection = uv_protection 67 bms_hardware.percent_discharge = percent_discharge 68 bms_hardware.total_ah = total_ah 69 70 bms_hardware.run_discharge_step_cycle()
Run a step discharge cycle.
def
standard_rest(max_time: float = 30, sample_rate: float = 1):
73def standard_rest(max_time: float = RESTING_TIME, sample_rate: float = RESTING_SAMPLE_INTERVAL): 74 """Run a resting cycle.""" 75 bms_hardware.max_time = max_time 76 bms_hardware.sample_interval = sample_rate 77 bms_hardware.run_resting_cycle()
Run a resting cycle.
def
test_full_charge_cycle_1():
80def test_full_charge_cycle_1(): 81 """Completely charge cells.""" 82 bms_hardware.voltage = CHARGE_VOLTAGE 83 bms_hardware.ov_protection = CHARGE_OV_PROTECTION 84 bms_hardware.current = CHARGE_CURRENT_A 85 bms_hardware.termination_current = CHARGE_TERMINATION_CURRENT 86 bms_hardware.max_time = MAX_CHARGE_TIME 87 bms_hardware.sample_interval = CHARGE_SAMPLE_INTERVAL 88 bms_hardware.minimum_readings = MIN_READINGS 89 90 plateset.ce_switch = True 91 bms_hardware.run_li_charge_cycle() 92 plateset.ce_switch = False
Completely charge cells.
def
test_full_discharge_cycle():
95def test_full_discharge_cycle(): 96 """Completely discharge cells. Required to get capacity.""" 97 global CAPACITY 98 99 bms_hardware.max_time = MAX_DISCHARGE_TIME 100 bms_hardware.sample_interval = DISCHARGE_SAMPLE_INTERVAL 101 bms_hardware.discharge_type = DischargeType.CONSTANT_CURRENT 102 bms_hardware.current = DISCHARGE_CURRENT_A 103 bms_hardware.uv_protection = DISCHARGE_UV_PROTECTION 104 bms_hardware.voltage = DISCHARGE_VOLTAGE 105 106 CAPACITY = bms_hardware.run_discharge_cycle() 107 print(f"Ah = {CAPACITY}") 108 assert CAPACITY != 0 # make sure we didn't get an error
Completely discharge cells. Required to get capacity.
def
test_full_charge_cycle_2():
111def test_full_charge_cycle_2(): 112 """Completely charge cells.""" 113 bms_hardware.voltage = CHARGE_VOLTAGE 114 bms_hardware.ov_protection = CHARGE_OV_PROTECTION 115 bms_hardware.current = CHARGE_CURRENT_A 116 bms_hardware.termination_current = CHARGE_TERMINATION_CURRENT 117 bms_hardware.max_time = MAX_CHARGE_TIME 118 bms_hardware.sample_interval = CHARGE_SAMPLE_INTERVAL 119 bms_hardware.minimum_readings = MIN_READINGS 120 121 plateset.ce_switch = True 122 bms_hardware.run_li_charge_cycle() 123 plateset.ce_switch = False 124 UserInterrupt.force_pause() # Suspend the test
Completely charge cells.
def
test_step_discharge():
127def test_step_discharge(): 128 """Record the voltage at each state of charge.""" 129 soc = 100 130 ocv_steps = (100, 95, 90, 80, 70, 60, 50, 40, 30, 20, 10, 7.5, 5, 2.5, 0) 131 for new_soc in ocv_steps: 132 if new_soc != ocv_steps[0]: 133 if new_soc == ocv_steps[-1]: 134 bms_hardware.discharge_until_undervoltage = True 135 standard_step_discharge((soc - new_soc) / 100, CAPACITY) 136 standard_rest() 137 soc = new_soc
Record the voltage at each state of charge.