Jawaril99 commited on
Commit
4b3a341
·
1 Parent(s): 318de84

Improve planner with 14-day safety buffer

Browse files
batteryswap_example/planners/best.pickle CHANGED
@@ -1,3 +1,3 @@
1
  version https://git-lfs.github.com/spec/v1
2
- oid sha256:d298671d2c8f3c4374b5f2d75edae3e7eceff08985f857bd406ebc9e3f450df4
3
- size 6525860
 
1
  version https://git-lfs.github.com/spec/v1
2
+ oid sha256:f8971ab518a017a7ee9be0c377fc31e15fe8f79fc3195f1ac9b336ac4f98d4e2
3
+ size 6525899
batteryswap_example/train.py CHANGED
@@ -25,40 +25,147 @@ log = structlog.get_logger()
25
 
26
 
27
  class OrderedPlanner(Planner):
28
- def __init__(self, rul_estimator):
 
 
 
 
 
29
  self.rul_estimator = rul_estimator
 
 
30
 
31
  def plan(self, battery_data, locations, travel_costs, settings):
32
 
33
- # Remaining Useful Life estimation
34
- # FIXME: consider balance of over/under-estimation
35
- percentile = 'p50'
 
 
36
  rul = self.rul_estimator.predict(battery_data)
37
  rul_days = rul[percentile]
38
- # convert to a date
39
- start_time = battery_data.reset_index()['end_time'].max().normalize()
40
- predict_eol = start_time + pandas.to_timedelta(rul_days, unit='D')
41
-
42
- loc = locations.copy().set_index('battery')
43
- loc['eol_time'] = predict_eol
44
- order = loc.sort_values('eol_time', ascending=True)
45
-
46
- # Planner
47
- # Stupid heuristic: Do one swap per day
48
- # FIXME: take travel distances into account
49
- # FIXME: take co-location into account
50
- # FIXME: take daily and weekly limits into account
51
- days = start_time + pandas.to_timedelta(numpy.arange(len(order)), unit='D')
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
52
  plan = pandas.DataFrame({
53
- 'day': days,
54
- 'battery': order.index,
55
  })
56
 
57
- check_plan_valid(plan, locations, start_time=start_time)
 
 
 
 
58
 
59
  return plan
60
 
61
 
 
 
 
62
  class DummyRULModel(RULModel):
63
  # RUL model that predicts (no information rate)
64
  # FIXME: make a model that actually uses the data to improve predictions
 
25
 
26
 
27
  class OrderedPlanner(Planner):
28
+ def __init__(
29
+ self,
30
+ rul_estimator,
31
+ safety_days=14,
32
+ max_swaps_per_day=6,
33
+ ):
34
  self.rul_estimator = rul_estimator
35
+ self.safety_days = safety_days
36
+ self.max_swaps_per_day = max_swaps_per_day
37
 
38
  def plan(self, battery_data, locations, travel_costs, settings):
39
 
40
+
41
+ # Predict Remaining Useful Life
42
+
43
+ percentile = "p50"
44
+
45
  rul = self.rul_estimator.predict(battery_data)
46
  rul_days = rul[percentile]
47
+
48
+ start_time = (
49
+ battery_data
50
+ .reset_index()["end_time"]
51
+ .max()
52
+ .normalize()
53
+ )
54
+
55
+ predicted_eol = (
56
+ start_time
57
+ + pandas.to_timedelta(rul_days, unit="D")
58
+ )
59
+
60
+
61
+ # Create location table
62
+
63
+ loc = locations.copy().set_index("battery")
64
+
65
+ loc["predicted_eol"] = predicted_eol
66
+
67
+ # Replace BEFORE predicted EOL.
68
+ #
69
+ # Late swaps are much more expensive than early swaps,
70
+ # so give ourselves a safety margin.
71
+ loc["target_day"] = (
72
+ loc["predicted_eol"]
73
+ - pandas.to_timedelta(self.safety_days, unit="D")
74
+ )
75
+ loc["target_day"] = loc["target_day"].dt.normalize()
76
+
77
+ # Never schedule before the planning period starts
78
+ loc["target_day"] = loc["target_day"].clip(lower=start_time)
79
+
80
+
81
+ # Find location columns
82
+
83
+ # We want batteries in the same building/room to appear
84
+ # close together in the ordering.
85
+ #
86
+ location_columns = []
87
+
88
+ for candidate in [
89
+ "building",
90
+ "building_id",
91
+ "room",
92
+ "room_id",
93
+ ]:
94
+ if candidate in loc.columns:
95
+ location_columns.append(candidate)
96
+
97
+
98
+ # Sort by urgency first, then location
99
+
100
+ sort_columns = ["target_day"] + location_columns
101
+
102
+ order = loc.sort_values(
103
+ sort_columns,
104
+ ascending=True,
105
+ )
106
+
107
+
108
+ # Schedule several batteries per day
109
+ # Baseline:
110
+ # battery 1 -> day 1
111
+ # battery 2 -> day 2
112
+ # battery 3 -> day 3
113
+ # ...
114
+ #
115
+ # V3:
116
+ # batteries 1-6 -> day 1
117
+ # batteries 7-12 -> day 2
118
+ # ...
119
+ #
120
+ # This should strongly reduce late swaps.
121
+ #
122
+ planned_days = []
123
+
124
+ current_day = start_time
125
+ swaps_today = 0
126
+
127
+ for battery, row in order.iterrows():
128
+
129
+ desired_day = max(
130
+ start_time,
131
+ row["target_day"],
132
+ )
133
+
134
+ # If target date is later than our current day,
135
+ # move forward to that date.
136
+ if desired_day > current_day:
137
+ current_day = desired_day
138
+ swaps_today = 0
139
+
140
+ # Daily capacity reached -> next day
141
+ if swaps_today >= self.max_swaps_per_day:
142
+ current_day += pandas.Timedelta(days=1)
143
+ swaps_today = 0
144
+
145
+ planned_days.append(current_day)
146
+
147
+ swaps_today += 1
148
+
149
+ # Normalize only AFTER all planned days have been created
150
+ planned_days = pandas.to_datetime(planned_days).normalize()
151
+ #Construct final plan
152
  plan = pandas.DataFrame({
153
+ "day": planned_days,
154
+ "battery": order.index,
155
  })
156
 
157
+ check_plan_valid(
158
+ plan,
159
+ locations,
160
+ start_time=start_time,
161
+ )
162
 
163
  return plan
164
 
165
 
166
+
167
+
168
+
169
  class DummyRULModel(RULModel):
170
  # RUL model that predicts (no information rate)
171
  # FIXME: make a model that actually uses the data to improve predictions