Skip to content

Commit 53ad032

Browse files
committed
d
1 parent 834d3c6 commit 53ad032

1 file changed

Lines changed: 120 additions & 75 deletions

File tree

lemonlib/lemonbot/lemon_robot.py

Lines changed: 120 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
from typing import Callable, List
1+
from typing import Callable, Dict, List
22

33
import magicbot
4-
from wpilib import DriverStation, SmartDashboard, Timer
4+
from wpilib import DriverStation, Timer
55

6-
from lemonlib.smart import SmartPreference
6+
from lemonlib.smart import SmartNT, SmartPreference
77

88

99
class LemonRobot(magicbot.MagicRobot):
@@ -18,14 +18,25 @@ class LemonRobot(magicbot.MagicRobot):
1818
watchdog_profile = SmartPreference(False)
1919
watchdog_profile_period = SmartPreference(0.25)
2020

21+
# EMA coefficient (0 < alpha <= 1)
22+
watchdog_ema_alpha = SmartPreference(0.25)
23+
2124
def __init__(self):
2225
super().__init__()
26+
2327
self._periodic_callbacks: List[List] = []
2428

2529
self.loop_time = self.control_loop_wait_time
2630
self._last_watchdog_profile_time = 0.0
2731
self._overrun_count = 0
28-
self._last_overrun_epochs: dict = {}
32+
33+
# Profiling storage
34+
self._last_overrun_epochs: Dict[str, float] = {}
35+
36+
self._epoch_ema_all: Dict[str, float] = {}
37+
self._epoch_ema_overrun: Dict[str, float] = {}
38+
39+
self._smart_nt = SmartNT("LemonRobot")
2940

3041
def add_periodic(self, callback: Callable[[], None], period: float):
3142
now = Timer.getFPGATimestamp()
@@ -40,27 +51,13 @@ def _run_periodics(self):
4051
callback()
4152

4253
def autonomousPeriodic(self):
43-
"""
44-
Periodic code for autonomous mode should go here.
45-
Runs when not enabled for trajectory display.
46-
47-
Users should override this method for code which will be called
48-
periodically at a regular rate while the robot is in autonomous mode.
49-
50-
This code executes before the ``execute`` functions of all
51-
components are called.
52-
"""
5354
pass
5455

5556
def autonomous(self):
5657
super().autonomous()
5758
self.autonomousPeriodic()
5859

5960
def enabledperiodic(self) -> None:
60-
"""Periodic code for when the bot is enabled should go here.
61-
Runs when not enabled for trajectory display.
62-
63-
Users should override this method for code which will be called"""
6461
pass
6562

6663
def _on_mode_enable_components(self):
@@ -71,13 +68,11 @@ def on_enable(self):
7168
pass
7269

7370
def _enabled_periodic(self) -> None:
74-
"""Run components and all periodic methods."""
7571
watchdog = self.watchdog
7672

7773
for name, component in self._components:
7874
try:
7975
component.execute()
80-
8176
except Exception:
8277
self.onException()
8378
watchdog.addEpoch(name)
@@ -91,67 +86,117 @@ def _enabled_periodic(self) -> None:
9186
def _do_periodics(self):
9287
super()._do_periodics()
9388

94-
is_overrun = self.watchdog.getTime() > self.control_loop_wait_time
89+
if not self.watchdog_profile:
90+
self.loop_time = max(self.control_loop_wait_time, self.watchdog.getTime())
91+
return
92+
93+
wd = self.watchdog
94+
loop_time = wd.getTime()
95+
is_overrun = loop_time > self.control_loop_wait_time
96+
97+
epochs = wd._epochs
98+
prev = wd._startTime
99+
100+
alpha = float(self.watchdog_ema_alpha)
101+
one_minus_alpha = 1.0 - alpha
102+
103+
ema_all = self._epoch_ema_all
104+
ema_overrun = self._epoch_ema_overrun
105+
last_overrun = self._last_overrun_epochs
106+
107+
for key, value in epochs:
108+
delta = (value - prev) * 1e-6
109+
110+
prev_ema = ema_all.get(key)
111+
if prev_ema is None:
112+
ema_all[key] = delta
113+
else:
114+
ema_all[key] = alpha * delta + one_minus_alpha * prev_ema
115+
116+
prev = value
117+
95118
if is_overrun:
96119
self._overrun_count += 1
97-
# Capture epoch data for overrun
98-
prev = self.watchdog._startTime
99-
now = self.watchdog._get_time()
100-
for key, value in self.watchdog._epochs:
101-
self._last_overrun_epochs[key] = (value - prev) / 1e6
120+
121+
prev = wd._startTime
122+
for key, value in epochs:
123+
delta = (value - prev) * 1e-6
124+
last_overrun[key] = delta
125+
126+
prev_ema = ema_overrun.get(key)
127+
if prev_ema is None:
128+
ema_overrun[key] = delta
129+
else:
130+
ema_overrun[key] = alpha * delta + one_minus_alpha * prev_ema
131+
102132
prev = value
103133

