diff --git a/openpilot/selfdrive/controls/lib/longitudinal_planner.py b/openpilot/selfdrive/controls/lib/longitudinal_planner.py index 8b62808dc0..bf91c5e1f9 100755 --- a/openpilot/selfdrive/controls/lib/longitudinal_planner.py +++ b/openpilot/selfdrive/controls/lib/longitudinal_planner.py @@ -49,9 +49,8 @@ def get_cruise_accel(e2e, v_cruise, v_ego, a_cruise_prev, angle_steers, CP, dt, max_accel = min(max_accel, coast_limit) target_accel = np.clip(v_cruise - v_ego, A_CRUISE_MIN, max_accel) - if not e2e: - j_cruise = np.interp(v_ego, A_CRUISE_MAX_BP, J_CRUISE_VALS) - target_accel = float(np.clip(target_accel, a_cruise_prev - j_cruise * dt, a_cruise_prev + j_cruise * dt)) + j_cruise = np.interp(v_ego, A_CRUISE_MAX_BP, J_CRUISE_VALS) + target_accel = float(np.clip(target_accel, a_cruise_prev - j_cruise * dt, a_cruise_prev + j_cruise * dt)) return target_accel @@ -65,10 +64,9 @@ class LongitudinalPlanner(LongitudinalPlannerSP): self.dt = dt self.allow_throttle = True - self.a_desired = init_a self.v_desired_filter = FirstOrderFilter(init_v, 2.0, self.dt) - self.a_cruise = 0.0 - self.output_a_target = 0.0 + self.a_cruise = init_a + self.output_a_target = init_a self.output_should_stop = False self.v_desired_trajectory = np.zeros(CONTROL_N) @@ -105,7 +103,8 @@ class LongitudinalPlanner(LongitudinalPlannerSP): if reset_state: self.v_desired_filter.x = v_ego - self.a_desired = np.clip(sm['carState'].aEgo, ACCEL_MIN, ACCEL_MAX) + self.output_a_target = np.clip(sm['carState'].aEgo, ACCEL_MIN, ACCEL_MAX) + self.a_cruise = self.output_a_target # Prevent divergence, smooth in current v_ego self.v_desired_filter.x = max(0.0, self.v_desired_filter.update(v_ego)) @@ -113,11 +112,11 @@ class LongitudinalPlanner(LongitudinalPlannerSP): # No change cost when user is controlling the speed, or when standstill prev_accel_constraint = not (reset_state or sm['carState'].standstill) - # Get new v_cruise and a_desired from Smart Cruise Control and Speed Limit Assist - v_cruise, self.a_desired = LongitudinalPlannerSP.update_targets(self, sm, self.v_desired_filter.x, self.a_desired, v_cruise) + # Get new v_cruise and a_target from Smart Cruise Control and Speed Limit Assist + v_cruise, self.output_a_target = LongitudinalPlannerSP.update_targets(self, sm, self.v_desired_filter.x, self.output_a_target, v_cruise) self.mpc.set_weights(prev_accel_constraint, personality=sm['selfdriveState'].personality) - self.mpc.set_cur_state(self.v_desired_filter.x, self.a_desired) + self.mpc.set_cur_state(self.v_desired_filter.x, self.output_a_target) self.mpc.update(sm['radarState'], personality=sm['selfdriveState'].personality) self.v_desired_trajectory = np.interp(CONTROL_N_T_IDX, T_IDXS_MPC, self.mpc.v_solution) @@ -130,7 +129,7 @@ class LongitudinalPlanner(LongitudinalPlannerSP): cloudlog.info("FCW triggered") # Save starting point for next iteration - a_prev = self.a_desired + a_prev = self.output_a_target action_t = self.CP.longitudinalActuatorDelay + DT_MDL output_a_target_mpc = get_accel_from_plan(self.v_desired_trajectory, self.a_desired_trajectory, CONTROL_N_T_IDX, @@ -155,7 +154,6 @@ class LongitudinalPlanner(LongitudinalPlannerSP): self.output_should_stop = any(should_stop for _, _, should_stop in candidates) self.output_a_target = np.clip(output_a_target, ACCEL_MIN, ACCEL_MAX) - self.a_desired = float(self.output_a_target) self.v_desired_filter.x = self.v_desired_filter.x + self.dt * (self.output_a_target + a_prev) / 2.0 def publish(self, sm, pm): diff --git a/openpilot/system/hardware/hardwared.py b/openpilot/system/hardware/hardwared.py index 7250cd4d63..3c22f1cbd7 100755 --- a/openpilot/system/hardware/hardwared.py +++ b/openpilot/system/hardware/hardwared.py @@ -16,6 +16,7 @@ from openpilot.common.utils import strip_deprecated_keys from openpilot.common.filter_simple import FirstOrderFilter from openpilot.common.params import Params from openpilot.common.realtime import DT_HW +from openpilot.selfdrive.modeld.helpers import MODELS_DIR, usbgpu_compiled from openpilot.selfdrive.selfdrived.alertmanager import set_offroad_alert from openpilot.common.hardware import HARDWARE, COMMA_HARDWARE from openpilot.common.basedir import BASEDIR @@ -238,8 +239,7 @@ def hardware_thread(end_event, hw_queue) -> None: fan_controller = FanController(int(1./DT_HW)) chestnut = Chestnut() - big_model_available = os.path.isfile(os.path.join(BASEDIR, "openpilot/selfdrive/modeld/models/big_driving_supercombo.onnx")) or \ - os.path.isfile(os.path.join(BASEDIR, "openpilot/selfdrive/modeld/models/big_driving_tinygrad.pkl.chunkmanifest")) + big_model_available = (MODELS_DIR / 'big_driving_supercombo.onnx').is_file() or usbgpu_compiled() while not end_event.is_set(): sm.update(PANDA_STATES_TIMEOUT) diff --git a/openpilot/system/webrtc/tests/test_stream_session.py b/openpilot/system/webrtc/tests/test_stream_session.py index d64cf6aed6..03d540c3c2 100644 --- a/openpilot/system/webrtc/tests/test_stream_session.py +++ b/openpilot/system/webrtc/tests/test_stream_session.py @@ -7,7 +7,7 @@ from openpilot.common.test import OpenpilotTestCase from openpilot.cereal import messaging, log from teleoprtc.tracks import VIDEO_CLOCK_RATE -from openpilot.system.webrtc.webrtcd import CerealOutgoingMessageProxy, CerealIncomingMessageProxy +from openpilot.system.webrtc.webrtcd import CerealOutgoingMessageProxy, CerealIncomingMessageProxy, ServerState, handle_get_stream from openpilot.system.webrtc.device.video import LiveStreamVideoStreamTrack @@ -80,3 +80,8 @@ class TestStreamSession(OpenpilotTestCase): start_pts = packet.pts assert abs(i + packet.pts - (start_pts + (((time.monotonic_ns() - start_ns) * VIDEO_CLOCK_RATE) // 1_000_000_000))) < 450 #5ms assert bytes(packet) == b"" + + def test_stream_rejects_non_json_content_type(self): + response = self.loop.run_until_complete(handle_get_stream(ServerState(), b"{}", "text/plain")) + + assert response == (415, b'{"error": "unsupported media type"}', "application/json; charset=utf-8") diff --git a/openpilot/system/webrtc/webrtcd.py b/openpilot/system/webrtc/webrtcd.py index 8e022eda1c..3c0c3037f3 100755 --- a/openpilot/system/webrtc/webrtcd.py +++ b/openpilot/system/webrtc/webrtcd.py @@ -395,7 +395,10 @@ def _text_response(text: str, status: int = 200) -> tuple[int, bytes, str]: return (status, text.encode(), "text/plain; charset=utf-8") -async def handle_get_stream(state: ServerState, raw_body: bytes) -> tuple[int, bytes, str]: +async def handle_get_stream(state: ServerState, raw_body: bytes, content_type: str) -> tuple[int, bytes, str]: + if content_type != "application/json": + return _json_response({"error": "unsupported media type"}, status=415) + stream_dict = state.streams body = StreamRequestBody(**json.loads(raw_body)) @@ -508,7 +511,7 @@ class WebrtcdHandler(BaseHTTPRequestHandler): services = parse_qs(parsed.query).get("services", [""])[0] result = self._run(handle_get_schema(self.server.state, services)) elif parsed.path == "/stream": - result = self._run(handle_get_stream(self.server.state, self._read_body())) + result = self._run(handle_get_stream(self.server.state, self._read_body(), self.headers.get_content_type())) else: # /notify try: payload = json.loads(self._read_body()) @@ -611,7 +614,7 @@ def webrtcd_thread(host: str, port: int): def main(): parser = argparse.ArgumentParser(description="WebRTC daemon") - parser.add_argument("--host", type=str, default="0.0.0.0", help="Host to listen on") + parser.add_argument("--host", type=str, default="127.0.0.1", help="Host to listen on") parser.add_argument("--port", type=int, default=5001, help="Port to listen on") args = parser.parse_args() diff --git a/pyproject.toml b/pyproject.toml index dcc1024479..6a13796d70 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -21,7 +21,7 @@ dependencies = [ "tqdm", # cars (fw_versions.py) on start + many one-off uses # core - "scons", + "scons==4.10.1", # 4.11 removed the qt3 tool still used to build Cabana "pycapnp==2.1.0", # 2.2 introduces a memory leak due to cyclic references "numpy >=2.0", diff --git a/uv.lock b/uv.lock index 8f3d4ca96c..a7967e8e11 100644 --- a/uv.lock +++ b/uv.lock @@ -847,7 +847,7 @@ requires-dist = [ { name = "rednose", marker = "extra == 'submodules'", editable = "rednose_repo" }, { name = "requests" }, { name = "ruff", marker = "extra == 'testing'" }, - { name = "scons" }, + { name = "scons", specifier = "==4.10.1" }, { name = "sentry-sdk" }, { name = "setproctitle" }, { name = "sounddevice" }, @@ -1136,11 +1136,11 @@ wheels = [ [[package]] name = "scons" -version = "4.11.0" +version = "4.10.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/dd/82/3c4e089ac8df2eaee8a7f14e489b2a76f94f4c1d8defa4e46c8ad15cae86/scons-4.11.0.tar.gz", hash = "sha256:5ba48f9e2eb6b9178cabdc9893792418e6970c84f43f4b027e4468e20616a89c", size = 3269126, upload-time = "2026-08-11T04:29:45.62Z" } +sdist = { url = "https://files.pythonhosted.org/packages/7d/c9/2f430bb39e4eccba32ce8008df4a3206df651276422204e177a09e12b30b/scons-4.10.1.tar.gz", hash = "sha256:99c0e94a42a2c1182fa6859b0be697953db07ba936ecc9817ae0d218ced20b15", size = 3258403, upload-time = "2025-11-16T22:43:39.258Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/fc/ac/a4445bbbd58a5fa6a5c8b3b0458ffbee04e4acaff87677058eab9c6af682/scons-4.11.0-py3-none-any.whl", hash = "sha256:2edc077aaeafc43377ba46ce1fa3e7b40edea59c62db9ef7e39e07dc88b754fa", size = 4123742, upload-time = "2026-08-11T04:29:42.881Z" }, + { url = "https://files.pythonhosted.org/packages/ce/bf/931fb9fbb87234c32b8b1b1c15fba23472a10777c12043336675633809a7/scons-4.10.1-py3-none-any.whl", hash = "sha256:bd9d1c52f908d874eba92a8c0c0a8dcf2ed9f3b88ab956d0fce1da479c4e7126", size = 4136069, upload-time = "2025-11-16T22:43:35.933Z" }, ] [[package]] @@ -1285,7 +1285,6 @@ requires-dist = [ { name = "pylint", marker = "extra == 'linting'" }, { name = "pytest", marker = "extra == 'testing-minimal'" }, { name = "pytest-split", marker = "extra == 'testing-minimal'" }, - { name = "pytest-timeout", marker = "extra == 'testing-minimal'" }, { name = "pytest-xdist", marker = "extra == 'testing-minimal'" }, { name = "ruff", marker = "extra == 'linting'", specifier = "==0.14.10" }, { name = "safetensors", marker = "extra == 'testing-unit'" }, @@ -1294,6 +1293,7 @@ requires-dist = [ { name = "tiktoken", marker = "extra == 'testing'" }, { name = "tinygrad", extras = ["testing-minimal"], marker = "extra == 'testing-unit'" }, { name = "tinygrad", extras = ["testing-unit"], marker = "extra == 'testing'" }, + { name = "tinymesa", marker = "extra == 'mesa'", specifier = "==25.2.7.2" }, { name = "torch", marker = "extra == 'testing-minimal'", specifier = "==2.9.1" }, { name = "tqdm", marker = "extra == 'testing-unit'" }, { name = "transformers", marker = "extra == 'testing'" }, @@ -1301,7 +1301,7 @@ requires-dist = [ { name = "typing-extensions", marker = "extra == 'linting'" }, { name = "z3-solver", marker = "extra == 'testing-minimal'", specifier = "<4.15.4" }, ] -provides-extras = ["linting", "testing-minimal", "testing-unit", "testing", "docs"] +provides-extras = ["linting", "testing-minimal", "testing-unit", "testing", "docs", "mesa"] [[package]] name = "tomli"