[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