104-
if self.watchdog_profile:
105-
now_fpga = Timer.getFPGATimestamp()
106-
if (
107-
now_fpga - self._last_watchdog_profile_time
108-
>= self.watchdog_profile_period
109-
):
110-
self._last_watchdog_profile_time = now_fpga
111-
112-
now = self.watchdog._get_time()
113-
self._lastEpochsPrintTime = now
114-
prev = self.watchdog._startTime
115-
max_epoch_time = 0.0
116-
max_epoch_key = ""
117-
for key, value in self.watchdog._epochs:
118-
time = (value - prev) / 1e6
119-
prev = value
120-
if time > max_epoch_time:
121-
max_epoch_time = time
122-
max_epoch_key = key
123-
SmartDashboard.putNumber(f"Watchdog Epochs/{key}", time)
124-
125-
total_time = (now - self.watchdog._startTime) / 1e6
126-
SmartDashboard.putNumber("Watchdog Epochs/Total", total_time)
127-
SmartDashboard.putNumber("Watchdog Epochs/Max", max_epoch_time)
128-
SmartDashboard.putString("Watchdog Epochs/MaxKey", max_epoch_key)
129-
130-
SmartDashboard.putNumber("Watchdog/LoopTime", self.watchdog.getTime())
131-
SmartDashboard.putNumber(
132-
"Watchdog/ControlPeriod", self.control_loop_wait_time
134+
now_fpga = Timer.getFPGATimestamp()
135+
if now_fpga - self._last_watchdog_profile_time >= self.watchdog_profile_period:
136+
self._last_watchdog_profile_time = now_fpga
137+
138+
start = wd._startTime
139+
now = wd._get_time()
140+
total_time = (now - start) * 1e-6
141+
142+
self._smart_nt.put_number("Watchdog/LoopTime", round(loop_time, 6))
143+
self._smart_nt.put_number(
144+
"Watchdog/ControlPeriod", round(self.control_loop_wait_time, 6)
145+
)
146+
self._smart_nt.put_boolean("Watchdog/Overrun", is_overrun)
147+
self._smart_nt.put_number("Watchdog/OverrunCount", self._overrun_count)
148+
self._smart_nt.put_number("Watchdog Epochs/Total", round(total_time, 6))
149+
150+
if last_overrun:
151+
max_last = 0.0
152+
total_last = 0.0
153+
for v in last_overrun.values():
154+
total_last += v
155+
if v > max_last:
156+
max_last = v
157+
158+
self._smart_nt.put_number(
159+
"Watchdog LastOverrun/Max", round(max_last, 6)
160+
)
161+
self._smart_nt.put_number(
162+
"Watchdog LastOverrun/Total", round(total_last, 6)
133163
)
134-
SmartDashboard.putBoolean(
135-
"Watchdog/Overrun",
136-
self.watchdog.getTime() > self.control_loop_wait_time,
164+
165+
for k, v in last_overrun.items():
166+
self._smart_nt.put_number(f"Watchdog LastOverrun/{k}", round(v, 6))
167+
168+
if ema_all:
169+
max_all = 0.0
170+
total_all = 0.0
171+
for v in ema_all.values():
172+
total_all += v
173+
if v > max_all:
174+
max_all = v
175+
176+
self._smart_nt.put_number("Watchdog EMA/Max", round(max_all, 6))
177+
self._smart_nt.put_number("Watchdog EMA/Total", round(total_all, 6))
178+
179+
for k, v in ema_all.items():
180+
self._smart_nt.put_number(f"Watchdog EMA/{k}", round(v, 6))
181+
182+
if ema_overrun:
183+
max_or = 0.0
184+
total_or = 0.0
185+
for v in ema_overrun.values():
186+
total_or += v
187+
if v > max_or:
188+
max_or = v
189+
190+
self._smart_nt.put_number("Watchdog EMAOverrun/Max", round(max_or, 6))
191+
self._smart_nt.put_number(
192+
"Watchdog EMAOverrun/Total", round(total_or, 6)
137193
)
138-
SmartDashboard.putNumber("Watchdog/OverrunCount", self._overrun_count)
139-
140-
# Display last overrun epochs
141-
if self._last_overrun_epochs:
142-
max_overrun_time = max(self._last_overrun_epochs.values())
143-
total_overrun_time = sum(self._last_overrun_epochs.values())
144-
SmartDashboard.putNumber(
145-
"Watchdog LastOverrun/Max", max_overrun_time
146-
)
147-
SmartDashboard.putNumber(
148-
"Watchdog LastOverrun/Total", total_overrun_time
149-
)
150-
for key, time in self._last_overrun_epochs.items():
151-
SmartDashboard.putNumber(f"Watchdog LastOverrun/{key}", time)
152-
153-
self.watchdog.addEpoch("watchdog_profile")
154-
self.loop_time = max(self.control_loop_wait_time, self.watchdog.getTime())
194+
195+
for k, v in ema_overrun.items():
196+
self._smart_nt.put_number(f"Watchdog EMAOverrun/{k}", round(v, 6))
197+
198+
wd.addEpoch("watchdog_profile")
199+
self.loop_time = max(self.control_loop_wait_time, loop_time)
155200

156201
def get_period(self) -> float:
157202
"""Get the period of the robot loop in seconds."""

0 commit comments

Comments
 (0)