[Ovmsdev] Remaining Charge calculator

Michael Geddes frog at bunyip.wheelycreek.net
Sun Jan 1 08:25:39 HKT 2023


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/712fbc9d/attachment-0001.htm>


More information about the OvmsDev mailing list