IQ.Pilot Release Commit @ 98a2c61

This commit is contained in:
IQ.Lvbs CI [bot]
2026-08-19 14:10:31 -05:00
parent dca8172e55
commit 7e3d70380a
55 changed files with 699 additions and 114 deletions

View File

@@ -91,9 +91,5 @@ class LongControl:
freeze_integrator=gas_override)
self.smooth.reset()
if gas_override:
# safety blocks braking while the gas is pressed, and a blocked tx drops the whole frame
output_accel = max(output_accel, 0.0)
self.last_output_accel = np.clip(output_accel, accel_limits[0], accel_limits[1])
return self.last_output_accel

View File

@@ -16,17 +16,17 @@ from iqpilot.selfdrive.controls.lib.latcontrol_torque import TORQUE_NN_MODEL_PAT
# layers). Used as a fallback so the loader logic is still exercised when no trained
# models are shipped (they are removed pending retraining and re-added over time).
_SYNTHETIC_MODEL = {
"input_size": 4,
"input_size": 18,
"output_size": 1,
"input_mean": [[0.0], [0.0], [0.0], [0.0]],
"input_std": [[1.0], [1.0], [1.0], [1.0]],
"input_mean": [[0.0]] * 18,
"input_std": [[1.0]] * 18,
"layers": [
{"dense_1_W": [[0.5, 0.5, 0.5, 0.5], [0.5, 0.5, 0.5, 0.5]], "dense_1_b": [[0.0], [0.0]], "activation": "sigmoid"},
{"dense_1_W": [[0.5] * 18, [0.5] * 18], "dense_1_b": [[0.0], [0.0]], "activation": "sigmoid"},
{"dense_2_W": [[2.0, 2.0]], "dense_2_b": [[-1.0]], "activation": "identity"},
],
}
MODEL_FILES = sorted(f for f in os.listdir(TORQUE_NN_MODEL_PATH) if f.endswith(".json"))
MODEL_FILES = sorted(f for f in os.listdir(TORQUE_NN_MODEL_PATH) if f.endswith(".json")) if os.path.isdir(TORQUE_NN_MODEL_PATH) else []
if MODEL_FILES:
_MODEL_DIR = TORQUE_NN_MODEL_PATH
_NAMES = MODEL_FILES

View File

@@ -15,15 +15,9 @@ from iqpilot.common.params import Params
from iqpilot.common.pid import PIDController
from iqpilot.selfdrive.iqmodeld.config import ModelConstants
from iqpilot.selfdrive.controls.lib.latcontrol_torque import NeuralNetworkFeedForward
from iqpilot.selfdrive.controls.lib.latcontrol_torque import TORQUE_NN_MODEL_PATH
from iqpilot.selfdrive.controls.lib.neural_network_feed_forward.tests.test_network import _MODEL_DIR, _NAMES
_REAL_MODEL = next((f for f in sorted(os.listdir(TORQUE_NN_MODEL_PATH))
if f.endswith(".json") and f != "MOCK.json"), None)
# Models are shipped separately and re-added as retrained; with none present,
# NNFF is a no-op (falls back to stock torque FF), so the assembly tests skip.
pytestmark = pytest.mark.skipif(_REAL_MODEL is None,
reason="no NNFF models present (nuked pending retraining)")
_REAL_MODEL = next((f for f in _NAMES if f != "MOCK.json"), _NAMES[0])
def _torque_fn():
@@ -53,7 +47,7 @@ def _model_v2():
def _make_controller(model_file):
Params().put_bool("NeuralNetworkFeedForward", True)
path = os.path.join(TORQUE_NN_MODEL_PATH, model_file)
path = os.path.join(_MODEL_DIR, model_file)
cp = SimpleNamespace(steerActuatorDelay=0.15)
cp_iq = SimpleNamespace(iqLateralNet=SimpleNamespace(
model=SimpleNamespace(path=path, name=os.path.splitext(model_file)[0])))
@@ -84,7 +78,10 @@ class TestControllerWiring:
def test_mock_model_reports_absent(self):
nnff = _make_controller("MOCK.json")
assert nnff.has_nn_model is False
assert nnff.model.input_size >= 2 # MOCK still loads as a valid net
if "MOCK.json" in _NAMES:
assert nnff.model.input_size >= 2
else:
assert nnff.model is None
def test_update_returns_finite_torque(self):
nnff = _make_controller(_REAL_MODEL)

View File

@@ -1,5 +1,7 @@
from types import SimpleNamespace
from iqpilot.cereal import custom
from iqpilot.selfdrive.controls.lib.longcontrol import LongCtrlState, long_control_state_trans
from iqpilot.selfdrive.controls.lib.longcontrol import LongControl, LongCtrlState, long_control_state_trans
class TestLongControlStateTransition:
@@ -41,3 +43,30 @@ def test_engage():
next_state = long_control_state_trans(CP_IQ, active, current_state,
should_stop=False, brake_pressed=False, cruise_standstill=False)
assert next_state == LongCtrlState.pid
def test_gas_override_preserves_negative_accel_command():
pid_calls = []
control = object.__new__(LongControl)
control.CP = SimpleNamespace(stopAccel=-0.55)
control.CP_IQ = SimpleNamespace(enableGasInterceptor=False)
control.long_control_state = LongCtrlState.pid
control.pid = SimpleNamespace(
update=lambda error, **kwargs: pid_calls.append((error, kwargs)) or -0.5,
reset=lambda: None,
)
control.last_output_accel = -0.4
control.stopping_decel_rate = 1.0
control.smooth = SimpleNamespace(enabled=False, update=lambda: None, reset=lambda: None)
car_state = SimpleNamespace(
vEgo=15.0,
aEgo=0.0,
brakePressed=False,
standstill=False,
cruiseState=SimpleNamespace(standstill=False),
)
output = control.update(True, car_state, -0.5, False, (-3.5, 2.0), gas_override=True)
assert output == -0.5
assert pid_calls == [(-0.5, {"speed": 15.0, "feedforward": -0.5, "freeze_integrator": True})]