From df2f6a8de93ab109b127c52de84129938cf8924e Mon Sep 17 00:00:00 2001 From: premultiply Date: Mon, 14 Sep 2020 08:27:05 +0200 Subject: [PATCH] Improve prognosis by continuously updating virtual battery capacity (#335) Authored-by: premultiply --- core/wrapper/socestimator.go | 18 ++++++++++++++---- core/wrapper/socestimator_test.go | 6 ++++-- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/core/wrapper/socestimator.go b/core/wrapper/socestimator.go index a0d17be91..fec10d07f 100644 --- a/core/wrapper/socestimator.go +++ b/core/wrapper/socestimator.go @@ -16,6 +16,7 @@ type SocEstimator struct { estimate bool capacity float64 // vehicle capacity in Wh cached to simplify testing + virtualCapacity float64 // estimated virtual vehicle capacity in Wh socCharge float64 // estimated vehicle SoC prevSoC float64 // previous vehicle SoC in % prevChargedEnergy float64 // previous charged energy in Wh @@ -40,7 +41,8 @@ func (s *SocEstimator) Reset() { s.prevSoC = 0 s.prevChargedEnergy = 0 s.capacity = float64(s.vehicle.Capacity()) * 1e3 // cache to simplify debugging - s.energyPerSocStep = s.capacity / 100 + s.virtualCapacity = s.capacity / 0.9 // assume charge 90% efficiency + s.energyPerSocStep = s.virtualCapacity / 100 } // RemainingChargeDuration returns the remaining duration estimate based on SoC, target and charge power @@ -62,7 +64,7 @@ func (s *SocEstimator) RemainingChargeDuration(chargePower float64, targetSoC in } // estimate remaining time - whRemaining := percentRemaining / 100 * s.capacity + whRemaining := percentRemaining / 100 * s.virtualCapacity return time.Duration(float64(time.Hour) * whRemaining / chargePower).Round(time.Second) } @@ -73,7 +75,14 @@ func (s *SocEstimator) RemainingChargeDuration(chargePower float64, targetSoC in func (s *SocEstimator) SoC(chargedEnergy float64) (float64, error) { f, err := s.vehicle.ChargeState() if err != nil { - return s.socCharge, err + s.log.WARN.Printf("updating soc failed: %v", err) + + // try to recover from temporary vehicle-api errors + if s.prevSoC == 0 { // never received a soc value + return s.socCharge, err + } + + f = s.prevSoC // recover last received soc } s.socCharge = f @@ -86,7 +95,8 @@ func (s *SocEstimator) SoC(chargedEnergy float64) (float64, error) { // calculate gradient, wh per soc % if socDelta > 1 && energyDelta > 0 && s.prevSoC > 0 { s.energyPerSocStep = energyDelta / socDelta - s.log.TRACE.Printf("soc gradient updated: energyPerSocStep: %0.0fWh, virtualBatCap: %0.1fkWh", s.energyPerSocStep, s.energyPerSocStep*100/1e3) + s.virtualCapacity = s.energyPerSocStep * 100 / 1e3 + s.log.TRACE.Printf("soc gradient updated: energyPerSocStep: %0.0fWh, virtualCapacity: %0.1fkWh", s.energyPerSocStep, s.virtualCapacity) } // sample charged energy at soc change, reset energy delta diff --git a/core/wrapper/socestimator_test.go b/core/wrapper/socestimator_test.go index d74e7930e..7a4616966 100644 --- a/core/wrapper/socestimator_test.go +++ b/core/wrapper/socestimator_test.go @@ -12,7 +12,8 @@ import ( func TestRemainingChargeDuration(t *testing.T) { ctrl := gomock.NewController(t) vehicle := mock.NewMockVehicle(ctrl) - vehicle.EXPECT().Capacity().Return(int64(10)) + //9 kWh userBatCap => 10 kWh virtualBatCap + vehicle.EXPECT().Capacity().Return(int64(9)) ce := NewSocEstimator(util.NewLogger("foo"), vehicle, false) ce.socCharge = 20.0 @@ -28,7 +29,8 @@ func TestRemainingChargeDuration(t *testing.T) { func TestSoCEstimation(t *testing.T) { ctrl := gomock.NewController(t) vehicle := mock.NewMockVehicle(ctrl) - vehicle.EXPECT().Capacity().Return(int64(10)) + //9 kWh userBatCap => 10 kWh virtualBatCap + vehicle.EXPECT().Capacity().Return(int64(9)) ce := NewSocEstimator(util.NewLogger("foo"), vehicle, true) ce.socCharge = 20.0