From c5372e904127a44aed4fd01749b7bf5dfefa667b Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Fri, 27 Feb 2026 08:04:24 -0800 Subject: [PATCH] new demo route (#37456) --- selfdrive/debug/mem_usage.py | 9 ++--- tools/clip/run.py | 68 ++++++++++++++++++++++++++++-------- tools/jotpluggler/pluggle.py | 29 ++++++++++----- tools/plotjuggler/juggle.py | 5 ++- tools/replay/replay.h | 2 +- 5 files changed, 81 insertions(+), 32 deletions(-) diff --git a/selfdrive/debug/mem_usage.py b/selfdrive/debug/mem_usage.py index 3451bfc3d..66e742f3e 100755 --- a/selfdrive/debug/mem_usage.py +++ b/selfdrive/debug/mem_usage.py @@ -8,13 +8,14 @@ import numpy as np from openpilot.common.utils import tabulate from openpilot.tools.lib.logreader import LogReader -DEMO_ROUTE = "a2a0ccea32023010|2023-07-27--13-01-19" +DEMO_ROUTE = "5beb9b58bd12b691/0000010a--a51155e496" MB = 1024 * 1024 TABULATE_OPTS = dict(tablefmt="simple_grid", stralign="center", numalign="center") def _get_procs(): from openpilot.selfdrive.test.test_onroad import PROCS + return PROCS @@ -137,9 +138,9 @@ def print_process_tables(op_procs, other_procs, total_mb, use_pss): op_rows, op_total = process_table_rows(op_procs, total_mb, use_pss, show_detail) # filter other: >5MB avg and not bare interpreter paths (test infra noise) - other_filtered = {n: v for n, v in other_procs.items() - if np.mean(v['pss' if use_pss else 'rss']) > 5.0 - and os.path.basename(n.split()[0]) not in ('python', 'python3')} + other_filtered = { + n: v for n, v in other_procs.items() if np.mean(v['pss' if use_pss else 'rss']) > 5.0 and os.path.basename(n.split()[0]) not in ('python', 'python3') + } other_rows, other_total = process_table_rows(other_filtered, total_mb, use_pss, show_detail) rows = op_rows diff --git a/tools/clip/run.py b/tools/clip/run.py index 5711cafa5..0aa90ec0a 100755 --- a/tools/clip/run.py +++ b/tools/clip/run.py @@ -24,7 +24,7 @@ from openpilot.common.utils import Timer from msgq.visionipc import VisionIpcServer, VisionStreamType FRAMERATE = 20 -DEMO_ROUTE, DEMO_START, DEMO_END = 'a2a0ccea32023010/2023-07-27--13-01-19', 90, 105 +DEMO_ROUTE, DEMO_START, DEMO_END = '5beb9b58bd12b691/0000010a--a51155e496', 90, 105 logger = logging.getLogger('clip') @@ -81,6 +81,7 @@ def _download_segment(path: str) -> bytes: def _parse_and_chunk_segment(args: tuple) -> list[dict]: raw_data, fps = args from openpilot.tools.lib.logreader import _LogFileReader + messages = migrate_all(list(_LogFileReader("", dat=raw_data, sort_by_time=True))) if not messages: return [] @@ -122,6 +123,7 @@ def patch_submaster(message_chunks, ui_state): sm.data[svc] = getattr(msg.as_builder(), svc) sm.logMonoTime[svc], sm.recv_time[svc], sm.recv_frame[svc] = msg.logMonoTime, t, sm.frame sm.frame += 1 + ui_state.sm.update = mock_update @@ -150,8 +152,7 @@ def iter_segment_frames(camera_paths, start_time, end_time, fps=20, use_qcam=Fal if use_qcam: w, h = frame_size or get_frame_dimensions(path) with FileReader(path) as f: - result = subprocess.run(["ffmpeg", "-v", "quiet", "-i", "-", "-f", "rawvideo", "-pix_fmt", "nv12", "-"], - input=f.read(), capture_output=True) + result = subprocess.run(["ffmpeg", "-v", "quiet", "-i", "-", "-f", "rawvideo", "-pix_fmt", "nv12", "-"], input=f.read(), capture_output=True) if result.returncode != 0: raise RuntimeError(f"ffmpeg failed: {result.stderr.decode()}") seg_frames = np.frombuffer(result.stdout, dtype=np.uint8).reshape(-1, w * h * 3 // 2) @@ -172,8 +173,7 @@ class FrameQueue: self.frame_w, self.frame_h = get_frame_dimensions(first_path) self._queue, self._stop, self._error = queue.Queue(maxsize=prefetch_count), threading.Event(), None - self._thread = threading.Thread(target=self._worker, - args=(camera_paths, start_time, end_time, fps, use_qcam, (self.frame_w, self.frame_h)), daemon=True) + self._thread = threading.Thread(target=self._worker, args=(camera_paths, start_time, end_time, fps, use_qcam, (self.frame_w, self.frame_h)), daemon=True) self._thread.start() def _worker(self, camera_paths, start_time, end_time, fps, use_qcam, frame_size): @@ -208,6 +208,7 @@ class FrameQueue: def load_route_metadata(route): from openpilot.common.params import Params, UnknownKeyName + path = next((item for item in route.log_paths() if item), None) if not path: raise Exception('error getting route metadata: cannot find any uploaded logs') @@ -223,15 +224,20 @@ def load_route_metadata(route): origin = init_data.gitRemote.split('/')[3] if len(init_data.gitRemote.split('/')) > 3 else 'unknown' return { - 'version': init_data.version, 'route': route.name.canonical_name, - 'car': car_params.carFingerprint if car_params else 'unknown', 'origin': origin, - 'branch': init_data.gitBranch, 'commit': init_data.gitCommit[:7], 'modified': str(init_data.dirty).lower(), + 'version': init_data.version, + 'route': route.name.canonical_name, + 'car': car_params.carFingerprint if car_params else 'unknown', + 'origin': origin, + 'branch': init_data.gitBranch, + 'commit': init_data.gitCommit[:7], + 'modified': str(init_data.dirty).lower(), } def draw_text_box(text, x, y, size, gui_app, font, color=None, center=False): import pyray as rl from openpilot.system.ui.lib.text_measure import measure_text_cached + box_color, text_color = rl.Color(0, 0, 0, 85), color or rl.WHITE text_size = measure_text_cached(font, text, size) text_width, text_height = int(text_size.x), int(text_size.y) @@ -244,6 +250,7 @@ def draw_text_box(text, x, y, size, gui_app, font, color=None, center=False): def render_overlays(gui_app, font, big, metadata, title, start_time, frame_idx, show_metadata, show_time): from openpilot.system.ui.lib.text_measure import measure_text_cached from openpilot.system.ui.lib.wrap_text import wrap_text + metadata_size = 16 if big else 12 title_size = 32 if big else 24 time_size = 24 if big else 16 @@ -259,8 +266,17 @@ def render_overlays(gui_app, font, big, metadata, title, start_time, frame_idx, # Metadata overlay (first 5 seconds) if show_metadata and metadata and frame_idx < FRAMERATE * 5: m = metadata - text = ", ".join([f"openpilot v{m['version']}", f"route: {m['route']}", f"car: {m['car']}", f"origin: {m['origin']}", - f"branch: {m['branch']}", f"commit: {m['commit']}", f"modified: {m['modified']}"]) + text = ", ".join( + [ + f"openpilot v{m['version']}", + f"route: {m['route']}", + f"car: {m['car']}", + f"origin: {m['origin']}", + f"branch: {m['branch']}", + f"commit: {m['commit']}", + f"modified: {m['modified']}", + ] + ) # Wrap text if too wide (leave margin on each side) margin = 2 * (time_width + 10 if show_time else 20) # leave enough margin for time overlay max_width = gui_app.width - margin @@ -278,17 +294,29 @@ def render_overlays(gui_app, font, big, metadata, title, start_time, frame_idx, draw_text_box(title, 0, 60, title_size, gui_app, font, center=True) -def clip(route: Route, output: str, start: int, end: int, headless: bool = True, big: bool = False, - title: str | None = None, show_metadata: bool = True, show_time: bool = True, use_qcam: bool = False): +def clip( + route: Route, + output: str, + start: int, + end: int, + headless: bool = True, + big: bool = False, + title: str | None = None, + show_metadata: bool = True, + show_time: bool = True, + use_qcam: bool = False, +): timer, duration = Timer(), end - start import pyray as rl + if big: from openpilot.selfdrive.ui.onroad.augmented_road_view import AugmentedRoadView else: from openpilot.selfdrive.ui.mici.onroad.augmented_road_view import AugmentedRoadView from openpilot.selfdrive.ui.ui_state import ui_state from openpilot.system.ui.lib.application import gui_app, FontWeight + timer.lap("import") logger.info(f"Clipping {route.name.canonical_name}, {start}s-{end}s ({duration}s)") @@ -297,7 +325,7 @@ def clip(route: Route, output: str, start: int, end: int, headless: bool = True, timer.lap("logs") frame_start = (start - seg_start * 60) * FRAMERATE - message_chunks = all_chunks[frame_start:frame_start + duration * FRAMERATE] + message_chunks = all_chunks[frame_start : frame_start + duration * FRAMERATE] if not message_chunks: logger.error("No messages to render") sys.exit(1) @@ -350,8 +378,18 @@ def main(): args = parse_args() setup_env(args.output, big=args.big, speed=args.speed, target_mb=args.file_size, duration=args.end - args.start) - clip(Route(args.route, data_dir=args.data_dir), args.output, args.start, args.end, not args.windowed, - args.big, args.title, not args.no_metadata, not args.no_time_overlay, args.qcam) + clip( + Route(args.route, data_dir=args.data_dir), + args.output, + args.start, + args.end, + not args.windowed, + args.big, + args.title, + not args.no_metadata, + not args.no_time_overlay, + args.qcam, + ) if __name__ == "__main__": diff --git a/tools/jotpluggler/pluggle.py b/tools/jotpluggler/pluggle.py index 92664ae5b..67531ce90 100755 --- a/tools/jotpluggler/pluggle.py +++ b/tools/jotpluggler/pluggle.py @@ -12,7 +12,7 @@ from openpilot.tools.jotpluggler.data import DataManager from openpilot.tools.jotpluggler.datatree import DataTree from openpilot.tools.jotpluggler.layout import LayoutManager -DEMO_ROUTE = "a2a0ccea32023010|2023-07-27--13-01-19" +DEMO_ROUTE = "5beb9b58bd12b691/0000010a--a51155e496" class WorkerManager: @@ -120,6 +120,7 @@ class PlaybackManager: if callback in self.x_axis_observers: self.x_axis_observers.remove(callback) + class MainController: def __init__(self, scale: float = 1.0): self.scale = scale @@ -197,8 +198,12 @@ class MainController: if dpg.does_item_exist("save_layout_dialog"): dpg.delete_item("save_layout_dialog") with dpg.file_dialog( - callback=self._save_layout_callback, tag="save_layout_dialog", width=int(700 * self.scale), height=int(400 * self.scale), - default_filename="layout", default_path=os.path.join(os.path.dirname(os.path.realpath(__file__)), "layouts") + callback=self._save_layout_callback, + tag="save_layout_dialog", + width=int(700 * self.scale), + height=int(400 * self.scale), + default_filename="layout", + default_path=os.path.join(os.path.dirname(os.path.realpath(__file__)), "layouts"), ): dpg.add_file_extension(".yaml") @@ -206,8 +211,11 @@ class MainController: if dpg.does_item_exist("load_layout_dialog"): dpg.delete_item("load_layout_dialog") with dpg.file_dialog( - callback=self._load_layout_callback, tag="load_layout_dialog", width=int(700 * self.scale), height=int(400 * self.scale), - default_path=os.path.join(os.path.dirname(os.path.realpath(__file__)), "layouts") + callback=self._load_layout_callback, + tag="load_layout_dialog", + width=int(700 * self.scale), + height=int(400 * self.scale), + default_path=os.path.join(os.path.dirname(os.path.realpath(__file__)), "layouts"), ): dpg.add_file_extension(".yaml") @@ -314,21 +322,23 @@ def main(route_to_load=None, layout_to_load=None): dpg.create_context() # TODO: find better way of calculating display scaling - #try: + # try: # w, h = next(tuple(map(int, l.split()[0].split('x'))) for l in subprocess.check_output(['xrandr']).decode().split('\n') if '*' in l) # actual resolution # scale = pyautogui.size()[0] / w # scaled resolution - #except Exception: + # except Exception: # scale = 1 scale = 1 with dpg.font_registry(): - default_font = dpg.add_font(os.path.join(BASEDIR, "selfdrive/assets/fonts/JetBrainsMono-Medium.ttf"), int(13 * scale * 2)) # 2x then scale for hidpi + default_font = dpg.add_font(os.path.join(BASEDIR, "selfdrive/assets/fonts/JetBrainsMono-Medium.ttf"), int(13 * scale * 2)) # 2x then scale for hidpi dpg.bind_font(default_font) dpg.set_global_font_scale(0.5) viewport_width, viewport_height = int(1200 * scale), int(800 * scale) dpg.create_viewport( - title='JotPluggler', width=viewport_width, height=viewport_height, + title='JotPluggler', + width=viewport_width, + height=viewport_height, ) dpg.setup_dearpygui() @@ -358,6 +368,7 @@ def main(route_to_load=None, layout_to_load=None): controller.shutdown() dpg.destroy_context() + if __name__ == "__main__": parser = argparse.ArgumentParser(description="A tool for visualizing openpilot logs.") parser.add_argument("--demo", action="store_true", help="Use the demo route instead of providing one") diff --git a/tools/plotjuggler/juggle.py b/tools/plotjuggler/juggle.py index c04efd50b..0cab39bc6 100755 --- a/tools/plotjuggler/juggle.py +++ b/tools/plotjuggler/juggle.py @@ -21,7 +21,7 @@ juggle_dir = os.path.dirname(os.path.realpath(__file__)) os.environ['LD_LIBRARY_PATH'] = os.environ.get('LD_LIBRARY_PATH', '') + f":{juggle_dir}/bin/" -DEMO_ROUTE = "a2a0ccea32023010|2023-07-27--13-01-19" +DEMO_ROUTE = "5beb9b58bd12b691/0000010a--a51155e496" RELEASES_URL = "https://github.com/commaai/PlotJuggler/releases/download/latest" INSTALL_DIR = os.path.join(juggle_dir, "bin") PLOTJUGGLER_BIN = os.path.join(juggle_dir, "bin/plotjuggler") @@ -105,8 +105,7 @@ def juggle_route(route_or_segment_name, can, layout, dbc, should_migrate): if __name__ == "__main__": - parser = argparse.ArgumentParser(description="A helper to run PlotJuggler on openpilot routes", - formatter_class=argparse.ArgumentDefaultsHelpFormatter) + parser = argparse.ArgumentParser(description="A helper to run PlotJuggler on openpilot routes", formatter_class=argparse.ArgumentDefaultsHelpFormatter) parser.add_argument("--demo", action="store_true", help="Use the demo route instead of providing one") parser.add_argument("--can", action="store_true", help="Parse CAN data") diff --git a/tools/replay/replay.h b/tools/replay/replay.h index 58c1b71b8..3e2bc7c00 100644 --- a/tools/replay/replay.h +++ b/tools/replay/replay.h @@ -12,7 +12,7 @@ #include "tools/replay/seg_mgr.h" #include "tools/replay/timeline.h" -#define DEMO_ROUTE "a2a0ccea32023010|2023-07-27--13-01-19" +#define DEMO_ROUTE "5beb9b58bd12b691/0000010a--a51155e496" enum REPLAY_FLAGS { REPLAY_FLAG_NONE = 0x0000,