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:
|
| 3 |
-
size
|
|
|
|
| 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__(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 29 |
self.rul_estimator = rul_estimator
|
|
|
|
|
|
|
| 30 |
|
| 31 |
def plan(self, battery_data, locations, travel_costs, settings):
|
| 32 |
|
| 33 |
-
|
| 34 |
-
#
|
| 35 |
-
|
|
|
|
|
|
|
| 36 |
rul = self.rul_estimator.predict(battery_data)
|
| 37 |
rul_days = rul[percentile]
|
| 38 |
-
|
| 39 |
-
start_time =
|
| 40 |
-
|
| 41 |
-
|
| 42 |
-
|
| 43 |
-
|
| 44 |
-
|
| 45 |
-
|
| 46 |
-
|
| 47 |
-
|
| 48 |
-
|
| 49 |
-
|
| 50 |
-
|
| 51 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 52 |
plan = pandas.DataFrame({
|
| 53 |
-
|
| 54 |
-
|
| 55 |
})
|
| 56 |
|
| 57 |
-
check_plan_valid(
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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
|