Improve estimator behavior (#2418)
This commit is contained in:
parent
bea4b8a55d
commit
b3fdef1c0b
2 changed files with 42 additions and 42 deletions
|
|
@ -22,7 +22,9 @@ type Estimator struct {
|
|||
capacity float64 // vehicle capacity in Wh cached to simplify testing
|
||||
virtualCapacity float64 // estimated virtual vehicle capacity in Wh
|
||||
vehicleSoc float64 // estimated vehicle SoC
|
||||
prevSoC float64 // previous vehicle SoC in %
|
||||
initialSoc float64 // first received valid vehicle SoC
|
||||
initialEnergy float64 // energy counter at first valid SoC
|
||||
prevSoc float64 // previous vehicle SoC in %
|
||||
prevChargedEnergy float64 // previous charged energy in Wh
|
||||
energyPerSocStep float64 // Energy per SoC percent in Wh
|
||||
}
|
||||
|
|
@ -43,8 +45,9 @@ func NewEstimator(log *util.Logger, charger api.Charger, vehicle api.Vehicle, es
|
|||
|
||||
// Reset resets the estimation process to default values
|
||||
func (s *Estimator) Reset() {
|
||||
s.prevSoC = 0
|
||||
s.prevSoc = 0
|
||||
s.prevChargedEnergy = 0
|
||||
s.initialSoc = 0
|
||||
s.capacity = float64(s.vehicle.Capacity()) * 1e3 // cache to simplify debugging
|
||||
s.virtualCapacity = s.capacity / chargeEfficiency // initial capacity taking efficiency into account
|
||||
s.energyPerSocStep = s.virtualCapacity / 100
|
||||
|
|
@ -112,12 +115,12 @@ func (s *Estimator) SoC(chargedEnergy float64) (float64, error) {
|
|||
if err == nil || !errors.Is(err, api.ErrNotAvailable) {
|
||||
if err != nil {
|
||||
// never received a soc value
|
||||
if s.prevSoC == 0 {
|
||||
if s.prevSoc == 0 {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
// recover from temporary api errors
|
||||
f = s.prevSoC
|
||||
f = s.prevSoc
|
||||
s.log.WARN.Printf("vehicle soc (charger): %v (ignored by estimator)", err)
|
||||
}
|
||||
|
||||
|
|
@ -135,12 +138,12 @@ func (s *Estimator) SoC(chargedEnergy float64) (float64, error) {
|
|||
}
|
||||
|
||||
// never received a soc value
|
||||
if s.prevSoC == 0 {
|
||||
if s.prevSoc == 0 {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
// recover from temporary api errors
|
||||
f = s.prevSoC
|
||||
f = s.prevSoc
|
||||
s.log.WARN.Printf("vehicle soc: %v (ignored by estimator)", err)
|
||||
}
|
||||
|
||||
|
|
@ -149,7 +152,7 @@ func (s *Estimator) SoC(chargedEnergy float64) (float64, error) {
|
|||
}
|
||||
|
||||
if s.estimate {
|
||||
socDelta := s.vehicleSoc - s.prevSoC
|
||||
socDelta := s.vehicleSoc - s.prevSoc
|
||||
energyDelta := math.Max(chargedEnergy, 0) - s.prevChargedEnergy
|
||||
|
||||
if socDelta != 0 || energyDelta < 0 { // soc value change or unexpected energy reset
|
||||
|
|
@ -170,16 +173,26 @@ func (s *Estimator) SoC(chargedEnergy float64) (float64, error) {
|
|||
invalid = vcs != ccs
|
||||
}
|
||||
|
||||
// calculate gradient, wh per soc %
|
||||
if !invalid && socDelta > 2 && energyDelta > 0 && s.prevSoC > 0 {
|
||||
s.energyPerSocStep = energyDelta / socDelta
|
||||
s.virtualCapacity = s.energyPerSocStep * 100
|
||||
s.log.DEBUG.Printf("soc gradient updated: energyPerSocStep: %0.0fWh, virtualCapacity: %0.0fWh", s.energyPerSocStep, s.virtualCapacity)
|
||||
if !invalid {
|
||||
if s.initialSoc == 0 {
|
||||
s.initialSoc = s.vehicleSoc
|
||||
s.initialEnergy = chargedEnergy
|
||||
}
|
||||
|
||||
socDiff := s.vehicleSoc - s.initialSoc
|
||||
energyDiff := chargedEnergy - s.initialEnergy
|
||||
|
||||
// recalculate gradient, wh per soc %
|
||||
if socDiff > 10 && energyDiff > 0 {
|
||||
s.energyPerSocStep = energyDiff / socDiff
|
||||
s.virtualCapacity = s.energyPerSocStep * 100
|
||||
s.log.DEBUG.Printf("soc gradient updated: soc: %.1f%%, socDiff: %.1f%%, energyDiff: %.0fWh, energyPerSocStep: %.1fWh, virtualCapacity: %.0fWh", s.vehicleSoc, socDiff, energyDiff, s.energyPerSocStep, s.virtualCapacity)
|
||||
}
|
||||
}
|
||||
|
||||
// sample charged energy at soc change, reset energy delta
|
||||
s.prevChargedEnergy = math.Max(chargedEnergy, 0)
|
||||
s.prevSoC = s.vehicleSoc
|
||||
s.prevSoc = s.vehicleSoc
|
||||
} else {
|
||||
s.vehicleSoc = math.Min(*fetchedSoC+energyDelta/s.energyPerSocStep, 100)
|
||||
s.log.DEBUG.Printf("soc estimated: %.2f%% (vehicle: %.2f%%)", s.vehicleSoc, *fetchedSoC)
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ func TestSoCEstimation(t *testing.T) {
|
|||
vehicle.EXPECT().Capacity().Return(capacity)
|
||||
|
||||
ce := NewEstimator(util.NewLogger("foo"), charger, vehicle, true)
|
||||
ce.vehicleSoc = 20.0
|
||||
ce.vehicleSoc = 0.0
|
||||
|
||||
tc := []struct {
|
||||
chargedEnergy float64
|
||||
|
|
@ -52,7 +52,7 @@ func TestSoCEstimation(t *testing.T) {
|
|||
estimatedSoC float64
|
||||
virtualCapacity float64
|
||||
}{
|
||||
{10, 20.0, 20.0, 10000},
|
||||
{0, 0.0, 0.0, 10000},
|
||||
{0, 20.0, 20.0, 10000},
|
||||
{123, 20.0, 21.23, 10000},
|
||||
{1000, 20.0, 30.0, 10000},
|
||||
|
|
@ -60,23 +60,15 @@ func TestSoCEstimation(t *testing.T) {
|
|||
{1200, 32.0, 32.0, 10000},
|
||||
{1900, 39.0, 39.0, 10000},
|
||||
{2000, 40.0, 40.0, 10000},
|
||||
{4000, 50.0, 50.0, 20000}, // 2kWh add 10% -> 20kWh battery
|
||||
{6000, 60.0, 60.0, 20000}, // 2kWh add 10% -> 20kWh battery
|
||||
{6500, 65.0, 65.0, 10000},
|
||||
{7000, 65.0, 70.0, 10000},
|
||||
{7100, 71.0, 71.0, 10000},
|
||||
{7300, 72.0, 72.0, 10000},
|
||||
{7400, 73.0, 73.0, 10000},
|
||||
{7700, 75.0, 75.0, 10000},
|
||||
{8200, 80.0, 80.0, 10000},
|
||||
{6000, 80.0, 80.0, 10000},
|
||||
{0, 25.0, 25.0, 10000},
|
||||
{2500, 25.0, 50.0, 10000},
|
||||
{0, 50.0, 50.0, 10000}, // -10000
|
||||
{4990, 50.0, 99.9, 10000},
|
||||
{5000, 50.0, 100.0, 10000},
|
||||
{5001, 50.0, 100.0, 10000},
|
||||
{0, 0.0, 0.0, 10000},
|
||||
{1000, 0.0, 10.0, 10000},
|
||||
{0, 20.0, 20.0, 10000},
|
||||
{1000, 30.0, 30.0, 10000},
|
||||
}
|
||||
|
||||
for i := 1; i < 3; i++ {
|
||||
|
|
@ -149,8 +141,7 @@ func TestSoCFromChargerAndVehicleWithErrors(t *testing.T) {
|
|||
}{
|
||||
// start with SoC from charger and errors
|
||||
{0, 0.0, 0.0, 10000, false, errors.New("some error"), nil},
|
||||
{10, 0.0, 20.0, 10000, false, api.ErrMustRetry, nil},
|
||||
{10, 20.0, 20.0, 10000, false, nil, nil},
|
||||
{0, 0.0, 20.0, 10000, false, api.ErrMustRetry, nil},
|
||||
{0, 20.0, 20.0, 10000, false, nil, nil},
|
||||
{123, 20.0, 21.23, 10000, false, nil, nil},
|
||||
{123, 0.0, 21.23, 10000, false, errors.New("another error"), nil},
|
||||
|
|
@ -159,19 +150,15 @@ func TestSoCFromChargerAndVehicleWithErrors(t *testing.T) {
|
|||
{1200, 32.0, 32.0, 10000, false, nil, nil},
|
||||
{1900, 39.0, 39.0, 10000, false, nil, nil},
|
||||
{2000, 40.0, 40.0, 10000, false, nil, nil},
|
||||
{4000, 50.0, 50.0, 20000, false, nil, nil}, // 2kWh add 10% -> 20kWh battery
|
||||
{6000, 60.0, 60.0, 20000, false, nil, nil}, // 2kWh add 10% -> 20kWh battery
|
||||
{6500, 65.0, 65.0, 10000, false, nil, nil},
|
||||
{7000, 65.0, 70.0, 10000, false, nil, nil},
|
||||
// move to SoC from vehicle
|
||||
{7000, 0.0, 70.0, 10000, true, api.ErrNotAvailable, errors.New("some error")},
|
||||
{7100, 0.0, 71.0, 10000, true, api.ErrNotAvailable, api.ErrMustRetry},
|
||||
{7100, 71.0, 71.0, 10000, true, api.ErrNotAvailable, nil},
|
||||
{7300, 72.0, 72.0, 10000, true, api.ErrNotAvailable, nil},
|
||||
{7400, 0.0, 73.0, 10000, true, api.ErrNotAvailable, errors.New("another error")},
|
||||
{7400, 73.0, 73.0, 10000, true, api.ErrNotAvailable, nil},
|
||||
{7700, 75.0, 75.0, 10000, true, api.ErrNotAvailable, nil},
|
||||
{8200, 80.0, 80.0, 10000, true, api.ErrNotAvailable, nil},
|
||||
{3000, 0.0, 50.0, 10000, true, api.ErrNotAvailable, errors.New("some error")},
|
||||
{3100, 0.0, 51.0, 10000, true, api.ErrNotAvailable, api.ErrMustRetry},
|
||||
{5100, 71.0, 71.0, 10000, true, api.ErrNotAvailable, nil},
|
||||
{5200, 72.0, 72.0, 10000, true, api.ErrNotAvailable, nil},
|
||||
{5300, 0.0, 73.0, 10000, true, api.ErrNotAvailable, errors.New("another error")},
|
||||
{5300, 73.0, 73.0, 10000, true, api.ErrNotAvailable, nil},
|
||||
{5500, 75.0, 75.0, 10000, true, api.ErrNotAvailable, nil},
|
||||
{6000, 80.0, 80.0, 10000, true, api.ErrNotAvailable, nil},
|
||||
{0, 25.0, 25.0, 10000, true, api.ErrNotAvailable, nil},
|
||||
{2500, 25.0, 50.0, 10000, true, api.ErrNotAvailable, nil},
|
||||
{0, 50.0, 50.0, 10000, true, api.ErrNotAvailable, nil}, // -10000
|
||||
|
|
@ -179,8 +166,8 @@ func TestSoCFromChargerAndVehicleWithErrors(t *testing.T) {
|
|||
{5000, 50.0, 100.0, 10000, true, api.ErrNotAvailable, nil},
|
||||
// back to SoC from charger
|
||||
{5001, 50.0, 100.0, 10000, false, nil, nil},
|
||||
{0, 0.0, 0.0, 10000, false, nil, nil},
|
||||
{1000, 0.0, 10.0, 10000, false, nil, nil},
|
||||
{0, 20.0, 20.0, 10000, false, nil, nil},
|
||||
{1000, 30.0, 30.0, 10000, false, nil, nil},
|
||||
}
|
||||
|
||||
for _, tc := range tc {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue