44
55from collections import Counter
66import logging
7+ import os
8+ import signal
79import time
10+ import threading
811from typing import Any
912
1013import hydra
@@ -164,6 +167,31 @@ def _build_provider(cfg: DictConfig, video_enabled: bool) -> Pico4InputProvider:
164167 )
165168
166169
170+ def _install_signal_handlers (stop_event : threading .Event ) -> None :
171+ def _handle_signal (signum : int , _frame : Any ) -> None :
172+ if stop_event .is_set ():
173+ os ._exit (130 )
174+ logger .info ("Received signal %s -- shutting down" , signum )
175+ stop_event .set ()
176+
177+ signal .signal (signal .SIGINT , _handle_signal )
178+ signal .signal (signal .SIGTERM , _handle_signal )
179+
180+
181+ def _start_video_runtime_async (video_runtime : PicoVideoRuntime ) -> threading .Event :
182+ done = threading .Event ()
183+
184+ def _run () -> None :
185+ try :
186+ video_runtime .start ()
187+ finally :
188+ done .set ()
189+
190+ thread = threading .Thread (target = _run , name = "pico_video_start" , daemon = True )
191+ thread .start ()
192+ return done
193+
194+
167195@hydra .main (version_base = None , config_path = "../../teleopit/configs" , config_name = "pico4_sim2real" )
168196def main (cfg : DictConfig ) -> None :
169197 logging .basicConfig (level = logging .INFO , format = "%(levelname)s:%(name)s:%(message)s" )
@@ -195,6 +223,8 @@ def main(cfg: DictConfig) -> None:
195223 video_cfg .source ,
196224 )
197225
226+ stop_event = threading .Event ()
227+ _install_signal_handlers (stop_event )
198228 provider = _build_provider (cfg , video_cfg .enabled )
199229 video_runtime = PicoVideoRuntime (provider = provider , config = video_cfg , mode = "sim2real" )
200230 total = 0
@@ -206,10 +236,13 @@ def main(cfg: DictConfig) -> None:
206236 window_start_s = time .monotonic ()
207237 start_s = window_start_s
208238 sleep_s = 1.0 / max (poll_hz , 1.0 )
239+ video_start_done : threading .Event | None = None
209240
210241 try :
211- video_runtime .start ()
212- while True :
242+ if video_cfg .enabled :
243+ logger .info ("Starting Pico video backend asynchronously" )
244+ video_start_done = _start_video_runtime_async (video_runtime )
245+ while not stop_event .is_set ():
213246 now = time .monotonic ()
214247 if duration_s > 0.0 and now - start_s >= duration_s :
215248 break
@@ -252,7 +285,10 @@ def main(cfg: DictConfig) -> None:
252285 invalid_reasons .clear ()
253286 window_start_s = now
254287
255- time .sleep (sleep_s )
288+ if video_start_done is not None and not video_start_done .is_set () and now - start_s >= 5.0 :
289+ logger .info ("Waiting for Pico video backend to become ready in the background" )
290+ video_start_done = None
291+ stop_event .wait (timeout = sleep_s )
256292 except KeyboardInterrupt :
257293 logger .info ("KeyboardInterrupt -- stopping Pico signal diagnostic" )
258294 finally :
0 commit comments