stage rendering to auxilary screen

This commit is contained in:
Ruben van de Ven 2025-11-12 13:35:59 +01:00
parent a387cae62c
commit 509ad16733
12 changed files with 488 additions and 92 deletions

View file

@ -16,7 +16,7 @@ dependencies = [
"gdown>=4.7.1,<5", "gdown>=4.7.1,<5",
"pandas-helper-calc", "pandas-helper-calc",
"tsmoothie>=1.0.5,<2", "tsmoothie>=1.0.5,<2",
"pyglet>=2.0.15,<3", "pyglet>=2.1.8,<3",
"pyglet-cornerpin>=0.3.0,<0.4", "pyglet-cornerpin>=0.3.0,<0.4",
"opencv-python", "opencv-python",
"setproctitle>=1.3.3,<2", "setproctitle>=1.3.3,<2",
@ -62,6 +62,7 @@ trap_tracker = "trap.tracker:Tracker.parse_and_start"
trap_track_writer = "trap.track_writer:TrackWriter.parse_and_start" trap_track_writer = "trap.track_writer:TrackWriter.parse_and_start"
trap_lidar = "trap.lidar_tracker:Lidar.parse_and_start" trap_lidar = "trap.lidar_tracker:Lidar.parse_and_start"
trap_stage = "trap.stage:Stage.parse_and_start" trap_stage = "trap.stage:Stage.parse_and_start"
trap_render_stage = "trap.stage_renderer:StageRenderer.parse_and_start"
trap_prediction = "trap.prediction_server:PredictionServer.parse_and_start" trap_prediction = "trap.prediction_server:PredictionServer.parse_and_start"
trap_render_cv = "trap.cv_renderer:CvRenderer.parse_and_start" trap_render_cv = "trap.cv_renderer:CvRenderer.parse_and_start"
trap_monitor = "trap.monitor:Monitor.parse_and_start" # migrate timer trap_monitor = "trap.monitor:Monitor.parse_and_start" # migrate timer

View file

@ -35,7 +35,7 @@ directory=%(here)s
autostart=false autostart=false
[program:lidar] [program:lidar]
command=uv run trap_lidar --min-box-area 0.1 --viz --smooth-tracks command=uv run trap_lidar --min-box-area 0.1 --viz
environment=DISPLAY=":0" environment=DISPLAY=":0"
directory=%(here)s directory=%(here)s
autostart=false autostart=false
@ -82,7 +82,7 @@ autostart=false
stopwaitsecs=60 stopwaitsecs=60
[program:laserspace] [program:laserspace]
command=cargo run --release tcp://127.0.0.1:99174 command=cargo run --release tcp://127.0.0.1:99174 ../trap/SETTINGS/2025-11-dortmund/laserspace.json
directory=%(here)s/../laserspace directory=%(here)s/../laserspace
environment=DISPLAY=":0" environment=DISPLAY=":0"
autostart=false autostart=false

View file

@ -223,6 +223,8 @@ class Lidar(Node):
self.tracker = BYTETracker(frame_rate=ASSUMED_FPS) self.tracker = BYTETracker(frame_rate=ASSUMED_FPS)
self.tracks: DefaultDict[str, Track] = defaultdict(lambda: Track()) self.tracks: DefaultDict[str, Track] = defaultdict(lambda: Track())
@ -257,7 +259,11 @@ class Lidar(Node):
if len(lines) > 1: if len(lines) > 1:
logger.warning("Multiple lines in outline file, using first only") logger.warning("Multiple lines in outline file, using first only")
polygon_points =np.array([[*p.position, 0] for p in lines[0].points]) axis_min = .3
axis_max = 2.2
polygon_points =np.array([[*p.position, axis_min] for p in lines[0].points])
# self.map_outline = shapely.Polygon([p.position for p in lines[0].points]) # self.map_outline = shapely.Polygon([p.position for p in lines[0].points])
boundary_lines = [[i, (i+1) % len(lines[0].points)] for i in range(len(lines[0].points))] boundary_lines = [[i, (i+1) % len(lines[0].points)] for i in range(len(lines[0].points))]
line_set = o3d.geometry.LineSet( line_set = o3d.geometry.LineSet(
@ -272,8 +278,8 @@ class Lidar(Node):
self.map_outline_volume = o3d.visualization.SelectionPolygonVolume() self.map_outline_volume = o3d.visualization.SelectionPolygonVolume()
self.map_outline_volume.orthogonal_axis = "Z" # extrusion direction of polygon self.map_outline_volume.orthogonal_axis = "Z" # extrusion direction of polygon
self.map_outline_volume.axis_min = .3 # filter from slightly above ground self.map_outline_volume.axis_min = axis_min # filter from slightly above ground
self.map_outline_volume.axis_max = 2.2 self.map_outline_volume.axis_max = axis_max
@ -283,7 +289,7 @@ class Lidar(Node):
if self.config.smooth_tracks: if self.config.smooth_tracks:
# TODO)) make configurable # TODO)) make configurable
logger.info(f"Smoother enabled, assuming {ASSUMED_FPS} fps") logger.info(f"Smoother enabled, assuming {ASSUMED_FPS} fps")
self.smoother = Smoother(window_len=ASSUMED_FPS*5, convolution=False) self.smoother = Smoother(window_len=int(ASSUMED_FPS*.6), convolution=True)
else: else:
logger.info("Smoother Disabled (enable with --smooth-tracks)") logger.info("Smoother Disabled (enable with --smooth-tracks)")
@ -389,9 +395,16 @@ class Lidar(Node):
counter = CounterSender() counter = CounterSender()
frame_idx = 0 frame_idx = 0
while self.run_loop(): kalmain_init_pos =self.tracker.kalman_filter._std_weight_position
kalmain_init_vel =self.tracker.kalman_filter._std_weight_velocity
# limit at lidar framefrate to avoid flickering if multiple lidars are connected
while self.run_loop_capped_fps(12):
frame_idx += 1 frame_idx += 1
self.tracker.kalman_filter._std_weight_position = kalmain_init_pos * self.get_setting('lidar.kalman_factor', 1.3)
self.tracker.kalman_filter._std_weight_velocity = kalmain_init_vel * self.get_setting('lidar.kalman_factor', 1.3)
lidar_items = next(self.sequence_generator) lidar_items = next(self.sequence_generator)
@ -457,24 +470,26 @@ class Lidar(Node):
stat_static = len(filtered_pcd.points) stat_static = len(filtered_pcd.points)
counter.set('lidar.unstatic', stat_static) counter.set('lidar.unstatic', stat_static)
timers=[]
if self.room_filter.initialised and self.get_setting('lidar.tracking_enabled',True): if self.room_filter.initialised and self.get_setting('lidar.tracking_enabled',True):
# filtered_pcd, _ = filtered_pcd.remove_statistical_outlier(nb_neighbors=20, std_ratio=2.0) # filtered_pcd, _ = filtered_pcd.remove_statistical_outlier(nb_neighbors=20, std_ratio=2.0)
timers.append(('a', time.perf_counter()))
denoised_pcd, inlier_indexes = filtered_pcd.remove_radius_outlier(nb_points=5, radius=0.8) denoised_pcd, inlier_indexes = filtered_pcd.remove_radius_outlier(nb_points=5, radius=0.8)
stat_denoise = len(filtered_pcd.points) stat_denoise = len(filtered_pcd.points)
counter.set('lidar.denoised', stat_denoise) counter.set('lidar.denoised', stat_denoise)
timers.append(('denoise', time.perf_counter()))
if self.config.viz: if self.config.viz:
outlier_pcd = filtered_pcd.select_by_index(inlier_indexes) outlier_pcd = filtered_pcd.select_by_index(inlier_indexes)
outlier_pcd.paint_uniform_color((1,0,0)) outlier_pcd.paint_uniform_color((1,0,0))
dropped_pcds.append(outlier_pcd) dropped_pcds.append(outlier_pcd)
timers.append(('viz', time.perf_counter()))
filtered_pcd = denoised_pcd filtered_pcd = denoised_pcd
# down sample # down sample
filtered_pcd = filtered_pcd.voxel_down_sample(voxel_size=0.08) filtered_pcd = filtered_pcd.voxel_down_sample(voxel_size=0.04)
stat_downsampled = len(filtered_pcd.points) stat_downsampled = len(filtered_pcd.points)
timers.append(('downsample', time.perf_counter()))
if self.config.viz: if self.config.viz:
@ -483,8 +498,8 @@ class Lidar(Node):
counter.set('lidar.downsampled', stat_downsampled) counter.set('lidar.downsampled', stat_downsampled)
points_2d = project_to_xy(filtered_pcd) points_2d = project_to_xy(filtered_pcd)
timers.append(('project', time.perf_counter()))
# with open('/tmp/points.pcl', 'wb') as fp: # with open('/tmp/points.pcl', 'wb') as fp:
# pickle.dump(points_2d, fp) # pickle.dump(points_2d, fp)
@ -492,10 +507,13 @@ class Lidar(Node):
clusters = self.cluster_2d( clusters = self.cluster_2d(
points_2d, points_2d,
) )
timers.append(('cluster2d', time.perf_counter()))
# print(len(clusters)) # print(len(clusters))
# boxes, centroids = get_cluster_boxes(points_2d, labels, min_area= 0.3*0.3) # boxes, centroids = get_cluster_boxes(points_2d, labels, min_area= 0.3*0.3)
boxes, centroids = get_cluster_boxes(clusters, min_area= self.get_setting('lidar.min_box_area', .1)) boxes, centroids = get_cluster_boxes(clusters, min_area= self.get_setting('lidar.min_box_area', .1), max_area= self.get_setting('lidar.max_box_area', 5))
timers.append(('boxes', time.perf_counter()))
# print(centroids) # print(centroids)
# append confidence and class (placeholders) # append confidence and class (placeholders)
@ -504,6 +522,8 @@ class Lidar(Node):
detections[:,:-2] = boxes detections[:,:-2] = boxes
online_tracks = self.tracker.update(detections) online_tracks = self.tracker.update(detections)
timers.append(('update', time.perf_counter()))
self.logger.debug(f"online tracks: {[t[4] for t in online_tracks]}") self.logger.debug(f"online tracks: {[t[4] for t in online_tracks]}")
removed_tracks = self.tracker.removed_stracks removed_tracks = self.tracker.removed_stracks
# active_stracks = [track for track in self.tracker.tracked_stracks if track.is_activated] # active_stracks = [track for track in self.tracker.tracked_stracks if track.is_activated]
@ -511,9 +531,11 @@ class Lidar(Node):
detections = [Detection.from_bytetrack(track, frame_idx) for track in active_stracks] detections = [Detection.from_bytetrack(track, frame_idx) for track in active_stracks]
counter.set('detections', len(detections)) counter.set('detections', len(detections))
timers.append(('tracks', time.perf_counter()))
self.detection_sock.send_pyobj(detections) self.detection_sock.send_pyobj(detections)
timers.append(('sent', time.perf_counter()))
for detection in detections: for detection in detections:
track = self.tracks[detection.track_id] track = self.tracks[detection.track_id]
@ -526,6 +548,8 @@ class Lidar(Node):
for t in removed_tracks: for t in removed_tracks:
if t.track_id in self.tracks: if t.track_id in self.tracks:
if t.is_activated:
self.logger.info(f"Lost track: {t.track_id}")
del self.tracks[t.track_id] del self.tracks[t.track_id]
# TODO: fix this oddity: # TODO: fix this oddity:
# else: # else:
@ -537,6 +561,7 @@ class Lidar(Node):
active_track_ids = [d.track_id for d in detections] active_track_ids = [d.track_id for d in detections]
active_tracks = {t.track_id: t.get_with_interpolated_history() for t in self.tracks.values() if t.track_id in active_track_ids} active_tracks = {t.track_id: t.get_with_interpolated_history() for t in self.tracks.values() if t.track_id in active_track_ids}
# active_tracks = displacement_filter.apply_to_dict(active_tracks, frame.camera)# a filter to remove just detecting static objects # active_tracks = displacement_filter.apply_to_dict(active_tracks, frame.camera)# a filter to remove just detecting static objects
timers.append(('interpolated', time.perf_counter()))
# frame = Frame(frame_idx, None, time.time(), self.tracks, camera.H, camera) # frame = Frame(frame_idx, None, time.time(), self.tracks, camera.H, camera)
frame = Frame(frame_idx, None, time.time(), active_tracks, frame = Frame(frame_idx, None, time.time(), active_tracks,
@ -545,10 +570,14 @@ class Lidar(Node):
if self.config.smooth_tracks: if self.config.smooth_tracks:
frame = self.smoother.smooth_frame_tracks(frame) frame = self.smoother.smooth_frame_tracks(frame)
timers.append(('smooth', time.perf_counter()))
counter.set('tracks', len(active_tracks)) counter.set('tracks', len(active_tracks))
self.track_sock.send_pyobj(frame) self.track_sock.send_pyobj(frame)
timers.append(('sent', time.perf_counter()))
if len(centroids): if len(centroids):
@ -567,6 +596,12 @@ class Lidar(Node):
for line_set in line_sets: for line_set in line_sets:
vis.add_geometry(line_set, False) vis.add_geometry(line_set, False)
timers.append(('viz2', time.perf_counter()))
total_time = timers[-1][1]-timers[0][1]
for t0, t1 in zip(timers, timers[1:]):
difftime = t1[1]-t0[1]
counter.set(f'tracker.timer.{t1[0]}', difftime/total_time)
elif self.config.viz: elif self.config.viz:
print('initializing') print('initializing')
if hasattr(self.room_filter, 'scan_counter'): if hasattr(self.room_filter, 'scan_counter'):
@ -641,7 +676,8 @@ class Lidar(Node):
argparser.add_argument('--ip', argparser.add_argument('--ip',
help='IP of this computer on which to listen for IP packets broadcast by lidar (so NOT the ip of the Lidar itself)', help='IP of this computer on which to listen for IP packets broadcast by lidar (so NOT the ip of the Lidar itself)',
type=str, type=str,
default="192.168.0.70") # default="192.168.0.70")
default="0.0.0.0")
argparser.add_argument('--port', argparser.add_argument('--port',
help='Port of the incomming ip packets', help='Port of the incomming ip packets',
type=int, type=int,
@ -854,7 +890,7 @@ def split_cluster_by_convex_hull(points, max_hull_area):
def get_cluster_boxes(clusters, min_area = 0): def get_cluster_boxes(clusters, min_area = 0, max_area=5):
if not len(clusters): if not len(clusters):
return np.empty([0,4]), np.empty([0,2]) return np.empty([0,4]), np.empty([0,2])
@ -865,7 +901,7 @@ def get_cluster_boxes(clusters, min_area = 0):
x_max, y_max = cluster.max(axis=0) x_max, y_max = cluster.max(axis=0)
area = (x_max-x_min) * (y_max - y_min) area = (x_max-x_min) * (y_max - y_min)
if area < min_area: if area < min_area or area > max_area:
logger.warning(f"Dropping box {area} ") logger.warning(f"Dropping box {area} ")
continue continue

View file

@ -59,7 +59,7 @@ class SrgbaColor():
return math.isclose(self.red, other.red) and math.isclose(self.green, other.green) and math.isclose(self.blue, other.blue) and math.isclose(self.alpha, other.alpha) return math.isclose(self.red, other.red) and math.isclose(self.green, other.green) and math.isclose(self.blue, other.blue) and math.isclose(self.alpha, other.alpha)
def as_array(self): def as_array(self):
return [self.red, self.green, self.blue, self.alpha] return np.array([self.red, self.green, self.blue, self.alpha])
@dataclass @dataclass
@ -511,6 +511,9 @@ class LineAnimator(StaticLine):
self.start_t = time.perf_counter() self.start_t = time.perf_counter()
return True return True
def is_started(self):
return bool(self.start_t)
def is_running(self): def is_running(self):
# when ready, consider not running # when ready, consider not running
return bool(self.start_t) and not self.is_ready() return bool(self.start_t) and not self.is_ready()
@ -536,9 +539,10 @@ class AppendableLineAnimator(LineAnimator):
def apply(self, target_line, dt: DeltaT) -> RenderableLine: def apply(self, target_line, dt: DeltaT) -> RenderableLine:
if len(target_line) == 0: if len(target_line) < 2:
return target_line
# nothing to draw yet # nothing to draw yet
return RenderableLine([]) # return RenderableLine([])
@ -1062,6 +1066,7 @@ class DashedLine(LineAnimator):
segments.append(dash) segments.append(dash)
pos += dash_len + gap_len pos += dash_len + gap_len
segments = [segment for segment in segments if isinstance(segment, shapely.geometry.LineString)]
# TODO: return all color together with the points # TODO: return all color together with the points
return shapely.geometry.MultiLineString(segments) return shapely.geometry.MultiLineString(segments)
@ -1210,6 +1215,8 @@ class RotatingLine(LineAnimator):
# progress = associated_diff.nr_of_passed_points() # progress = associated_diff.nr_of_passed_points()
is_ready: List[bool] = [] is_ready: List[bool] = []
# target_point = target_line.points[-1]
for i, (target_point, drawn_point) in enumerate(zip(target_line.points, list(self.drawn_points))): for i, (target_point, drawn_point) in enumerate(zip(target_line.points, list(self.drawn_points))):
# TODO: this should be done in polar space starting from origin (i.e. self.drawn_posision[-1]) # TODO: this should be done in polar space starting from origin (i.e. self.drawn_posision[-1])
decay = max(3, (18/i) if i else 10) # points further away move with more delay decay = max(3, (18/i) if i else 10) # points further away move with more delay
@ -1220,6 +1227,7 @@ class RotatingLine(LineAnimator):
r = exponentialDecay(drawn_r, pred_r, decay, dt) r = exponentialDecay(drawn_r, pred_r, decay, dt)
# make circular coordinates transition through the smaller arc # make circular coordinates transition through the smaller arc
# TODO 20251108 bring this back, but calculated for the whole line:
if abs(drawn_angle - pred_angle) > math.pi: if abs(drawn_angle - pred_angle) > math.pi:
pred_angle -= math.pi * 2 pred_angle -= math.pi * 2
angle = exponentialDecay(drawn_angle, pred_angle, decay, dt) angle = exponentialDecay(drawn_angle, pred_angle, decay, dt)
@ -1627,3 +1635,34 @@ def layers_to_message(layers: RenderableLayers):
# print( t2-t1,t3-t2) # print( t2-t1,t3-t2)
return s return s
def message_to_layers(s: str) -> RenderableLayers:
"""Decode the protobuf"""
pb_layers = renderable_pb2.RenderableLayers()
pb_layers.ParseFromString(s)
layers = {}
for n, pb_lines in pb_layers.layers.items():
lines = []
for pb_line in pb_lines.lines:
points = []
for pb_point in pb_line.points:
color = SrgbaColor(
pb_point.color.red,
pb_point.color.green,
pb_point.color.blue,
pb_point.color.alpha,
)
point = RenderablePoint(
(pb_point.position.x, pb_point.position.y),
color
)
points.append(point)
lines.append(RenderableLine(points))
layers[n] = RenderableLines(lines, CoordinateSpace.WORLD)
return layers

View file

@ -72,13 +72,15 @@ class Node():
return self.is_running.is_set() return self.is_running.is_set()
def check_config(self): def check_config(self):
try: while True:
config = self.config_sock.recv_json(zmq.NOBLOCK) try:
for field, value in config.items(): config = self.config_sock.recv_json(zmq.NOBLOCK)
self.settings[field] = value
except zmq.ZMQError as e: for field, value in config.items():
# no msgs self.settings[field] = value
pass except zmq.ZMQError as e:
# no msgs
break
def get_setting(self, name: str, default: Any): def get_setting(self, name: str, default: Any):
if name in self.settings: if name in self.settings:

View file

@ -449,7 +449,7 @@ class PredictionServer(Node):
# print('preds', len(predictions[0][0])) # print('preds', len(predictions[0][0]))
if not len(history) or np.isnan(history[-1]).any(): if not len(history) or np.isnan(history[-1]).any():
logger.warning(f'skip for no history @ {ts_key} [{len(prediction_dict)=}, {len(histories_dict)=}, {len(futures_dict)=}]') logger.warning(f'skip for no history for {node} @ {ts_key} [{len(prediction_dict)=}, {len(histories_dict)=}, {len(futures_dict)=}]')
# logger.info(f"{preds=}") # logger.info(f"{preds=}")
continue continue

View file

@ -84,7 +84,7 @@ class TrackIteration:
for n in range(noisy_variations+1): for n in range(noisy_variations+1):
for f in range(offset_variations+1): for f in range(offset_variations+1):
iterations.append(TrackIteration(smooth, sample_step_size, i, noisy=bool(n), offset=bool(f))) iterations.append(TrackIteration(smooth, sample_step_size, i, noisy=bool(n), offset=bool(f)))
if toggle_smooth: if smooth and toggle_smooth:
iterations.append(TrackIteration(not smooth, sample_step_size, i, noisy=bool(n), offset=bool(f))) iterations.append(TrackIteration(not smooth, sample_step_size, i, noisy=bool(n), offset=bool(f)))
return iterations return iterations
@ -345,16 +345,18 @@ def process_data(src_dir: Path, dst_dir: Path, name: str, smooth_tracks: bool, n
# print(f"Non-Linear: {nl}") # print(f"Non-Linear: {nl}")
print(f"error: {skipped_for_error}, used: {created}") print(f"error: {skipped_for_error}, used: {created}")
print("Run with") print("Run with")
target_model_dir = (dst_dir / "../models/").resolve()
target_config = (dst_dir / "../trajectron.json").resolve()
# set eval_every very high, because we're not interested in theoretical evaluations, and we don't mind overfitting # set eval_every very high, because we're not interested in theoretical evaluations, and we don't mind overfitting
print(f""" print(f"""
uv run trajectron_train --eval_every 200 \\ uv run trajectron_train --eval_every 200 \\
--train_data_dict {names['train'].name} \\ --train_data_dict {names['train'].name} \\
--eval_data_dict {names['val'].name} \\ --eval_data_dict {names['val'].name} \\
--offline_scene_graph no --preprocess_workers 8 \\ --offline_scene_graph no --preprocess_workers 8 \\
--log_dir EXPERIMENTS/models \\ --log_dir {target_model_dir} \\
--log_tag _{name} \\ --log_tag _{name} \\
--train_epochs 100 \\ --train_epochs 100 \\
--conf EXPERIMENTS/config.json \\ --conf {target_config} \\
--data_dir {dst_dir} \\ --data_dir {dst_dir} \\
{"--map_encoding" if map_img_path else ""} {"--map_encoding" if map_img_path else ""}
""") """)

View file

@ -38,14 +38,25 @@ class Settings(Node):
dpg.add_text(f"Settings from {self.config.settings_file}") dpg.add_text(f"Settings from {self.config.settings_file}")
dpg.add_button(label="Save", callback=self.save) dpg.add_button(label="Save", callback=self.save)
with dpg.window(label="Renderer", pos=(0, 600)):
for i in range(8) :
self.register_setting(f'stagerenderer.layer.{i}', dpg.add_checkbox(label=f"layer {i}", default_value=self.get_setting(f'stagerenderer.layer.{i}', True), callback=self.on_change))
self.register_setting(f'stagerenderer.scale', dpg.add_slider_float(label="scale", default_value=self.get_setting(f'stagerenderer.scale', 1), max_value=3, callback=self.on_change))
self.register_setting(f'stagerenderer.dx', dpg.add_slider_int(label="dx", default_value=self.get_setting(f'stagerenderer.dx', 0), min_value=-300, max_value=300, callback=self.on_change))
self.register_setting(f'stagerenderer.dy', dpg.add_slider_int(label="dy", default_value=self.get_setting(f'stagerenderer.dy', 0), min_value=-300, max_value=300, callback=self.on_change))
self.register_setting(f'stagerenderer.fade', dpg.add_slider_float(label="fade factor", default_value=self.get_setting(f'stagerenderer.fade', 0.27), max_value=1, callback=self.on_change))
with dpg.window(label="Stage", pos=(150, 0)): with dpg.window(label="Stage", pos=(150, 0)):
self.register_setting(f'stage.fps', dpg.add_slider_int(label="FPS cap", default_value=self.get_setting(f'stage.fps', 30), callback=self.on_change)) self.register_setting(f'stage.fps', dpg.add_slider_int(label="FPS cap", default_value=self.get_setting(f'stage.fps', 30), callback=self.on_change))
self.register_setting(f'stage.prediction_interval', dpg.add_slider_int(label="prediction interval", default_value=self.get_setting('stage.prediction_interval', 18), callback=self.on_change)) self.register_setting(f'stage.prediction_interval', dpg.add_slider_int(label="prediction interval", default_value=self.get_setting('stage.prediction_interval', 18), callback=self.on_change))
self.register_setting(f'stage.loitering_animation', dpg.add_checkbox(label="loitering_animation", default_value=self.get_setting('stage.loitering_animation', True), callback=self.on_change))
with dpg.window(label="Lidar", pos=(0, 100), autosize=True): with dpg.window(label="Lidar", pos=(0, 100), autosize=True):
self.register_setting(f'lidar.crop_map_boundaries', dpg.add_checkbox(label="crop_map_boundaries", default_value=self.get_setting(f'lidar.crop_map_boundaries', True), callback=self.on_change)) self.register_setting(f'lidar.crop_map_boundaries', dpg.add_checkbox(label="crop_map_boundaries", default_value=self.get_setting(f'lidar.crop_map_boundaries', True), callback=self.on_change))
self.register_setting(f'lidar.viz_cropping', dpg.add_checkbox(label="viz_cropping", default_value=self.get_setting(f'lidar.viz_cropping', True), callback=self.on_change)) self.register_setting(f'lidar.viz_cropping', dpg.add_checkbox(label="viz_cropping", default_value=self.get_setting(f'lidar.viz_cropping', True), callback=self.on_change))
# self.register_setting(f'lidar.voxel_downsample', dpg.add_checkbox(label="voxel_downsample", default_value=self.get_setting(f'lidar.voxel_downsample', True), callback=self.on_change))
self.register_setting(f'lidar.tracking_enabled', dpg.add_checkbox(label="tracking_enabled", default_value=self.get_setting(f'lidar.tracking_enabled', True), callback=self.on_change)) self.register_setting(f'lidar.tracking_enabled', dpg.add_checkbox(label="tracking_enabled", default_value=self.get_setting(f'lidar.tracking_enabled', True), callback=self.on_change))
self.register_setting(f'lidar.kalman_factor', dpg.add_slider_float(label="kalman_factor", default_value=self.get_setting(f'lidar.kalman_factor', 1.3), max_value=3, callback=self.on_change))
dpg.add_separator(label="Clustering") dpg.add_separator(label="Clustering")
@ -60,8 +71,9 @@ class Settings(Node):
dpg.add_separator(label="Cluster filter") dpg.add_separator(label="Cluster filter")
self.register_setting(f'lidar.min_box_area', dpg.add_slider_float(label="min_box_area", default_value=self.get_setting(f'lidar.min_box_area', .1), min_value=0, max_value=1, callback=self.on_change)) self.register_setting(f'lidar.min_box_area', dpg.add_slider_float(label="min_box_area", default_value=self.get_setting(f'lidar.min_box_area', .1), min_value=0, max_value=1, callback=self.on_change))
self.register_setting(f'lidar.max_box_area', dpg.add_slider_float(label="max_box_area", default_value=self.get_setting(f'lidar.max_box_area', 5), min_value=.5, max_value=10, callback=self.on_change))
for i, lidar in enumerate(["192.168.0.16", "192.168.0.10"]): for i, lidar in enumerate(["192.168.1.16", "192.168.0.10"]):
name = lidar.replace(".", "_") name = lidar.replace(".", "_")
with dpg.window(label=f"Lidar {lidar}", pos=(i * 300, 450),autosize=True): with dpg.window(label=f"Lidar {lidar}", pos=(i * 300, 450),autosize=True):
# dpg.add_text("test") # dpg.add_text("test")

View file

@ -119,6 +119,9 @@ class PrioritySlotItem():
self.start_time = time.perf_counter() self.start_time = time.perf_counter()
self.is_running = True self.is_running = True
def running_for(self):
return time.perf_counter() - self.start_time
@abstractmethod @abstractmethod
def get_priority(self) -> int: def get_priority(self) -> int:
raise RuntimeError("Not implemented") raise RuntimeError("Not implemented")
@ -161,7 +164,11 @@ class Scenario(PrioritySlotItem):
return self.scene.name return self.scene.name
def get_priority(self) -> int: def get_priority(self) -> int:
return self.scene.value.priority # newer higher prio
distance = 0
if self.track and len(self.track.projected_history) > 5:
distance = np.linalg.norm(self.track.projected_history[-1] - self.track.projected_history[0])
return (self.scene.value.priority, distance)
def can_be_taken_over(self): def can_be_taken_over(self):
if self.scene.value.takeover_possible: if self.scene.value.takeover_possible:
@ -221,11 +228,12 @@ class Scenario(PrioritySlotItem):
def set_scene(self, scene: ScenarioScene): def set_scene(self, scene: ScenarioScene):
if self.scene is scene: if self.scene is scene:
return return False
logger.info(f"Changing scene for {self.track_id}: {self.scene.name} -> {scene.name}") logger.info(f"Changing scene for {self.track_id}: {self.scene.name} -> {scene.name}")
self.scene = scene self.scene = scene
self.state_change_at = time.perf_counter() self.state_change_at = time.perf_counter()
return True
def update_state(self): def update_state(self):
self.check_lost() or self.check_loitering() or self.check_track() self.check_lost() or self.check_loitering() or self.check_track()
@ -252,10 +260,10 @@ class Scenario(PrioritySlotItem):
def check_track(self): def check_track(self):
predictions = len(self.prediction_tracks) predictions = len(self.prediction_tracks)
if predictions == 1: if predictions and self.running_for() < 20:
self.set_scene(ScenarioScene.FIRST_PREDICTION) self.set_scene(ScenarioScene.FIRST_PREDICTION)
return True return True
if predictions > 30: if predictions and self.running_for() > 120:
self.set_scene(ScenarioScene.PLAY) self.set_scene(ScenarioScene.PLAY)
return True return True
if predictions: if predictions:
@ -350,7 +358,7 @@ class DrawnScenario(Scenario):
history_color = SrgbaColor(1.,0.,1.,1.) history_color = SrgbaColor(1.,0.,1.,1.)
history = StaticLine([], history_color) history = StaticLine([], history_color)
self.line_history = LineAnimationStack(history) self.line_history = LineAnimationStack(history)
self.line_history.add(AppendableLineAnimator(self.line_history.tail, draw_decay_speed=120)) self.line_history.add(AppendableLineAnimator(self.line_history.tail, draw_decay_speed=120, transition_in_on_init=False))
self.line_history.add(CropLine(self.line_history.tail, self.MAX_HISTORY)) self.line_history.add(CropLine(self.line_history.tail, self.MAX_HISTORY))
self.line_history.add(SimplifyLine(self.line_history.tail, 0.003)) # Simplify before effects, so they don't distort self.line_history.add(SimplifyLine(self.line_history.tail, 0.003)) # Simplify before effects, so they don't distort
self.line_history.add(FadedTailLine(self.line_history.tail, TRACK_FADE_AFTER_DURATION * TRACK_ASSUMED_FPS, TRACK_END_FADE)) self.line_history.add(FadedTailLine(self.line_history.tail, TRACK_FADE_AFTER_DURATION * TRACK_ASSUMED_FPS, TRACK_END_FADE))
@ -394,10 +402,12 @@ class DrawnScenario(Scenario):
if self.track: if self.track:
self.line_history.root.points = self.track.projected_history self.line_history.root.points = self.track.projected_history
lf = self.lost_factor() lost_factor = self.lost_factor() # fade out when lost
self.line_history.get(FadeOutJitterLine).set_alpha(1-lf) start_factor = 0#1 - min(1, self.running_for()) # fade in when starting
self.line_prediction.get(FadeOutLine).set_alpha(1-lf) # print(start_factor)
self.line_history.get(NoiseLine).amplitude = lf * 1.8 self.line_history.get(FadeOutJitterLine).set_alpha(1- lost_factor - start_factor)
self.line_prediction.get(FadeOutLine).set_alpha(1-lost_factor)
self.line_history.get(NoiseLine).amplitude = lost_factor * 1.8
if len(self.prediction_tracks): if len(self.prediction_tracks):
# now_p = np.array(self.line_history.root.points[-1]) # now_p = np.array(self.line_history.root.points[-1])
@ -408,49 +418,50 @@ class DrawnScenario(Scenario):
# TODO: only when animation is ready for it? or collect lines # TODO: only when animation is ready for it? or collect lines
if not self.active_ptrack: if self.is_running:
# draw the first prediction if not self.active_ptrack:
self.active_ptrack = self.prediction_tracks[-1] # draw the first prediction
self.line_prediction.root.points = self.active_ptrack._track.predictions[0] self.active_ptrack = self.prediction_tracks[-1]
self.line_prediction.root.points = self.active_ptrack._track.predictions[0]
self.line_prediction.start() # reset positions
elif self.active_ptrack._track.updated_at < self.prediction_tracks[-1]._track.updated_at:
# stale prediction
# switch only if drawing animation is ready
# if self.line_prediction.is_ready():
self.active_ptrack = self.prediction_tracks[-1]
self.line_prediction.root.points = self.active_ptrack._track.predictions[0]
if self.line_prediction.is_ready() and self.line_prediction.get(DashedLine).skip == True:
self.line_prediction.get(SegmentLine).skip = True
self.line_prediction.get(DashedLine).skip = False
self.line_prediction.start() # reset positions self.line_prediction.start() # reset positions
# self.line_prediction.get(SegmentLine).anim_f = partial(SegmentLine.anim_arrive, length=.3) elif self.active_ptrack._track.updated_at < self.prediction_tracks[-1]._track.updated_at:
# self.line_prediction.get(SegmentLine).duration = .5 # stale prediction
# self.line_prediction.get(DashedLine).skip = True # switch only if drawing animation is ready
# # print('restart') # if self.line_prediction.is_ready():
# self.line_prediction.start() # reset positions self.active_ptrack = self.prediction_tracks[-1]
# # print(self.line_prediction.get(SegmentLine).running_for()) self.line_prediction.root.points = self.active_ptrack._track.predictions[0]
# else: if self.line_prediction.is_ready() and self.line_prediction.get(DashedLine).skip == True:
# if self.line_prediction.is_ready(): self.line_prediction.get(SegmentLine).skip = True
# # little hack: check is dashedline skips, to only run this once per animation: self.line_prediction.get(DashedLine).skip = False
# if self.line_prediction.get(DashedLine).skip:
# # no new yet, but ready with anim, start stage 2
# self.line_prediction.get(SegmentLine).anim_f = partial(SegmentLine.anim_grow)
# self.line_prediction.get(SegmentLine).duration = 1
# # self.line_prediction.get(SegmentLine).skip = True
# self.line_prediction.get(DashedLine).skip = False
# self.line_prediction.start()
# elif self.line_prediction.get(SegmentLine).duration != 2: # hack to only play once
# self.line_prediction.get(SegmentLine).anim_f = partial(SegmentLine.anim_grow, reverse=True)
# self.line_prediction.get(SegmentLine).duration = 2
# self.line_prediction.get(SegmentLine).start()
if self.active_ptrack: self.line_prediction.start() # reset positions
# TODO: this should crop by distance/lenght
self.line_prediction.get(CropLine).start_offset = self.track._track.frame_index - self.active_ptrack._track.frame_index # self.line_prediction.get(SegmentLine).anim_f = partial(SegmentLine.anim_arrive, length=.3)
# self.line_prediction.get(SegmentLine).duration = .5
# self.line_prediction.get(DashedLine).skip = True
# # print('restart')
# self.line_prediction.start() # reset positions
# # print(self.line_prediction.get(SegmentLine).running_for())
# else:
# if self.line_prediction.is_ready():
# # little hack: check is dashedline skips, to only run this once per animation:
# if self.line_prediction.get(DashedLine).skip:
# # no new yet, but ready with anim, start stage 2
# self.line_prediction.get(SegmentLine).anim_f = partial(SegmentLine.anim_grow)
# self.line_prediction.get(SegmentLine).duration = 1
# # self.line_prediction.get(SegmentLine).skip = True
# self.line_prediction.get(DashedLine).skip = False
# self.line_prediction.start()
# elif self.line_prediction.get(SegmentLine).duration != 2: # hack to only play once
# self.line_prediction.get(SegmentLine).anim_f = partial(SegmentLine.anim_grow, reverse=True)
# self.line_prediction.get(SegmentLine).duration = 2
# self.line_prediction.get(SegmentLine).start()
if self.active_ptrack:
# TODO: this should crop by distance/lenght
self.line_prediction.get(CropLine).start_offset = self.track._track.frame_index - self.active_ptrack._track.frame_index
@ -459,7 +470,7 @@ class DrawnScenario(Scenario):
# special case: LOITERING # special case: LOITERING
if self.scene is ScenarioScene.LOITERING: # or self.state_change_at: if self.stage.get_setting('stage.loitering_animation', True) and self.scene is ScenarioScene.LOITERING: # or self.state_change_at:
# logger.info('loitering') # logger.info('loitering')
transition = min(1, (time.perf_counter() - self.state_change_at)/1.4) transition = min(1, (time.perf_counter() - self.state_change_at)/1.4)
# print('loitering factor', transition) # print('loitering factor', transition)
@ -556,6 +567,14 @@ class DrawnScenario(Scenario):
others_line others_line
]), timings ]), timings
def set_scene(self, scene):
"""Create log message for the auxilary interface
"""
original = self.scene.name
changed = super().set_scene(scene)
if changed:
self.stage.log_sock.send_string(f"Change {self.track_id}: {original} -> {self.scene.name}")
return changed
class NoTracksScenario(PrioritySlotItem): class NoTracksScenario(PrioritySlotItem):
TAKEOVER_FADEOUT = 1 # override default to be faster TAKEOVER_FADEOUT = 1 # override default to be faster
@ -567,7 +586,7 @@ class NoTracksScenario(PrioritySlotItem):
def get_priority(self): def get_priority(self):
# super low priority # super low priority
return -1 return (-1, -1)
def can_be_taken_over(self): def can_be_taken_over(self):
return True return True
@ -599,6 +618,21 @@ class NoTracksScenario(PrioritySlotItem):
return lines, timings return lines, timings
class DebugDrawer():
def __init__(self, stage: Stage):
self.stage = stage
def to_renderable_lines(self, dt: DeltaT):
lines = RenderableLines([], CoordinateSpace.WORLD)
past_color = SrgbaColor(1,0,1,1)
future_color = SrgbaColor(0,1,0,1)
for scenario in self.stage.scenarios.values():
lines.append(StaticLine(scenario.track.projected_history, past_color).as_renderable_line(dt))
if scenario.active_ptrack:
lines.append(StaticLine(scenario.active_ptrack._track.predictions[0], future_color).as_renderable_line(dt))
return lines
class DatasetDrawer(): class DatasetDrawer():
def __init__(self, stage: Stage): def __init__(self, stage: Stage):
self.stage = stage self.stage = stage
@ -649,6 +683,8 @@ class Stage(Node):
self.prediction_sock = self.sub(self.config.zmq_prediction_addr) self.prediction_sock = self.sub(self.config.zmq_prediction_addr)
self.detection_sock = self.sub(self.config.zmq_detection_addr) self.detection_sock = self.sub(self.config.zmq_detection_addr)
self.stage_sock = self.pub(self.config.zmq_stage_addr) self.stage_sock = self.pub(self.config.zmq_stage_addr)
self.log_sock = self.push(self.config.zmq_log_addr)
# self.stage_py_sock = self.pub(self.config.zmq_stage_py_addr)
self.counter = CounterSender() self.counter = CounterSender()
@ -659,6 +695,7 @@ class Stage(Node):
self.history = TrackHistory(self.config.tracker_output_dir, self.config.camera, self.config.cache_path) self.history = TrackHistory(self.config.tracker_output_dir, self.config.camera, self.config.cache_path)
self.auxilary = DatasetDrawer(self) self.auxilary = DatasetDrawer(self)
self.debug_drawer = DebugDrawer(self)
# 'screensavers' # 'screensavers'
self.notrack_scenarios = [] #[NoTracksScenario(self, i) for i in range(self.config.max_active_scenarios)] self.notrack_scenarios = [] #[NoTracksScenario(self, i) for i in range(self.config.max_active_scenarios)]
@ -758,6 +795,7 @@ class Stage(Node):
# TODO: sometimes very slow! # TODO: sometimes very slow!
t1 = time.perf_counter() t1 = time.perf_counter()
training_lines = self.auxilary.to_renderable_lines(dt) training_lines = self.auxilary.to_renderable_lines(dt)
all_active_tracks = self.debug_drawer.to_renderable_lines(dt)
t2 = time.perf_counter() t2 = time.perf_counter()
@ -782,6 +820,7 @@ class Stage(Node):
1: lines, 1: lines,
2: self.debug_lines, 2: self.debug_lines,
3: training_lines, 3: training_lines,
4: all_active_tracks,
} }
t4 = time.perf_counter() t4 = time.perf_counter()
@ -791,6 +830,7 @@ class Stage(Node):
t5 = time.perf_counter() t5 = time.perf_counter()
self.stage_sock.send(msg) self.stage_sock.send(msg)
# self.stage_sock.send_pyobj(layers)
# self.stage_sock.send_json(obj=layers, cls=DataclassJSONEncoder) # self.stage_sock.send_json(obj=layers, cls=DataclassJSONEncoder)
@ -831,6 +871,14 @@ class Stage(Node):
help='Manually specity communication addr for the stage messages (the rendered lines)', help='Manually specity communication addr for the stage messages (the rendered lines)',
type=str, type=str,
default="tcp://0.0.0.0:99174") default="tcp://0.0.0.0:99174")
argparser.add_argument('--zmq-log-addr',
help='Manually specity communication addr for the log messages',
type=str,
default="tcp://0.0.0.0:99188")
argparser.add_argument('--zmq-stage-py-addr',
help='Sometimes there is no need for protobuf',
type=str,
default="ipc:///tmp/feeds_stage")
argparser.add_argument('--debug-map', argparser.add_argument('--debug-map',
help='specify a map (svg-file) from which to load lines which will be overlayed', help='specify a map (svg-file) from which to load lines which will be overlayed',
type=str, type=str,

255
trap/stage_renderer.py Normal file
View file

@ -0,0 +1,255 @@
from argparse import ArgumentParser
from collections import deque
import math
from typing import List
import numpy as np
import pyglet
from torch import mul
import zmq
from trap.lines import RenderableLayers, message_to_layers
from trap.node import Node
BG_COLOR = (0,0,0)
class StageRenderer(Node):
def setup(self):
# self.prediction_sock = self.sub(self.config.zmq_prediction_addr)
# self.tracker_sock = self.sub(self.config.zmq_trajectory_addr)
# self.detector_sock = self.sub(self.config.zmq_detection_addr)
# self.frame_sock = self.sub(self.config.zmq_frame_addr)
self.stage_sock = self.sub(self.config.zmq_stage_addr)
self.log_sock = self.pull(self.config.zmq_log_addr)
# setup pyglet:
display = pyglet.display.get_display()
screens = display.get_screens()
# use configured montior, fall back to whatever is available
self.screen = sorted(screens, reverse=True, key=lambda s: s.get_monitor_name() == self.config.monitor)[0]
if self.screen.get_monitor_name() != self.config.monitor:
self.logger.warning(f"Not displaying on configured monitor. {self.screen.get_monitor_name()} instead of {self.config.monitor}")
# print(self.screen.get_modes())
config = pyglet.gl.Config(sample_buffers=1, samples=4)
# when screen is in portrait, window mode here expects still (larger x smaller) number.
# self.window.get_size() will be reported properly
wh = sorted((self.screen.width, self.screen.height), reverse=self.config.fullscreen)
self.window = pyglet.window.Window(width=wh[0], height=wh[1], config=config, fullscreen=self.config.fullscreen, screen=self.screen)
self.window.set_exclusive_keyboard(True)
self.window.set_exclusive_keyboard(False)
self.window.set_exclusive_mouse(True)
self.window.set_exclusive_mouse(False)
# self.window.set_size(1080, 1920)
window_size = self.window.get_size()
print(window_size)
self.window.set_handler('on_draw', self.on_draw)
# self.window.set_handler('on_close', self.on_close)
# pyglet.gl.glClearColor(81./255, 20/255, 46./255, 0)
pyglet.gl.glClearColor(0/255, 0/255, 255/255, 0)
self.fps_display = pyglet.window.FPSDisplay(window=self.window, color=(255,255,255,255))
self.fps_display.label.x = self.window.width - 50
self.fps_display.label.y = self.window.height - 17
self.fps_display.label.bold = False
self.fps_display.label.font_size = 10
self.current_layers: RenderableLayers = {}
self.lines: List[pyglet.shapes.Line] = []
self.lines_batch = pyglet.graphics.Batch()
self.text = pyglet.text.document.FormattedDocument("")
self.text_batch = pyglet.graphics.Batch()
self.text_layout = pyglet.text.layout.TextLayout(
self.text, 20, 350,
width=self.window.get_size()[1],
height=self.window.get_size()[0] // 3,
multiline=True, wrap_lines=False, batch=self.text_batch)
max_len = 30
self.log_msgs = deque([], maxlen=max_len)
self.log_msgs.extend(["..."] * max_len)
translate = (10,-400)
# scale = 5
smallest_dimension = min(self.window.get_size())
max_x = 16.3
max_y = 14.3
scale = min(smallest_dimension / max_x, smallest_dimension/max_y)
padding = 40
self.logger.info(f"Use {scale=}")
self.transform = np.array([
[scale, 0,translate[0]],
[0,-scale,window_size[1]],
[0,0,1]
])
self.bg_image = pyglet.image.load(self.config.floorplan)
scale = (window_size[0] - padding*2) / (self.bg_image.width)
print('image_scale', scale, self.bg_image.width, self.bg_image.height)
# self.bg_image.height = int(self.bg_image.height / 3)
# self.bg_image.width = int(self.bg_image.width / 3)
img_y = window_size[1]-int(self.bg_image.height*scale)-padding*2
self.bg_sprite = pyglet.sprite.Sprite(img=self.bg_image, x=padding, y=img_y)
self.bg_sprite.scale = scale
clear_area = img_y
self.clear_transparent = pyglet.shapes.Rectangle(0, window_size[1]-clear_area, window_size[0], clear_area, color=(*BG_COLOR,255//70))
self.clear_fully= pyglet.shapes.Rectangle(0, 0, window_size[0], window_size[1]-clear_area, color=(*BG_COLOR,255))
def check_running(self, dt):
if not self.run_loop():
self.window.close()
self.event_loop.exit()
def run(self):
self.event_loop = pyglet.app.EventLoop()
pyglet.clock.schedule_interval(self.check_running, 0.1)
# pyglet.clock.schedule(self.receive)
self.event_loop.run()
def receive(self, dt):
try:
msg = self.stage_sock.recv(zmq.NOBLOCK)
self.current_layers = message_to_layers(msg)
self.update_lines()
except zmq.ZMQError as e:
# idx = frame.index if frame else "NONE"
# logger.debug(f"reuse video frame {idx}")
pass
while True:
try:
log_msg = self.log_sock.recv_string(zmq.NOBLOCK)
self.log_msgs.append(log_msg)
except zmq.ZMQError as e:
# idx = frame.index if frame else "NONE"
# logger.debug(f"reuse video frame {idx}")
break
self.update_msgs()
def update_lines(self):
"""
Render the renderable lines of selected layers
"""
additional_scale = self.get_setting('stagerenderer.scale', 1)
dx = self.get_setting('stagerenderer.dx', 0)
dy = self.get_setting('stagerenderer.dy', 0)
transform = self.transform.copy()
transform[0][0] *= additional_scale
transform[1][1] *= additional_scale
transform[0][2] += dx
transform[1][2] += dy
i = -1
for nr, lines in self.current_layers.items():
if not self.get_setting(f'stagerenderer.layer.{nr}', True):
continue
for line in lines.lines:
for p1, p2 in zip(line.points, line.points[1:]):
i += 1
pp1 = np.array([p1.position[0], p1.position[1], 1])
pp2 = np.array([p2.position[0], p2.position[1], 1])
pos1 = (transform@pp1)[:2].astype(int)
pos2 = (transform@pp2)[:2].astype(int)
color = (p2.color.as_array()*255).astype(int)
if i < len(self.lines):
print('reuse')
shape = self.lines[i]
shape.x = pos1[0]
shape.y = pos1[1]
shape.x2 = pos2[0]
shape.y2 = pos2[1]
shape.color = color
self.lines.append(pyglet.shapes.Line(pos1[0], pos1[1],
pos2[0],
pos2[1],
3,
color,
batch=self.lines_batch))
print(len(self.lines), i)
too_many = len(self.lines) - 1 - i
if too_many > 0:
print('del', too_many)
for j in reversed(range(i, i+too_many)):
self.lines[i].delete()
del self.lines[i]
def update_msgs(self):
text = "\n".join(self.log_msgs)
self.text.text = text
self.text.set_style(0, len(self.text.text), dict(
font_name='Arial', # change to a font installed on your system
font_size=18,
color=(255, 255, 255, 255),
))
def on_draw(self):
self.receive(.1)
self.window.clear()
# self.clear_transparent.color = (*BG_COLOR, int(255*self.get_setting('stagerenderer.fade', .27)))
# self.clear_transparent.draw()
# self.clear_fully.draw()
self.fps_display.draw()
# self.bg_sprite.draw()
# self.lines_batch.draw()
# self.text_batch.draw()
@classmethod
def arg_parser(cls):
render_parser = ArgumentParser()
render_parser.add_argument('--zmq-stage-addr',
help='Manually specity communication addr for the stage messages (the rendered lines)',
type=str,
default="tcp://0.0.0.0:99174")
render_parser.add_argument('--zmq-log-addr',
help='Manually specity communication addr for the log messages',
type=str,
default="tcp://0.0.0.0:99188")
render_parser.add_argument("--fullscreen",
help="Set Window full screen",
action='store_true')
render_parser.add_argument('--floorplan',
help='specify a map (png-file) onto which overlayed',
type=str,
default="SETTINGS/2025-11-dortmund/space/floorplan.png")
render_parser.add_argument('--monitor',
help='Specify a screen on which to output (eg. HDMI-1)',
type=str,
default="HDMI-1")
return render_parser

View file

@ -833,7 +833,8 @@ class Smoother(TrackPointFilter):
else: else:
# "Unlike Kalman filtering, which focuses on predicting and updating the current state using historical measurements, Kalman smoothing enhances the accuracy of past state values" # "Unlike Kalman filtering, which focuses on predicting and updating the current state using historical measurements, Kalman smoothing enhances the accuracy of past state values"
# see https://medium.com/@shahalkp1/kalman-smoothing-using-tsmoothie-0175260464e5 # see https://medium.com/@shahalkp1/kalman-smoothing-using-tsmoothie-0175260464e5
self.smoother = KalmanSmoother(component='level_trend', component_noise={'level':0.02, 'season': .01, 'trend':0.02},n_seasons = 2, copy=None) # self.smoother = KalmanSmoother(component='level_trend', component_noise={'level':0.02, 'season': .01, 'trend':0.02},n_seasons = 2, copy=False)
self.smoother = KalmanSmoother(component='level', component_noise={'level':0.01},observation_noise=.3, n_seasons = 0, copy=False)

View file

@ -1889,11 +1889,11 @@ wheels = [
[[package]] [[package]]
name = "pyglet" name = "pyglet"
version = "2.1.3" version = "2.1.11"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/08/90/7f8a8d939dbf8f6875b957540cc3091e936e41c4ac8f190a9517589678f8/pyglet-2.1.3.tar.gz", hash = "sha256:9a2c3c84228402ea7103443ac8a52060cc1c91419951ec1105845ce30fed2ce8", size = 6515859 } sdist = { url = "https://files.pythonhosted.org/packages/e3/6b/84c397a74cd33eb377168c682e9e3d6b90c1c10c661e11ea5b397ac8497c/pyglet-2.1.11.tar.gz", hash = "sha256:8285d0af7d0ab443232a81df4d941e0d5c48c18a23ec770b3e5c59a222f5d56e", size = 6594448 }
wheels = [ wheels = [
{ url = "https://files.pythonhosted.org/packages/83/d6/41208b6741e732a7faf160e89346a17e81b14899bd7ae90058da858083d6/pyglet-2.1.3-py3-none-any.whl", hash = "sha256:5a7eaf35869ecf7451fb49cc064c4c0e9a118eaa5e771667c607125b13f85e33", size = 962091 }, { url = "https://files.pythonhosted.org/packages/77/a2/2b09fbff0eedbe44fbf164b321439a38f7c5568d8b754aa197ee45886431/pyglet-2.1.11-py3-none-any.whl", hash = "sha256:fa0f4fdf366cfc5040aeb462416910b0db2fa374b7d620b7a432178ca3fa8af1", size = 1032213 },
] ]
[[package]] [[package]]
@ -2825,7 +2825,7 @@ requires-dist = [
{ name = "opencv-python", path = "opencv_python-4.10.0.84-cp310-cp310-linux_x86_64.whl" }, { name = "opencv-python", path = "opencv_python-4.10.0.84-cp310-cp310-linux_x86_64.whl" },
{ name = "pandas-helper-calc", git = "https://github.com/scls19fr/pandas-helper-calc" }, { name = "pandas-helper-calc", git = "https://github.com/scls19fr/pandas-helper-calc" },
{ name = "py-to-proto", specifier = ">=0.6.0" }, { name = "py-to-proto", specifier = ">=0.6.0" },
{ name = "pyglet", specifier = ">=2.0.15,<3" }, { name = "pyglet", specifier = ">=2.1.8,<3" },
{ name = "pyglet-cornerpin", specifier = ">=0.3.0,<0.4" }, { name = "pyglet-cornerpin", specifier = ">=0.3.0,<0.4" },
{ name = "python-statemachine", specifier = ">=2.5.0" }, { name = "python-statemachine", specifier = ">=2.5.0" },
{ name = "pyusb", specifier = ">=1.3.1,<2" }, { name = "pyusb", specifier = ">=1.3.1,<2" },