IQ.Pilot Release Commit @ 98a2c61
This commit is contained in:
Binary file not shown.
Binary file not shown.
@@ -12,7 +12,7 @@ from iqpilot.cereal import car, log, custom
|
||||
|
||||
from iqpilot.common.iq_perf import PerfSample, PerfTraceEmitter, PerfTraceRing
|
||||
from iqpilot.common.params import Params, UnknownKeyName
|
||||
from iqpilot.common.realtime import config_realtime_process, lock_memory, Priority, Ratekeeper
|
||||
from iqpilot.common.realtime import config_background_thread, config_realtime_process, lock_memory, Priority, Ratekeeper
|
||||
from iqpilot.common.swaglog import cloudlog, ForwardingHandler
|
||||
|
||||
from iqdbc.car import DT_CTRL, structs
|
||||
@@ -497,6 +497,7 @@ class Car:
|
||||
pass
|
||||
|
||||
def params_thread(self, evt):
|
||||
config_background_thread()
|
||||
while not evt.is_set():
|
||||
self.is_metric = self.params.get_bool("IsMetric")
|
||||
self.experimental_mode = self.params.get_bool("ExperimentalMode") and self.CP.openpilotLongitudinalControl
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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})]
|
||||
|
||||
@@ -11,6 +11,7 @@ MODELS_DIR = MODELD_DIR / 'models'
|
||||
BASEDIR = MODELD_DIR.parents[2]
|
||||
METADATA_SCRIPT = MODELD_DIR / 'get_model_metadata.py'
|
||||
PYPROJECT = BASEDIR / 'pyproject.toml'
|
||||
TINYGRAD_REVISION_FILE = BASEDIR / 'artifacts/package_sources/tinygrad/.iqpilot-revision'
|
||||
|
||||
MODEL_NAMES = ['dmonitoring_model']
|
||||
|
||||
@@ -30,9 +31,16 @@ def _file_sha256(path: Path) -> str:
|
||||
@functools.lru_cache(maxsize=1)
|
||||
def _tinygrad_revision() -> str:
|
||||
match = re.search(r'"tinygrad @ git\+https://[^@]+@([0-9a-f]{40})"', PYPROJECT.read_text())
|
||||
if match is None:
|
||||
raise RuntimeError("missing pinned tinygrad revision")
|
||||
return match.group(1)
|
||||
if match is not None:
|
||||
return match.group(1)
|
||||
|
||||
try:
|
||||
revision = TINYGRAD_REVISION_FILE.read_text().strip()
|
||||
except OSError as e:
|
||||
raise RuntimeError("missing pinned tinygrad revision") from e
|
||||
if re.fullmatch(r'[0-9a-f]{40}', revision) is None:
|
||||
raise RuntimeError("invalid pinned tinygrad revision")
|
||||
return revision
|
||||
|
||||
|
||||
CHECK_PATH = MODELS_DIR / 'prebuilt_check.json'
|
||||
|
||||
@@ -49,3 +49,17 @@ def test_source_checkout_is_not_packaged_prebuilt(tmp_path, monkeypatch):
|
||||
monkeypatch.setattr(prebuilt_models, 'CHECK_PATH', check_path)
|
||||
|
||||
assert not prebuilt_models.packaged_prebuilt_matches('dmonitoring_model')
|
||||
|
||||
|
||||
def test_vendored_tinygrad_revision(tmp_path, monkeypatch):
|
||||
revision = '0123456789abcdef0123456789abcdef01234567'
|
||||
pyproject = tmp_path / 'pyproject.toml'
|
||||
revision_file = tmp_path / '.iqpilot-revision'
|
||||
pyproject.write_text('dependencies = ["tinygrad"]\n')
|
||||
revision_file.write_text(f'{revision}\n')
|
||||
monkeypatch.setattr(prebuilt_models, 'PYPROJECT', pyproject)
|
||||
monkeypatch.setattr(prebuilt_models, 'TINYGRAD_REVISION_FILE', revision_file)
|
||||
prebuilt_models._tinygrad_revision.cache_clear()
|
||||
|
||||
assert prebuilt_models._tinygrad_revision() == revision
|
||||
prebuilt_models._tinygrad_revision.cache_clear()
|
||||
|
||||
@@ -12,7 +12,7 @@ from msgq.visionipc import VisionIpcClient
|
||||
|
||||
from iqpilot.common.params import Params
|
||||
from iqpilot.common.issue_debug import log_issue_limited
|
||||
from iqpilot.common.realtime import config_realtime_process, lock_memory, Priority, Ratekeeper, DT_CTRL
|
||||
from iqpilot.common.realtime import config_background_thread, config_realtime_process, lock_memory, Priority, Ratekeeper, DT_CTRL
|
||||
from iqpilot.common.swaglog import cloudlog
|
||||
from iqpilot.common.gps import get_gps_location_service
|
||||
|
||||
@@ -755,6 +755,7 @@ class SelfdriveD(GapButtonActions):
|
||||
)
|
||||
|
||||
def params_thread(self, evt):
|
||||
config_background_thread()
|
||||
while not evt.is_set():
|
||||
self.is_metric = self.params.get_bool("IsMetric")
|
||||
self.is_ldw_enabled = self.params.get_bool("IsLdwEnabled")
|
||||
|
||||
@@ -98,7 +98,7 @@ class ChangelogWidget(Widget):
|
||||
return
|
||||
self._last_load = now
|
||||
|
||||
paths = [os.path.join(BASEDIR, "docs", "CHANGELOG.md")]
|
||||
paths = [os.path.join(BASEDIR, "iqpilot", "docs", "CHANGELOG.md")]
|
||||
content = ""
|
||||
for p in paths:
|
||||
try:
|
||||
@@ -110,7 +110,7 @@ class ChangelogWidget(Widget):
|
||||
pass
|
||||
|
||||
if not content:
|
||||
content = "No changelog found.\n\nAdd docs/CHANGELOG.md."
|
||||
content = "No changelog found.\n\nAdd iqpilot/docs/CHANGELOG.md."
|
||||
|
||||
ordered = self._reorder_sections_newest_first(content)
|
||||
self._latest_text = self._build_latest_text(ordered)
|
||||
|
||||
@@ -60,11 +60,6 @@ sound_list: dict[int, tuple[str, int | None, float]] = {
|
||||
|
||||
**sound_list_iq,
|
||||
}
|
||||
if HARDWARE.get_device_type() in ("tizi", "tici"):
|
||||
sound_list.update({
|
||||
AudibleAlert.engage: ("engage.wav", 1, MAX_VOLUME),
|
||||
AudibleAlert.disengage: ("disengage.wav", 1, MAX_VOLUME),
|
||||
})
|
||||
|
||||
def check_selfdrive_timeout_alert(sm):
|
||||
ss_missing = time.monotonic() - sm.recv_time['selfdriveState']
|
||||
|
||||
Reference in New Issue
Block a user