diff --git a/openpilot/selfdrive/modeld/modeld.py b/openpilot/selfdrive/modeld/modeld.py index 3c7dd935a2..8eea895ccf 100755 --- a/openpilot/selfdrive/modeld/modeld.py +++ b/openpilot/selfdrive/modeld/modeld.py @@ -26,7 +26,7 @@ from openpilot.common.file_chunker import open_file_chunked, get_manifest_path from openpilot.selfdrive.modeld.constants import ModelConstants, Plan from openpilot.selfdrive.modeld.helpers import usbgpu_present, modeld_pkl_path, get_tg_input_devices, load_oob from openpilot.selfdrive.modeld.usbgpu_link import wait_usbgpu_link -from openpilot.tools.wgpu.zmq import ZmqPubMaster, ZmqSubMaster +from openpilot.tools.wgpu.zmq import WGPU_CAR_PARAMS, ZmqPubMaster, ZmqSubMaster, ZmqSubSocket from openpilot.sunnypilot.livedelay.helpers import get_lat_delay from openpilot.sunnypilot.modeld_v2.modeld_base import ModelStateBase @@ -181,6 +181,18 @@ def main(demo=False, remote_addr: str | None = None, big_model: bool = False): config_realtime_process(7, 54) + remote_CP = None + if remote_addr is not None: + # Do not attach to VisionIPC until all startup prerequisites are available. + # Otherwise its notification queue grows while waiting for the infrequent + # bridged carParams message and reconnect starts tens of seconds behind. + cloudlog.warning("waiting for remote carParams") + car_params_socket = ZmqSubSocket(WGPU_CAR_PARAMS, remote_addr, conflate=True) + raw_car_params = car_params_socket.receive() + assert raw_car_params is not None + remote_CP = messaging.log_from_bytes(raw_car_params, car.CarParams) + cloudlog.info("modeld got remote CarParams: %s", remote_CP.brand) + # visionipc clients while True: available_streams = VisionIpcClient.available_streams("camerad", block=False) @@ -215,8 +227,6 @@ def main(demo=False, remote_addr: str | None = None, big_model: bool = False): output_services = ["modelV2", "drivingModelData", "cameraOdometry", "modelDataV2SP"] pm = ZmqPubMaster(output_services) if remote_addr is not None else PubMaster(output_services) services = ["deviceState", "carState", "roadCameraState", "liveCalibration", "driverMonitoringState", "carControl", "liveDelay"] - if remote_addr is not None: - services.append("carParams") sm = ZmqSubMaster(services, remote_addr) if remote_addr is not None else SubMaster(services) publish_state = PublishState() @@ -238,10 +248,8 @@ def main(demo=False, remote_addr: str | None = None, big_model: bool = False): if demo: CP = get_demo_car_params() elif remote_addr is not None: - cloudlog.warning("waiting for remote carParams") - while not sm.seen["carParams"]: - sm.update(100) - CP = sm["carParams"] + assert remote_CP is not None + CP = remote_CP else: CP = messaging.log_from_bytes(params.get("CarParams", block=True), car.CarParams) cloudlog.info("modeld got CarParams: %s", CP.brand) diff --git a/openpilot/tools/wgpu/README.md b/openpilot/tools/wgpu/README.md index 2c948ffc5f..fb67372be7 100644 --- a/openpilot/tools/wgpu/README.md +++ b/openpilot/tools/wgpu/README.md @@ -58,11 +58,13 @@ python3 -m openpilot.tools.wgpu.host COMMA_IP Add `--big-model` after `COMMA_IP` to use the locally compiled big model. -The first remote `carParams` packet can take up to 50 seconds. Stop either side -with Ctrl+C. A host disconnect automatically stops remote publication and -restores local `modeld` after a one-second timeout; the device helper then stays -running and waits for the next host session. Stop the device helper itself with -Ctrl+C before changing branches or rebooting. +The device helper sends cached `carParams` over a dedicated startup channel, so +the host does not attach to the camera streams and accumulate stale frames while +waiting for the periodic state bridge. Stop either side with Ctrl+C. A host +disconnect automatically stops remote publication and restores local `modeld` +after a one-second timeout; the device helper then stays running and waits for +the next host session. Stop the device helper itself with Ctrl+C before changing +branches or rebooting. While WGPU is active, model lag does not create an engagement-blocking alert. The fixed-column diagnostics panel at the top-right of the mici onroad UI shows diff --git a/openpilot/tools/wgpu/device.py b/openpilot/tools/wgpu/device.py index d77656b99d..d8e59830dc 100644 --- a/openpilot/tools/wgpu/device.py +++ b/openpilot/tools/wgpu/device.py @@ -7,7 +7,7 @@ from pathlib import Path import openpilot.cereal.messaging as messaging from openpilot.common.params import Params -from openpilot.tools.wgpu.zmq import ZmqSubSocket +from openpilot.tools.wgpu.zmq import WGPU_CAR_PARAMS, ZmqPubMaster, ZmqSubSocket MODEL_OUTPUTS = "modelV2,drivingModelData,cameraOdometry,modelDataV2SP" @@ -77,14 +77,22 @@ def main() -> None: try: forward = subprocess.Popen([str(BRIDGE)]) params.put_bool("WgpuReady", True, block=True) + car_params_pub = ZmqPubMaster([WGPU_CAR_PARAMS]) remote_model = ZmqSubSocket("modelV2", args.host, conflate=True) remote_status = ZmqSubSocket(WGPU_STATUS, args.host, conflate=True) + car_params = params.get("CarParams") or params.get("CarParamsPersistent") while True: # Keep local modeld publishing while a host connects and warms up. print(f"forwarding camera/state to {args.host}; waiting for a fresh remote model") model_name = None while True: + if car_params is None: + car_params = params.get("CarParams") or params.get("CarParamsPersistent") + if car_params is not None: + # This dedicated conflated startup channel lets remote modeld obtain CP + # before it connects to VisionIPC and accumulates stale frame metadata. + car_params_pub.send_raw(WGPU_CAR_PARAMS, car_params) received, model_age = receive_model(remote_model) model_name = receive_model_name(remote_status) or model_name if received and 0 <= model_age < REMOTE_MODEL_TIMEOUT and model_name is not None: diff --git a/openpilot/tools/wgpu/zmq.py b/openpilot/tools/wgpu/zmq.py index 64c74d9acb..dcf88fb33f 100644 --- a/openpilot/tools/wgpu/zmq.py +++ b/openpilot/tools/wgpu/zmq.py @@ -5,6 +5,9 @@ import zmq import openpilot.cereal.messaging as messaging +WGPU_CAR_PARAMS = "wgpuCarParams" + + def service_port(endpoint: str) -> int: # Keep this in sync with cereal/messaging/bridge_zmq.cc. value = 0xcbf29ce484222325