mirror of
https://github.com/firestar5683/StarPilot.git
synced 2026-08-25 10:13:43 +08:00
Compare commits
71 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 432d575c41 | |||
| 8a2f96967a | |||
| d194762f28 | |||
| 0518b67573 | |||
| e4ffae42c4 | |||
| 05594fc947 | |||
| 947c6af636 | |||
| 159c78147f | |||
| f6d38dbd69 | |||
| 4cac2f6231 | |||
| 011cf35abd | |||
| 5e02379759 | |||
| c309cf177c | |||
| 0ab17de780 | |||
| e993cf0bed | |||
| d9ace21a9b | |||
| ca6a6f79b7 | |||
| 17b8aca4f5 | |||
| 4f24c3e20e | |||
| 6277ecee4e | |||
| 0078eb477c | |||
| c8a1fb3895 | |||
| f65be7b584 | |||
| 7fbab917fa | |||
| a4a1d98f14 | |||
| 122672cd45 | |||
| fb1bfd16eb | |||
| 2eb88f4b6e | |||
| 5b06fb4968 | |||
| f8c251001f | |||
| 28d03e95d3 | |||
| 762ced4157 | |||
| b9a83f5873 | |||
| e8f526f6b8 | |||
| a0dc562f9a | |||
| 07adabd70d | |||
| 34ca49517f | |||
| 749235cda2 | |||
| 515f28fcbd | |||
| eaa5a9ad83 | |||
| 640187f350 | |||
| 3932dd9fe2 | |||
| f0e3414d1f | |||
| ba9f216931 | |||
| 4e711173e0 | |||
| 8f945fb6fe | |||
| ec6bf08a65 | |||
| 0351e7d817 | |||
| 7eae0e9670 | |||
| 8ef7e55e74 | |||
| 0d2f0b6825 | |||
| 6370abd73a | |||
| 8640f06054 | |||
| 779b42d8ea | |||
| f5f846f5b6 | |||
| d8b6e43a0d | |||
| b72042042f | |||
| bd6dced538 | |||
| 66245a5a2e | |||
| d4a3bc0f5c | |||
| fcc5cc359e | |||
| aa381d4d97 | |||
| bc3f215764 | |||
| daf0f84bc9 | |||
| b945d4c021 | |||
| 9f1066ce83 | |||
| 370654637a | |||
| fb854cecd3 | |||
| 713ce294cc | |||
| 7450b9de9a | |||
| 5b2b053afd |
@@ -34,7 +34,7 @@ runs:
|
||||
- name: setup github cache
|
||||
id: gha-cache
|
||||
if: ${{ !contains(runner.name, 'nsc') && inputs.save != 'false' }}
|
||||
uses: 'actions/cache@v4'
|
||||
uses: 'actions/cache@v5'
|
||||
with:
|
||||
path: ${{ inputs.path }}
|
||||
key: ${{ inputs.key }}
|
||||
@@ -43,7 +43,7 @@ runs:
|
||||
- name: setup github cache
|
||||
id: gha-cache-ro
|
||||
if: ${{ !contains(runner.name, 'nsc') && inputs.save == 'false' }}
|
||||
uses: 'actions/cache/restore@v4'
|
||||
uses: 'actions/cache/restore@v5'
|
||||
with:
|
||||
path: ${{ inputs.path }}
|
||||
key: ${{ inputs.key }}
|
||||
|
||||
@@ -14,7 +14,7 @@ runs:
|
||||
${{ env.RUN }} "rm -rf /tmp/scons_cache/* && \
|
||||
scons -j$(nproc) --cache-populate"
|
||||
- name: Save scons cache
|
||||
uses: actions/cache/save@v4
|
||||
uses: actions/cache/save@v5
|
||||
if: github.ref == 'refs/heads/master'
|
||||
with:
|
||||
path: .ci_cache/scons_cache
|
||||
|
||||
@@ -88,7 +88,7 @@ jobs:
|
||||
fi
|
||||
|
||||
- name: Checkout target branch
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
ref: ${{ env.BUILD_BRANCH }}
|
||||
fetch-depth: 0
|
||||
|
||||
@@ -48,7 +48,7 @@ jobs:
|
||||
fi
|
||||
|
||||
- name: Checkout target branch
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
ref: ${{ env.BUILD_BRANCH }}
|
||||
fetch-depth: 0
|
||||
|
||||
@@ -17,17 +17,17 @@ jobs:
|
||||
publish:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
- name: Log in to GHCR
|
||||
uses: docker/login-action@v3
|
||||
uses: docker/login-action@v4
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Build and push runner image
|
||||
uses: docker/build-push-action@v6
|
||||
uses: docker/build-push-action@v7
|
||||
with:
|
||||
context: .
|
||||
file: tools/truenas_github_runner/Dockerfile
|
||||
|
||||
@@ -224,6 +224,16 @@ struct StarPilotPlan @0xf98d843bfd7004a3 {
|
||||
struct StarPilotRadarState @0xb86e6369214c01c8 {
|
||||
leadLeft @0 :LeadData;
|
||||
leadRight @1 :LeadData;
|
||||
adjacentStopped @2 :AdjacentStopped;
|
||||
|
||||
# A vehicle in an adjacent lane that was observed MOVING and then came to rest.
|
||||
# Distinct from leadLeft/leadRight, which are moving-target-only by design.
|
||||
struct AdjacentStopped {
|
||||
status @0 :Bool;
|
||||
dRel @1 :Float32;
|
||||
yRel @2 :Float32;
|
||||
radarTrackId @3 :Int32 = -1;
|
||||
}
|
||||
|
||||
struct LeadData {
|
||||
dRel @0 :Float32;
|
||||
|
||||
Binary file not shown.
+15
-7
@@ -58,26 +58,34 @@ def file_chunked_exists(path) -> bool:
|
||||
class ChunkStream(io.RawIOBase):
|
||||
def __init__(self, paths):
|
||||
self._paths = iter(paths)
|
||||
self._buffer = memoryview(b"")
|
||||
self._file = None
|
||||
|
||||
def readable(self):
|
||||
return True
|
||||
|
||||
def readinto(self, buffer):
|
||||
count = 0
|
||||
view = memoryview(buffer)
|
||||
while count < len(buffer):
|
||||
if not self._buffer:
|
||||
if self._file is None:
|
||||
path = next(self._paths, None)
|
||||
if path is None:
|
||||
break
|
||||
self._buffer = memoryview(Path(path).read_bytes())
|
||||
self._file = open(path, "rb")
|
||||
bytes_read = self._file.readinto(view[count:])
|
||||
if not bytes_read:
|
||||
self._file.close()
|
||||
self._file = None
|
||||
continue
|
||||
take = min(len(buffer) - count, len(self._buffer))
|
||||
buffer[count:count + take] = self._buffer[:take]
|
||||
self._buffer = self._buffer[take:]
|
||||
count += take
|
||||
count += bytes_read
|
||||
return count
|
||||
|
||||
def close(self):
|
||||
if self._file is not None:
|
||||
self._file.close()
|
||||
self._file = None
|
||||
super().close()
|
||||
|
||||
|
||||
def open_file_chunked(path):
|
||||
chunks = get_existing_chunks(path)
|
||||
|
||||
Binary file not shown.
+15
-1
@@ -338,7 +338,6 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
|
||||
{"StarPilotFavoriteSlots", {PERSISTENT, JSON, "[]", "[]", 1}},
|
||||
{"StarPilotStats", {PERSISTENT | DONT_LOG, JSON, "{}", "{}"}},
|
||||
{"StarPilotTogglesUpdated", {CLEAR_ON_MANAGER_START, BOOL, "0", "0"}},
|
||||
{"FrogsGoMoosTweak", {PERSISTENT, BOOL, "1", "0", 2}},
|
||||
{"GoatScream", {PERSISTENT, BOOL, "0", "0", 1, SETTINGS_SIMPLE}},
|
||||
{"GoatScreamCriticalAlerts", {PERSISTENT, BOOL, "0", "0", 1, SETTINGS_SIMPLE}},
|
||||
{"GreenLightAlert", {PERSISTENT, BOOL, "0", "0", 0, SETTINGS_SIMPLE}},
|
||||
@@ -371,7 +370,12 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
|
||||
{"IssueReported", {CLEAR_ON_MANAGER_START, JSON, "{}", "{}"}},
|
||||
{"KonikDongleId", {PERSISTENT, STRING, "", "", 0}},
|
||||
{"KonikMinutes", {PERSISTENT, INT, "0", "0", 0}},
|
||||
{"LaneCentering", {PERSISTENT, BOOL, "0", "0", 2}},
|
||||
{"LaneCenteringE2EAuthority", {PERSISTENT, FLOAT, "1.0", "1.0", 3}},
|
||||
{"LaneCenterOffset", {PERSISTENT, FLOAT, "0.0", "0.0", 3}},
|
||||
{"LaneChanges", {PERSISTENT, BOOL, "1", "1", 0, SETTINGS_SIMPLE}},
|
||||
{"LaneChangeCloseGap", {PERSISTENT, BOOL, "0", "0", 1, SETTINGS_SIMPLE}},
|
||||
{"LaneChangeCloseGapSeconds", {PERSISTENT, FLOAT, "1.0", "1.0", 1, SETTINGS_SIMPLE}},
|
||||
{"LaneChangeSmoothing", {PERSISTENT, INT, "5", "10", 1, SETTINGS_SIMPLE}},
|
||||
{"LaneChangeTime", {PERSISTENT, FLOAT, "1.0", "0.0", 1, SETTINGS_SIMPLE}},
|
||||
{"LaneDetectionWidth", {PERSISTENT, FLOAT, "0.0", "0.0", 1, SETTINGS_SIMPLE}},
|
||||
@@ -647,6 +651,16 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
|
||||
{"UseSI", {PERSISTENT, BOOL, "1", "1", 3}},
|
||||
{"UserFavorites", {PERSISTENT, STRING, "", "", 1}},
|
||||
{"UseVienna", {PERSISTENT, BOOL, "0", "0", 1, SETTINGS_SIMPLE}},
|
||||
{"VASMAnnotationConfig", {PERSISTENT, JSON, "{}", "{}", 2}},
|
||||
{"VASMConfidenceThreshold", {PERSISTENT, FLOAT, "0.85", "0.85", 2}},
|
||||
{"VASMEnabled", {PERSISTENT, BOOL, "0", "0", 1}},
|
||||
{"VASMLeftActive", {CLEAR_ON_MANAGER_START, STRING, "0", "0", 2}},
|
||||
{"VASMLeftConfidence", {CLEAR_ON_MANAGER_START, STRING, "0.0", "0.0", 2}},
|
||||
{"VASMLastUpdateMonoTime", {CLEAR_ON_MANAGER_START, STRING, "0", "0", 2}},
|
||||
{"VASMRightActive", {CLEAR_ON_MANAGER_START, STRING, "0", "0", 2}},
|
||||
{"VASMRightConfidence", {CLEAR_ON_MANAGER_START, STRING, "0.0", "0.0", 2}},
|
||||
{"VASMSmoothSeconds", {PERSISTENT, FLOAT, "0.2", "0.2", 2}},
|
||||
{"VASMTimestampEof", {CLEAR_ON_MANAGER_START, STRING, "0", "0", 2}},
|
||||
{"VEgoStarting", {PERSISTENT, FLOAT, "0.0", "0.0", 3}},
|
||||
{"VEgoStartingStock", {PERSISTENT, FLOAT, "0.0", "0.0", 3}},
|
||||
{"VEgoStopping", {PERSISTENT, FLOAT, "0.0", "0.0", 3}},
|
||||
|
||||
Binary file not shown.
@@ -1,3 +1,5 @@
|
||||
from pathlib import Path
|
||||
|
||||
from openpilot.common import file_chunker
|
||||
|
||||
|
||||
@@ -14,3 +16,19 @@ def test_chunked_stream_round_trip(tmp_path, monkeypatch):
|
||||
assert file_chunker.read_file_chunked(path) == payload
|
||||
with file_chunker.open_file_chunked(path) as stream:
|
||||
assert stream.read(9) + stream.read() == payload
|
||||
|
||||
|
||||
def test_unchunked_stream_does_not_materialize_file(tmp_path, monkeypatch):
|
||||
path = tmp_path / "fat.onnx"
|
||||
payload = b"large model read through bounded buffers"
|
||||
path.write_bytes(payload)
|
||||
original_read_bytes = Path.read_bytes
|
||||
|
||||
def reject_whole_file_read(candidate):
|
||||
if candidate == path:
|
||||
raise AssertionError("streaming must not call Path.read_bytes()")
|
||||
return original_read_bytes(candidate)
|
||||
|
||||
monkeypatch.setattr(Path, "read_bytes", reject_whole_file_read)
|
||||
with file_chunker.open_file_chunked(path) as stream:
|
||||
assert stream.read() == payload
|
||||
|
||||
Binary file not shown.
@@ -603,6 +603,7 @@ class CarInterface(CarInterfaceBase):
|
||||
ret.longitudinalTuning.kpV = [0.095, 0.085, 0.065, 0.050]
|
||||
ret.longitudinalTuning.kiV = [0.07, 0.10, 0.15, 0.24]
|
||||
ret.longitudinalTuning.kfDEPRECATED = 0.20
|
||||
ret.longitudinalActuatorDelay = 0.6
|
||||
else:
|
||||
ret.longitudinalTuning.kfDEPRECATED = 0.25
|
||||
|
||||
|
||||
@@ -66,6 +66,38 @@ class TestGMFingerprint:
|
||||
|
||||
|
||||
class TestGMInterface:
|
||||
@parameterized.expand([
|
||||
CAR.CHEVROLET_BOLT_CC_2017,
|
||||
CAR.CHEVROLET_BOLT_CC_2018_2021,
|
||||
CAR.CHEVROLET_BOLT_ACC_2022_2023_PEDAL,
|
||||
CAR.CHEVROLET_BOLT_CC_2022_2023,
|
||||
CAR.CHEVROLET_MALIBU_HYBRID_CC,
|
||||
])
|
||||
def test_bolt_pedal_long_uses_shared_planning_delay_without_retuning_pid(self, car_model):
|
||||
CarInterface = interfaces[car_model]
|
||||
fingerprint = _empty_fingerprint()
|
||||
fingerprint[0][0x201] = 8
|
||||
params = Params()
|
||||
|
||||
try:
|
||||
params.put_bool("GMPedalLongitudinal", True)
|
||||
car_params = CarInterface.get_params(
|
||||
car_model,
|
||||
fingerprint,
|
||||
[],
|
||||
alpha_long=False,
|
||||
is_release=False,
|
||||
docs=False,
|
||||
starpilot_toggles=_test_starpilot_toggles(),
|
||||
)
|
||||
finally:
|
||||
params.remove("GMPedalLongitudinal")
|
||||
|
||||
assert car_params.longitudinalActuatorDelay == pytest.approx(0.6)
|
||||
assert list(car_params.longitudinalTuning.kpV) == pytest.approx([0.095, 0.085, 0.065, 0.050])
|
||||
assert list(car_params.longitudinalTuning.kiV) == pytest.approx([0.07, 0.10, 0.15, 0.24])
|
||||
assert car_params.longitudinalTuning.kfDEPRECATED == pytest.approx(0.20)
|
||||
|
||||
def test_bolt_acc_pedal_pid_accel_limits_keep_full_negative_authority(self):
|
||||
cp = SimpleNamespace(
|
||||
enableGasInterceptorDEPRECATED=True,
|
||||
|
||||
@@ -28,6 +28,7 @@ ENABLE_BUTTONS = (ButtonType.accelCruise, ButtonType.decelCruise, ButtonType.can
|
||||
# Track when ECU disable happened - used to permanently suppress CAN errors from disabled ECU
|
||||
ECU_DISABLE_TIMESTAMP = 0.0
|
||||
KONA_NON_SCC_FCA_RADAR_ADDR = 0x602
|
||||
KIA_EV9_ACCEL_MAX = 2.5
|
||||
|
||||
|
||||
def apply_platform_longitudinal_params(ret: structs.CarParams) -> None:
|
||||
@@ -80,7 +81,8 @@ class CarInterface(CarInterfaceBase):
|
||||
|
||||
@staticmethod
|
||||
def get_pid_accel_limits(CP, current_speed, cruise_speed):
|
||||
return ACCEL_MIN, CarControllerParams.ACCEL_MAX
|
||||
accel_max = KIA_EV9_ACCEL_MAX if CP.carFingerprint == CAR.KIA_EV9 else CarControllerParams.ACCEL_MAX
|
||||
return ACCEL_MIN, accel_max
|
||||
|
||||
@staticmethod
|
||||
def apply_post_fingerprint_params(CP: structs.CarParams, candidate, fingerprint, car_fw) -> None:
|
||||
|
||||
@@ -17,7 +17,7 @@ from opendbc.car.hyundai.carcontroller import CarController, Ioniq6LongitudinalT
|
||||
direct_angle_request_allowed, get_angle_smoothing_alpha, \
|
||||
should_use_ev6_gt_line_stop_direct_tracking
|
||||
from opendbc.car.hyundai.carstate import CarState, decode_canfd_camera_lead, decode_ioniq_6_blindspot_radar_state
|
||||
from opendbc.car.hyundai.interface import CarInterface
|
||||
from opendbc.car.hyundai.interface import CarInterface, KIA_EV9_ACCEL_MAX
|
||||
from opendbc.car.hyundai import hyundaican, hyundaicanfd
|
||||
from opendbc.car.hyundai.hyundaicanfd import CanBus
|
||||
from opendbc.car.hyundai.radar_interface import MRREVO14F_RADAR_START_ADDR, MRR30_RADAR_START_ADDR, MRR35_RADAR_START_ADDR, \
|
||||
@@ -26,7 +26,7 @@ from opendbc.car.hyundai.values import CAMERA_SCC_CAR, CANFD_CAR, CAN_GEARS, CAR
|
||||
HYBRID_CAR, EV_CAR, FW_QUERY_CONFIG, LEGACY_SAFETY_MODE_CAR, CANFD_FUZZY_WHITELIST, \
|
||||
UNSUPPORTED_LONGITUDINAL_CAR, PLATFORM_CODE_ECUS, HYUNDAI_VERSION_REQUEST_LONG, \
|
||||
LEGACY_LONGITUDINAL_CAR, DBC, HyundaiFlags, get_platform_codes, HyundaiSafetyFlags, \
|
||||
HyundaiStarPilotSafetyFlags, Buttons, kia_ev6_gt_line_longitudinal_tuning
|
||||
HyundaiStarPilotSafetyFlags, Buttons, CarControllerParams, kia_ev6_gt_line_longitudinal_tuning
|
||||
|
||||
LongCtrlState = CarControl.Actuators.LongControlState
|
||||
from opendbc.car.hyundai.fingerprints import FW_VERSIONS
|
||||
@@ -1117,6 +1117,10 @@ class TestHyundaiFingerprint:
|
||||
assert CP.startAccel == pytest.approx(0.2)
|
||||
assert CP.vEgoStarting == pytest.approx(0.5)
|
||||
assert CP.longitudinalActuatorDelay == pytest.approx(0.3)
|
||||
assert CarInterface.get_pid_accel_limits(CP, 0.0, 0.0)[1] == pytest.approx(KIA_EV9_ACCEL_MAX)
|
||||
|
||||
ioniq_6_cp = CarInterface.get_params(CAR.HYUNDAI_IONIQ_6, gen_empty_fingerprint(), [], True, False, False, toggles)
|
||||
assert CarInterface.get_pid_accel_limits(ioniq_6_cp, 0.0, 0.0)[1] == pytest.approx(CarControllerParams.ACCEL_MAX)
|
||||
|
||||
def test_ioniq_6_longitudinal_tuning_helper_matches_dynamic_profile(self):
|
||||
state = Ioniq6LongitudinalTuningState()
|
||||
|
||||
@@ -73,7 +73,6 @@ def get_test_starpilot_toggles() -> SimpleNamespace:
|
||||
cluster_offset=1.0,
|
||||
disable_openpilot_long=False,
|
||||
force_fingerprint=False,
|
||||
frogsgomoo_tweak=False,
|
||||
lock_doors=False,
|
||||
reverse_cruise_increase=False,
|
||||
sng_hack=False,
|
||||
|
||||
@@ -444,10 +444,7 @@ class CarController(CarControllerBase):
|
||||
self.aego.update(a_ego_blended)
|
||||
j_ego = (self.aego.x - prev_aego) / (DT_CTRL * 3)
|
||||
|
||||
if starpilot_toggles.frogsgomoo_tweak:
|
||||
future_t = float(np.interp(CS.out.vEgo, [2., 5.], [0.35, 1.0]))
|
||||
else:
|
||||
future_t = float(np.interp(CS.out.vEgo, [2., 5.], [0.25, 0.5]))
|
||||
future_t = float(np.interp(CS.out.vEgo, [2., 5.], [0.25, 0.5]))
|
||||
a_ego_future = a_ego_blended + j_ego * future_t
|
||||
|
||||
if CC.longActive:
|
||||
|
||||
@@ -194,6 +194,9 @@ class CarInterface(CarInterfaceBase):
|
||||
ret.vEgoStarting = 0.25
|
||||
ret.stoppingDecelRate = 0.3
|
||||
|
||||
if candidate == CAR.TOYOTA_HIGHLANDER and ret.openpilotLongitudinalControl and not ret.flags & ToyotaFlags.HYBRID.value:
|
||||
ret.longitudinalActuatorDelay = 0.4
|
||||
|
||||
if ret.enableGasInterceptorDEPRECATED:
|
||||
# Pedal/SDSU Toyotas feel best with a softer final stop clamp.
|
||||
ret.longitudinalActuatorDelay = max(ret.longitudinalActuatorDelay, 0.2)
|
||||
|
||||
@@ -101,6 +101,32 @@ class TestToyotaInterfaces:
|
||||
assert abs(car_params.vEgoStopping - 0.25) < 1e-6
|
||||
assert abs(car_params.vEgoStarting - 0.25) < 1e-6
|
||||
|
||||
def test_highlander_ice_openpilot_long_uses_measured_actuator_delay(self):
|
||||
stock_params = CarInterface.get_params(
|
||||
CAR.TOYOTA_HIGHLANDER,
|
||||
{bus: {} for bus in range(8)},
|
||||
[],
|
||||
alpha_long=False,
|
||||
is_release=False,
|
||||
docs=False,
|
||||
starpilot_toggles=SimpleNamespace(),
|
||||
)
|
||||
long_params = CarInterface.get_params(
|
||||
CAR.TOYOTA_HIGHLANDER,
|
||||
{bus: ({0x2FF: 8} if bus == 0 else {}) for bus in range(8)},
|
||||
[],
|
||||
alpha_long=False,
|
||||
is_release=False,
|
||||
docs=False,
|
||||
starpilot_toggles=SimpleNamespace(),
|
||||
)
|
||||
|
||||
assert not stock_params.openpilotLongitudinalControl
|
||||
assert stock_params.longitudinalActuatorDelay == pytest.approx(0.15)
|
||||
assert long_params.openpilotLongitudinalControl
|
||||
assert not long_params.flags & ToyotaFlags.HYBRID.value
|
||||
assert long_params.longitudinalActuatorDelay == pytest.approx(0.4)
|
||||
|
||||
@pytest.mark.parametrize("camera_message", [0x343, 0x4CB])
|
||||
def test_dsu_bypass_enables_longitudinal(self, camera_message):
|
||||
fingerprint = {bus: {} for bus in range(8)}
|
||||
|
||||
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.
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.
Binary file not shown.
Binary file not shown.
@@ -1,2 +1,2 @@
|
||||
extern const uint8_t gitversion[19];
|
||||
const uint8_t gitversion[19] = "DEV-d773ad90-DEBUG";
|
||||
const uint8_t gitversion[19] = "DEV-f6d38dbd-DEBUG";
|
||||
|
||||
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.
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.
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.
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.
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.
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.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1 +1 @@
|
||||
DEV-d773ad90-DEBUG
|
||||
DEV-f6d38dbd-DEBUG
|
||||
@@ -166,7 +166,12 @@ expected_capnp_version() {
|
||||
|
||||
image_capnp_version() {
|
||||
local engine="$1"
|
||||
"${engine}" run --rm --platform linux/arm64 "${IMAGE_NAME}" bash -lc "capnp --version | awk '{print \$4}'" 2>/dev/null || true
|
||||
local version_output=""
|
||||
|
||||
version_output="$("${engine}" run --rm --platform linux/arm64 "${IMAGE_NAME}" capnp --version 2>&1 || true)"
|
||||
printf '%s\n' "${version_output}" \
|
||||
| sed -nE 's/.*version[[:space:]]+([0-9]+\.[0-9]+\.[0-9]+).*/\1/p' \
|
||||
| head -n 1
|
||||
}
|
||||
|
||||
ensure_image_capnp_version() {
|
||||
|
||||
@@ -210,6 +210,8 @@ import msgq.ipc_pyx # noqa: F401
|
||||
import msgq.visionipc.visionipc_pyx # noqa: F401
|
||||
import openpilot.selfdrive.controls.lib.lateral_mpc_lib.c_generated_code.acados_ocp_solver_pyx # noqa: F401
|
||||
import openpilot.selfdrive.controls.lib.longitudinal_mpc_lib.c_generated_code.acados_ocp_solver_pyx # noqa: F401
|
||||
|
||||
assert hasattr(msgq.visionipc.visionipc_pyx.VisionBuf, "frame_id")
|
||||
PY
|
||||
}
|
||||
|
||||
|
||||
@@ -212,6 +212,8 @@ import msgq.ipc_pyx # noqa: F401
|
||||
import msgq.visionipc.visionipc_pyx # noqa: F401
|
||||
import openpilot.selfdrive.controls.lib.lateral_mpc_lib.c_generated_code.acados_ocp_solver_pyx # noqa: F401
|
||||
import openpilot.selfdrive.controls.lib.longitudinal_mpc_lib.c_generated_code.acados_ocp_solver_pyx # noqa: F401
|
||||
|
||||
assert hasattr(msgq.visionipc.visionipc_pyx.VisionBuf, "frame_id")
|
||||
PY
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import hashlib
|
||||
import json
|
||||
import os
|
||||
import pickle
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
@@ -19,6 +20,7 @@ COMPILE_SCRIPT = REPO_ROOT / "tinygrad_repo/examples/openpilot/compile3.py"
|
||||
DRIVING_COMPILE_SCRIPT = REPO_ROOT / "selfdrive/modeld/compile_modeld.py"
|
||||
DM_WARP_COMPILE_SCRIPT = REPO_ROOT / "selfdrive/modeld/compile_dm_warp.py"
|
||||
MODEL_VERSIONS_CACHE = Path("/data/models/.model_versions.json")
|
||||
MODELS_PATH = MODEL_VERSIONS_CACHE.parent # runtime dir modeld loads from: /data/models
|
||||
|
||||
DM_MODEL_KEY = "dm"
|
||||
DM_MODEL_NAME = "dmonitoring_model"
|
||||
@@ -84,6 +86,11 @@ def parse_args() -> argparse.Namespace:
|
||||
help="Compile the driving artifact for the USB AMD GPU.")
|
||||
parser.add_argument("--split-artifact", type=Path, help="Split an existing oversized PKL without compiling.")
|
||||
parser.add_argument("--chunk-size-mib", type=int, default=95, help="Multipart size in MiB; must be below 100.")
|
||||
parser.add_argument("--no-split", action="store_true",
|
||||
help="Keep a single .pkl even if >100 MiB (for local installs, which need one "
|
||||
"file). Auto-enabled for local- model IDs.")
|
||||
parser.add_argument("--no-install", action="store_true",
|
||||
help="Do not auto-copy a local- model into /data/models after compiling.")
|
||||
parser.add_argument(
|
||||
"--image-history-pipeline",
|
||||
choices=("policy", "warp"),
|
||||
@@ -302,6 +309,48 @@ def multipart_output_paths(artifact: Path, output_dir: Path | None = None) -> li
|
||||
]
|
||||
|
||||
|
||||
def install_local_artifact(artifact: Path, model_key: str, version: str) -> None:
|
||||
"""Copy a freshly compiled local- model into the runtime dir modeld loads from,
|
||||
and ensure its <id>.json sidecar carries the correct version.
|
||||
|
||||
The sidecar version is NOT cosmetic: without it _discover_local_models() records
|
||||
an empty version, which downstream parses on the wrong contract (a v15 model then
|
||||
drives like v11). Since we know the build version here, we write it so the local
|
||||
install is correct by default. Local models must be a single is_file() in
|
||||
/data/models to show in the picker. No-ops off-device (no /data/models).
|
||||
"""
|
||||
if not MODELS_PATH.is_dir():
|
||||
print(f" skipped auto-install: {MODELS_PATH} not present (not on device?)")
|
||||
return
|
||||
dest = MODELS_PATH / artifact.name
|
||||
shutil.copy2(artifact, dest)
|
||||
print(f" installed -> {dest}")
|
||||
|
||||
sidecar = MODELS_PATH / f"{model_key}.json"
|
||||
info: dict = {}
|
||||
if sidecar.is_file():
|
||||
try:
|
||||
loaded = json.loads(sidecar.read_text())
|
||||
if isinstance(loaded, dict):
|
||||
info = loaded
|
||||
except Exception as error:
|
||||
print(f" WARN: existing sidecar {sidecar.name} is malformed, rewriting: {error}")
|
||||
if not version:
|
||||
if not str(info.get("version") or "").strip():
|
||||
print(f" WARN: could not determine version -- set it by hand in {sidecar.name} "
|
||||
"or the model may drive on the wrong version contract")
|
||||
return
|
||||
# keep any user-set name/series; only guarantee a correct, non-empty version
|
||||
if str(info.get("version") or "").strip() == version and sidecar.is_file():
|
||||
print(f" sidecar ok: {sidecar.name} (version {version})")
|
||||
return
|
||||
info.setdefault("name", model_key[len("local-"):].replace("_", " ").replace("-", " ").strip())
|
||||
info.setdefault("series", "Local")
|
||||
info["version"] = version
|
||||
sidecar.write_text(json.dumps(info, indent=2) + "\n")
|
||||
print(f" wrote sidecar {sidecar.name} (version {version})")
|
||||
|
||||
|
||||
def split_oversized_artifact(
|
||||
artifact: Path,
|
||||
output_dir: Path | None = None,
|
||||
@@ -507,17 +556,34 @@ def main() -> int:
|
||||
if not version and input_format == "supercombo":
|
||||
version = "v15"
|
||||
version_label = version or "unspecified behavior"
|
||||
print(f"Compiling {model_key} ({input_format}, {version_label}) from {args.input_dir} -> {args.output_dir}")
|
||||
will_install = model_key.startswith("local-") and not args.no_install
|
||||
target = f"{args.output_dir}" + (f" -> {MODELS_PATH} (auto-install)" if will_install else "")
|
||||
print(f"Compiling {model_key} ({input_format}, {version_label}) from {args.input_dir} -> {target}")
|
||||
output = compile_driving(model_key, files, input_format, version, args.output_dir,
|
||||
args.image_history_pipeline, args.external_gpu)
|
||||
print(f" saved {output.name}")
|
||||
multipart_outputs = split_oversized_artifact(output)
|
||||
if multipart_outputs:
|
||||
print(" artifact exceeds 100 MiB; created repository-safe multipart files:")
|
||||
for multipart_output in multipart_outputs:
|
||||
print(f" {multipart_output.name} ({multipart_output.stat().st_size} bytes)")
|
||||
output.unlink()
|
||||
print(f" removed oversized source artifact {output.name}")
|
||||
# Local models install as a single is_file() and never go to GitHub, so the >100 MiB
|
||||
# repo split is pointless for them (you'd only have to reassemble it). Keep one .pkl.
|
||||
keep_single = args.no_split or model_key.startswith("local-")
|
||||
if keep_single:
|
||||
is_local = model_key.startswith("local-")
|
||||
if output.stat().st_size > REPOSITORY_FILE_LIMIT:
|
||||
size_mb = output.stat().st_size / 1e6
|
||||
if is_local:
|
||||
print(f" local model: kept as one {size_mb:.1f} MB file (repo split not needed)")
|
||||
else:
|
||||
print(f" --no-split: kept one {size_mb:.1f} MB file; over 100 MB, so split it "
|
||||
"before committing to a repo (re-run without --no-split, or --split-artifact)")
|
||||
if is_local and not args.no_install:
|
||||
install_local_artifact(output, model_key, version)
|
||||
else:
|
||||
multipart_outputs = split_oversized_artifact(output)
|
||||
if multipart_outputs:
|
||||
print(" artifact exceeds 100 MiB; created repository-safe multipart files:")
|
||||
for multipart_output in multipart_outputs:
|
||||
print(f" {multipart_output.name} ({multipart_output.stat().st_size} bytes)")
|
||||
output.unlink()
|
||||
print(f" removed oversized source artifact {output.name}")
|
||||
print("Done.")
|
||||
return 0
|
||||
|
||||
|
||||
@@ -42,7 +42,6 @@ def get_test_starpilot_toggles() -> SimpleNamespace:
|
||||
cluster_offset=1.0,
|
||||
disable_openpilot_long=False,
|
||||
force_fingerprint=False,
|
||||
frogsgomoo_tweak=False,
|
||||
lock_doors=False,
|
||||
reverse_cruise_increase=False,
|
||||
sng_hack=False,
|
||||
|
||||
@@ -14,6 +14,7 @@ from opendbc.car.chrysler.values import pacifica_hybrid_aol_stock_acc_mode
|
||||
from opendbc.car.gm.values import CAR as GM_CAR
|
||||
from opendbc.car.vehicle_model import VehicleModel
|
||||
from openpilot.selfdrive.controls.lib.drive_helpers import MAX_LATERAL_JERK, clip_curvature, get_lateral_active
|
||||
from openpilot.selfdrive.controls.lib.lane_centering import LaneCenteringController
|
||||
from openpilot.selfdrive.controls.lib.latcontrol import LatControl
|
||||
from openpilot.selfdrive.controls.lib.latcontrol_pid import LatControlPID
|
||||
from openpilot.selfdrive.controls.lib.latcontrol_angle import LatControlAngle, STEER_ANGLE_SATURATION_THRESHOLD
|
||||
@@ -305,6 +306,7 @@ class Controls:
|
||||
self.curvature = 0.0
|
||||
self.desired_curvature = 0.0
|
||||
self.lc_smooth_release = 0.0
|
||||
self.lane_centering = LaneCenteringController()
|
||||
self.lc_entry_sign = 0.0
|
||||
self.lc_arrest_jerk_factor = 1.0
|
||||
self.turn_hold_curvature = 0.0
|
||||
@@ -419,6 +421,7 @@ class Controls:
|
||||
|
||||
if not CC.latActive:
|
||||
self.LaC.reset()
|
||||
self.lane_centering.reset()
|
||||
if not CC.longActive:
|
||||
self.LoC.reset()
|
||||
|
||||
@@ -585,6 +588,14 @@ class Controls:
|
||||
held_mag = min(lead_curvature * blinker_dir, abs(self.turn_hold_curvature) + CURVATURE_HOLD_RATCHET_RATE * DT_CTRL)
|
||||
self.turn_hold_curvature = math.copysign(held_mag, lead_curvature)
|
||||
|
||||
new_desired_curvature = self.lane_centering.update(
|
||||
new_desired_curvature, model_v2, CS.vEgo,
|
||||
self.starpilot_toggles.lane_centering,
|
||||
self.starpilot_toggles.lane_center_offset,
|
||||
self.starpilot_toggles.lane_centering_e2e_authority,
|
||||
CC.latActive,
|
||||
bool(self.sm.all_checks(['modelV2'])))
|
||||
|
||||
jerk_factor = 1.0
|
||||
if self.starpilot_toggles.lane_change_pace < 10:
|
||||
set_jerk = self.starpilot_toggles.lane_change_jerk_factor
|
||||
|
||||
@@ -0,0 +1,134 @@
|
||||
from cereal import log
|
||||
import numpy as np
|
||||
|
||||
from openpilot.common.realtime import DT_CTRL
|
||||
from openpilot.selfdrive.controls.lib.drive_helpers import smooth_value
|
||||
|
||||
|
||||
_MIN_V_EGO = 5.0
|
||||
_MIN_LANE_PROB = 0.6
|
||||
_MAX_LANE_STD = 0.3
|
||||
_MIN_LANE_WIDTH = 2.6
|
||||
_MAX_LANE_WIDTH = 4.8
|
||||
_MAX_OFFSET = 0.3
|
||||
_MIN_CENTER_TO_LINE = 1.1
|
||||
_MAX_RAW_CORRECTION = 0.004
|
||||
_MAX_GAIN = 0.30
|
||||
_SMOOTH_TAU = 0.4
|
||||
|
||||
_E2E_MAX_PATH_STD = 0.35
|
||||
_E2E_BREAK_IN_START = 0.25
|
||||
_E2E_BREAK_IN_FULL = 0.75
|
||||
|
||||
|
||||
class LaneCenteringController:
|
||||
def __init__(self) -> None:
|
||||
self._correction = 0.0
|
||||
|
||||
def reset(self) -> None:
|
||||
self._correction = 0.0
|
||||
|
||||
def update(self, model_curvature, model_v2, v_ego, enabled, offset, e2e_authority, lat_active, model_valid) -> float:
|
||||
model_curvature = float(model_curvature)
|
||||
|
||||
try:
|
||||
v_ego = float(v_ego)
|
||||
offset = float(offset)
|
||||
e2e_authority = float(e2e_authority)
|
||||
except (TypeError, ValueError):
|
||||
self.reset()
|
||||
return model_curvature
|
||||
|
||||
if not np.isfinite([v_ego, offset, e2e_authority]).all():
|
||||
self.reset()
|
||||
return model_curvature
|
||||
|
||||
if not model_valid or not enabled or not lat_active or v_ego < _MIN_V_EGO:
|
||||
self.reset()
|
||||
return model_curvature
|
||||
|
||||
try:
|
||||
if model_v2.meta.laneChangeState != log.LaneChangeState.off:
|
||||
self.reset()
|
||||
return model_curvature
|
||||
except (AttributeError, TypeError, ValueError):
|
||||
self.reset()
|
||||
return model_curvature
|
||||
|
||||
valid, raw_correction = self._raw_correction(
|
||||
model_v2,
|
||||
v_ego,
|
||||
float(np.clip(offset, -_MAX_OFFSET, _MAX_OFFSET)),
|
||||
float(np.clip(e2e_authority, 0.0, 1.0)),
|
||||
)
|
||||
if not valid:
|
||||
self.reset()
|
||||
return model_curvature
|
||||
|
||||
target = float(np.clip(raw_correction, -_MAX_RAW_CORRECTION, _MAX_RAW_CORRECTION)) * _MAX_GAIN
|
||||
self._correction = float(smooth_value(target, self._correction, _SMOOTH_TAU, dt=DT_CTRL))
|
||||
return model_curvature + self._correction
|
||||
|
||||
@staticmethod
|
||||
def _valid_path(x, y) -> bool:
|
||||
return x.size >= 2 and x.size == y.size and np.isfinite(x).all() and np.isfinite(y).all() and np.all(np.diff(x) > 0)
|
||||
|
||||
@staticmethod
|
||||
def _covers(x, distance: float) -> bool:
|
||||
return bool(x[0] <= distance <= x[-1])
|
||||
|
||||
def _raw_correction(self, model_v2, v_ego: float, offset: float, e2e_authority: float) -> tuple[bool, float]:
|
||||
try:
|
||||
lane_lines = model_v2.laneLines
|
||||
probs = np.asarray(model_v2.laneLineProbs, dtype=float)
|
||||
stds = np.asarray(model_v2.laneLineStds, dtype=float)
|
||||
if len(lane_lines) < 3 or probs.size < 3 or stds.size < 3:
|
||||
return False, 0.0
|
||||
if not np.isfinite(probs[[1, 2]]).all() or not np.isfinite(stds[[1, 2]]).all():
|
||||
return False, 0.0
|
||||
if np.any(probs[[1, 2]] < _MIN_LANE_PROB) or np.any(probs[[1, 2]] > 1.0):
|
||||
return False, 0.0
|
||||
if np.any(stds[[1, 2]] < 0.0) or np.any(stds[[1, 2]] > _MAX_LANE_STD):
|
||||
return False, 0.0
|
||||
|
||||
left_x = np.asarray(lane_lines[1].x, dtype=float)
|
||||
left_y = np.asarray(lane_lines[1].y, dtype=float)
|
||||
right_x = np.asarray(lane_lines[2].x, dtype=float)
|
||||
right_y = np.asarray(lane_lines[2].y, dtype=float)
|
||||
pos_x = np.asarray(model_v2.position.x, dtype=float)
|
||||
pos_y = np.asarray(model_v2.position.y, dtype=float)
|
||||
if not (self._valid_path(left_x, left_y) and self._valid_path(right_x, right_y) and self._valid_path(pos_x, pos_y)):
|
||||
return False, 0.0
|
||||
|
||||
lookahead = float(np.clip(v_ego, 8.0, 35.0))
|
||||
if not all(self._covers(x, lookahead) for x in (left_x, right_x, pos_x)):
|
||||
return False, 0.0
|
||||
|
||||
left = float(np.interp(lookahead, left_x, left_y))
|
||||
right = float(np.interp(lookahead, right_x, right_y))
|
||||
width = right - left
|
||||
if not _MIN_LANE_WIDTH <= width <= _MAX_LANE_WIDTH:
|
||||
return False, 0.0
|
||||
|
||||
max_safe_offset = min(_MAX_OFFSET, max(0.0, width * 0.5 - _MIN_CENTER_TO_LINE))
|
||||
target_y = 0.5 * (left + right) + float(np.clip(offset, -max_safe_offset, max_safe_offset))
|
||||
model_y = float(np.interp(lookahead, pos_x, pos_y))
|
||||
error = target_y - model_y
|
||||
|
||||
try:
|
||||
pos_y_std = np.asarray(model_v2.position.yStd, dtype=float)
|
||||
if self._valid_path(pos_x, pos_y_std):
|
||||
path_std = float(np.interp(lookahead, pos_x, pos_y_std))
|
||||
if 0.0 <= path_std <= _E2E_MAX_PATH_STD:
|
||||
break_in = np.clip(
|
||||
(abs(error) - _E2E_BREAK_IN_START) / (_E2E_BREAK_IN_FULL - _E2E_BREAK_IN_START),
|
||||
0.0,
|
||||
1.0,
|
||||
)
|
||||
error *= 1.0 - e2e_authority * float(break_in)
|
||||
except (AttributeError, TypeError, ValueError):
|
||||
pass
|
||||
|
||||
return True, float(2.0 * error / lookahead ** 2)
|
||||
except (AttributeError, IndexError, TypeError, ValueError):
|
||||
return False, 0.0
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user