1717*/
1818
1919#include " SIM_Battery.h"
20+ #include < float.h>
2021#include < AP_Math/AP_Math.h>
2122
2223using namespace SITL ;
@@ -72,9 +73,14 @@ static const struct {
7273/*
7374 use table to get resting voltage from remaining capacity
7475 */
75- float Battery::get_resting_voltage (float charge_pct ) const
76+ float Battery::get_resting_voltage (void ) const
7677{
78+ if (capacity_is_unlimited ()) {
79+ return max_voltage;
80+ }
81+ float charge_pct = 100 * remaining_Ah / capacity_Ah;
7782 const float max_cell_voltage = soc_table[0 ].volt_per_cell ;
83+ const float min_cell_voltage = soc_table[ARRAY_SIZE (soc_table) - 1 ].volt_per_cell ;
7884 for (uint8_t i=1 ; i<ARRAY_SIZE (soc_table); i++) {
7985 if (charge_pct >= soc_table[i].soc_pct ) {
8086 // linear interpolation between table rows
@@ -86,15 +92,21 @@ float Battery::get_resting_voltage(float charge_pct) const
8692 return (cell_volt / max_cell_voltage) * max_voltage;
8793 }
8894 }
89- // off the bottom of the table, return a small non-zero to prevent math errors
90- return 0.001 ;
95+ // off the bottom of the table
96+ return min_cell_voltage ;
9197}
9298
9399/*
94- use table to set initial state of charge from voltage
100+ return remaining Amp-hours (aka "charge", "state of charge") corresponding to a voltage
101+
102+ this is const for readability: it has no "side effects"
95103 */
96- void Battery::set_initial_SoC (float voltage)
104+ float Battery::compute_remaining_ah (float voltage) const
97105{
106+ if (capacity_is_unlimited ()) {
107+ return FLT_MAX ;
108+ }
109+
98110 const float max_cell_voltage = soc_table[0 ].volt_per_cell ;
99111 float cell_volt = (voltage / max_voltage) * max_cell_voltage;
100112
@@ -106,24 +118,28 @@ void Battery::set_initial_SoC(float voltage)
106118 float soc1 = soc_table[i].soc_pct ;
107119 float soc2 = soc_table[i-1 ].soc_pct ;
108120 float soc = soc1 + (dv1 / dv2) * (soc2 - soc1);
109- remaining_Ah = capacity_Ah * soc * 0.01 ;
110- return ;
121+ return capacity_Ah * soc * 0.01 ;
111122 }
112123 }
113-
114124 // off the bottom of the table
115- remaining_Ah = 0 ;
125+ return 0 . 0f ;
116126}
117127
118- void Battery::setup (float _capacity_Ah, float _resistance, float _max_voltage)
128+ void Battery::set_remaining_ah (void )
129+ {
130+ remaining_Ah = compute_remaining_ah (voltage_set);
131+ }
132+
133+ // Reminder: capacity <= 0 means **unlimited**
134+ void Battery::setup (float _capacity_Ah, float _resistance_ohm, float _max_voltage)
119135{
120136 capacity_Ah = _capacity_Ah;
121- resistance = _resistance ;
137+ resistance_ohm = _resistance_ohm ;
122138 max_voltage = _max_voltage;
123139
124140 voltage_set = max_voltage;
125141 voltage_filter.reset (voltage_set);
126- set_initial_SoC (voltage_set );
142+ set_remaining_ah ( );
127143}
128144
129145void Battery::maybe_reset (float desired_voltage, float desired_capacity_Ah)
@@ -138,23 +154,10 @@ void Battery::maybe_reset(float desired_voltage, float desired_capacity_Ah)
138154 // a negative desired voltage is unexpected, but not problematic
139155 voltage_set = MIN (desired_voltage, max_voltage);
140156 voltage_filter.reset (voltage_set);
141- set_initial_SoC (voltage_set );
157+ set_remaining_ah ( );
142158}
143159
144- void Battery::init_voltage (float voltage)
145- {
146- voltage_filter.reset (voltage);
147- voltage_set = voltage;
148- set_initial_SoC (voltage);
149- }
150-
151- void Battery::init_capacity (float capacity)
152- {
153- capacity_Ah = capacity;
154- set_initial_SoC (voltage_set);
155- }
156-
157- void Battery::set_current (float current)
160+ void Battery::consume_energy (float current_amps)
158161{
159162 uint64_t now = AP_HAL::micros64 ();
160163 float dt = (now - last_us) * 1.0e-6 ;
@@ -163,31 +166,29 @@ void Battery::set_current(float current)
163166 dt = 0 ;
164167 }
165168 last_us = now;
166- float delta_Ah = current * dt / 3600 ;
169+ float delta_Ah = current_amps * dt / 3600 ;
167170 remaining_Ah -= delta_Ah;
168171 remaining_Ah = MAX (0 , remaining_Ah);
169172
170- float voltage_delta = current * resistance;
171- float voltage;
172- if (!is_positive (capacity_Ah)) {
173- voltage = voltage_set;
174- } else {
175- voltage = get_resting_voltage (100 * remaining_Ah / capacity_Ah) - voltage_delta;
176- }
177-
178- voltage_filter.apply (voltage, dt);
173+ float voltage_delta = current_amps * resistance_ohm;
174+ float sagged_voltage = get_resting_voltage () - voltage_delta;
175+ voltage_filter.apply (sagged_voltage, dt);
179176
180- {
181- const uint64_t temperature_dt = now - temperature.last_update_micros ;
182- temperature.last_update_micros = now;
183- // 1 amp*1 second == 0.1 degrees of energy. Did those units hurt?
184- temperature.kelvin += 0.1 * current * temperature_dt * 0.000001 ;
185- // decay temperature at some %second towards ambient
186- temperature.kelvin -= (temperature.kelvin - 273 ) * 0.10 * temperature_dt * 0.000001 ;
187- }
177+ update_temperature (current_amps, now);
188178}
189179
190- float Battery::get_voltage ( void ) const
180+ void Battery::update_temperature ( float current_amps, uint64_t now)
191181{
192- return voltage_filter.get ();
182+ const float dt = (now - temperature.last_update_micros ) * 1.0e-6 ;
183+ temperature.last_update_micros = now;
184+
185+ // temperature growth model
186+
187+ // thermal_capacity value chosen to match previous steady-state behavior at 28amps
188+ // (reminder: thermal_capacity = mass * specific_heat)
189+ const float inverse_of_thermal_capacity = 0 .36f ; // use inverse so we can multiply, not divide
190+ temperature.kelvin += (current_amps * current_amps) * resistance_ohm * dt * inverse_of_thermal_capacity;
191+
192+ // temperature decay model: first-order
193+ temperature.kelvin -= (temperature.kelvin - (ambient_temperature + 273 .15f )) * 0 .10f * dt;
193194}
0 commit comments