IQ.Pilot Release Commit @ 763bad7
This commit is contained in:
@@ -1,8 +1,8 @@
|
|||||||
{
|
{
|
||||||
"python/_iqclosure/_vendored.json": {
|
"python/_iqclosure/_vendored.json": {
|
||||||
"mode": 420,
|
"mode": 420,
|
||||||
"sha256": "c5dde802e17116d9759e661e7d80a1a86d396fb3040734c1eadb2a50c5a3cf4c",
|
"sha256": "2cd59a784bfe6c6d1ce382e832dd355f62900b1f73a74ed91c53397aa8581754",
|
||||||
"size": 3439
|
"size": 3495
|
||||||
},
|
},
|
||||||
"python/_iqclosure/iqpilot/__init__.py": {
|
"python/_iqclosure/iqpilot/__init__.py": {
|
||||||
"mode": 420,
|
"mode": 420,
|
||||||
@@ -196,8 +196,8 @@
|
|||||||
},
|
},
|
||||||
"python/_iqclosure/iqpilot/selfdrive/iqmodeld/egpu_helpers.py": {
|
"python/_iqclosure/iqpilot/selfdrive/iqmodeld/egpu_helpers.py": {
|
||||||
"mode": 420,
|
"mode": 420,
|
||||||
"sha256": "5d00df0cf831ea75a91d1d817818dc3847d971e581170671f12e289461eccabc",
|
"sha256": "4df0ac0a3f720ee01a70f82908e582682e84848023592a72bd45f2ca74c9b161",
|
||||||
"size": 4846
|
"size": 5535
|
||||||
},
|
},
|
||||||
"python/_iqclosure/iqpilot/selfdrive/iqmodeld/egpu_model.py": {
|
"python/_iqclosure/iqpilot/selfdrive/iqmodeld/egpu_model.py": {
|
||||||
"mode": 420,
|
"mode": 420,
|
||||||
@@ -219,6 +219,11 @@
|
|||||||
"sha256": "dd1559099407687ee2dc81e180e05b9954e712c39e1a82e39c1b00b70c43374a",
|
"sha256": "dd1559099407687ee2dc81e180e05b9954e712c39e1a82e39c1b00b70c43374a",
|
||||||
"size": 3016
|
"size": 3016
|
||||||
},
|
},
|
||||||
|
"python/_iqclosure/iqpilot/selfdrive/iqmodeld/model_bundle_downloader.py": {
|
||||||
|
"mode": 420,
|
||||||
|
"sha256": "62c2c2830c7c8819bf42eb54a34db635128a31fbc2bbb71bc95415e5b873a3c0",
|
||||||
|
"size": 3189
|
||||||
|
},
|
||||||
"python/_iqclosure/iqpilot/selfdrive/iqmodeld/model_channel.py": {
|
"python/_iqclosure/iqpilot/selfdrive/iqmodeld/model_channel.py": {
|
||||||
"mode": 420,
|
"mode": 420,
|
||||||
"sha256": "2d706993df418501d93dc766ffe452131e565bfbc2e8412b527dc14915e92832",
|
"sha256": "2d706993df418501d93dc766ffe452131e565bfbc2e8412b527dc14915e92832",
|
||||||
@@ -526,22 +531,22 @@
|
|||||||
},
|
},
|
||||||
"python/iqpilot_private/emac/bulk_transport.cpython-312-aarch64-linux-gnu.so": {
|
"python/iqpilot_private/emac/bulk_transport.cpython-312-aarch64-linux-gnu.so": {
|
||||||
"mode": 493,
|
"mode": 493,
|
||||||
"sha256": "0b580306ea908ad46b280f2fd5f828083e736b83c951a33ab007a6fb8a642442",
|
"sha256": "3f55e4c621ea86c0a6ac9ecac091fa90491048df5fc5bf09cbb668f0c0b71051",
|
||||||
"size": 202152
|
"size": 334016
|
||||||
},
|
},
|
||||||
"python/iqpilot_private/emac/mac_client.cpython-312-aarch64-linux-gnu.so": {
|
"python/iqpilot_private/emac/mac_client.cpython-312-aarch64-linux-gnu.so": {
|
||||||
"mode": 493,
|
"mode": 493,
|
||||||
"sha256": "b4d721d043cad44826a7b1ac304105ff65676a00570e1b1e0f845c2a4ab88d51",
|
"sha256": "e748eba2eec567e5f0c9e1dce60ef71795d2a99f58238ad89495dcee7672c70c",
|
||||||
"size": 267376
|
"size": 267952
|
||||||
},
|
},
|
||||||
"python/iqpilot_private/emac/mac_protocol.cpython-312-aarch64-linux-gnu.so": {
|
"python/iqpilot_private/emac/mac_protocol.cpython-312-aarch64-linux-gnu.so": {
|
||||||
"mode": 493,
|
"mode": 493,
|
||||||
"sha256": "c63d56f9a4329f8a16c4a2144ddc7761876fd20f7c51ac40005c516573f7b19e",
|
"sha256": "46f79e0fe9d799003b49e5fe11969a1716974a9c6d20d52488d79ef8b7137a5a",
|
||||||
"size": 203544
|
"size": 203544
|
||||||
},
|
},
|
||||||
"python/iqpilot_private/emac/maciqmodeld.cpython-312-aarch64-linux-gnu.so": {
|
"python/iqpilot_private/emac/maciqmodeld.cpython-312-aarch64-linux-gnu.so": {
|
||||||
"mode": 493,
|
"mode": 493,
|
||||||
"sha256": "311a13e5db0e6f8719e2b6383a2d9c74c2f70db28bdd424c5ceec4958d70f201",
|
"sha256": "b7acc1edcf50dd2e9c2d88f2f4824d0a28b0def3a57b144a340ddca1bdd14254",
|
||||||
"size": 333336
|
"size": 333336
|
||||||
},
|
},
|
||||||
"runtime": {
|
"runtime": {
|
||||||
@@ -553,9 +558,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"signatures": {
|
"signatures": {
|
||||||
"python/iqpilot_private/emac/bulk_transport.cpython-312-aarch64-linux-gnu.so": "mxq4MaNi+2F0fpbVaypztLeUf9DqFo8LSohXAzb+1tXsdKZZYQmmU90hlqIRFv4PuwWRyVbXJE/Ge48amXDhBw==",
|
"python/iqpilot_private/emac/bulk_transport.cpython-312-aarch64-linux-gnu.so": "zdchbAhGhL0eeX6qgQUw0CwrPGIg896Sv/0AG0hHrJ/1Ek7KnB4llxmWUA/e4HB4hlCqTEMO416N3csIfDikBA==",
|
||||||
"python/iqpilot_private/emac/mac_client.cpython-312-aarch64-linux-gnu.so": "Zm41VmfRMMJkj3TvxrqLLyUy+0Xq71z9V18gpKkstM5hWtrZsdP4Q+G04eeiujl45Q8hO1ysW0MMP6gJFQGfAg==",
|
"python/iqpilot_private/emac/mac_client.cpython-312-aarch64-linux-gnu.so": "p0ukaERkDTTF6vVVn0TK1ImrPMArUWmqAY2kJnctlb38486SIeFZxC4gCP0cU9++j7i7ydmXNOcUqxG7qIPECQ==",
|
||||||
"python/iqpilot_private/emac/mac_protocol.cpython-312-aarch64-linux-gnu.so": "3quWJnVdRjcA+zrHojJIRcmxySkJAbskBYfVpXjpN3BHxFvjrC+TK9ReVEeTwQdp6b6xR0Fk+40Ngo0MphsbAQ==",
|
"python/iqpilot_private/emac/mac_protocol.cpython-312-aarch64-linux-gnu.so": "5rLpvBoiOP+/N/zblRnXcltpOtULO3uZx2bQYeCM/A8x8m9HLhIBUMJzSVIYj5VcbvgHmzc911UE18RrwEmyCg==",
|
||||||
"python/iqpilot_private/emac/maciqmodeld.cpython-312-aarch64-linux-gnu.so": "wqTQKJ8HOYoBP6dLbdVLvMRZ81kMfvwRB77qSj3jikgyPtz2ARen/Eo/SydGqQByqn0FUR7mHWm65I7/flEEBw=="
|
"python/iqpilot_private/emac/maciqmodeld.cpython-312-aarch64-linux-gnu.so": "otKpewDX8CPqirHPXn+mTht7oi/AgkQiZ2VXPZM3/wryLSvzRKJzMyf5nXZ/lb0Z8J8vsFKUkdVhxKgYkeLIDw=="
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
ML9DoBJeI+4hKgeyK5ssDKYFVeaAg1dVrqTrEjizxFHJtuAv1wYohJz8hxdJnvkQgvrA4pFVsOsu3kvEdIXWAg==
|
9uZxIFB8JGkbVFGkwQJ+7gxvqL+EJf7PS0DqSsFYuiaw39lYS4qp5rzPOmn0ixSDSYlzsEwnwq5ojMdbX/ZJCQ==
|
||||||
|
|||||||
@@ -34,6 +34,7 @@
|
|||||||
"iqpilot.selfdrive.iqmodeld.emac_model_meta",
|
"iqpilot.selfdrive.iqmodeld.emac_model_meta",
|
||||||
"iqpilot.selfdrive.iqmodeld.messaging",
|
"iqpilot.selfdrive.iqmodeld.messaging",
|
||||||
"iqpilot.selfdrive.iqmodeld.metadata",
|
"iqpilot.selfdrive.iqmodeld.metadata",
|
||||||
|
"iqpilot.selfdrive.iqmodeld.model_bundle_downloader",
|
||||||
"iqpilot.selfdrive.iqmodeld.model_channel",
|
"iqpilot.selfdrive.iqmodeld.model_channel",
|
||||||
"iqpilot.selfdrive.iqmodeld.model_warp",
|
"iqpilot.selfdrive.iqmodeld.model_warp",
|
||||||
"iqpilot.selfdrive.iqmodeld.models",
|
"iqpilot.selfdrive.iqmodeld.models",
|
||||||
|
|||||||
@@ -57,6 +57,11 @@ def egpu_pkl_path(meta: dict) -> str:
|
|||||||
return os.path.join(Paths.model_root(), f"egpu_{meta['key']}_{meta['sha256'][:8]}_amd_tinygrad.pkl")
|
return os.path.join(Paths.model_root(), f"egpu_{meta['key']}_{meta['sha256'][:8]}_amd_tinygrad.pkl")
|
||||||
|
|
||||||
|
|
||||||
|
def egpu_policy_pkl_path(meta: dict) -> str:
|
||||||
|
from iqpilot.system.hardware.hw import Paths
|
||||||
|
return os.path.join(Paths.model_root(), f"egpu_{meta['key']}_{meta['sha256'][:8]}_amd_policy.pkl")
|
||||||
|
|
||||||
|
|
||||||
def onnx_cache_path(meta: dict) -> str:
|
def onnx_cache_path(meta: dict) -> str:
|
||||||
from iqpilot.system.hardware.hw import Paths
|
from iqpilot.system.hardware.hw import Paths
|
||||||
return os.path.join(Paths.model_root(), f"{meta['model_name']}_{meta['sha256'][:8]}.onnx")
|
return os.path.join(Paths.model_root(), f"{meta['model_name']}_{meta['sha256'][:8]}.onnx")
|
||||||
@@ -137,6 +142,15 @@ def download_onnx(meta: dict, progress_cb=None) -> str:
|
|||||||
return path
|
return path
|
||||||
|
|
||||||
|
|
||||||
|
def download_precompiled(meta: dict, progress_cb=None, policy: bool = False) -> str | None:
|
||||||
|
art = meta.get("egpu_policy_artifact" if policy else "egpu_artifact")
|
||||||
|
if not art or not art.get("objects"):
|
||||||
|
return None
|
||||||
|
from iqpilot.selfdrive.iqmodeld.model_bundle_downloader import download_lfs_bundle
|
||||||
|
dest = egpu_policy_pkl_path(meta) if policy else egpu_pkl_path(meta)
|
||||||
|
return download_lfs_bundle(art["objects"], dest, art["sha256"], int(art.get("size", 0)), progress_cb=progress_cb)
|
||||||
|
|
||||||
|
|
||||||
def patch_tinygrad_fetch_fw() -> None:
|
def patch_tinygrad_fetch_fw() -> None:
|
||||||
import pathlib
|
import pathlib
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,87 @@
|
|||||||
|
"""
|
||||||
|
Copyright © IQ.Lvbs, apart of Project Teal Lvbs, All Rights Reserved, licensed under https://konn3kt.com/tos/
|
||||||
|
"""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import hashlib
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
|
||||||
|
MODELS_BASE_URLS = (
|
||||||
|
"https://git.konn3kt.com/teal/IQModels/raw/branch/main",
|
||||||
|
"https://gitlvb.teallvbs.xyz/teal/IQModels/raw/branch/main",
|
||||||
|
)
|
||||||
|
CHUNK = 4 * 1024 * 1024
|
||||||
|
HTTP_TIMEOUT_S = 60.0
|
||||||
|
STREAM_RETRIES = 6
|
||||||
|
|
||||||
|
|
||||||
|
def _requests_auth():
|
||||||
|
import importlib
|
||||||
|
for mod in ("iqpilot_private.models.git_auth", "iqpilot.models_private_src.git_auth",
|
||||||
|
"iqpilot.selfdrive.iqmodeld.models.git_auth"):
|
||||||
|
try:
|
||||||
|
return importlib.import_module(mod).get_requests_auth()
|
||||||
|
except Exception:
|
||||||
|
continue
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def _lfs_endpoint(base_url: str) -> str:
|
||||||
|
return base_url.split("/raw/", 1)[0] + ".git/info/lfs"
|
||||||
|
|
||||||
|
|
||||||
|
def _resolve_oid(session, base_url: str, oid: str, size: int, auth):
|
||||||
|
import requests
|
||||||
|
batch = session.post(f"{_lfs_endpoint(base_url)}/objects/batch",
|
||||||
|
data=json.dumps({"operation": "download", "transfers": ["basic"],
|
||||||
|
"objects": [{"oid": oid, "size": size}]}),
|
||||||
|
headers={"Content-Type": "application/vnd.git-lfs+json",
|
||||||
|
"Accept": "application/vnd.git-lfs+json"},
|
||||||
|
auth=auth, timeout=HTTP_TIMEOUT_S)
|
||||||
|
batch.raise_for_status()
|
||||||
|
entry = batch.json()["objects"][0]
|
||||||
|
if "actions" not in entry:
|
||||||
|
raise requests.RequestException(f"LFS object unavailable: {entry.get('error', oid)}")
|
||||||
|
action = entry["actions"]["download"]
|
||||||
|
return action["href"], action.get("header", {})
|
||||||
|
|
||||||
|
|
||||||
|
def download_lfs_bundle(objects: list, dst: str, sha256: str, size: int, progress_cb=None) -> str:
|
||||||
|
import requests
|
||||||
|
auth = _requests_auth()
|
||||||
|
session = requests.Session()
|
||||||
|
os.makedirs(os.path.dirname(dst), exist_ok=True)
|
||||||
|
tmp = dst + ".part"
|
||||||
|
total = int(size) or sum(int(o["size"]) for o in objects)
|
||||||
|
last_error: Exception | None = None
|
||||||
|
for base_url in MODELS_BASE_URLS:
|
||||||
|
for attempt in range(STREAM_RETRIES):
|
||||||
|
try:
|
||||||
|
digest = hashlib.sha256()
|
||||||
|
got = 0
|
||||||
|
with open(tmp, "wb") as f:
|
||||||
|
for obj in objects:
|
||||||
|
href, headers = _resolve_oid(session, base_url, obj["oid"], int(obj["size"]), auth)
|
||||||
|
obj_auth = None if headers.get("Authorization") else auth
|
||||||
|
with session.get(href, headers=headers, stream=True, timeout=120, auth=obj_auth) as r:
|
||||||
|
r.raise_for_status()
|
||||||
|
for chunk in r.iter_content(CHUNK):
|
||||||
|
f.write(chunk)
|
||||||
|
digest.update(chunk)
|
||||||
|
got += len(chunk)
|
||||||
|
if progress_cb is not None and total:
|
||||||
|
progress_cb(min(1.0, got / total))
|
||||||
|
if total and got != total:
|
||||||
|
raise RuntimeError(f"size mismatch: {got}/{total} bytes")
|
||||||
|
if sha256 and digest.hexdigest() != sha256:
|
||||||
|
raise RuntimeError("sha256 mismatch")
|
||||||
|
os.replace(tmp, dst)
|
||||||
|
return dst
|
||||||
|
except Exception as e:
|
||||||
|
last_error = e
|
||||||
|
try:
|
||||||
|
os.remove(tmp)
|
||||||
|
except OSError:
|
||||||
|
pass
|
||||||
|
raise RuntimeError(f"model bundle download failed: {last_error}")
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -176,7 +176,7 @@
|
|||||||
},
|
},
|
||||||
"python/iqpilot_private/models/__init__.cpython-312-aarch64-linux-gnu.so": {
|
"python/iqpilot_private/models/__init__.cpython-312-aarch64-linux-gnu.so": {
|
||||||
"mode": 493,
|
"mode": 493,
|
||||||
"sha256": "b7ae194bc219d344d92e77e7ad56232c48e75bab4d0bdcb2ebf20dccc7ba2c4d",
|
"sha256": "c6ff7efa8f20decbd9ee75a2b11d6be6b2b0888b45d90dce65ac3a1615e3afc5",
|
||||||
"size": 67616
|
"size": 67616
|
||||||
},
|
},
|
||||||
"python/iqpilot_private/models/__init__.py": {
|
"python/iqpilot_private/models/__init__.py": {
|
||||||
@@ -186,37 +186,37 @@
|
|||||||
},
|
},
|
||||||
"python/iqpilot_private/models/big_catalog.cpython-312-aarch64-linux-gnu.so": {
|
"python/iqpilot_private/models/big_catalog.cpython-312-aarch64-linux-gnu.so": {
|
||||||
"mode": 493,
|
"mode": 493,
|
||||||
"sha256": "447634f6ee186df629e3fc03556fab3222d84037da0e9061aa44e7238b0f9b5e",
|
"sha256": "60bb1b539739e75249c3034ff505aeb9bf38d6fcf8d7f713d61395104bee0002",
|
||||||
"size": 133840
|
"size": 133840
|
||||||
},
|
},
|
||||||
"python/iqpilot_private/models/egpu_model.cpython-312-aarch64-linux-gnu.so": {
|
"python/iqpilot_private/models/egpu_model.cpython-312-aarch64-linux-gnu.so": {
|
||||||
"mode": 493,
|
"mode": 493,
|
||||||
"sha256": "5a69a9967bdeaf8258b26487be40420c8f37935161699527f2bafc3ddfa5e710",
|
"sha256": "7111179a66be1ac8bbcefcd2c6f836150e9e31b01d36244315203123f980beb8",
|
||||||
"size": 68112
|
"size": 68112
|
||||||
},
|
},
|
||||||
"python/iqpilot_private/models/emac_model_meta.cpython-312-aarch64-linux-gnu.so": {
|
"python/iqpilot_private/models/emac_model_meta.cpython-312-aarch64-linux-gnu.so": {
|
||||||
"mode": 493,
|
"mode": 493,
|
||||||
"sha256": "24785c0b8eee854e1da2f03ee961de6650fcd7b975797c45d5d1d8ea995b8273",
|
"sha256": "16bec7e29dc9b07b743d523735eb3adfd5b43b0ffa7cc89d189ee050065aa08d",
|
||||||
"size": 68264
|
"size": 68264
|
||||||
},
|
},
|
||||||
"python/iqpilot_private/models/fetcher.cpython-312-aarch64-linux-gnu.so": {
|
"python/iqpilot_private/models/fetcher.cpython-312-aarch64-linux-gnu.so": {
|
||||||
"mode": 493,
|
"mode": 493,
|
||||||
"sha256": "37042bfe3e87db35e8abb8a03562c90be5ce835e832b2b4159f57e6f2643764e",
|
"sha256": "8eaf6bc52ad788587e3a9bfbd06246f68931c8a151772e37c895c396001e5243",
|
||||||
"size": 136200
|
"size": 136200
|
||||||
},
|
},
|
||||||
"python/iqpilot_private/models/git_auth.cpython-312-aarch64-linux-gnu.so": {
|
"python/iqpilot_private/models/git_auth.cpython-312-aarch64-linux-gnu.so": {
|
||||||
"mode": 493,
|
"mode": 493,
|
||||||
"sha256": "9818f5eb5c850cdee9ec6d62d239de0451659cd62b891c01083433f41ca8dbde",
|
"sha256": "c785e13bf86fcfdc5bf0d5e5acc6a49b764f800e4a5e87bb740cd187fdeb4584",
|
||||||
"size": 67864
|
"size": 67864
|
||||||
},
|
},
|
||||||
"python/iqpilot_private/models/helpers.cpython-312-aarch64-linux-gnu.so": {
|
"python/iqpilot_private/models/helpers.cpython-312-aarch64-linux-gnu.so": {
|
||||||
"mode": 493,
|
"mode": 493,
|
||||||
"sha256": "6e6c9e9e66579d1f03861f1535da3a960f53f50e734a6030b41b9d8f8516c981",
|
"sha256": "d98244f8f9b3b1e1f412802d93095a0a05dedf5c6a98c9cefbee6c27e999d341",
|
||||||
"size": 137944
|
"size": 137944
|
||||||
},
|
},
|
||||||
"python/iqpilot_private/models/manager.cpython-312-aarch64-linux-gnu.so": {
|
"python/iqpilot_private/models/manager.cpython-312-aarch64-linux-gnu.so": {
|
||||||
"mode": 493,
|
"mode": 493,
|
||||||
"sha256": "ca16d91129cf70227a92b81dd4204760681fc6f2ca7bd9ed84e58b7cd2efd2de",
|
"sha256": "9df118ff8a45497e258ac4cf520db3c80f74b8cd22a41b54bba26d372bc6a8e5",
|
||||||
"size": 340000
|
"size": 340000
|
||||||
},
|
},
|
||||||
"runtime": {
|
"runtime": {
|
||||||
@@ -228,13 +228,13 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"signatures": {
|
"signatures": {
|
||||||
"python/iqpilot_private/models/__init__.cpython-312-aarch64-linux-gnu.so": "lSYzYS2zoycLPzqpLu8RE5hnpVFgSA0e6ZXgH57Ve2f3jwV9OSCbM+JHqxy5fDDiPW1ksRM6j01vFE+6SY5hCw==",
|
"python/iqpilot_private/models/__init__.cpython-312-aarch64-linux-gnu.so": "XBVN/BJBOnlVrPJ0pq/+rAkGwSDkJk3t29YpPaYsWCQPWD0JADTip4a8vj0GvCQgXdGwNsMJb2b7Cu6TeoOOAw==",
|
||||||
"python/iqpilot_private/models/big_catalog.cpython-312-aarch64-linux-gnu.so": "WHHAjMetD6q1tsUdG54ulOLADQImspgfqr7p7lqPp0NMmvy+MYVJLQPmxrjrSokZTwtGivlLNEy8wo4EZ6OuCQ==",
|
"python/iqpilot_private/models/big_catalog.cpython-312-aarch64-linux-gnu.so": "zhE70qcpwK9vSvqYp7fKuMptsV2JMMpmsnUeyFuA89mtFDKzUoJi+jxwOsvcH1b0qk6CuemH86l0/UpA0AbRAg==",
|
||||||
"python/iqpilot_private/models/egpu_model.cpython-312-aarch64-linux-gnu.so": "+QTJvOd9zd1UfXcwn0+EhoMHhGMvPyBOB59GglVi5fx9j1TUqDWu00Ip/30IlbxWTbTMZQzDNFWR3puQZ/tgCA==",
|
"python/iqpilot_private/models/egpu_model.cpython-312-aarch64-linux-gnu.so": "4JFNjPPV2i8xPXrWrwpkuOWPDRCD/m0+bVGgWO9TEmFLKbAu+RQslAHesptxfDAyomJbeRLBvXk1KVRaTzQ+CA==",
|
||||||
"python/iqpilot_private/models/emac_model_meta.cpython-312-aarch64-linux-gnu.so": "e9ZmfuFD/FSWPAt/9nmyFafkGojmCuRxmaHixSCMPLaFVDrwwomuDoaq4ogaQ6HmHDdCosBkNh7w/6ZIHgpzDg==",
|
"python/iqpilot_private/models/emac_model_meta.cpython-312-aarch64-linux-gnu.so": "Q1luLZ5apcCJ3Im8Py3uP0D42VYXdsw5y48FAtofFrCQJlZ+LTPshjSVbu2GYQnxny9fFt4J6DT9zvMtLheqAA==",
|
||||||
"python/iqpilot_private/models/fetcher.cpython-312-aarch64-linux-gnu.so": "ImIktR4cuqUzWtoZe1nSA0sIygmpuVW76jNZwerd+qG1qmJojkW7mfQzZ6yiwRTV51FybjLjLzhZgAA5kqY9Cw==",
|
"python/iqpilot_private/models/fetcher.cpython-312-aarch64-linux-gnu.so": "YPc7Ix5gW6YCQX2DrpL5bWu5RKMIOCJvaR/xDwTJmo3/dfkgvfAv7jG41rTxpXaRl8rM8ipUoPI8tlKGzucqDw==",
|
||||||
"python/iqpilot_private/models/git_auth.cpython-312-aarch64-linux-gnu.so": "qGUpnjJFC67qdIuc3JeTtDHVKRuK9oEe59f/43IJF3avDTzbFTHnLWM3AfvO+j3i/m4vuuRjAlKVCQEFRlNWDA==",
|
"python/iqpilot_private/models/git_auth.cpython-312-aarch64-linux-gnu.so": "R6WdFIgsLn4/8fq5qSU64ye7n+Ql9tr3Vhy4gYKIGKWY5GkXplkc8KMA1ciO7yy0n6Qw5HPCmGKrAbkQerrfBw==",
|
||||||
"python/iqpilot_private/models/helpers.cpython-312-aarch64-linux-gnu.so": "rB3MDS2Ue0p8889DbKn+gpzyaM1IeR4cb38uy3taitPwV4Em2Zbyr47W2hbvlKL32m2pBWwiJCM8EeqlafEtDg==",
|
"python/iqpilot_private/models/helpers.cpython-312-aarch64-linux-gnu.so": "BoA4zydMAnpLyAFxzjwvggxB8Hzx+OJd3puAAckzi0UiiGhdiTGl9jexUcGpOolST3pPCRSoCXT0fVihW/OFDw==",
|
||||||
"python/iqpilot_private/models/manager.cpython-312-aarch64-linux-gnu.so": "qsLTZana4Y5dxS0m8gGvtYVTFeXixkPUawT2SGwQF8ccxRQogAUt1bQJfJLZA4xmOnTG2Da0qhxFQ13VsFBeCA=="
|
"python/iqpilot_private/models/manager.cpython-312-aarch64-linux-gnu.so": "8VccT47QBNee1ZcnKSsHchV10dOJP0q55FtabpSZr+azLP+hmWiClG3j9VqRe5ctAzuMa53m7eSRjXiTKCpwDw=="
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
ZZZzIcyCSoU2lVTy3IXBcSS//qvFxL5C0ivJT9huKojaRWV7cBqJDBRD457NwknzAaUHiz9HmAwguFLjvWgxCA==
|
LQYoKAbNbtkF/8GkU70iyR+NZQI0fmM7PWUQ2IphOB0P4nCRjgRCnkkE/g3KS+d4f/R60fgakG135GVCQgsaBw==
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
1
artifacts/iqpilot_navd_private/.source_sha
Normal file
1
artifacts/iqpilot_navd_private/.source_sha
Normal file
@@ -0,0 +1 @@
|
|||||||
|
44200007e6d1ccd1e6016db08abf3f54d733913f
|
||||||
@@ -599,6 +599,8 @@ struct IQNavState @0xaae9afb364368cd9 {
|
|||||||
cameraChime @59 :Bool;
|
cameraChime @59 :Bool;
|
||||||
cameraTrusted @60 :Bool;
|
cameraTrusted @60 :Bool;
|
||||||
cameraSourceAge @61 :Float32;
|
cameraSourceAge @61 :Float32;
|
||||||
|
mapboxSpeedLimit @62 :Float32;
|
||||||
|
mapboxSpeedLimitValid @63 :Bool;
|
||||||
|
|
||||||
enum CameraType {
|
enum CameraType {
|
||||||
none @0;
|
none @0;
|
||||||
|
|||||||
@@ -199,6 +199,7 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
|
|||||||
{"ModelRunnerTypeCache", {CLEAR_ON_ONROAD_TRANSITION, INT}},
|
{"ModelRunnerTypeCache", {CLEAR_ON_ONROAD_TRANSITION, INT}},
|
||||||
{"ForceOnroadUntil", {CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION, INT, "0"}},
|
{"ForceOnroadUntil", {CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION, INT, "0"}},
|
||||||
{"IQAlwaysOffroad", {CLEAR_ON_MANAGER_START, BOOL}},
|
{"IQAlwaysOffroad", {CLEAR_ON_MANAGER_START, BOOL}},
|
||||||
|
{"IQBenchIgnition", {PERSISTENT, BOOL, "0"}},
|
||||||
{"Offroad_TiciSupport", {CLEAR_ON_MANAGER_START, JSON}},
|
{"Offroad_TiciSupport", {CLEAR_ON_MANAGER_START, JSON}},
|
||||||
{"OnroadScreenOffBrightness", {PERSISTENT, INT, "0"}},
|
{"OnroadScreenOffBrightness", {PERSISTENT, INT, "0"}},
|
||||||
{"OnroadScreenOffTimer", {PERSISTENT, INT, "15"}},
|
{"OnroadScreenOffTimer", {PERSISTENT, INT, "15"}},
|
||||||
@@ -241,6 +242,7 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
|
|||||||
{"MacModelReachable", {CLEAR_ON_MANAGER_START, BOOL}},
|
{"MacModelReachable", {CLEAR_ON_MANAGER_START, BOOL}},
|
||||||
{"MacModelReady", {CLEAR_ON_MANAGER_START, BOOL}},
|
{"MacModelReady", {CLEAR_ON_MANAGER_START, BOOL}},
|
||||||
{"MacModelActive", {CLEAR_ON_MANAGER_START, BOOL}},
|
{"MacModelActive", {CLEAR_ON_MANAGER_START, BOOL}},
|
||||||
|
{"MacModelFault", {CLEAR_ON_MANAGER_START, BOOL}},
|
||||||
{"MacModelFailed", {CLEAR_ON_MANAGER_START, BOOL}},
|
{"MacModelFailed", {CLEAR_ON_MANAGER_START, BOOL}},
|
||||||
{"MacModelLastError", {CLEAR_ON_MANAGER_START, STRING}},
|
{"MacModelLastError", {CLEAR_ON_MANAGER_START, STRING}},
|
||||||
{"MacModelLatencyMs", {CLEAR_ON_MANAGER_START, FLOAT, "0.0"}},
|
{"MacModelLatencyMs", {CLEAR_ON_MANAGER_START, FLOAT, "0.0"}},
|
||||||
@@ -251,6 +253,7 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
|
|||||||
{"IQEgpuDisabled", {PERSISTENT, BOOL, "0"}},
|
{"IQEgpuDisabled", {PERSISTENT, BOOL, "0"}},
|
||||||
{"UsbGpuPresent", {CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION, BOOL}},
|
{"UsbGpuPresent", {CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION, BOOL}},
|
||||||
{"UsbGpuCompiled", {CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION, BOOL}},
|
{"UsbGpuCompiled", {CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION, BOOL}},
|
||||||
|
{"UsbGpuReady", {PERSISTENT, BOOL, "0"}},
|
||||||
{"UsbGpuLoading", {CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION | CLEAR_ON_IGNITION_ON, BOOL}},
|
{"UsbGpuLoading", {CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION | CLEAR_ON_IGNITION_ON, BOOL}},
|
||||||
{"UsbGpuActive", {CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION | CLEAR_ON_IGNITION_ON, BOOL}},
|
{"UsbGpuActive", {CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION | CLEAR_ON_IGNITION_ON, BOOL}},
|
||||||
{"UsbGpuFailed", {CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION | CLEAR_ON_IGNITION_ON, BOOL}},
|
{"UsbGpuFailed", {CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION | CLEAR_ON_IGNITION_ON, BOOL}},
|
||||||
@@ -319,6 +322,7 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
|
|||||||
{"MacModelCompiled", {CLEAR_ON_MANAGER_START, BOOL}},
|
{"MacModelCompiled", {CLEAR_ON_MANAGER_START, BOOL}},
|
||||||
{"MacModelReady", {CLEAR_ON_MANAGER_START, BOOL}},
|
{"MacModelReady", {CLEAR_ON_MANAGER_START, BOOL}},
|
||||||
{"MacModelActive", {CLEAR_ON_MANAGER_START, BOOL}},
|
{"MacModelActive", {CLEAR_ON_MANAGER_START, BOOL}},
|
||||||
|
{"MacModelFault", {CLEAR_ON_MANAGER_START, BOOL}},
|
||||||
{"MacModelFailed", {CLEAR_ON_MANAGER_START, BOOL}},
|
{"MacModelFailed", {CLEAR_ON_MANAGER_START, BOOL}},
|
||||||
{"MacModelLastError", {CLEAR_ON_MANAGER_START, STRING}},
|
{"MacModelLastError", {CLEAR_ON_MANAGER_START, STRING}},
|
||||||
{"MacModelDownloadProgress", {CLEAR_ON_MANAGER_START, FLOAT}},
|
{"MacModelDownloadProgress", {CLEAR_ON_MANAGER_START, FLOAT}},
|
||||||
|
|||||||
BIN
iqpilot/selfdrive/assets/icons_mici/egpu.png
Normal file
BIN
iqpilot/selfdrive/assets/icons_mici/egpu.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.9 KiB |
BIN
iqpilot/selfdrive/assets/icons_mici/egpu_green.png
Normal file
BIN
iqpilot/selfdrive/assets/icons_mici/egpu_green.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.1 KiB |
BIN
iqpilot/selfdrive/assets/icons_mici/egpu_orange.png
Normal file
BIN
iqpilot/selfdrive/assets/icons_mici/egpu_orange.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 6.1 KiB |
BIN
iqpilot/selfdrive/assets/icons_mici/mac.png
Normal file
BIN
iqpilot/selfdrive/assets/icons_mici/mac.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.2 KiB |
BIN
iqpilot/selfdrive/assets/icons_mici/mac_green.png
Normal file
BIN
iqpilot/selfdrive/assets/icons_mici/mac_green.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 7.5 KiB |
BIN
iqpilot/selfdrive/assets/icons_mici/mac_orange.png
Normal file
BIN
iqpilot/selfdrive/assets/icons_mici/mac_orange.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 8.6 KiB |
@@ -777,7 +777,15 @@ class SpeedLimitController:
|
|||||||
self.segment_distance = 0.0
|
self.segment_distance = 0.0
|
||||||
self.tomtom_segment_distance = 0.0
|
self.tomtom_segment_distance = 0.0
|
||||||
|
|
||||||
online_limit = self.tomtom_limit if self.tomtom_limit > 0 else self.mapbox_limit
|
nav_mapbox_limit = 0.0
|
||||||
|
if getattr(sm, "alive", {}).get("iqNavState", False) and getattr(sm, "valid", {}).get("iqNavState", False):
|
||||||
|
nav_state = sm["iqNavState"]
|
||||||
|
if getattr(nav_state, "mapboxSpeedLimitValid", False):
|
||||||
|
candidate = float(getattr(nav_state, "mapboxSpeedLimit", 0.0))
|
||||||
|
if math.isfinite(candidate) and candidate >= LIMIT_MIN_SPEED:
|
||||||
|
nav_mapbox_limit = candidate
|
||||||
|
mapbox_limit = nav_mapbox_limit if nav_mapbox_limit > 0 else self.mapbox_limit
|
||||||
|
online_limit = self.tomtom_limit if self.tomtom_limit > 0 else mapbox_limit
|
||||||
|
|
||||||
dashboard_limit = float(dashboard_speed_limit) if dashboard_speed_limit else 0.0
|
dashboard_limit = float(dashboard_speed_limit) if dashboard_speed_limit else 0.0
|
||||||
resolved_limit, resolved_source = self._resolver.resolve(dashboard_limit, online_limit, slc_params)
|
resolved_limit, resolved_source = self._resolver.resolve(dashboard_limit, online_limit, slc_params)
|
||||||
|
|||||||
@@ -663,3 +663,51 @@ def test_construction_zone_fires_event_once_per_zone_entry():
|
|||||||
assert event not in controller.pending_events
|
assert event not in controller.pending_events
|
||||||
controller.update_limits(0.0, None, True, 33.0, 30.0, _construction_sm(), slc_params)
|
controller.update_limits(0.0, None, True, 33.0, 30.0, _construction_sm(), slc_params)
|
||||||
assert event in controller.pending_events
|
assert event in controller.pending_events
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("alive,valid,limit_valid,limit", [
|
||||||
|
(False, True, True, 25.0), (True, False, True, 25.0), (True, True, False, 25.0),
|
||||||
|
(True, True, True, 0.0), (True, True, True, float("nan")), (True, True, True, float("inf")),
|
||||||
|
])
|
||||||
|
def test_navigation_mapbox_limit_requires_fresh_valid_data(alive, valid, limit_valid, limit):
|
||||||
|
controller = _construction_controller()
|
||||||
|
controller.get_tomtom_speed_limit = lambda *_args: None
|
||||||
|
controller.mapbox_limit = 20.0
|
||||||
|
sm = _FakeSM(_build_sm())
|
||||||
|
sm["iqNavState"] = custom.IQNavState.new_message(mapboxSpeedLimit=limit, mapboxSpeedLimitValid=limit_valid)
|
||||||
|
sm.alive["iqNavState"] = alive
|
||||||
|
sm.valid = {"iqNavState": valid}
|
||||||
|
controller.update_limits(0, datetime.now(), True, 30, 20, sm, _base_slc_params_controller())
|
||||||
|
assert controller.target == pytest.approx(20.0)
|
||||||
|
assert controller.source == "Mapbox"
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("policy,expected", [(0, 0.0), (1, 25.0), (2, 25.0)])
|
||||||
|
@pytest.mark.parametrize("online_filler", [False, True])
|
||||||
|
def test_navigation_mapbox_only_limit_obeys_slc_policy(policy, expected, online_filler):
|
||||||
|
controller = _construction_controller()
|
||||||
|
controller.get_tomtom_speed_limit = lambda *_args: None
|
||||||
|
sm = _FakeSM(_build_sm())
|
||||||
|
sm["iqNavState"] = custom.IQNavState.new_message(mapboxSpeedLimit=25.0, mapboxSpeedLimitValid=True)
|
||||||
|
sm.alive["iqNavState"] = True
|
||||||
|
sm.valid = {"iqNavState": True}
|
||||||
|
params = _base_slc_params_controller() | {"slc_policy": policy, "slc_online_filler": online_filler}
|
||||||
|
controller.update_limits(0, datetime.now(), True, 30, 20, sm, params)
|
||||||
|
assert controller.target == pytest.approx(expected)
|
||||||
|
|
||||||
|
|
||||||
|
def test_navigation_mapbox_limit_requires_confirmation_before_override(set_speed_slc):
|
||||||
|
system = set_speed_slc
|
||||||
|
system.params["speed_limit_confirmation_higher"] = True
|
||||||
|
system.slc.slc._resolver.map_speed_limit = 0
|
||||||
|
system.sm["iqNavState"] = custom.IQNavState.new_message(mapboxSpeedLimit=60 * system.unit, mapboxSpeedLimitValid=True)
|
||||||
|
system.sm.alive["iqNavState"] = True
|
||||||
|
system.sm.valid = {"iqNavState": True}
|
||||||
|
system.step(50, new_gesture=True)
|
||||||
|
assert system.slc.assist_state == custom.IQPlan.SpeedLimit.AssistState.preActive
|
||||||
|
assert system.step(70, increase=True) == pytest.approx(50)
|
||||||
|
system.sm["carState"].buttonEvents = [car.CarState.ButtonEvent(type="accelCruise", pressed=False)]
|
||||||
|
assert system.step(71, increase=True) == pytest.approx(60)
|
||||||
|
system.sm["carState"].buttonEvents = []
|
||||||
|
assert system.step(72, increase=True) == pytest.approx(60)
|
||||||
|
assert system.step(73, increase=True, new_gesture=True) == pytest.approx(73)
|
||||||
|
|||||||
@@ -57,6 +57,11 @@ def egpu_pkl_path(meta: dict) -> str:
|
|||||||
return os.path.join(Paths.model_root(), f"egpu_{meta['key']}_{meta['sha256'][:8]}_amd_tinygrad.pkl")
|
return os.path.join(Paths.model_root(), f"egpu_{meta['key']}_{meta['sha256'][:8]}_amd_tinygrad.pkl")
|
||||||
|
|
||||||
|
|
||||||
|
def egpu_policy_pkl_path(meta: dict) -> str:
|
||||||
|
from iqpilot.system.hardware.hw import Paths
|
||||||
|
return os.path.join(Paths.model_root(), f"egpu_{meta['key']}_{meta['sha256'][:8]}_amd_policy.pkl")
|
||||||
|
|
||||||
|
|
||||||
def onnx_cache_path(meta: dict) -> str:
|
def onnx_cache_path(meta: dict) -> str:
|
||||||
from iqpilot.system.hardware.hw import Paths
|
from iqpilot.system.hardware.hw import Paths
|
||||||
return os.path.join(Paths.model_root(), f"{meta['model_name']}_{meta['sha256'][:8]}.onnx")
|
return os.path.join(Paths.model_root(), f"{meta['model_name']}_{meta['sha256'][:8]}.onnx")
|
||||||
@@ -137,6 +142,15 @@ def download_onnx(meta: dict, progress_cb=None) -> str:
|
|||||||
return path
|
return path
|
||||||
|
|
||||||
|
|
||||||
|
def download_precompiled(meta: dict, progress_cb=None, policy: bool = False) -> str | None:
|
||||||
|
art = meta.get("egpu_policy_artifact" if policy else "egpu_artifact")
|
||||||
|
if not art or not art.get("objects"):
|
||||||
|
return None
|
||||||
|
from iqpilot.selfdrive.iqmodeld.model_bundle_downloader import download_lfs_bundle
|
||||||
|
dest = egpu_policy_pkl_path(meta) if policy else egpu_pkl_path(meta)
|
||||||
|
return download_lfs_bundle(art["objects"], dest, art["sha256"], int(art.get("size", 0)), progress_cb=progress_cb)
|
||||||
|
|
||||||
|
|
||||||
def patch_tinygrad_fetch_fw() -> None:
|
def patch_tinygrad_fetch_fw() -> None:
|
||||||
import pathlib
|
import pathlib
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ from __future__ import annotations
|
|||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
|
from iqpilot.selfdrive.iqmodeld.egpu_policy import PolicyRunner
|
||||||
from iqpilot.selfdrive.iqmodeld.temporal_state import MODEL_INPUT_SPEC, TemporalInputState, spec_from_meta
|
from iqpilot.selfdrive.iqmodeld.temporal_state import MODEL_INPUT_SPEC, TemporalInputState, spec_from_meta
|
||||||
|
|
||||||
|
|
||||||
@@ -25,13 +26,17 @@ class EgpuPipeline:
|
|||||||
|
|
||||||
def run(self, warped: np.ndarray, desire_vec: np.ndarray, traffic_convention: np.ndarray,
|
def run(self, warped: np.ndarray, desire_vec: np.ndarray, traffic_convention: np.ndarray,
|
||||||
action_t: np.ndarray) -> np.ndarray:
|
action_t: np.ndarray) -> np.ndarray:
|
||||||
inputs = self.state.push_and_materialize(warped, desire_vec, traffic_convention, action_t)
|
if isinstance(self.infer_fn, PolicyRunner):
|
||||||
out = np.asarray(self.infer_fn(inputs), dtype=np.float32).reshape(-1)
|
out = np.asarray(self.infer_fn.run(warped, desire_vec, traffic_convention, action_t), dtype=np.float32).reshape(-1)
|
||||||
|
else:
|
||||||
|
inputs = self.state.push_and_materialize(warped, desire_vec, traffic_convention, action_t)
|
||||||
|
out = np.asarray(self.infer_fn(inputs), dtype=np.float32).reshape(-1)
|
||||||
if out.shape[0] != self.output_len:
|
if out.shape[0] != self.output_len:
|
||||||
raise EgpuPipelineError(f"eGPU output length {out.shape[0]} != {self.output_len}")
|
raise EgpuPipelineError(f"eGPU output length {out.shape[0]} != {self.output_len}")
|
||||||
if not np.isfinite(out).all():
|
if not np.isfinite(out).all():
|
||||||
raise EgpuPipelineError("eGPU output contains non-finite values")
|
raise EgpuPipelineError("eGPU output contains non-finite values")
|
||||||
self.state.note_hidden_state(out, self.hidden_slice)
|
if not isinstance(self.infer_fn, PolicyRunner):
|
||||||
|
self.state.note_hidden_state(out, self.hidden_slice)
|
||||||
return out
|
return out
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
117
iqpilot/selfdrive/iqmodeld/egpu_policy.py
Normal file
117
iqpilot/selfdrive/iqmodeld/egpu_policy.py
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
"""
|
||||||
|
Copyright © IQ.Lvbs, apart of Project Teal Lvbs, All Rights Reserved, licensed under https://konn3kt.com/tos/
|
||||||
|
"""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import math
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
POLICY_FORMAT = 2
|
||||||
|
QUEUE_NAMES = ("img_q", "big_img_q", "feat_q", "desire_q")
|
||||||
|
PACKED_ORDER = ("desire", "traffic_convention", "action_t", "prev_feat")
|
||||||
|
|
||||||
|
|
||||||
|
def packed_layout(input_spec: dict) -> tuple[dict[str, tuple[int, ...]], list[int]]:
|
||||||
|
dp = input_spec["desire_pulse"][0]
|
||||||
|
fb = input_spec["features_buffer"][0]
|
||||||
|
shapes = {
|
||||||
|
"desire": (dp[2],),
|
||||||
|
"traffic_convention": tuple(input_spec["traffic_convention"][0]),
|
||||||
|
"action_t": tuple(input_spec["action_t"][0]),
|
||||||
|
"prev_feat": (fb[0], math.prod(fb[2:])),
|
||||||
|
}
|
||||||
|
return shapes, [math.prod(s) for s in shapes.values()]
|
||||||
|
|
||||||
|
|
||||||
|
def queue_shapes(input_spec: dict, frame_skip: int) -> dict[str, tuple[tuple[int, ...], str]]:
|
||||||
|
img = input_spec["img"][0]
|
||||||
|
fb = input_spec["features_buffer"][0]
|
||||||
|
dp = input_spec["desire_pulse"][0]
|
||||||
|
n_frames = img[1] // 6
|
||||||
|
img_buf = (frame_skip * (n_frames - 1) + 1, 6, img[2], img[3])
|
||||||
|
return {
|
||||||
|
"img_q": (img_buf, "uint8"),
|
||||||
|
"big_img_q": (img_buf, "uint8"),
|
||||||
|
"feat_q": ((frame_skip * fb[1], fb[0], math.prod(fb[2:])), "float32"),
|
||||||
|
"desire_q": ((frame_skip * dp[1], dp[0], dp[2]), "float32"),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def make_queues(input_spec: dict, frame_skip: int, device: str) -> dict:
|
||||||
|
from tinygrad.tensor import Tensor
|
||||||
|
return {name: Tensor(np.zeros(shape, dtype=dtype), device=device).contiguous().realize()
|
||||||
|
for name, (shape, dtype) in queue_shapes(input_spec, frame_skip).items()}
|
||||||
|
|
||||||
|
|
||||||
|
class PackedInputs:
|
||||||
|
def __init__(self, input_spec: dict):
|
||||||
|
from tinygrad.tensor import Tensor
|
||||||
|
self.shapes, self.sizes = packed_layout(input_spec)
|
||||||
|
self.array = np.zeros(sum(self.sizes), dtype=np.float32)
|
||||||
|
self.views = dict(zip(self.shapes, [v.reshape(s) for s, v in zip(self.shapes.values(), np.split(self.array, np.cumsum(self.sizes[:-1])))], strict=True))
|
||||||
|
self.tensor = Tensor(self.array, device="NPY").realize()
|
||||||
|
|
||||||
|
|
||||||
|
def make_run_policy(model_runner, input_spec: dict, frame_skip: int, device: str):
|
||||||
|
from tinygrad.tensor import Tensor
|
||||||
|
shapes, sizes = packed_layout(input_spec)
|
||||||
|
fb = input_spec["features_buffer"][0]
|
||||||
|
|
||||||
|
def shift_and_sample(buf, new_val, sample_fn):
|
||||||
|
buf.assign(buf[1:].cat(new_val, dim=0).contiguous())
|
||||||
|
return sample_fn(buf)
|
||||||
|
|
||||||
|
def sample_skip(buf):
|
||||||
|
return buf[::frame_skip].contiguous().flatten(0, 1).unsqueeze(0)
|
||||||
|
|
||||||
|
def sample_desire(buf):
|
||||||
|
return buf.reshape(-1, frame_skip, *buf.shape[1:]).max(1).flatten(0, 1).unsqueeze(0)
|
||||||
|
|
||||||
|
def run_policy(warped, img_q, big_img_q, feat_q, desire_q, packed_npy_inputs):
|
||||||
|
packed_npy_inputs = packed_npy_inputs.to(device)
|
||||||
|
warped = warped.to(device)
|
||||||
|
Tensor.realize(packed_npy_inputs, warped)
|
||||||
|
img = shift_and_sample(img_q, warped[0:1], sample_skip)
|
||||||
|
big_img = shift_and_sample(big_img_q, warped[1:2], sample_skip)
|
||||||
|
desire, traffic_convention, action_t, prev_feat = (t.reshape(s) for t, s in zip(packed_npy_inputs.split(sizes), shapes.values(), strict=True))
|
||||||
|
desire_buf = shift_and_sample(desire_q, desire.reshape(1, 1, -1), sample_desire)
|
||||||
|
feat_buf = shift_and_sample(feat_q, prev_feat.reshape(1, 1, -1), sample_skip)
|
||||||
|
inputs = {
|
||||||
|
"img": img,
|
||||||
|
"big_img": big_img,
|
||||||
|
"features_buffer": feat_buf.reshape(fb),
|
||||||
|
"desire_pulse": desire_buf,
|
||||||
|
"traffic_convention": traffic_convention,
|
||||||
|
"action_t": action_t,
|
||||||
|
}
|
||||||
|
out = next(iter(model_runner(inputs).values())).cast("float32")
|
||||||
|
return out.reshape(-1),
|
||||||
|
|
||||||
|
return run_policy
|
||||||
|
|
||||||
|
|
||||||
|
class PolicyRunner:
|
||||||
|
def __init__(self, jit, input_spec: dict, frame_skip: int, hidden_slice: slice, device: str):
|
||||||
|
from tinygrad.tensor import Tensor
|
||||||
|
self._Tensor = Tensor
|
||||||
|
self._jit = jit
|
||||||
|
self._queues = make_queues(input_spec, frame_skip, device)
|
||||||
|
self._packed = PackedInputs(input_spec)
|
||||||
|
self._hidden = hidden_slice
|
||||||
|
self._prev_desire = np.zeros(input_spec["desire_pulse"][0][2], dtype=np.float32)
|
||||||
|
self._warped_shape = (2, 6, *input_spec["img"][0][2:])
|
||||||
|
|
||||||
|
def run(self, warped: np.ndarray, desire_pulse: np.ndarray, traffic_convention: np.ndarray,
|
||||||
|
action_t: np.ndarray) -> np.ndarray:
|
||||||
|
cur = desire_pulse.astype(np.float32, copy=False)
|
||||||
|
v = self._packed.views
|
||||||
|
v["desire"][:] = np.where(cur - self._prev_desire > 0.99, cur, 0)
|
||||||
|
self._prev_desire[:] = cur
|
||||||
|
v["traffic_convention"][:] = np.asarray(traffic_convention, dtype=np.float32).reshape(v["traffic_convention"].shape)
|
||||||
|
v["action_t"][:] = np.asarray(action_t, dtype=np.float32).reshape(v["action_t"].shape)
|
||||||
|
warped_t = self._Tensor(np.ascontiguousarray(warped, dtype=np.uint8).reshape(self._warped_shape), device="NPY").realize()
|
||||||
|
out, = self._jit(warped=warped_t, packed_npy_inputs=self._packed.tensor, **self._queues)
|
||||||
|
flat = out.numpy().reshape(-1)
|
||||||
|
v["prev_feat"][:] = flat[self._hidden].reshape(v["prev_feat"].shape)
|
||||||
|
return flat
|
||||||
@@ -38,8 +38,8 @@ from iqpilot.selfdrive.iqmodeld.driving_action import (
|
|||||||
DESIRE_LEN, LAT_SMOOTH_SECONDS, LONG_SMOOTH_SECONDS, get_action_from_model,
|
DESIRE_LEN, LAT_SMOOTH_SECONDS, LONG_SMOOTH_SECONDS, get_action_from_model,
|
||||||
)
|
)
|
||||||
from iqpilot.selfdrive.iqmodeld.egpu_helpers import (
|
from iqpilot.selfdrive.iqmodeld.egpu_helpers import (
|
||||||
download_onnx, egpu_pkl_path, egpu_present_consented, egpu_selected, local_onnx, patch_tinygrad_fetch_fw,
|
download_onnx, download_precompiled, egpu_pkl_path, egpu_policy_pkl_path, egpu_present_consented, egpu_selected, local_onnx,
|
||||||
quarantine_artifact, resolve_backend, usbgpu_present,
|
patch_tinygrad_fetch_fw, quarantine_artifact, resolve_backend, usbgpu_present,
|
||||||
)
|
)
|
||||||
from iqpilot.selfdrive.iqmodeld.egpu_model import resolve_egpu_model
|
from iqpilot.selfdrive.iqmodeld.egpu_model import resolve_egpu_model
|
||||||
from iqpilot.selfdrive.iqmodeld.egpu_pipeline import EgpuPipeline, EgpuPipelineError, make_big_channel_payload
|
from iqpilot.selfdrive.iqmodeld.egpu_pipeline import EgpuPipeline, EgpuPipelineError, make_big_channel_payload
|
||||||
@@ -47,6 +47,7 @@ from iqpilot.selfdrive.iqmodeld.egpu_telemetry import EgpuDockTelemetry
|
|||||||
from iqpilot.selfdrive.iqmodeld.messaging import DrivePacketMemory, populate_drive_messages, populate_odometry_message
|
from iqpilot.selfdrive.iqmodeld.messaging import DrivePacketMemory, populate_drive_messages, populate_odometry_message
|
||||||
from iqpilot.selfdrive.iqmodeld.metadata import Meta20hz
|
from iqpilot.selfdrive.iqmodeld.metadata import Meta20hz
|
||||||
from iqpilot.selfdrive.iqmodeld.model_channel import BIG_CHANNEL, ModelChannel
|
from iqpilot.selfdrive.iqmodeld.model_channel import BIG_CHANNEL, ModelChannel
|
||||||
|
from iqpilot.selfdrive.iqmodeld.egpu_policy import POLICY_FORMAT, PolicyRunner
|
||||||
from iqpilot.selfdrive.iqmodeld.model_warp import FrameWarp
|
from iqpilot.selfdrive.iqmodeld.model_warp import FrameWarp
|
||||||
from iqpilot.selfdrive.iqmodeld.parser import PhaseParser
|
from iqpilot.selfdrive.iqmodeld.parser import PhaseParser
|
||||||
|
|
||||||
@@ -89,9 +90,10 @@ def _wait_for_egpu(params: Params) -> None:
|
|||||||
|
|
||||||
def _compile_in_subprocess(meta: dict, onnx_path: str, pkl_path: str) -> None:
|
def _compile_in_subprocess(meta: dict, onnx_path: str, pkl_path: str) -> None:
|
||||||
cmd = [sys.executable, "-m", "iqpilot.selfdrive.iqmodeld.tools.compile_egpu_model",
|
cmd = [sys.executable, "-m", "iqpilot.selfdrive.iqmodeld.tools.compile_egpu_model",
|
||||||
"--model", meta["key"], "--onnx", onnx_path, "--output", pkl_path]
|
"--model", meta["key"], "--onnx", onnx_path, "--output", pkl_path,
|
||||||
|
"--progress-param", "UsbGpuSetupProgress", "--progress-base", "0.5", "--progress-span", "0.48"]
|
||||||
compile_env = {**os.environ, "DEV": "USB+AMD:LLVM", "FLOAT16": "1",
|
compile_env = {**os.environ, "DEV": "USB+AMD:LLVM", "FLOAT16": "1",
|
||||||
"JIT_BATCH_SIZE": "0", "GMMU": "0"}
|
"JIT_BATCH_SIZE": "0", "GMMU": "0", "TC_OPT": "2"}
|
||||||
proc = subprocess.run(cmd, timeout=COMPILE_TIMEOUT_S, capture_output=True, text=True,
|
proc = subprocess.run(cmd, timeout=COMPILE_TIMEOUT_S, capture_output=True, text=True,
|
||||||
env=compile_env, preexec_fn=lambda: os.nice(20))
|
env=compile_env, preexec_fn=lambda: os.nice(20))
|
||||||
if proc.returncode != 0:
|
if proc.returncode != 0:
|
||||||
@@ -99,12 +101,43 @@ def _compile_in_subprocess(meta: dict, onnx_path: str, pkl_path: str) -> None:
|
|||||||
raise RuntimeError(f"eGPU model compile failed (rc={proc.returncode}): {tail}")
|
raise RuntimeError(f"eGPU model compile failed (rc={proc.returncode}): {tail}")
|
||||||
|
|
||||||
|
|
||||||
|
_precompiled_tried = False
|
||||||
|
|
||||||
|
|
||||||
def _ensure_artifact(params: Params, meta: dict) -> str:
|
def _ensure_artifact(params: Params, meta: dict) -> str:
|
||||||
pkl_path = egpu_pkl_path(meta)
|
global _precompiled_tried
|
||||||
if os.path.isfile(pkl_path):
|
policy_path = egpu_policy_pkl_path(meta)
|
||||||
return pkl_path
|
if os.path.isfile(policy_path):
|
||||||
|
return policy_path
|
||||||
|
legacy_path = egpu_pkl_path(meta)
|
||||||
|
|
||||||
params.put_bool("UsbGpuCompiled", False)
|
params.put_bool("UsbGpuCompiled", False)
|
||||||
|
params.put_bool("UsbGpuReady", False)
|
||||||
|
|
||||||
|
if meta.get("egpu_policy_artifact") and not _precompiled_tried:
|
||||||
|
_precompiled_tried = True
|
||||||
|
params.put("UsbGpuSetupProgress", "0.0")
|
||||||
|
dl_last = [-1.0]
|
||||||
|
|
||||||
|
def _dl_prog(p: float) -> None:
|
||||||
|
if p - dl_last[0] >= 0.02 or p >= 1.0:
|
||||||
|
dl_last[0] = p
|
||||||
|
params.put("UsbGpuSetupProgress", f"{p:.3f}")
|
||||||
|
|
||||||
|
try:
|
||||||
|
cloudlog.warning(f"iqegpumodeld downloading precompiled {meta['key']} policy "
|
||||||
|
f"({int(meta['egpu_policy_artifact'].get('size', 0)) / 1e6:.0f}MB)")
|
||||||
|
precompiled = download_precompiled(meta, progress_cb=_dl_prog, policy=True)
|
||||||
|
if precompiled is not None:
|
||||||
|
cloudlog.warning(f"iqegpumodeld precompiled ready -> {precompiled}")
|
||||||
|
return precompiled
|
||||||
|
except Exception as e:
|
||||||
|
cloudlog.warning(f"iqegpumodeld precompiled policy unavailable ({e}); falling back")
|
||||||
|
|
||||||
|
if os.path.isfile(legacy_path):
|
||||||
|
cloudlog.warning(f"iqegpumodeld using legacy per-tensor artifact {legacy_path}; policy artifact not hosted yet")
|
||||||
|
return legacy_path
|
||||||
|
|
||||||
onnx_path = local_onnx(meta)
|
onnx_path = local_onnx(meta)
|
||||||
if onnx_path is None:
|
if onnx_path is None:
|
||||||
params.put("UsbGpuSetupProgress", "0.0")
|
params.put("UsbGpuSetupProgress", "0.0")
|
||||||
@@ -114,14 +147,14 @@ def _ensure_artifact(params: Params, meta: dict) -> str:
|
|||||||
def _prog(p: float) -> None:
|
def _prog(p: float) -> None:
|
||||||
if p - last[0] >= 0.02 or p >= 1.0:
|
if p - last[0] >= 0.02 or p >= 1.0:
|
||||||
last[0] = p
|
last[0] = p
|
||||||
params.put("UsbGpuSetupProgress", f"{p:.3f}")
|
params.put("UsbGpuSetupProgress", f"{p * 0.5:.3f}")
|
||||||
|
|
||||||
onnx_path = download_onnx(meta, progress_cb=_prog)
|
onnx_path = download_onnx(meta, progress_cb=_prog)
|
||||||
|
|
||||||
cloudlog.warning(f"iqegpumodeld compiling {meta['key']} for USB-AMD (one-time, can take minutes)")
|
cloudlog.warning(f"iqegpumodeld compiling {meta['key']} for USB-AMD (one-time, can take minutes)")
|
||||||
_compile_in_subprocess(meta, onnx_path, pkl_path)
|
_compile_in_subprocess(meta, onnx_path, policy_path)
|
||||||
cloudlog.warning(f"iqegpumodeld compiled -> {pkl_path}")
|
cloudlog.warning(f"iqegpumodeld compiled -> {policy_path}")
|
||||||
return pkl_path
|
return policy_path
|
||||||
|
|
||||||
|
|
||||||
def _load_infer_fn(pkl_path: str, meta: dict):
|
def _load_infer_fn(pkl_path: str, meta: dict):
|
||||||
@@ -136,6 +169,10 @@ def _load_infer_fn(pkl_path: str, meta: dict):
|
|||||||
if int(bundle.get("output_len", -1)) != int(meta["output_len"]):
|
if int(bundle.get("output_len", -1)) != int(meta["output_len"]):
|
||||||
quarantine_artifact(pkl_path, "pkl output_len mismatch")
|
quarantine_artifact(pkl_path, "pkl output_len mismatch")
|
||||||
raise RuntimeError(f"artifact output_len {bundle.get('output_len')} != {meta['output_len']}")
|
raise RuntimeError(f"artifact output_len {bundle.get('output_len')} != {meta['output_len']}")
|
||||||
|
if bundle.get("format") == POLICY_FORMAT:
|
||||||
|
runner = PolicyRunner(bundle["run_policy"], bundle["input_spec"], int(bundle["frame_skip"]),
|
||||||
|
meta["output_slices"]["hidden_state"], bundle.get("input_device", "AMD"))
|
||||||
|
return runner, bundle["input_spec"]
|
||||||
jit = bundle["run_model"]
|
jit = bundle["run_model"]
|
||||||
input_dev = bundle.get("input_device", "AMD")
|
input_dev = bundle.get("input_device", "AMD")
|
||||||
input_spec = bundle["input_spec"]
|
input_spec = bundle["input_spec"]
|
||||||
@@ -152,7 +189,12 @@ def _load_infer_fn(pkl_path: str, meta: dict):
|
|||||||
def _warmup(infer_fn, input_spec: dict, output_len: int) -> float:
|
def _warmup(infer_fn, input_spec: dict, output_len: int) -> float:
|
||||||
zeros = {name: np.zeros(shape, dtype=dtype) for name, (shape, dtype) in input_spec.items()}
|
zeros = {name: np.zeros(shape, dtype=dtype) for name, (shape, dtype) in input_spec.items()}
|
||||||
t0 = time.perf_counter()
|
t0 = time.perf_counter()
|
||||||
out = infer_fn(zeros)
|
if isinstance(infer_fn, PolicyRunner):
|
||||||
|
img = input_spec["img"][0]
|
||||||
|
out = infer_fn.run(np.zeros((2, 6, img[2], img[3]), dtype=np.uint8), np.zeros(input_spec["desire_pulse"][0][2], dtype=np.float32),
|
||||||
|
np.zeros(2, dtype=np.float32), np.zeros(2, dtype=np.float32))
|
||||||
|
else:
|
||||||
|
out = infer_fn(zeros)
|
||||||
dt = time.perf_counter() - t0
|
dt = time.perf_counter() - t0
|
||||||
if out.shape[0] != output_len or not np.isfinite(out).all():
|
if out.shape[0] != output_len or not np.isfinite(out).all():
|
||||||
raise RuntimeError(f"warmup produced invalid output (len={out.shape[0]})")
|
raise RuntimeError(f"warmup produced invalid output (len={out.shape[0]})")
|
||||||
@@ -207,6 +249,7 @@ def main(demo: bool = False) -> None:
|
|||||||
|
|
||||||
params.put_bool("UsbGpuLoading", False)
|
params.put_bool("UsbGpuLoading", False)
|
||||||
params.put_bool("UsbGpuCompiled", True)
|
params.put_bool("UsbGpuCompiled", True)
|
||||||
|
params.put_bool("UsbGpuReady", True)
|
||||||
params.put("UsbGpuSetupProgress", "1.0")
|
params.put("UsbGpuSetupProgress", "1.0")
|
||||||
cloudlog.warning(f"iqegpumodeld model: {meta['key']} ({meta['model_name']})")
|
cloudlog.warning(f"iqegpumodeld model: {meta['key']} ({meta['model_name']})")
|
||||||
cloudlog.warning(f"iqegpumodeld model up (warmup {warm_s * 1e3:.0f}ms)")
|
cloudlog.warning(f"iqegpumodeld model up (warmup {warm_s * 1e3:.0f}ms)")
|
||||||
|
|||||||
87
iqpilot/selfdrive/iqmodeld/model_bundle_downloader.py
Normal file
87
iqpilot/selfdrive/iqmodeld/model_bundle_downloader.py
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
"""
|
||||||
|
Copyright © IQ.Lvbs, apart of Project Teal Lvbs, All Rights Reserved, licensed under https://konn3kt.com/tos/
|
||||||
|
"""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import hashlib
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
|
||||||
|
MODELS_BASE_URLS = (
|
||||||
|
"https://git.konn3kt.com/teal/IQModels/raw/branch/main",
|
||||||
|
"https://gitlvb.teallvbs.xyz/teal/IQModels/raw/branch/main",
|
||||||
|
)
|
||||||
|
CHUNK = 4 * 1024 * 1024
|
||||||
|
HTTP_TIMEOUT_S = 60.0
|
||||||
|
STREAM_RETRIES = 6
|
||||||
|
|
||||||
|
|
||||||
|
def _requests_auth():
|
||||||
|
import importlib
|
||||||
|
for mod in ("iqpilot_private.models.git_auth", "iqpilot.models_private_src.git_auth",
|
||||||
|
"iqpilot.selfdrive.iqmodeld.models.git_auth"):
|
||||||
|
try:
|
||||||
|
return importlib.import_module(mod).get_requests_auth()
|
||||||
|
except Exception:
|
||||||
|
continue
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def _lfs_endpoint(base_url: str) -> str:
|
||||||
|
return base_url.split("/raw/", 1)[0] + ".git/info/lfs"
|
||||||
|
|
||||||
|
|
||||||
|
def _resolve_oid(session, base_url: str, oid: str, size: int, auth):
|
||||||
|
import requests
|
||||||
|
batch = session.post(f"{_lfs_endpoint(base_url)}/objects/batch",
|
||||||
|
data=json.dumps({"operation": "download", "transfers": ["basic"],
|
||||||
|
"objects": [{"oid": oid, "size": size}]}),
|
||||||
|
headers={"Content-Type": "application/vnd.git-lfs+json",
|
||||||
|
"Accept": "application/vnd.git-lfs+json"},
|
||||||
|
auth=auth, timeout=HTTP_TIMEOUT_S)
|
||||||
|
batch.raise_for_status()
|
||||||
|
entry = batch.json()["objects"][0]
|
||||||
|
if "actions" not in entry:
|
||||||
|
raise requests.RequestException(f"LFS object unavailable: {entry.get('error', oid)}")
|
||||||
|
action = entry["actions"]["download"]
|
||||||
|
return action["href"], action.get("header", {})
|
||||||
|
|
||||||
|
|
||||||
|
def download_lfs_bundle(objects: list, dst: str, sha256: str, size: int, progress_cb=None) -> str:
|
||||||
|
import requests
|
||||||
|
auth = _requests_auth()
|
||||||
|
session = requests.Session()
|
||||||
|
os.makedirs(os.path.dirname(dst), exist_ok=True)
|
||||||
|
tmp = dst + ".part"
|
||||||
|
total = int(size) or sum(int(o["size"]) for o in objects)
|
||||||
|
last_error: Exception | None = None
|
||||||
|
for base_url in MODELS_BASE_URLS:
|
||||||
|
for attempt in range(STREAM_RETRIES):
|
||||||
|
try:
|
||||||
|
digest = hashlib.sha256()
|
||||||
|
got = 0
|
||||||
|
with open(tmp, "wb") as f:
|
||||||
|
for obj in objects:
|
||||||
|
href, headers = _resolve_oid(session, base_url, obj["oid"], int(obj["size"]), auth)
|
||||||
|
obj_auth = None if headers.get("Authorization") else auth
|
||||||
|
with session.get(href, headers=headers, stream=True, timeout=120, auth=obj_auth) as r:
|
||||||
|
r.raise_for_status()
|
||||||
|
for chunk in r.iter_content(CHUNK):
|
||||||
|
f.write(chunk)
|
||||||
|
digest.update(chunk)
|
||||||
|
got += len(chunk)
|
||||||
|
if progress_cb is not None and total:
|
||||||
|
progress_cb(min(1.0, got / total))
|
||||||
|
if total and got != total:
|
||||||
|
raise RuntimeError(f"size mismatch: {got}/{total} bytes")
|
||||||
|
if sha256 and digest.hexdigest() != sha256:
|
||||||
|
raise RuntimeError("sha256 mismatch")
|
||||||
|
os.replace(tmp, dst)
|
||||||
|
return dst
|
||||||
|
except Exception as e:
|
||||||
|
last_error = e
|
||||||
|
try:
|
||||||
|
os.remove(tmp)
|
||||||
|
except OSError:
|
||||||
|
pass
|
||||||
|
raise RuntimeError(f"model bundle download failed: {last_error}")
|
||||||
82
iqpilot/selfdrive/iqmodeld/tests/test_egpu_policy.py
Normal file
82
iqpilot/selfdrive/iqmodeld/tests/test_egpu_policy.py
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
"""
|
||||||
|
Copyright © IQ.Lvbs, apart of Project Teal Lvbs, All Rights Reserved, licensed under https://konn3kt.com/tos/
|
||||||
|
"""
|
||||||
|
import os
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
os.environ["DEV"] = "CPU"
|
||||||
|
|
||||||
|
from iqpilot.selfdrive.iqmodeld.egpu_policy import PolicyRunner, make_run_policy, packed_layout, queue_shapes
|
||||||
|
from iqpilot.selfdrive.iqmodeld.temporal_state import TemporalInputState
|
||||||
|
|
||||||
|
SPEC = {
|
||||||
|
"img": ((1, 12, 8, 16), "uint8"),
|
||||||
|
"big_img": ((1, 12, 8, 16), "uint8"),
|
||||||
|
"desire_pulse": ((1, 25, 8), "float32"),
|
||||||
|
"traffic_convention": ((1, 2), "float32"),
|
||||||
|
"action_t": ((1, 2), "float32"),
|
||||||
|
"features_buffer": ((1, 24, 512), "float32"),
|
||||||
|
}
|
||||||
|
FS = 4
|
||||||
|
OUT_LEN = 2580
|
||||||
|
HIDDEN = slice(1064, 1576)
|
||||||
|
|
||||||
|
|
||||||
|
def _pack(inputs):
|
||||||
|
from tinygrad.tensor import Tensor
|
||||||
|
parts = [inputs[k].cast("float32").reshape(-1) for k in ("img", "big_img", "features_buffer", "desire_pulse", "traffic_convention", "action_t")]
|
||||||
|
flat = Tensor.cat(*parts)
|
||||||
|
hidden = (flat[:512] * 0.001).reshape(1, 512)
|
||||||
|
return flat, hidden
|
||||||
|
|
||||||
|
|
||||||
|
def _fake_model(inputs):
|
||||||
|
from tinygrad.tensor import Tensor
|
||||||
|
flat, hidden = _pack(inputs)
|
||||||
|
n = flat.shape[0]
|
||||||
|
head = flat[:min(n, HIDDEN.start)]
|
||||||
|
out = Tensor.cat(head.pad((0, HIDDEN.start - head.shape[0])), hidden.reshape(-1), Tensor.zeros(OUT_LEN - HIDDEN.stop, device="CPU"))
|
||||||
|
return {"outputs": out.reshape(1, -1)}
|
||||||
|
|
||||||
|
|
||||||
|
class _Reference:
|
||||||
|
def __init__(self):
|
||||||
|
self.state = TemporalInputState(FS, SPEC)
|
||||||
|
|
||||||
|
def run(self, warped, desire, traffic, action_t):
|
||||||
|
inputs = self.state.push_and_materialize(warped, desire, traffic, action_t)
|
||||||
|
from tinygrad.tensor import Tensor
|
||||||
|
t = {k: Tensor(np.ascontiguousarray(v), device="CPU") for k, v in inputs.items()}
|
||||||
|
out = _fake_model(t)["outputs"].numpy().reshape(-1)
|
||||||
|
self.state.note_hidden_state(out, HIDDEN)
|
||||||
|
return out
|
||||||
|
|
||||||
|
|
||||||
|
def test_policy_queues_match_temporal_state():
|
||||||
|
from tinygrad.engine.jit import TinyJit
|
||||||
|
jit = TinyJit(make_run_policy(_fake_model, SPEC, FS, "CPU"), prune=True)
|
||||||
|
runner = PolicyRunner(jit, SPEC, FS, HIDDEN, "CPU")
|
||||||
|
ref = _Reference()
|
||||||
|
rng = np.random.default_rng(3)
|
||||||
|
desire = np.zeros(8, dtype=np.float32)
|
||||||
|
for i in range(14):
|
||||||
|
warped = rng.integers(0, 256, (2, 6, 8, 16), dtype=np.int64).astype(np.uint8)
|
||||||
|
if i in (2, 3, 9):
|
||||||
|
desire[:] = 0
|
||||||
|
desire[1 + (i % 3)] = 1
|
||||||
|
elif i == 5:
|
||||||
|
desire[:] = 0
|
||||||
|
traffic = np.array([1.0, 0.0], dtype=np.float32) if i % 2 else np.array([0.0, 1.0], dtype=np.float32)
|
||||||
|
action_t = np.array([0.1 * i, 0.2], dtype=np.float32)
|
||||||
|
got = runner.run(warped, desire, traffic, action_t)
|
||||||
|
want = ref.run(warped, desire, traffic, action_t)
|
||||||
|
np.testing.assert_array_equal(got, want, err_msg=f"frame {i}")
|
||||||
|
|
||||||
|
|
||||||
|
def test_layouts():
|
||||||
|
shapes, sizes = packed_layout(SPEC)
|
||||||
|
assert list(shapes) == ["desire", "traffic_convention", "action_t", "prev_feat"]
|
||||||
|
assert sum(sizes) == 8 + 2 + 2 + 512
|
||||||
|
q = queue_shapes(SPEC, FS)
|
||||||
|
assert q["img_q"][0] == (5, 6, 8, 16) and q["feat_q"][0] == (96, 1, 512) and q["desire_q"][0] == (100, 1, 8)
|
||||||
@@ -4,6 +4,7 @@ Copyright © IQ.Lvbs, apart of Project Teal Lvbs, All Rights Reserved, licensed
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
|
import gc
|
||||||
import os
|
import os
|
||||||
import pickle
|
import pickle
|
||||||
import time
|
import time
|
||||||
@@ -12,6 +13,7 @@ os.environ.setdefault("DEV", "USB+AMD:LLVM")
|
|||||||
os.environ.setdefault("FLOAT16", "1")
|
os.environ.setdefault("FLOAT16", "1")
|
||||||
os.environ.setdefault("JIT_BATCH_SIZE", "0")
|
os.environ.setdefault("JIT_BATCH_SIZE", "0")
|
||||||
os.environ.setdefault("GMMU", "0")
|
os.environ.setdefault("GMMU", "0")
|
||||||
|
os.environ.setdefault("TC_OPT", "2")
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
@@ -24,6 +26,22 @@ INPUT_SPEC = dict(MODEL_INPUT_SPEC)
|
|||||||
patch_tinygrad_fetch_fw()
|
patch_tinygrad_fetch_fw()
|
||||||
|
|
||||||
SEED = 42
|
SEED = 42
|
||||||
|
KERNEL_PROGRESS_SCALE = 260.0
|
||||||
|
|
||||||
|
|
||||||
|
def _progress_sampler(param: str, base: float, span: float, stop) -> None:
|
||||||
|
import math
|
||||||
|
|
||||||
|
from iqpilot.common.params import Params
|
||||||
|
from tinygrad.helpers import GlobalCounters
|
||||||
|
pm = Params()
|
||||||
|
last = -1.0
|
||||||
|
while not stop.wait(0.5):
|
||||||
|
kernels = float(getattr(GlobalCounters, "kernel_count", 0))
|
||||||
|
value = base + span * (1.0 - math.exp(-kernels / KERNEL_PROGRESS_SCALE))
|
||||||
|
if value - last >= 0.01:
|
||||||
|
last = value
|
||||||
|
pm.put(param, f"{min(base + span, value):.3f}")
|
||||||
|
|
||||||
|
|
||||||
def set_input_spec(meta: dict) -> None:
|
def set_input_spec(meta: dict) -> None:
|
||||||
@@ -81,8 +99,27 @@ def compile_model(meta: dict, onnx_path: str, out_path: str) -> str:
|
|||||||
if not np.isfinite(baseline).all():
|
if not np.isfinite(baseline).all():
|
||||||
raise RuntimeError("compiled model produced non-finite outputs")
|
raise RuntimeError("compiled model produced non-finite outputs")
|
||||||
|
|
||||||
print("pickle round trip")
|
bundle = {
|
||||||
jit = pickle.loads(pickle.dumps(jit))
|
"run_model": jit,
|
||||||
|
"model_key": meta["key"],
|
||||||
|
"model_sha256": meta["sha256"],
|
||||||
|
"output_len": int(meta["output_len"]),
|
||||||
|
"frame_skip": int(meta["frame_skip"]),
|
||||||
|
"input_spec": {name: (tuple(shape), dtype) for name, (shape, dtype) in INPUT_SPEC.items()},
|
||||||
|
"input_device": Device.DEFAULT,
|
||||||
|
}
|
||||||
|
os.makedirs(os.path.dirname(out_path), exist_ok=True)
|
||||||
|
tmp = out_path + ".part"
|
||||||
|
print("serialize")
|
||||||
|
with open(tmp, "wb") as f:
|
||||||
|
pickle.dump(bundle, f, protocol=pickle.HIGHEST_PROTOCOL)
|
||||||
|
|
||||||
|
del bundle, jit
|
||||||
|
gc.collect()
|
||||||
|
|
||||||
|
print("reload + validate")
|
||||||
|
with open(tmp, "rb") as f:
|
||||||
|
jit = pickle.load(f)["run_model"]
|
||||||
if not np.array_equal(_run(jit, SEED), baseline):
|
if not np.array_equal(_run(jit, SEED), baseline):
|
||||||
raise RuntimeError("outputs differ from baseline after pickle round trip")
|
raise RuntimeError("outputs differ from baseline after pickle round trip")
|
||||||
if np.array_equal(_run(jit, SEED + 1), baseline):
|
if np.array_equal(_run(jit, SEED + 1), baseline):
|
||||||
@@ -96,19 +133,94 @@ def compile_model(meta: dict, onnx_path: str, out_path: str) -> str:
|
|||||||
from iqpilot.selfdrive.iqmodeld.tools.compile_supercombo import _slice_outputs, _validate_pose_outputs
|
from iqpilot.selfdrive.iqmodeld.tools.compile_supercombo import _slice_outputs, _validate_pose_outputs
|
||||||
_validate_pose_outputs(PhaseParser().parse_vision_outputs(_slice_outputs(flat, meta["output_slices"])))
|
_validate_pose_outputs(PhaseParser().parse_vision_outputs(_slice_outputs(flat, meta["output_slices"])))
|
||||||
|
|
||||||
|
os.replace(tmp, out_path)
|
||||||
|
return out_path
|
||||||
|
|
||||||
|
|
||||||
|
def _policy_frame(seed: int, input_spec: dict):
|
||||||
|
from tinygrad.tensor import Tensor
|
||||||
|
rng = np.random.default_rng(seed)
|
||||||
|
img = input_spec["img"][0]
|
||||||
|
warped = Tensor(rng.integers(0, 256, (2, 6, img[2], img[3])).astype(np.uint8), device="NPY").realize()
|
||||||
|
return warped
|
||||||
|
|
||||||
|
|
||||||
|
def compile_policy_model(meta: dict, onnx_path: str, out_path: str) -> str:
|
||||||
|
from tinygrad.device import Device
|
||||||
|
from tinygrad.engine.jit import TinyJit
|
||||||
|
from tinygrad.nn.onnx import OnnxRunner
|
||||||
|
|
||||||
|
from iqpilot.selfdrive.iqmodeld.egpu_policy import POLICY_FORMAT, PackedInputs, make_queues, make_run_policy
|
||||||
|
|
||||||
|
if meta.get("split"):
|
||||||
|
raise RuntimeError(f"model {meta['key']} is a split model; eGPU compiles fused models only")
|
||||||
|
input_spec = {name: (tuple(shape), dtype) for name, (shape, dtype) in INPUT_SPEC.items()}
|
||||||
|
frame_skip = int(meta["frame_skip"])
|
||||||
|
device = Device.DEFAULT
|
||||||
|
jit = TinyJit(make_run_policy(OnnxRunner(onnx_path), input_spec, frame_skip, device), prune=True)
|
||||||
|
queues = make_queues(input_spec, frame_skip, device)
|
||||||
|
packed = PackedInputs(input_spec)
|
||||||
|
|
||||||
|
def step(seed: int) -> np.ndarray:
|
||||||
|
packed.views["traffic_convention"][:] = [1, 0]
|
||||||
|
packed.views["action_t"][:] = [0.2, 0.3]
|
||||||
|
st = time.perf_counter()
|
||||||
|
out, = jit(warped=_policy_frame(seed, input_spec), packed_npy_inputs=packed.tensor, **queues)
|
||||||
|
flat = out.numpy().reshape(-1)
|
||||||
|
print(f" policy step(seed={seed}) {(time.perf_counter() - st) * 1e3:6.1f} ms")
|
||||||
|
packed.views["prev_feat"][:] = flat[meta["output_slices"]["hidden_state"]].reshape(packed.views["prev_feat"].shape)
|
||||||
|
return flat
|
||||||
|
|
||||||
|
print("capture + replay")
|
||||||
|
for i in range(3):
|
||||||
|
baseline = step(SEED + i)
|
||||||
|
if baseline.shape[0] != meta["output_len"]:
|
||||||
|
raise RuntimeError(f"model output length {baseline.shape[0]} != registry {meta['output_len']}")
|
||||||
|
if not np.isfinite(baseline).all():
|
||||||
|
raise RuntimeError("compiled policy produced non-finite outputs")
|
||||||
|
|
||||||
bundle = {
|
bundle = {
|
||||||
"run_model": jit,
|
"format": POLICY_FORMAT,
|
||||||
|
"run_policy": jit,
|
||||||
"model_key": meta["key"],
|
"model_key": meta["key"],
|
||||||
"model_sha256": meta["sha256"],
|
"model_sha256": meta["sha256"],
|
||||||
"output_len": int(meta["output_len"]),
|
"output_len": int(meta["output_len"]),
|
||||||
"frame_skip": int(meta["frame_skip"]),
|
"frame_skip": frame_skip,
|
||||||
"input_spec": {name: (tuple(shape), dtype) for name, (shape, dtype) in INPUT_SPEC.items()},
|
"input_spec": input_spec,
|
||||||
"input_device": Device.DEFAULT,
|
"input_device": device,
|
||||||
}
|
}
|
||||||
os.makedirs(os.path.dirname(out_path), exist_ok=True)
|
os.makedirs(os.path.dirname(out_path), exist_ok=True)
|
||||||
tmp = out_path + ".part"
|
tmp = out_path + ".part"
|
||||||
|
print("serialize")
|
||||||
with open(tmp, "wb") as f:
|
with open(tmp, "wb") as f:
|
||||||
pickle.dump(bundle, f, protocol=pickle.HIGHEST_PROTOCOL)
|
pickle.dump(bundle, f, protocol=pickle.HIGHEST_PROTOCOL)
|
||||||
|
|
||||||
|
del bundle, jit, queues, packed
|
||||||
|
gc.collect()
|
||||||
|
|
||||||
|
print("reload + validate")
|
||||||
|
with open(tmp, "rb") as f:
|
||||||
|
jit = pickle.load(f)["run_policy"]
|
||||||
|
queues = make_queues(input_spec, frame_skip, device)
|
||||||
|
packed = PackedInputs(input_spec)
|
||||||
|
outs = []
|
||||||
|
for i in range(3):
|
||||||
|
packed.views["traffic_convention"][:] = [1, 0]
|
||||||
|
packed.views["action_t"][:] = [0.2, 0.3]
|
||||||
|
out, = jit(warped=_policy_frame(SEED + i, input_spec), packed_npy_inputs=packed.tensor, **queues)
|
||||||
|
flat = out.numpy().reshape(-1)
|
||||||
|
packed.views["prev_feat"][:] = flat[meta["output_slices"]["hidden_state"]].reshape(packed.views["prev_feat"].shape)
|
||||||
|
outs.append(flat)
|
||||||
|
if not np.array_equal(outs[-1], baseline):
|
||||||
|
raise RuntimeError("policy outputs differ from baseline after pickle round trip")
|
||||||
|
if np.array_equal(outs[0], outs[-1]):
|
||||||
|
raise RuntimeError("policy outputs insensitive to inputs after pickle round trip")
|
||||||
|
if not all(np.isfinite(o).all() for o in outs):
|
||||||
|
raise RuntimeError("reloaded policy produced non-finite outputs")
|
||||||
|
from iqpilot.selfdrive.iqmodeld.parser import PhaseParser
|
||||||
|
from iqpilot.selfdrive.iqmodeld.tools.compile_supercombo import _slice_outputs, _validate_pose_outputs
|
||||||
|
_validate_pose_outputs(PhaseParser().parse_vision_outputs(_slice_outputs(outs[-1], meta["output_slices"])))
|
||||||
|
|
||||||
os.replace(tmp, out_path)
|
os.replace(tmp, out_path)
|
||||||
return out_path
|
return out_path
|
||||||
|
|
||||||
@@ -118,6 +230,10 @@ def main() -> None:
|
|||||||
p.add_argument("--model", default=None, help=f"registry key, one of {sorted(EGPU_MODELS)}")
|
p.add_argument("--model", default=None, help=f"registry key, one of {sorted(EGPU_MODELS)}")
|
||||||
p.add_argument("--onnx", default=None)
|
p.add_argument("--onnx", default=None)
|
||||||
p.add_argument("--output", default=None)
|
p.add_argument("--output", default=None)
|
||||||
|
p.add_argument("--progress-param", default=None)
|
||||||
|
p.add_argument("--progress-base", type=float, default=None)
|
||||||
|
p.add_argument("--progress-span", type=float, default=0.0)
|
||||||
|
p.add_argument("--format", type=int, default=2, choices=(1, 2))
|
||||||
args = p.parse_args()
|
args = p.parse_args()
|
||||||
|
|
||||||
if args.model is not None:
|
if args.model is not None:
|
||||||
@@ -136,7 +252,23 @@ def main() -> None:
|
|||||||
if onnx_path is None or not os.path.isfile(onnx_path):
|
if onnx_path is None or not os.path.isfile(onnx_path):
|
||||||
raise SystemExit(f"onnx not found for {meta['key']}; pass --onnx or let iqegpumodeld download it first")
|
raise SystemExit(f"onnx not found for {meta['key']}; pass --onnx or let iqegpumodeld download it first")
|
||||||
|
|
||||||
out = compile_model(meta, onnx_path, args.output or egpu_pkl_path(meta))
|
stop = None
|
||||||
|
sampler = None
|
||||||
|
if args.progress_param and args.progress_base is not None:
|
||||||
|
import threading
|
||||||
|
stop = threading.Event()
|
||||||
|
sampler = threading.Thread(target=_progress_sampler,
|
||||||
|
args=(args.progress_param, args.progress_base, args.progress_span, stop),
|
||||||
|
daemon=True)
|
||||||
|
sampler.start()
|
||||||
|
try:
|
||||||
|
build = compile_policy_model if args.format == 2 else compile_model
|
||||||
|
out = build(meta, onnx_path, args.output or egpu_pkl_path(meta))
|
||||||
|
finally:
|
||||||
|
if stop is not None:
|
||||||
|
stop.set()
|
||||||
|
if sampler is not None:
|
||||||
|
sampler.join(timeout=2)
|
||||||
print(f"saved eGPU jit to {out} ({os.path.getsize(out) / 1e6:.2f} MB)")
|
print(f"saved eGPU jit to {out} ({os.path.getsize(out) / 1e6:.2f} MB)")
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -104,6 +104,16 @@ class MiciHomeLayout(Widget):
|
|||||||
self._iqstandard_txt = gui_app.texture("icons_mici/iqstandard_mode_mici.png", 48, 48)
|
self._iqstandard_txt = gui_app.texture("icons_mici/iqstandard_mode_mici.png", 48, 48)
|
||||||
self._mode_txt = None
|
self._mode_txt = None
|
||||||
self._mic_txt = gui_app.texture("icons_mici/microphone.png", 32, 46)
|
self._mic_txt = gui_app.texture("icons_mici/microphone.png", 32, 46)
|
||||||
|
self._egpu_txt = gui_app.texture("icons_mici/egpu.png", 62, 46)
|
||||||
|
self._egpu_green_txt = gui_app.texture("icons_mici/egpu_green.png", 62, 46)
|
||||||
|
self._egpu_orange_txt = gui_app.texture("icons_mici/egpu_orange.png", 78, 46)
|
||||||
|
self._mac_txt = gui_app.texture("icons_mici/mac.png", 62, 46)
|
||||||
|
self._mac_green_txt = gui_app.texture("icons_mici/mac_green.png", 62, 46)
|
||||||
|
self._mac_orange_txt = gui_app.texture("icons_mici/mac_orange.png", 78, 46)
|
||||||
|
self._egpu_state: str | None = None
|
||||||
|
self._mac_state: str | None = None
|
||||||
|
self._egpu_progress = 0.0
|
||||||
|
self._mac_progress = 0.0
|
||||||
|
|
||||||
self._net_type = NETWORK_TYPES.get(NetworkType.none)
|
self._net_type = NETWORK_TYPES.get(NetworkType.none)
|
||||||
self._net_strength = 0
|
self._net_strength = 0
|
||||||
@@ -176,6 +186,37 @@ class MiciHomeLayout(Widget):
|
|||||||
self._version_text = self._get_version_text()
|
self._version_text = self._get_version_text()
|
||||||
self._last_refresh = rl.get_time()
|
self._last_refresh = rl.get_time()
|
||||||
self._update_params()
|
self._update_params()
|
||||||
|
self._update_dock_status()
|
||||||
|
|
||||||
|
def _update_dock_status(self):
|
||||||
|
p = ui_state.params
|
||||||
|
egpu_present = bool(getattr(ui_state.sm['deviceState'], "egpuDockPresent", False))
|
||||||
|
if not egpu_present:
|
||||||
|
self._egpu_state = None
|
||||||
|
elif any(p.get_bool(k) for k in ("Offroad_EgpuPcieUnavailable", "Offroad_EgpuOverheated",
|
||||||
|
"Offroad_EgpuFansObstructed", "Offroad_EgpuUpdateFailed",
|
||||||
|
"Offroad_EgpuNotDetected")):
|
||||||
|
self._egpu_state = "orange"
|
||||||
|
elif p.get_bool("UsbGpuLoading") and not p.get_bool("UsbGpuCompiled"):
|
||||||
|
self._egpu_state = "compiling"
|
||||||
|
try:
|
||||||
|
self._egpu_progress = max(0.0, min(1.0, float(p.get("UsbGpuSetupProgress") or 0.0)))
|
||||||
|
except (TypeError, ValueError):
|
||||||
|
self._egpu_progress = 0.0
|
||||||
|
elif p.get_bool("Offroad_EgpuUsbSlow") or p.get_bool("Offroad_EgpuUncompiled"):
|
||||||
|
self._egpu_state = "grey"
|
||||||
|
else:
|
||||||
|
self._egpu_state = "green"
|
||||||
|
|
||||||
|
mac_present = p.get_bool("MacModelPresent") or p.get_bool("MacModelReachable")
|
||||||
|
if not mac_present:
|
||||||
|
self._mac_state = None
|
||||||
|
elif p.get_bool("MacModelFault"):
|
||||||
|
self._mac_state = "orange"
|
||||||
|
elif p.get_bool("MacModelReady") or p.get_bool("MacModelActive"):
|
||||||
|
self._mac_state = "green"
|
||||||
|
else:
|
||||||
|
self._mac_state = "grey"
|
||||||
|
|
||||||
def _update_network_status(self, device_state):
|
def _update_network_status(self, device_state):
|
||||||
self._net_type = device_state.networkType
|
self._net_type = device_state.networkType
|
||||||
@@ -330,6 +371,33 @@ class MiciHomeLayout(Widget):
|
|||||||
int(self._rect.y + self.rect.height - self._mic_txt.height / 2 - Y_CENTER), rl.Color(255, 255, 255, 255))
|
int(self._rect.y + self.rect.height - self._mic_txt.height / 2 - Y_CENTER), rl.Color(255, 255, 255, 255))
|
||||||
last_x += self._mic_txt.width + ITEM_SPACING
|
last_x += self._mic_txt.width + ITEM_SPACING
|
||||||
|
|
||||||
|
for state, base, green, orange, progress in (
|
||||||
|
(self._egpu_state, self._egpu_txt, self._egpu_green_txt, self._egpu_orange_txt, self._egpu_progress),
|
||||||
|
(self._mac_state, self._mac_txt, self._mac_green_txt, self._mac_orange_txt, self._mac_progress)):
|
||||||
|
if state is None:
|
||||||
|
continue
|
||||||
|
y_top = int(self._rect.y + self.rect.height - base.height / 2 - Y_CENTER)
|
||||||
|
if state == "compiling":
|
||||||
|
self._draw_compile_gauge(int(last_x), y_top, base, green, progress)
|
||||||
|
last_x += base.width + ITEM_SPACING
|
||||||
|
continue
|
||||||
|
if state == "green":
|
||||||
|
tex, tint = green, rl.Color(255, 255, 255, 255)
|
||||||
|
elif state == "orange":
|
||||||
|
tex, tint = orange, rl.Color(255, 255, 255, 255)
|
||||||
|
else:
|
||||||
|
tex, tint = base, rl.Color(165, 165, 170, 235)
|
||||||
|
rl.draw_texture(tex, int(last_x), y_top, tint)
|
||||||
|
last_x += tex.width + ITEM_SPACING
|
||||||
|
|
||||||
|
def _draw_compile_gauge(self, x: int, y_top: int, base, fill, progress: float):
|
||||||
|
rl.draw_texture(base, x, y_top, rl.Color(165, 165, 170, 235))
|
||||||
|
fill_h = int(base.height * max(0.0, min(1.0, progress)))
|
||||||
|
if fill_h > 0:
|
||||||
|
rl.begin_scissor_mode(x, y_top + base.height - fill_h, base.width, fill_h)
|
||||||
|
rl.draw_texture(fill, x, y_top, rl.Color(255, 255, 255, 255))
|
||||||
|
rl.end_scissor_mode()
|
||||||
|
|
||||||
def _draw_cellular_cluster(self, start_x: float, spacing: int, y_center: int, connected: bool) -> float:
|
def _draw_cellular_cluster(self, start_x: float, spacing: int, y_center: int, connected: bool) -> float:
|
||||||
draw_net_txt = {0: self._cell_none_txt,
|
draw_net_txt = {0: self._cell_none_txt,
|
||||||
2: self._cell_low_txt,
|
2: self._cell_low_txt,
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ Copyright © IQ.Lvbs, apart of Project Teal Lvbs, All Rights Reserved, licensed
|
|||||||
|
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
|
import threading
|
||||||
import time
|
import time
|
||||||
|
|
||||||
import pyray as rl
|
import pyray as rl
|
||||||
@@ -38,6 +39,31 @@ def _display_model_name(bundle) -> str:
|
|||||||
return bundle.internalName if getattr(bundle, "internalName", "") else bundle.displayName
|
return bundle.internalName if getattr(bundle, "internalName", "") else bundle.displayName
|
||||||
|
|
||||||
|
|
||||||
|
def _big_options() -> list[tuple[str, str]]:
|
||||||
|
try:
|
||||||
|
from iqpilot.selfdrive.iqmodeld.emac_model_meta import big_models
|
||||||
|
return big_models(ui_state.params)
|
||||||
|
except Exception:
|
||||||
|
return []
|
||||||
|
|
||||||
|
|
||||||
|
def _big_label(key: str) -> str:
|
||||||
|
for name, display in _big_options():
|
||||||
|
if name == key:
|
||||||
|
return display
|
||||||
|
return key or "lebrowski"
|
||||||
|
|
||||||
|
|
||||||
|
def _refresh_big_catalog() -> None:
|
||||||
|
def worker():
|
||||||
|
try:
|
||||||
|
from iqpilot.selfdrive.iqmodeld.emac_model_meta import refresh_catalog
|
||||||
|
refresh_catalog(ui_state.params)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
threading.Thread(target=worker, daemon=True).start()
|
||||||
|
|
||||||
|
|
||||||
class _ModelSelectPanel(NavScroller):
|
class _ModelSelectPanel(NavScroller):
|
||||||
"""A throwaway scroller panel (folder list or bundle list) pushed onto the nav stack."""
|
"""A throwaway scroller panel (folder list or bundle list) pushed onto the nav stack."""
|
||||||
def __init__(self, items):
|
def __init__(self, items):
|
||||||
@@ -99,6 +125,9 @@ class ModelsLayoutMici(NavScroller):
|
|||||||
self._current = BigButton(tr("active model"))
|
self._current = BigButton(tr("active model"))
|
||||||
self._current.set_click_callback(self._show_folders)
|
self._current.set_click_callback(self._show_folders)
|
||||||
|
|
||||||
|
self._big = BigButton(tr("big model"))
|
||||||
|
self._big.set_click_callback(self._show_big_models)
|
||||||
|
|
||||||
self._cancel = BigButton(tr("stop download"))
|
self._cancel = BigButton(tr("stop download"))
|
||||||
self._cancel.set_click_callback(self._cancel_model_request)
|
self._cancel.set_click_callback(self._cancel_model_request)
|
||||||
self._cancel.set_visible(self._is_downloading)
|
self._cancel.set_visible(self._is_downloading)
|
||||||
@@ -129,7 +158,7 @@ class ModelsLayoutMici(NavScroller):
|
|||||||
self._lane_speed = MappedParamToggle(tr("lane turn speed"), "IQLaneTurnValue", [tr("slow"), tr("normal"), tr("fast")], _LANE_TURN_VALUES)
|
self._lane_speed = MappedParamToggle(tr("lane turn speed"), "IQLaneTurnValue", [tr("slow"), tr("normal"), tr("fast")], _LANE_TURN_VALUES)
|
||||||
self._lane_speed.set_visible(lambda: self._lane_turn._checked)
|
self._lane_speed.set_visible(lambda: self._lane_turn._checked)
|
||||||
|
|
||||||
self._main_items = [self._current, self._cancel, self._supercombo, self._vision, self._policy, self._redownload, self._refresh, self._clear,
|
self._main_items = [self._current, self._big, self._cancel, self._supercombo, self._vision, self._policy, self._redownload, self._refresh, self._clear,
|
||||||
self._steer_delay, self._sw_delay, self._lane_turn, self._lane_speed]
|
self._steer_delay, self._sw_delay, self._lane_turn, self._lane_speed]
|
||||||
self._scroller.add_widgets(self._main_items)
|
self._scroller.add_widgets(self._main_items)
|
||||||
|
|
||||||
@@ -326,6 +355,55 @@ class ModelsLayoutMici(NavScroller):
|
|||||||
btns = [_ModelButton(b, self._select_model, self._toggle_favorite, b.ref in favorites) for b in bundles]
|
btns = [_ModelButton(b, self._select_model, self._toggle_favorite, b.ref in favorites) for b in bundles]
|
||||||
gui_app.push_widget(_ModelSelectPanel(btns))
|
gui_app.push_widget(_ModelSelectPanel(btns))
|
||||||
|
|
||||||
|
def _show_big_models(self):
|
||||||
|
options = _big_options()
|
||||||
|
if len(options) <= 1:
|
||||||
|
_refresh_big_catalog()
|
||||||
|
off = BigButton(tr("Off"))
|
||||||
|
off.set_click_callback(lambda: self._select_big(None))
|
||||||
|
btns = [off]
|
||||||
|
for key, display in options:
|
||||||
|
btn = BigButton(display)
|
||||||
|
btn.set_click_callback(lambda k=key: self._select_big(k))
|
||||||
|
btns.append(btn)
|
||||||
|
gui_app.push_widget(_ModelSelectPanel(btns))
|
||||||
|
|
||||||
|
def _select_big(self, key):
|
||||||
|
if key is None:
|
||||||
|
ui_state.params.put_bool("IQEmacEnabled", False)
|
||||||
|
else:
|
||||||
|
ui_state.params.put("IQEmacModel", key)
|
||||||
|
ui_state.params.put_bool("IQEmacEnabled", True)
|
||||||
|
gui_app.pop_widgets_to(self)
|
||||||
|
|
||||||
|
def _big_setup_progress(self) -> float | None:
|
||||||
|
p = ui_state.params
|
||||||
|
if p.get_bool("IQEmacEnabled"):
|
||||||
|
raw = p.get("MacModelDownloadProgress")
|
||||||
|
loading = not p.get_bool("MacModelReady")
|
||||||
|
else:
|
||||||
|
raw = p.get("UsbGpuSetupProgress")
|
||||||
|
loading = p.get_bool("UsbGpuLoading") and not p.get_bool("UsbGpuCompiled")
|
||||||
|
if not loading:
|
||||||
|
return None
|
||||||
|
try:
|
||||||
|
return max(0.0, min(1.0, float(raw)))
|
||||||
|
except (TypeError, ValueError):
|
||||||
|
return None
|
||||||
|
|
||||||
|
def _big_model_value(self) -> str:
|
||||||
|
p = ui_state.params
|
||||||
|
dock = bool(getattr(ui_state.sm["deviceState"], "egpuDockPresent", False))
|
||||||
|
if not p.get_bool("IQEmacEnabled") and not dock:
|
||||||
|
return tr("Off")
|
||||||
|
key = p.get("IQEmacModel")
|
||||||
|
key = key.decode() if isinstance(key, bytes) else (key or "")
|
||||||
|
label = _big_label(key)
|
||||||
|
progress = self._big_setup_progress()
|
||||||
|
if progress is not None and progress < 1.0:
|
||||||
|
return f"{label} {int(progress * 100)}%"
|
||||||
|
return label
|
||||||
|
|
||||||
def _generation_changed(self, bundle) -> bool:
|
def _generation_changed(self, bundle) -> bool:
|
||||||
try:
|
try:
|
||||||
active = self.model_manager.activeBundle
|
active = self.model_manager.activeBundle
|
||||||
@@ -360,6 +438,7 @@ class ModelsLayoutMici(NavScroller):
|
|||||||
self._handle_bundle_download_progress()
|
self._handle_bundle_download_progress()
|
||||||
self._current.set_value(self._current_model_value())
|
self._current.set_value(self._current_model_value())
|
||||||
self._current.set_enabled(ui_state.is_offroad())
|
self._current.set_enabled(ui_state.is_offroad())
|
||||||
|
self._big.set_value(self._big_model_value())
|
||||||
target = self._redownload_target_bundle()
|
target = self._redownload_target_bundle()
|
||||||
self._redownload.set_value(_display_model_name(target) if target else "")
|
self._redownload.set_value(_display_model_name(target) if target else "")
|
||||||
|
|
||||||
|
|||||||
@@ -427,6 +427,8 @@ def hardware_thread(end_event, hw_queue) -> None:
|
|||||||
|
|
||||||
# Set ignition based on any panda connected
|
# Set ignition based on any panda connected
|
||||||
onroad_conditions["ignition"] = any(ps.ignitionLine or ps.ignitionCan for ps in pandaStates if ps.pandaType != log.PandaState.PandaType.unknown)
|
onroad_conditions["ignition"] = any(ps.ignitionLine or ps.ignitionCan for ps in pandaStates if ps.pandaType != log.PandaState.PandaType.unknown)
|
||||||
|
if params.get_bool("IQBenchIgnition"):
|
||||||
|
onroad_conditions["ignition"] = True
|
||||||
|
|
||||||
pandaState = pandaStates[0]
|
pandaState = pandaStates[0]
|
||||||
|
|
||||||
@@ -498,7 +500,7 @@ def hardware_thread(end_event, hw_queue) -> None:
|
|||||||
egpu_valid = sm.alive["egpuDockState"] and sm.valid["egpuDockState"]
|
egpu_valid = sm.alive["egpuDockState"] and sm.valid["egpuDockState"]
|
||||||
egpu_dock_status.update(started_ts is None, last_hw_state.usb_state, egpu_dock_flasher.failed,
|
egpu_dock_status.update(started_ts is None, last_hw_state.usb_state, egpu_dock_flasher.failed,
|
||||||
params.get_bool("UsbGpuLoading"), params.get("UsbGpuActive"),
|
params.get_bool("UsbGpuLoading"), params.get("UsbGpuActive"),
|
||||||
params.get_bool("UsbGpuCompiled"),
|
params.get_bool("UsbGpuReady"),
|
||||||
sm["egpuDockState"] if egpu_valid else None, set_offroad_alert_if_changed)
|
sm["egpuDockState"] if egpu_valid else None, set_offroad_alert_if_changed)
|
||||||
|
|
||||||
msg.deviceState.screenBrightnessPercent = HARDWARE.get_screen_brightness()
|
msg.deviceState.screenBrightnessPercent = HARDWARE.get_screen_brightness()
|
||||||
|
|||||||
@@ -207,10 +207,20 @@ def _patch_mock_state():
|
|||||||
activeBundle = _MockBundle()
|
activeBundle = _MockBundle()
|
||||||
selectedBundle = None # None = not downloading
|
selectedBundle = None # None = not downloading
|
||||||
|
|
||||||
|
from iqpilot.cereal import log as _log
|
||||||
|
class _MockDeviceState:
|
||||||
|
networkType = _log.DeviceState.NetworkType.wifi
|
||||||
|
networkStrength = type("NS", (), {"raw": 3})()
|
||||||
|
egpuDockPresent = True
|
||||||
|
started = False
|
||||||
|
freeSpacePercent = 50.0
|
||||||
|
memoryUsagePercent = 40
|
||||||
|
|
||||||
class _MockSM:
|
class _MockSM:
|
||||||
"""Minimal SubMaster-like dict that returns sensible defaults."""
|
"""Minimal SubMaster-like dict that returns sensible defaults."""
|
||||||
_data = {
|
_data = {
|
||||||
"iqModelManager": _MockModelManager(),
|
"iqModelManager": _MockModelManager(),
|
||||||
|
"deviceState": _MockDeviceState(),
|
||||||
}
|
}
|
||||||
def __getitem__(self, key):
|
def __getitem__(self, key):
|
||||||
return self._data.get(key, type("Empty", (), {"enabled": False})())
|
return self._data.get(key, type("Empty", (), {"enabled": False})())
|
||||||
@@ -330,6 +340,16 @@ def load_panel(name: str):
|
|||||||
widget = cls()
|
widget = cls()
|
||||||
widget.set_rect(rect)
|
widget.set_rect(rect)
|
||||||
widget.show_event()
|
widget.show_event()
|
||||||
|
|
||||||
|
eg = os.environ.get("IQ_EGPU_STATE")
|
||||||
|
mc = os.environ.get("IQ_MAC_STATE")
|
||||||
|
if (eg or mc) and hasattr(widget, "_egpu_state"):
|
||||||
|
widget._egpu_state = eg or None
|
||||||
|
widget._mac_state = mc or None
|
||||||
|
widget._egpu_progress = float(os.environ.get("IQ_EGPU_PROGRESS", "0") or 0)
|
||||||
|
widget._mac_progress = float(os.environ.get("IQ_MAC_PROGRESS", "0") or 0)
|
||||||
|
widget._update_dock_status = lambda: None
|
||||||
|
|
||||||
return widget
|
return widget
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user