openpilot is an open source driver assistance system. openpilot performs the functions of Automated Lane Centering and Adaptive Cruise Control for over 200 supported car makes and models.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

181 lines
6.9 KiB

#!/usr/bin/env python3
import sys
import argparse
import multiprocessing
import rerun as rr
import rerun.blueprint as rrb
from functools import partial
from cereal.services import SERVICE_LIST
from openpilot.tools.rerun.camera_reader import probe_packet_info, CameraReader, CameraConfig, CameraType
from openpilot.tools.lib.logreader import LogReader
from openpilot.tools.lib.route import Route, SegmentRange
NUM_CPUS = multiprocessing.cpu_count()
DEMO_ROUTE = "a2a0ccea32023010|2023-07-27--13-01-19"
RR_TIMELINE_NAME = "Timeline"
RR_WIN = "openpilot logs"
"""
Relevant upstream Rerun issues:
- large time series: https://github.com/rerun-io/rerun/issues/5967
- loading videos directly: https://github.com/rerun-io/rerun/issues/6532
"""
class Rerunner:
def __init__(self, route, segment_range, camera_config, enabled_services):
self.enabled_services = [s.lower() for s in enabled_services]
self.log_all = "all" in self.enabled_services
self.lr = LogReader(route_or_segment_name)
# hevc files don't have start_time. We get it from qcamera.ts
start_time = 0
dat = probe_packet_info(r.qcamera_paths()[0])
for d in dat:
if d.startswith("pts_time="):
start_time = float(d.split('=')[1])
break
qcam, fcam, ecam, dcam = camera_config
self.camera_readers = {}
if qcam:
self.camera_readers[CameraType.qcam] = CameraReader(route.qcamera_paths(), start_time, segment_range.seg_idxs)
if fcam:
self.camera_readers[CameraType.fcam] = CameraReader(route.camera_paths(), start_time, segment_range.seg_idxs)
if ecam:
self.camera_readers[CameraType.ecam] = CameraReader(route.ecamera_paths(), start_time, segment_range.seg_idxs)
if dcam:
self.camera_readers[CameraType.dcam] = CameraReader(route.dcamera_paths(), start_time, segment_range.seg_idxs)
def _start_rerun(self):
self.blueprint = self._create_blueprint()
rr.init(RR_WIN, spawn=True)
def _create_blueprint(self):
blueprint = None
service_views = []
log_msg_visible = len(self.enabled_services) <= 3 and not self.log_all
for topic in sorted(SERVICE_LIST.keys()):
if not self.log_all and topic.lower() not in self.enabled_services:
continue
View = rrb.TimeSeriesView if topic != "thumbnail" else rrb.Spatial2DView
service_views.append(View(name=topic, origin=f"/{topic}/", visible=log_msg_visible))
rr.log(topic, rr.SeriesLine(name=topic), timeless=True)
blueprint = rrb.Blueprint(
rrb.Horizontal(
rrb.Vertical(*service_views),
rrb.Vertical(*[rrb.Spatial2DView(name=cam_type, origin=cam_type) for cam_type in self.camera_readers.keys()]),
),
rrb.SelectionPanel(expanded=False),
rrb.TimePanel(expanded=False)
)
return blueprint
@staticmethod
def _log_msg(msg, parent_key=''):
stack = [(msg, parent_key)]
while stack:
current_msg, current_parent_key = stack.pop()
if isinstance(current_msg, list):
for index, item in enumerate(current_msg):
new_key = f"{current_parent_key}/{index}"
if isinstance(item, (int, float)):
rr.log(new_key, rr.Scalar(item))
elif isinstance(item, dict):
stack.append((item, new_key))
elif isinstance(current_msg, dict):
for key, value in current_msg.items():
new_key = f"{current_parent_key}/{key}"
if isinstance(value, (int, float)):
rr.log(new_key, rr.Scalar(value))
elif isinstance(value, dict):
stack.append((value, new_key))
elif isinstance(value, list):
for index, item in enumerate(value):
if isinstance(item, (int, float)):
rr.log(f"{new_key}/{index}", rr.Scalar(item))
else:
pass # Not a plottable value
@staticmethod
@rr.shutdown_at_exit
def _process_log_msgs(blueprint, enabled_services, log_all, lr):
rr.init(RR_WIN)
rr.connect(default_blueprint=blueprint)
for msg in lr:
rr.set_time_nanos(RR_TIMELINE_NAME, msg.logMonoTime)
msg_type = msg.which()
if not log_all and msg_type.lower() not in enabled_services:
continue
if msg_type != "thumbnail":
Rerunner._log_msg(msg.to_dict()[msg.which()], msg.which())
else:
rr.log("/thumbnail", rr.ImageEncoded(contents=msg.to_dict()[msg.which()].get("thumbnail")))
return []
@staticmethod
@rr.shutdown_at_exit
def _process_cam_readers(blueprint, cam_type, h, w, fr):
rr.init(RR_WIN)
rr.connect(default_blueprint=blueprint)
for ts, frame in fr:
rr.set_time_nanos(RR_TIMELINE_NAME, int(ts * 1e9))
rr.log(cam_type, rr.Image(bytes=frame, width=w, height=h, pixel_format=rr.PixelFormat.NV12))
def load_data(self):
self._start_rerun()
if len(self.enabled_services) > 0:
self.lr.run_across_segments(NUM_CPUS, partial(self._process_log_msgs, self.blueprint, self.enabled_services, self.log_all))
for cam_type, cr in self.camera_readers.items():
cr.run_across_segments(NUM_CPUS, partial(self._process_cam_readers, self.blueprint, cam_type, cr.h, cr.w))
if __name__ == '__main__':
parser = argparse.ArgumentParser(description="A helper to run rerun 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("--qcam", action="store_true", help="Show low-res road camera")
parser.add_argument("--fcam", action="store_true", help="Show driving camera")
parser.add_argument("--ecam", action="store_true", help="Show wide camera")
parser.add_argument("--dcam", action="store_true", help="Show driver monitoring camera")
parser.add_argument("--print_services", action="store_true", help="List out openpilot services")
parser.add_argument("--services", default=[], nargs='*', help="Specify openpilot services that will be logged.\
No service will be logged if not specified.\
To log all services include 'all' as one of your services")
parser.add_argument("--route", nargs='?', help="The route or segment name to plot")
args = parser.parse_args()
if not args.demo and not args.route:
parser.print_help()
sys.exit()
if args.print_services:
print("\n".join(SERVICE_LIST.keys()))
sys.exit()
camera_config = CameraConfig(args.qcam, args.fcam, args.ecam, args.dcam)
route_or_segment_name = DEMO_ROUTE if args.demo else args.route.strip()
sr = SegmentRange(route_or_segment_name)
r = Route(sr.route_name)
if len(sr.seg_idxs) > 10:
print("You're requesting more than 10 segments of the route, " + \
"please be aware that might take a lot of memory")
response = input("Do you wish to continue? (Y/n): ")
if response.strip().lower() != "y":
sys.exit()
rerunner = Rerunner(r, sr, camera_config, args.services)
rerunner.load_data()