[Ovmsdev] Remaining Charge calculator
Michael Geddes
frog at bunyip.wheelycreek.net
Sun Jan 1 11:41:58 HKT 2023
Yeah - still going over it making sure it's correct.
maxCharge = last_charge_watts + new_percents_in_step * (maxCharge
- last_charge_watts) / percents_In_Step;
//.ichael
*OH.. and Happy New Year everybody.*
On Sun, 1 Jan 2023 at 08:25, Michael Geddes <frog at bunyip.wheelycreek.net>
wrote:
> Missed a few things; so how about this as a more general function:
>
> * chargeVolt is probably not going to get used - may remove it
> * chargewatts - is the current power being used to charge.
> * availwatts - Is the reported available amount to use (can leave as 0)
> * adjustTemp - true if 'tempCelcius' is valid.
> * tempCelcius - The current battery temperature.
> * fromSoc,toSoc - the current and target 'state of charge'
> * batterySizeWh - The battery size in whatt-hours
> * charge_steps - Array of steps.
>
> int CalcRemainingChargeMins(int chargeVolt, float chargewatts, float
> availwatts, bool adjustTemp, float tempCelcius, int fromSoc, int toSoc, int
> batterySizeWh, charging_step_t charge_steps[])
> {
> if (chargewatts <= 0 || fromSoc >= toSoc)
> return 0;
> if (availwatts < chargewatts) // encompasses '0' case.
> availwatts = chargewatts;
>
> int last_to_soc = fromSoc;
> int last_charge_watts = chargewatts;
>
> // Track the sum of percent/kw. Reduces floating point operations.
> // The 'batterySizeWh' * 60 / 100 would be used on every sum.. so
> factored to the end.
> float sumval = 0;
> bool finish = false;
> for (int i = 0; !finish; ++i) {
> const charging_step_t &step = charge_steps[i];
> if (step.maxChargeWatts <= 0)
> break;
>
> if (step.toPercent > last_to_soc) {
> int next_to_soc = step.toPercent;
> float maxCharge = step.maxChargeWatts ;
> if (adjustTemp && step.tempOptimal > 0) {
> maxCharge *= 1-(std::abs(tempCelcius - step.tempOptimal) *
> step.tempDegrade);
> if (maxCharge < 0) {
> // Shouldn't happen; it means the numbers are wrong.
> maxCharge = chargewatts;
> }
> }
> int percents_In_Step = next_to_soc - last_to_soc;
> if (toSoc < next_to_soc) { // This would be the last one.
> int new_percents_in_step = toSoc - last_to_soc;
> if (last_charge_watts < maxCharge) // Avoids -ve and 0 cases
> maxCharge += new_percents_in_step * (maxCharge -
> last_charge_watts) / percents_In_Step;
> next_to_soc = toSoc;
> percents_In_Step = new_percents_in_step;
> finish = true; // Last one.
> }
> float end_charge_watts = std::min(availwatts, maxCharge);
> // Keep it as a doubled value here to avoid a /2 which becomes *2
> below.
> float curSpeedDbl = last_charge_watts + end_charge_watts;
>
> // our one main division.
> sumval += ( percents_In_Step * 2) / curSpeedDbl;
> last_to_soc = next_to_soc;
> last_charge_watts = end_charge_watts;
> }
> }
> if (last_to_soc < toSoc) {
> sumval += (toSoc - last_to_soc) / last_charge_watts;
> }
> // convert to minutes (summary value to minutes)
> // 60 (hours to mins) / 100 (percent to ratio) -> 0.6
> return (int)((batterySizeWh * sumval * 0.6) + 0.5);
> }
>
> On Sat, 31 Dec 2022 at 18:10, Michael Geddes <frog at bunyip.wheelycreek.net>
> wrote:
>
>> Ok,
>> so I've reviewed my current charge curve and have another idea as to what
>> was happening... which is that the charger itself was dynamically changing
>> the power-available to be used for charging .. and stepping up and down in
>> increments. This makes sense of the difference between the two charge
>> sessions I recorded by hand, and the stepped nature of the curve I recorded.
>>
>> The only vehicle code that takes temperature into account is the
>> roadster .. which is definitely not generic... and is doing calculations in
>> miles and degrees Celsius (weird) and is possibly not going to be relevant
>> or easy to leverage. All other code (apart from VWUP) uses:
>> * A custom function (no table)
>> * a single flat value or
>> * a version of the flat stepped code with percent ranges.
>>
>> I'll move mine to use a (calculated) gradient between points and lose
>> the dependence on voltage.
>>
>> I don't see that your code significantly reduces the number of division
>> operations used (though I totally understand that aim) since it uses it for
>> the first and last soc section only.
>>
>> What might actually be better is a temperature efficiency gradient
>> centred around 20 degrees. Either a single one - or in the table. See below.
>>
>> //.ichael
>>
>> ----8<--
>>
>>
>> // Charging profile// - Must be from lowest to highest to%.// - max watts is at optimal temperature.// - MaxWattsUsed = maxChargeWatts * 1-(|battery_temp - tempOptimal| * tempDegrade)
>> // 0.0182 means degrades by 40% of original at zero degress// - GUESTIMATE ONLY needs data.
>> charging_step_t ioniq5_chargesteps[] = {
>> // to max optimal temp degrade
>> // % watts deg C gradient
>> { 10, 220000, 22, 0.0182 },
>> { 25, 210000, 22, 0.0182 },
>> { 45, 200000, 22, 0.0182 },
>> { 75, 150000, 22, 0.0182 },
>> { 80, 80000, 22, 0.0182 },
>> { 85, 60000, 22, 0.0182 },
>> { 90, 40000, 22, 0.0182 },
>> { 95, 25000, 22, 0.0182 },
>> { 100, 7200, 22, 0.0182 },
>> { 0, 0, 0, 0},
>> };/** Calculate the remaining minutes to charge.
>> * Works on the premise that the charge curve applies to the MAXIMUM only.
>> * Each section is calculated such that the 'max watts' is effectively the 'end' value at that 'to'..
>> * and the power use is assumed to be a gradient from the last 'to' value to the end 'to' value.*/int CalcRemainingChargeMins(int chargeVolt, float chargewatts, float availwatts, bool adjustTemp, float tempCelcius, int fromSoc, int toSoc, int batterySizeWh, charging_step_t charge_steps[])
>> { .....
>> }
>>
>>
>>
>> On Fri, 30 Dec 2022 at 07:59, Michael Geddes <frog at bunyip.wheelycreek.net>
>> wrote:
>>
>>> Firstly, the Voltage architecture (800V, 400V, 240V 3Phase, 240V
>>> 1Phase) in use is going to affect the amount of power able to be delivered
>>> because the limiting factor will be the current and possibly the potential
>>> difference across the individual cell (?)- so it is my _thesis_ that this
>>> will matter, especially in the mid section of the charging; I need to get
>>> more information on this so I have made a plugin to handle gathering data.
>>> I'm quite happy to ditch it if it doesn't bare out.
>>> Ah, also there are two more metrics that I should log that you have
>>> reminded me of: The available power (I think I have that metric) and the
>>> temperature (? ave of the batteries perhaps).
>>>
>>> I'll have to try and get to one of the few 800v chargers available in my
>>> state again - finding 50kw chargers is pretty easy.
>>>
>>> I have mapped by hand the charging 'curve' at an 800V charger (only
>>> 150kw or so though - so yes, power also matters), and my experience was
>>> that it was quite 'stepped' - ie it seemed to go in stages. Though I can
>>> see the advantage of the gradient, the only time it went to more of a
>>> curve/gradient is a little bit in the transition areas and above 90% SOC (
>>> which I'm not really fussed with being too precise above 90%).
>>>
>>> I'm still a little uncertain how to handle coming from a low SOC as the
>>> lower SOC charges a bit slower, so still need to manage somehow the maximum
>>> power available to the charger (which I possibly have as a metric - I
>>> should probably include that in the logging and in the calculation, and
>>> yes, that might end up being a better input into the algorithm).
>>>
>>> The way I have it working (btw) means that a higher voltage architecture
>>> defaults to the lower voltage curve (line) if a higher step isn't there. So
>>> where the curves (lines) converge I don't double up on data in the table.
>>> The AC Charger seems to be pretty constant right up to 90% for example.
>>>
>>> //.ichael
>>>
>>> On Fri, 30 Dec 2022 at 02:50, Michael Balzer <dexter at expeedo.de> wrote:
>>>
>>>> Michael,
>>>>
>>>> while it makes sense to add a general standard utility for this, I
>>>> don't
>>>> think the Hyundai/Kia code is a good candidate for this.
>>>>
>>>> I find it confusing you need to take the voltage into account rather
>>>> than the power. Also, the "WattHours" numbers don't seem to make sense
>>>> -- why would you have different capacities in the same SOC differences,
>>>> and why depending on the voltage? Also, the calculator seems to need /
>>>> be based on rectangular steps instead of an actual charge curve?
>>>>
>>>> A more natural generalized definition would in my opinion be based on
>>>> modeling the actual curve, like the one I've implemented for the VW Up,
>>>> see `OvmsVehicleVWeUp::CalcChargeTime()`. That takes the charge curve
>>>> as
>>>> direct (SOC → max power) coordinates, just adding the section gradients
>>>> as a speed optimization (float divisions are very CPU intense on the
>>>> ESP32):
>>>>
>>>> struct {
>>>> int soc; float pwr; float grd;
>>>> } ccurve[] = {
>>>> { 0, 30.0, (32.5-30.0) / ( 30- 0) },
>>>> { 30, 32.5, (26.5-32.5) / ( 55- 30) },
>>>> { 55, 26.5, (18.5-26.5) / ( 76- 55) },
>>>> { 76, 18.5, (11.0-18.5) / ( 81- 76) },
>>>> { 81, 11.0, ( 6.5-11.0) / ( 91- 81) },
>>>> { 91, 6.5, ( 3.0- 6.5) / (100- 91) },
>>>> { 100, 3.0, 0 },
>>>> };
>>>>
>>>> But maybe some other vehicle already has an even more generalized &
>>>> easy
>>>> to use approach. My function lacks temperature compensation, but
>>>> normally delivers sufficiently close results.
>>>>
>>>> Regards,
>>>> Michael
>>>>
>>>>
>>>> Am 29.12.22 um 13:02 schrieb Michael Geddes:
>>>> > Hi,
>>>> >
>>>> > There's a remaining charge calculator currently in the hyundai base
>>>> > class which I have improved upon. This configuration allows for the
>>>> > charge voltage to be part of the equation. This is the example from
>>>> > data I collected with the ioniq 5 on 800v charging .. and I've
>>>> written
>>>> > a plugin so I can get more charge profile information at other charge
>>>> > voltages.
>>>> >
>>>> > I'm happy to leave it in the Ioniq 5 code, but was wondering if I
>>>> > should put the implementation in a common area.. vehicle.h maybe?
>>>> >
>>>> > //.ichael
>>>> >
>>>> > ---8<----------------------
>>>> >
>>>> > // Charging profile
>>>> > // - Must be from lowest to highest to%.
>>>> > // - Higher voltages must come before lower voltages for the same to%
>>>> > charging_step_t ioniq5_chargesteps[] = {
>>>> > // voltage, to%, WattHours
>>>> > { 750, 10, 100000 },
>>>> > { 750, 25, 190000 },
>>>> > { 750, 45, 220000 },
>>>> > { 750, 75, 120000 },
>>>> > { 750, 80, 80000 },
>>>> > { 400, 85, 60000 },
>>>> > { 400, 90, 40000 },
>>>> > { 100, 90, 1100 },
>>>> > { 400, 95, 25000 },
>>>> > { 100, 95, 7400 },
>>>> > { 100, 100, 7200 },
>>>> > { 0, 0, 0 },
>>>> > };
>>>> >
>>>> > int CalcRemainingChargeMins(int chargeVolt, float chargespeed, int
>>>> > fromSoc, int toSoc, int batterySize, charging_step_t charge_steps[])
>>>> > {
>>>> >
>>>> > _______________________________________________
>>>> > OvmsDev mailing list
>>>> > OvmsDev at lists.openvehicles.com
>>>> > http://lists.openvehicles.com/mailman/listinfo/ovmsdev
>>>>
>>>> --
>>>> Michael Balzer * Helkenberger Weg 9 * D-58256 Ennepetal
>>>> Fon 02333 / 833 5735 * Handy 0176 / 206 989 26
>>>>
>>>> _______________________________________________
>>>> OvmsDev mailing list
>>>> OvmsDev at lists.openvehicles.com
>>>> http://lists.openvehicles.com/mailman/listinfo/ovmsdev
>>>>
>>>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.openvehicles.com/pipermail/ovmsdev/attachments/20230101/bce406d6/attachment-0001.htm>
More information about the OvmsDev
mailing list