diff --git a/RELEASES.md b/RELEASES.md index 8a5638bc84..071622f89a 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -6,7 +6,7 @@ Version 0.9.8 (2025-02-28) ======================== * New driving model * Model now gates applying positive acceleration in Chill mode -* New driving monitoring model +* New driver monitoring model * Reduced false positives related to passengers * Image processing pipeline moved to the ISP * More GPU time for bigger driving models diff --git a/cereal/log.capnp b/cereal/log.capnp index 059d3ecbb7..0587ba96fc 100644 --- a/cereal/log.capnp +++ b/cereal/log.capnp @@ -59,6 +59,7 @@ struct OnroadEvent @0xc4fa6047f024e718 { pcmEnable @23; pcmDisable @24; radarFault @25; + radarTempUnavailable @93; brakeHold @26; parkBrake @27; manualRestart @28; @@ -728,7 +729,7 @@ struct PeripheralState { struct RadarState @0x9a185389d6fdd05f { mdMonoTime @6 :UInt64; carStateMonoTime @11 :UInt64; - radarErrors @12 :List(Car.RadarData.Error); + radarErrors @13 :Car.RadarData.Error; leadOne @3 :LeadData; leadTwo @4 :LeadData; @@ -762,6 +763,7 @@ struct RadarState @0x9a185389d6fdd05f { calPercDEPRECATED @9 :Int8; canMonoTimesDEPRECATED @10 :List(UInt64); cumLagMsDEPRECATED @5 :Float32; + radarErrorsDEPRECATED @12 :List(Car.RadarData.ErrorDEPRECATED); } struct LiveCalibrationData { diff --git a/opendbc_repo b/opendbc_repo index 9fbd19d3a6..9298a1ac9d 160000 --- a/opendbc_repo +++ b/opendbc_repo @@ -1 +1 @@ -Subproject commit 9fbd19d3a6d45f5cd0bb979e0b1d2e2247246b85 +Subproject commit 9298a1ac9ddcd0fed977cd518fc62c45b780ef36 diff --git a/release/build_release.sh b/release/build_release.sh index a582dbab9f..5ada9c5ecc 100755 --- a/release/build_release.sh +++ b/release/build_release.sh @@ -40,8 +40,6 @@ rm -f panda/board/obj/panda.bin.signed rm -f panda/board/obj/panda_h7.bin.signed VERSION=$(cat common/version.h | awk -F[\"-] '{print $2}') -echo "#define COMMA_VERSION \"$VERSION-release\"" > common/version.h - echo "[-] committing version $VERSION T=$SECONDS" git add -f . git commit -a -m "openpilot v$VERSION release" diff --git a/selfdrive/car/card.py b/selfdrive/car/card.py index e7c450737b..39344f485f 100755 --- a/selfdrive/car/card.py +++ b/selfdrive/car/card.py @@ -16,7 +16,7 @@ from opendbc.car import DT_CTRL, structs from opendbc.car.can_definitions import CanData, CanRecvCallable, CanSendCallable from opendbc.car.carlog import carlog from opendbc.car.fw_versions import ObdCallback -from opendbc.car.car_helpers import get_car, get_radar_interface +from opendbc.car.car_helpers import get_car, interfaces from opendbc.car.interfaces import CarInterfaceBase, RadarInterfaceBase from opendbc.safety import ALTERNATIVE_EXPERIENCE from openpilot.selfdrive.pandad import can_capnp_to_list, can_list_to_can_capnp @@ -25,7 +25,7 @@ from openpilot.selfdrive.car.car_specific import MockCarState from openpilot.selfdrive.car.helpers import convert_carControlSP, convert_to_capnp from openpilot.sunnypilot.mads.mads import MadsParams -from openpilot.sunnypilot.selfdrive.car import interfaces +from openpilot.sunnypilot.selfdrive.car import interfaces as sunnypilot_interfaces REPLAY = "REPLAY" in os.environ @@ -108,8 +108,8 @@ class Car: fixed_fingerprint = json.loads(self.params.get("CarPlatformBundle", encoding='utf-8') or "{}").get("platform", None) self.CI = get_car(*self.can_callbacks, obd_callback(self.params), experimental_long_allowed, num_pandas, cached_params, fixed_fingerprint) - interfaces.setup_car_interface_sp(self.CI.CP, self.CI.CP_SP, self.params) - self.RI = get_radar_interface(self.CI.CP, self.CI.CP_SP) + sunnypilot_interfaces.setup_car_interface_sp(self.CI.CP, self.CI.CP_SP, self.params) + self.RI = interfaces[self.CI.CP.carFingerprint].RadarInterface(self.CI.CP, self.CI.CP_SP) self.CP = self.CI.CP self.CP_SP = self.CI.CP_SP @@ -192,7 +192,7 @@ class Car: self.rk = Ratekeeper(100, print_delay_threshold=None) # log fingerprint in sentry - interfaces.log_fingerprint(self.CP) + sunnypilot_interfaces.log_fingerprint(self.CP) def state_update(self) -> tuple[car.CarState, structs.RadarDataT | None]: """carState update loop, driven by can""" @@ -256,7 +256,7 @@ class Car: if RD is not None: tracks_msg = messaging.new_message('liveTracks') - tracks_msg.valid = len(RD.errors) == 0 + tracks_msg.valid = not any(RD.errors.to_dict().values()) tracks_msg.liveTracks = RD self.pm.send('liveTracks', tracks_msg) @@ -274,7 +274,7 @@ class Car: # Initialize CarInterface, once controls are ready # TODO: this can make us miss at least a few cycles when doing an ECU knockout self.CI.init(self.CP, self.CP_SP, *self.can_callbacks) - interfaces.initialize_car_interface_sp(self.CP, self.CP_SP, self.params, *self.can_callbacks) + sunnypilot_interfaces.initialize_car_interface_sp(self.CP, self.CP_SP, self.params, *self.can_callbacks) # signal pandad to switch to car safety mode self.params.put_bool_nonblocking("ControlsReady", True) diff --git a/selfdrive/car/tests/test_car_interfaces.py b/selfdrive/car/tests/test_car_interfaces.py index c10443c2f4..9e6b96ba59 100644 --- a/selfdrive/car/tests/test_car_interfaces.py +++ b/selfdrive/car/tests/test_car_interfaces.py @@ -35,7 +35,7 @@ class TestCarInterfaces: phases=(Phase.reuse, Phase.generate, Phase.shrink)) @given(data=st.data()) def test_car_interfaces(self, car_name, data): - CarInterface, CarController, CarState, RadarInterface = interfaces[car_name] + CarInterface = interfaces[car_name] args = get_fuzzy_car_interface_args(data.draw) @@ -44,7 +44,7 @@ class TestCarInterfaces: car_params_sp = CarInterface.get_params_sp(car_params, car_name, args['fingerprints'], args['car_fw'], experimental_long=args['experimental_long'], docs=False) car_params = car_params.as_reader() - car_interface = CarInterface(car_params, car_params_sp, CarController, CarState) + car_interface = CarInterface(car_params, car_params_sp) assert car_params assert car_params_sp assert car_interface diff --git a/selfdrive/car/tests/test_models.py b/selfdrive/car/tests/test_models.py index 573d818365..3193b68cd8 100644 --- a/selfdrive/car/tests/test_models.py +++ b/selfdrive/car/tests/test_models.py @@ -152,8 +152,8 @@ class TestCarModelBase(unittest.TestCase): # if relay is expected to be open in the route cls.openpilot_enabled = cls.car_safety_mode_frame is not None - cls.CarInterface, cls.CarController, cls.CarState, cls.RadarInterface = interfaces[cls.platform] - cls.CP = cls.CarInterface.get_params(cls.platform, cls.fingerprint, car_fw, experimental_long, docs=False) + cls.CarInterface = interfaces[cls.platform] + cls.CP = cls.CarInterface.get_params(cls.platform, cls.fingerprint, car_fw, experimental_long, docs=False) cls.CP_SP = cls.CarInterface.get_params_sp(cls.CP, cls.platform, cls.fingerprint, car_fw, experimental_long, docs=False) assert cls.CP assert cls.CP_SP @@ -166,7 +166,7 @@ class TestCarModelBase(unittest.TestCase): del cls.can_msgs def setUp(self): - self.CI = self.CarInterface(self.CP.copy(), copy.deepcopy(self.CP_SP), self.CarController, self.CarState) + self.CI = self.CarInterface(self.CP.copy(), copy.deepcopy(self.CP_SP)) assert self.CI # TODO: check safetyModel is in release panda build @@ -217,7 +217,7 @@ class TestCarModelBase(unittest.TestCase): self.assertEqual(can_invalid_cnt, 0) def test_radar_interface(self): - RI = self.RadarInterface(self.CP, self.CP_SP) + RI = self.CarInterface.RadarInterface(self.CP, self.CP_SP) assert RI # Since OBD port is multiplexed to bus 1 (commonly radar bus) while fingerprinting, @@ -226,7 +226,7 @@ class TestCarModelBase(unittest.TestCase): for i, msg in enumerate(self.can_msgs[self.elm_frame:]): rr: structs.RadarData | None = RI.update(msg) if rr is not None and i > 50: - error_cnt += structs.RadarData.Error.canError in rr.errors + error_cnt += rr.errors.canError self.assertEqual(error_cnt, 0) def test_panda_safety_rx_checks(self): @@ -280,7 +280,7 @@ class TestCarModelBase(unittest.TestCase): def test_car_controller(car_control, car_control_sp): now_nanos = 0 msgs_sent = 0 - CI = self.CarInterface(self.CP, self.CP_SP, self.CarController, self.CarState) + CI = self.CarInterface(self.CP, self.CP_SP) for _ in range(round(10.0 / DT_CTRL)): # make sure we hit the slowest messages CI.update([]) _, sendcan = CI.apply(car_control, car_control_sp, now_nanos) diff --git a/selfdrive/controls/controlsd.py b/selfdrive/controls/controlsd.py index 357f40c61f..f9ba54bfa7 100755 --- a/selfdrive/controls/controlsd.py +++ b/selfdrive/controls/controlsd.py @@ -9,7 +9,7 @@ from openpilot.common.params import Params from openpilot.common.realtime import config_realtime_process, Priority, Ratekeeper from openpilot.common.swaglog import cloudlog -from opendbc.car.car_helpers import get_car_interface +from opendbc.car.car_helpers import interfaces from opendbc.car.vehicle_model import VehicleModel from openpilot.selfdrive.controls.lib.drive_helpers import clip_curvature from openpilot.selfdrive.controls.lib.latcontrol import LatControl, MIN_LATERAL_CONTROL_SPEED @@ -37,7 +37,7 @@ class Controls: self.CP_SP = messaging.log_from_bytes(self.params.get("CarParamsSP", block=True), custom.CarParamsSP) cloudlog.info("controlsd got CarParamsSP") - self.CI = get_car_interface(self.CP, self.CP_SP) + self.CI = interfaces[self.CP.carFingerprint](self.CP, self.CP_SP) self.sm = messaging.SubMaster(['liveParameters', 'liveTorqueParameters', 'modelV2', 'selfdriveState', 'liveCalibration', 'livePose', 'longitudinalPlan', 'carState', 'carOutput', diff --git a/selfdrive/controls/lib/tests/test_latcontrol.py b/selfdrive/controls/lib/tests/test_latcontrol.py index 81edb15a98..299652ff18 100644 --- a/selfdrive/controls/lib/tests/test_latcontrol.py +++ b/selfdrive/controls/lib/tests/test_latcontrol.py @@ -17,10 +17,10 @@ class TestLatControl: @parameterized.expand([(HONDA.HONDA_CIVIC, LatControlPID), (TOYOTA.TOYOTA_RAV4, LatControlTorque), (NISSAN.NISSAN_LEAF, LatControlAngle)]) def test_saturation(self, car_name, controller): - CarInterface, CarController, CarState, RadarInterface = interfaces[car_name] + CarInterface = interfaces[car_name] CP = CarInterface.get_non_essential_params(car_name) CP_SP = CarInterface.get_non_essential_params_sp(CP, car_name) - CI = CarInterface(CP, CP_SP, CarController, CarState) + CI = CarInterface(CP, CP_SP) VM = VehicleModel(CP) controller = controller(CP.as_reader(), CI) diff --git a/selfdrive/controls/plannerd.py b/selfdrive/controls/plannerd.py index 99942f20d9..60036c3082 100755 --- a/selfdrive/controls/plannerd.py +++ b/selfdrive/controls/plannerd.py @@ -20,7 +20,7 @@ def main(): longitudinal_planner = LongitudinalPlanner(CP) pm = messaging.PubMaster(['longitudinalPlan', 'driverAssistance', 'longitudinalPlanSP']) sm = messaging.SubMaster(['carControl', 'carState', 'controlsState', 'liveParameters', 'radarState', 'modelV2', 'selfdriveState'], - poll='modelV2', ignore_avg_freq=['radarState']) + poll='modelV2') while True: sm.update() diff --git a/selfdrive/controls/radard.py b/selfdrive/controls/radard.py index 0267166795..98fce1cb26 100755 --- a/selfdrive/controls/radard.py +++ b/selfdrive/controls/radard.py @@ -6,6 +6,7 @@ from typing import Any import capnp from cereal import messaging, log, car +from openpilot.common.filter_simple import FirstOrderFilter from openpilot.common.params import Params from openpilot.common.realtime import DT_MDL, Priority, config_realtime_process from openpilot.common.swaglog import cloudlog @@ -51,7 +52,7 @@ class Track: def __init__(self, identifier: int, v_lead: float, kalman_params: KalmanParams): self.identifier = identifier self.cnt = 0 - self.aLeadTau = _LEAD_ACCEL_TAU + self.aLeadTau = FirstOrderFilter(_LEAD_ACCEL_TAU, 0.45, DT_MDL) self.K_A = kalman_params.A self.K_C = kalman_params.C self.K_K = kalman_params.K @@ -74,17 +75,12 @@ class Track: # Learn if constant acceleration if abs(self.aLeadK) < 0.5: - self.aLeadTau = _LEAD_ACCEL_TAU + self.aLeadTau.x = _LEAD_ACCEL_TAU else: - self.aLeadTau *= 0.9 + self.aLeadTau.update(0.0) self.cnt += 1 - def reset_a_lead(self, aLeadK: float, aLeadTau: float): - self.kf = KF1D([[self.vLead], [aLeadK]], self.K_A, self.K_C, self.K_K) - self.aLeadK = aLeadK - self.aLeadTau = aLeadTau - def get_RadarState(self, model_prob: float = 0.0): return { "dRel": float(self.dRel), @@ -93,7 +89,7 @@ class Track: "vLead": float(self.vLead), "vLeadK": float(self.vLeadK), "aLeadK": float(self.aLeadK), - "aLeadTau": float(self.aLeadTau), + "aLeadTau": float(self.aLeadTau.x), "status": True, "fcw": self.is_potential_fcw(model_prob), "modelProb": model_prob, @@ -231,10 +227,10 @@ class RadarD: self.tracks[ids].update(rpt[0], rpt[1], rpt[2], v_lead, rpt[3]) # *** publish radarState *** - self.radar_state_valid = sm.all_checks() and len(rr.errors) == 0 + self.radar_state_valid = sm.all_checks() self.radar_state = log.RadarState.new_message() self.radar_state.mdMonoTime = sm.logMonoTime['modelV2'] - self.radar_state.radarErrors = list(rr.errors) + self.radar_state.radarErrors = rr.errors self.radar_state.carStateMonoTime = sm.logMonoTime['carState'] if len(sm['modelV2'].velocity.x): diff --git a/selfdrive/controls/tests/test_leads.py b/selfdrive/controls/tests/test_leads.py index 89582d1e64..77384fea20 100644 --- a/selfdrive/controls/tests/test_leads.py +++ b/selfdrive/controls/tests/test_leads.py @@ -26,6 +26,6 @@ class TestLeads: msgs = [m for _ in range(3) for m in single_iter_pkg()] out = replay_process_with_name("card", msgs, fingerprint=TOYOTA.TOYOTA_COROLLA_TSS2) states = [m for m in out if m.which() == "liveTracks"] - failures = [not state.valid and len(state.liveTracks.errors) for state in states] + failures = [not state.valid for state in states] assert len(states) == 0 or all(failures) diff --git a/selfdrive/debug/run_process_on_route.py b/selfdrive/debug/run_process_on_route.py index 45665e8a48..2ccb0fb3e7 100755 --- a/selfdrive/debug/run_process_on_route.py +++ b/selfdrive/debug/run_process_on_route.py @@ -10,21 +10,21 @@ if __name__ == "__main__": formatter_class=argparse.ArgumentDefaultsHelpFormatter) parser.add_argument("--fingerprint", help="The fingerprint to use") parser.add_argument("route", help="The route name to use") - parser.add_argument("process", help="The process to run") + parser.add_argument("process", nargs='+', help="The process(s) to run") args = parser.parse_args() - cfg = [c for c in CONFIGS if c.proc_name == args.process][0] + cfgs = [c for c in CONFIGS if c.proc_name in args.process] lr = LogReader(args.route) inputs = list(lr) - outputs = replay_process(cfg, inputs, fingerprint=args.fingerprint) + outputs = replay_process(cfgs, inputs, fingerprint=args.fingerprint) # Remove message generated by the process under test and merge in the new messages produces = {o.which() for o in outputs} inputs = [i for i in inputs if i.which() not in produces] outputs = sorted(inputs + outputs, key=lambda x: x.logMonoTime) - fn = f"{args.route.replace('/', '_')}_{args.process}.zst" + fn = f"{args.route.replace('/', '_')}_{'_'.join(args.process)}.zst" print(f"Saving log to {fn}") save_log(fn, outputs) diff --git a/selfdrive/locationd/paramsd.py b/selfdrive/locationd/paramsd.py index 258fc97e5a..3819fba080 100755 --- a/selfdrive/locationd/paramsd.py +++ b/selfdrive/locationd/paramsd.py @@ -52,14 +52,14 @@ class ParamsLearner: device_pose = Pose.from_live_pose(msg) calibrated_pose = self.calibrator.build_calibrated_pose(device_pose) - yaw_rate_valid = msg.angularVelocityDevice.valid and self.calibrator.calib_valid + yaw_rate_valid = msg.angularVelocityDevice.valid yaw_rate_valid = yaw_rate_valid and 0 < self.yaw_rate_std < 10 # rad/s yaw_rate_valid = yaw_rate_valid and abs(self.yaw_rate) < 1 # rad/s if yaw_rate_valid: self.yaw_rate, self.yaw_rate_std = calibrated_pose.angular_velocity.z, calibrated_pose.angular_velocity.z_std else: # This is done to bound the yaw rate estimate when localizer values are invalid or calibrating - self.yaw_rate, self.yaw_rate_std = 0.0, np.radians(1) + self.yaw_rate, self.yaw_rate_std = 0.0, np.radians(10.0) localizer_roll, localizer_roll_std = device_pose.orientation.x, device_pose.orientation.x_std localizer_roll_std = np.radians(1) if np.isnan(localizer_roll_std) else localizer_roll_std @@ -225,13 +225,13 @@ def main(): liveParameters.posenetValid = True liveParameters.sensorValid = sensors_valid liveParameters.steerRatio = float(x[States.STEER_RATIO].item()) - liveParameters.steerRatioValid = min_sr <= liveParameters.steerRatio <= max_sr liveParameters.stiffnessFactor = float(x[States.STIFFNESS].item()) - liveParameters.stiffnessFactorValid = 0.2 <= liveParameters.stiffnessFactor <= 5.0 liveParameters.roll = float(roll) liveParameters.angleOffsetAverageDeg = float(angle_offset_average) - liveParameters.angleOffsetAverageValid = bool(avg_offset_valid) liveParameters.angleOffsetDeg = float(angle_offset) + liveParameters.steerRatioValid = min_sr <= liveParameters.steerRatio <= max_sr + liveParameters.stiffnessFactorValid = 0.2 <= liveParameters.stiffnessFactor <= 5.0 + liveParameters.angleOffsetAverageValid = bool(avg_offset_valid) liveParameters.angleOffsetValid = bool(total_offset_valid) liveParameters.valid = all(( liveParameters.angleOffsetAverageValid, diff --git a/selfdrive/pandad/panda.cc b/selfdrive/pandad/panda.cc index 8d8984c2d2..72b8ddcf81 100644 --- a/selfdrive/pandad/panda.cc +++ b/selfdrive/pandad/panda.cc @@ -66,6 +66,25 @@ void Panda::set_alternative_experience(uint16_t alternative_experience, uint16_t handle->control_write(0xdf, alternative_experience, safety_param_sp); } +std::string Panda::serial_read(int port_number) { + std::string ret; + char buffer[USBPACKET_MAX_SIZE] = {}; + + while (true) { + int bytes_read = handle->control_read(0xe0, port_number, 0, (unsigned char *)buffer, USBPACKET_MAX_SIZE); + if (bytes_read <= 0) { + break; + } + ret.append(buffer, bytes_read); + } + + return ret; +} + +void Panda::set_uart_baud(int uart, int rate) { + handle->control_write(0xe4, uart, int(rate / 300)); +} + cereal::PandaState::PandaType Panda::get_hw_type() { unsigned char hw_query[1] = {0}; diff --git a/selfdrive/pandad/panda.h b/selfdrive/pandad/panda.h index 5ed87104df..8545b558a9 100644 --- a/selfdrive/pandad/panda.h +++ b/selfdrive/pandad/panda.h @@ -64,6 +64,8 @@ public: cereal::PandaState::PandaType get_hw_type(); void set_safety_model(cereal::CarParams::SafetyModel safety_model, uint16_t safety_param=0U); void set_alternative_experience(uint16_t alternative_experience, uint16_t safety_param_sp=0U); + std::string serial_read(int port_number = 0); + void set_uart_baud(int uart, int rate); void set_fan_speed(uint16_t fan_speed); uint16_t get_fan_speed(); void set_ir_pwr(uint16_t ir_pwr); diff --git a/selfdrive/pandad/pandad.cc b/selfdrive/pandad/pandad.cc index 61da49470a..3025d0091b 100644 --- a/selfdrive/pandad/pandad.cc +++ b/selfdrive/pandad/pandad.cc @@ -470,6 +470,14 @@ void pandad_run(std::vector &pandas) { send_peripheral_state(peripheral_panda, &pm); } + // Forward logs from pandas to cloudlog if available + for (auto *panda : pandas) { + std::string log = panda->serial_read(); + if (!log.empty()) { + LOGD("%s", log.c_str()); + } + } + rk.keepTime(); } diff --git a/selfdrive/selfdrived/events.py b/selfdrive/selfdrived/events.py index f93cbaa740..d336334a4e 100755 --- a/selfdrive/selfdrived/events.py +++ b/selfdrive/selfdrived/events.py @@ -672,6 +672,11 @@ EVENTS: dict[int, dict[str, Alert | AlertCallbackType]] = { ET.NO_ENTRY: NoEntryAlert("Radar Error: Restart the Car"), }, + EventName.radarTempUnavailable: { + ET.SOFT_DISABLE: soft_disable_alert("Radar Temporarily Unavailable"), + ET.NO_ENTRY: NoEntryAlert("Radar Temporarily Unavailable"), + }, + # Every frame from the camera should be processed by the model. If modeld # is not processing frames fast enough they have to be dropped. This alert is # thrown when over 20% of frames are dropped. diff --git a/selfdrive/selfdrived/selfdrived.py b/selfdrive/selfdrived/selfdrived.py index b38b411db9..9a28a36169 100755 --- a/selfdrive/selfdrived/selfdrived.py +++ b/selfdrive/selfdrived/selfdrived.py @@ -92,7 +92,7 @@ class SelfdriveD(CruiseHelper): 'managerState', 'liveParameters', 'radarState', 'liveTorqueParameters', 'controlsState', 'carControl', 'driverAssistance', 'alertDebug'] + \ self.camera_packets + self.sensor_packets + self.gps_packets, - ignore_alive=ignore, ignore_avg_freq=ignore+['radarState',], + ignore_alive=ignore, ignore_avg_freq=ignore, ignore_valid=ignore, frequency=int(1/DT_CTRL)) # read params @@ -294,7 +294,13 @@ class SelfdriveD(CruiseHelper): self.events.add(EventName.cameraFrameRate) if not REPLAY and self.rk.lagging: self.events.add(EventName.selfdrivedLagging) - if len(self.sm['radarState'].radarErrors) or ((not self.rk.lagging or REPLAY) and not self.sm.all_checks(['radarState'])): + if not self.sm.valid['radarState']: + if self.sm['radarState'].radarErrors.canError: + self.events.add(EventName.canError) + elif self.sm['radarState'].radarErrors.radarUnavailableTemporary: + self.events.add(EventName.radarTempUnavailable) + else: + self.events.add(EventName.radarFault) self.events.add(EventName.radarFault) if not self.sm.valid['pandaStates']: self.events.add(EventName.usbError) @@ -330,7 +336,7 @@ class SelfdriveD(CruiseHelper): self.events.add(EventName.posenetInvalid) if not self.sm['livePose'].inputsOK: self.events.add(EventName.locationdTemporaryError) - if not self.sm['liveParameters'].valid and not TESTING_CLOSET and (not SIMULATION or REPLAY): + if not self.sm['liveParameters'].valid and cal_status == log.LiveCalibrationData.Status.calibrated and not TESTING_CLOSET and (not SIMULATION or REPLAY): self.events.add(EventName.paramsdTemporaryError) # conservative HW alert. if the data or frequency are off, locationd will throw an error diff --git a/selfdrive/test/process_replay/migration.py b/selfdrive/test/process_replay/migration.py index 2d42d31619..fd13c4f239 100644 --- a/selfdrive/test/process_replay/migration.py +++ b/selfdrive/test/process_replay/migration.py @@ -241,14 +241,16 @@ def migrate_gpsLocation(msgs): @migration(inputs=["deviceState", "initData"]) def migrate_deviceState(msgs): + init_data = next((m.initData for _, m in msgs if m.which() == 'initData'), None) + device_state = next((m.deviceState for _, m in msgs if m.which() == 'deviceState'), None) + if init_data is None or device_state is None: + return [], [], [] + ops = [] - dt = None for i, msg in msgs: - if msg.which() == 'initData': - dt = msg.initData.deviceType if msg.which() == 'deviceState': n = msg.as_builder() - n.deviceState.deviceType = dt + n.deviceState.deviceType = init_data.deviceType ops.append((i, n.as_reader())) return ops, [], [] diff --git a/selfdrive/test/process_replay/process_replay.py b/selfdrive/test/process_replay/process_replay.py index d1a0cc0827..e9b456e983 100755 --- a/selfdrive/test/process_replay/process_replay.py +++ b/selfdrive/test/process_replay/process_replay.py @@ -342,7 +342,7 @@ def card_fingerprint_callback(rc, pm, msgs, fingerprint): def get_car_params_callback(rc, pm, msgs, fingerprint): params = Params() if fingerprint: - CarInterface, _, _, _ = interfaces[fingerprint] + CarInterface = interfaces[fingerprint] CP = CarInterface.get_non_essential_params(fingerprint) CP_SP = CarInterface.get_non_essential_params_sp(CP, fingerprint) else: diff --git a/selfdrive/test/process_replay/ref_commit b/selfdrive/test/process_replay/ref_commit index cfd179fb79..353b8fa7ce 100644 --- a/selfdrive/test/process_replay/ref_commit +++ b/selfdrive/test/process_replay/ref_commit @@ -1 +1 @@ -37041a45841e83f0641ef1e87c0e567181d47172 \ No newline at end of file +e9d57157494480637a8ffb52257d2b660a48be67 \ No newline at end of file diff --git a/selfdrive/ui/qt/setup/setup.cc b/selfdrive/ui/qt/setup/setup.cc index aff9b015b3..dfa9e12b4e 100644 --- a/selfdrive/ui/qt/setup/setup.cc +++ b/selfdrive/ui/qt/setup/setup.cc @@ -48,7 +48,8 @@ void Setup::download(QString url) { auto version = util::read_file("/VERSION"); struct curl_slist *list = NULL; - list = curl_slist_append(list, ("X-openpilot-serial: " + Hardware::get_serial()).c_str()); + std::string header = "X-openpilot-serial: " + Hardware::get_serial(); + list = curl_slist_append(list, header.c_str()); char tmpfile[] = "/tmp/installer_XXXXXX"; FILE *fp = fdopen(mkstemp(tmpfile), "wb"); diff --git a/selfdrive/ui/translations/main_zh-CHS.ts b/selfdrive/ui/translations/main_zh-CHS.ts index caa32b4f2b..c9807fa2d6 100644 --- a/selfdrive/ui/translations/main_zh-CHS.ts +++ b/selfdrive/ui/translations/main_zh-CHS.ts @@ -425,28 +425,30 @@ openpilot learns to drive by watching humans, like you, drive. Firehose Mode allows you to maximize your training data uploads to improve openpilot's driving models. More data means bigger models, which means better Experimental Mode. - + openpilot 通过观察人类驾驶(包括您)来学习如何驾驶。 + +“训练数据上传模式”允许您最大化上传训练数据,以改进 openpilot 的驾驶模型。更多数据意味着更强大的模型,也就意味着更优秀的“实验模式”。 Firehose Mode: ACTIVE - + 训练数据上传模式:激活中 ACTIVE - + 激活中 <span stylesheet='font-size: 60px; font-weight: bold; color: #e74c3c;'>INACTIVE</span>: connect to unmetered network - + <span stylesheet='font-size: 60px; font-weight: bold; color: #e74c3c;'>未激活</span>:请连接至不限流量的网络 For maximum effectiveness, bring your device inside and connect to a good USB-C adapter and Wi-Fi weekly.<br><br>Firehose Mode can also work while you're driving if connected to a hotspot or unlimited SIM card.<br><br><br><b>Frequently Asked Questions</b><br><br><i>Does it matter how or where I drive?</i> Nope, just drive as you normally would.<br><br><i>Do all of my segments get pulled in Firehose Mode?</i> No, we selectively pull a subset of your segments.<br><br><i>What's a good USB-C adapter?</i> Any fast phone or laptop charger should be fine.<br><br><i>Does it matter which software I run?</i> Yes, only upstream openpilot (and particular forks) are able to be used for training. - + 为了达到最佳效果,请每周将您的设备带回室内,并连接到优质的 USB-C 充电器和 Wi-Fi。<br><br>Firehose 模式在行驶时也能运行,但需连接到移动热点或使用不限流量的 SIM 卡。<br><br><br><b>常见问题</b><br><br><i>我开车的方式或地点有影响吗?</i>不会,请像平常一样驾驶即可。<br><br><i>Firehose 模式会上传所有的驾驶片段吗?</i>不会,我们会选择性地上传部分片段。<br><br><i>什么是好的 USB-C 充电器?</i>任何快速手机或笔记本电脑充电器都应该适用。<br><br><i>我使用的软件版本有影响吗?</i>有的,只有官方 openpilot(以及特定的分支)可以用于训练。 <b>%n segment(s)</b> of your driving is in the training dataset so far. - - + + <b>目前已有 %n 段</b> 您的驾驶数据被纳入训练数据集。 @@ -1534,11 +1536,11 @@ This may take up to a minute. Welcome to openpilot - + 欢迎使用 openpilot You must accept the Terms and Conditions to use openpilot. Read the latest terms at <span style='color: #465BEA;'>https://comma.ai/terms</span> before continuing. - + 您必须接受《条款与条件》才能使用 openpilot。在继续之前,请先阅读最新条款:<span style='color: #465BEA;'>https://comma.ai/terms</span>。 diff --git a/selfdrive/ui/translations/main_zh-CHT.ts b/selfdrive/ui/translations/main_zh-CHT.ts index da6e52702a..1df2d934a8 100644 --- a/selfdrive/ui/translations/main_zh-CHT.ts +++ b/selfdrive/ui/translations/main_zh-CHT.ts @@ -425,28 +425,30 @@ openpilot learns to drive by watching humans, like you, drive. Firehose Mode allows you to maximize your training data uploads to improve openpilot's driving models. More data means bigger models, which means better Experimental Mode. - + openpilot 透過觀察人類駕駛(包括您)來學習如何駕駛。 + +「訓練資料上傳模式」可讓您最大化上傳訓練數據,以改進 openpilot 的駕駛模型。更多數據代表更強大的模型,也就意味著更優秀的「實驗模式」。 Firehose Mode: ACTIVE - + 訓練資料上傳模式:啟用中 ACTIVE - + 啟用中 <span stylesheet='font-size: 60px; font-weight: bold; color: #e74c3c;'>INACTIVE</span>: connect to unmetered network - + <span stylesheet='font-size: 60px; font-weight: bold; color: #e74c3c;'>未啟用</span>:請連接至不限流量的網路 For maximum effectiveness, bring your device inside and connect to a good USB-C adapter and Wi-Fi weekly.<br><br>Firehose Mode can also work while you're driving if connected to a hotspot or unlimited SIM card.<br><br><br><b>Frequently Asked Questions</b><br><br><i>Does it matter how or where I drive?</i> Nope, just drive as you normally would.<br><br><i>Do all of my segments get pulled in Firehose Mode?</i> No, we selectively pull a subset of your segments.<br><br><i>What's a good USB-C adapter?</i> Any fast phone or laptop charger should be fine.<br><br><i>Does it matter which software I run?</i> Yes, only upstream openpilot (and particular forks) are able to be used for training. - + 為了達到最佳效果,請每週將您的裝置帶回室內,並連接至優質的 USB-C 充電器與 Wi-Fi。<br><br>訓練資料上傳模式在行駛時也能運作,但需連接至行動熱點或使用不限流量的 SIM 卡。<br><br><br><b>常見問題</b><br><br><i>我開車的方式或地點有影響嗎?</i> 不會,請像平常一樣駕駛即可。<br><br><i>訓練資料上傳模式會上傳所有的駕駛片段嗎?</i>不會,我們會選擇性地上傳部分片段。<br><br><i>什麼是好的 USB-C 充電器?</i>任何快速手機或筆電充電器都應該適用。<br><br><i>我使用的軟體版本有影響嗎?</i>有的,只有官方 openpilot(以及特定的分支)可以用於訓練。 <b>%n segment(s)</b> of your driving is in the training dataset so far. - - + + <b>目前已有 %n 段</b> 您的駕駛數據被納入訓練資料集。 @@ -1534,11 +1536,11 @@ This may take up to a minute. Welcome to openpilot - + 歡迎使用 openpilot You must accept the Terms and Conditions to use openpilot. Read the latest terms at <span style='color: #465BEA;'>https://comma.ai/terms</span> before continuing. - + 您必須接受《條款與條件》才能使用 openpilot。在繼續之前,請先閱讀最新條款:<span style='color: #465BEA;'>https://comma.ai/terms</span>。 diff --git a/system/camerad/cameras/spectra.cc b/system/camerad/cameras/spectra.cc index 3005a4a550..d12dbb2444 100644 --- a/system/camerad/cameras/spectra.cc +++ b/system/camerad/cameras/spectra.cc @@ -237,7 +237,7 @@ SpectraCamera::SpectraCamera(SpectraMaster *master, const CameraConfig &config) : m(master), enabled(config.enabled), cc(config) { - ife_buf_depth = (cc.output_type == ISP_RAW_OUTPUT) ? 4 : VIPC_BUFFER_COUNT; + ife_buf_depth = VIPC_BUFFER_COUNT; assert(ife_buf_depth < MAX_IFE_BUFS); } @@ -266,7 +266,7 @@ int SpectraCamera::clear_req_queue() { req_mgr_flush_request.link_hdl = link_handle; req_mgr_flush_request.flush_type = CAM_REQ_MGR_FLUSH_TYPE_ALL; int ret = do_cam_control(m->video0_fd, CAM_REQ_MGR_FLUSH_REQ, &req_mgr_flush_request, sizeof(req_mgr_flush_request)); - LOGD("flushed all req: %d", ret); + LOGD("flushed all req: %d", ret); // returns a "time until timeout" on clearing the workq for (int i = 0; i < MAX_IFE_BUFS; ++i) { destroySyncObjectAt(i); @@ -1330,7 +1330,16 @@ bool SpectraCamera::handle_camera_event(const cam_req_mgr_message *event_data) { uint64_t timestamp = event_data->u.frame_msg.timestamp; // timestamped in the kernel's SOF IRQ callback //LOGD("handle cam %d ts %lu req id %lu frame id %lu", cc.camera_num, timestamp, request_id, frame_id_raw); - if (stress_test("skipping SOF event")) return false; + // if there's a lag, some more frames could have already come in before + // we cleared the queue, so we'll still get them with valid (> 0) request IDs. + if (timestamp < last_requeue_ts) { + LOGD("skipping frame: ts before requeue / cam %d ts %lu req id %lu frame id %lu", cc.camera_num, timestamp, request_id, frame_id_raw); + return false; + } + + if (stress_test("skipping SOF event")) { + return false; + } if (!validateEvent(request_id, frame_id_raw)) { return false; @@ -1381,7 +1390,7 @@ bool SpectraCamera::validateEvent(uint64_t request_id, uint64_t frame_id_raw) { if (request_id != request_id_last + 1) { LOGE("camera %d requests skipped %ld -> %ld", cc.camera_num, request_id_last, request_id); - clearAndRequeue(request_id_last + 1); + clearAndRequeue(request_id + 1); return false; } } @@ -1392,6 +1401,7 @@ void SpectraCamera::clearAndRequeue(uint64_t from_request_id) { // clear everything, then queue up a fresh set of frames LOGW("clearing and requeuing camera %d from %lu", cc.camera_num, from_request_id); clear_req_queue(); + last_requeue_ts = nanos_since_boot(); for (uint64_t id = from_request_id; id < from_request_id + ife_buf_depth; ++id) { enqueue_frame(id); } @@ -1402,12 +1412,19 @@ bool SpectraCamera::waitForFrameReady(uint64_t request_id) { int buf_idx = request_id % ife_buf_depth; assert(sync_objs_ife[buf_idx]); + if (stress_test("sync sleep time")) { + util::sleep_for(350); + return false; + } + auto waitForSync = [&](uint32_t sync_obj, int timeout_ms, const char *sync_type) { + double st = millis_since_boot(); struct cam_sync_wait sync_wait = {}; sync_wait.sync_obj = sync_obj; sync_wait.timeout_ms = stress_test(sync_type) ? 1 : timeout_ms; bool ret = do_sync_control(m->cam_sync_fd, CAM_SYNC_WAIT, &sync_wait, sizeof(sync_wait)) == 0; - if (!ret) LOGE("camera %d %s failed", cc.camera_num, sync_type); + double et = millis_since_boot(); + if (!ret) LOGE("camera %d %s failed after %.2fms", cc.camera_num, sync_type, et-st); return ret; }; @@ -1419,6 +1436,7 @@ bool SpectraCamera::waitForFrameReady(uint64_t request_id) { // BPS is typically 7ms success = waitForSync(sync_objs_bps[buf_idx], 50, "BPS sync"); } + return success; } diff --git a/system/camerad/cameras/spectra.h b/system/camerad/cameras/spectra.h index 21f1970326..13cb13f98f 100644 --- a/system/camerad/cameras/spectra.h +++ b/system/camerad/cameras/spectra.h @@ -181,6 +181,7 @@ public: int sync_objs_ife[MAX_IFE_BUFS] = {}; int sync_objs_bps[MAX_IFE_BUFS] = {}; uint64_t request_id_last = 0; + uint64_t last_requeue_ts = 0; uint64_t frame_id_raw_last = 0; int invalid_request_count = 0; bool skip_expected = true; @@ -202,11 +203,16 @@ private: inline static bool first_frame_synced = false; // a mode for stressing edge cases: realignment, sync failures, etc. - inline bool stress_test(const char* log) { - static double prob = std::stod(util::getenv("SPECTRA_ERROR_PROB", "-1"));; - bool triggered = (prob > 0) && ((static_cast(rand()) / RAND_MAX) < prob); + inline bool stress_test(std::string log) { + static double last_trigger = 0; + static double prob = std::stod(util::getenv("SPECTRA_ERROR_PROB", "-1")); + static double dt = std::stod(util::getenv("SPECTRA_ERROR_DT", "1")); + bool triggered = (prob > 0) && \ + ((static_cast(rand()) / RAND_MAX) < prob) && \ + (millis_since_boot() - last_trigger) > dt; if (triggered) { - LOGE("stress test (cam %d): %s", cc.camera_num, log); + last_trigger = millis_since_boot(); + LOGE("stress test (cam %d): %s", cc.camera_num, log.c_str()); } return triggered; } diff --git a/system/manager/process.py b/system/manager/process.py index 0e9c9b9804..6c233c21ab 100644 --- a/system/manager/process.py +++ b/system/manager/process.py @@ -221,8 +221,12 @@ class PythonProcess(ManagerProcess): if self.proc is not None: return + # TODO: this is just a workaround for this tinygrad check: + # https://github.com/tinygrad/tinygrad/blob/ac9c96dae1656dc220ee4acc39cef4dd449aa850/tinygrad/device.py#L26 + name = self.name if "modeld" not in self.name else "MainProcess" + cloudlog.info(f"starting python {self.module}") - self.proc = Process(name=self.name, target=self.launcher, args=(self.module, self.name)) + self.proc = Process(name=name, target=self.launcher, args=(self.module, self.name)) self.proc.start() self.watchdog_seen = False self.shutting_down = False diff --git a/system/manager/process_config.py b/system/manager/process_config.py index 48136bfa14..1d2ca45944 100644 --- a/system/manager/process_config.py +++ b/system/manager/process_config.py @@ -105,10 +105,8 @@ procs = [ PythonProcess("micd", "system.micd", iscar), PythonProcess("timed", "system.timed", always_run, enabled=not PC), - # TODO: Make python process once TG allows opening QCOM from child pro - # https://github.com/tinygrad/tinygrad/blob/ac9c96dae1656dc220ee4acc39cef4dd449aa850/tinygrad/device.py#L26 - NativeProcess("modeld", "selfdrive/modeld", ["./modeld.py"], and_(only_onroad, is_stock_model)), - NativeProcess("dmonitoringmodeld", "selfdrive/modeld", ["./dmonitoringmodeld.py"], driverview, enabled=(WEBCAM or not PC)), + PythonProcess("modeld", "selfdrive.modeld.modeld", and_(only_onroad, is_stock_model)), + PythonProcess("dmonitoringmodeld", "selfdrive.modeld.dmonitoringmodeld", driverview, enabled=(WEBCAM or not PC)), NativeProcess("sensord", "system/sensord", ["./sensord"], only_onroad, enabled=not PC), NativeProcess("ui", "selfdrive/ui", ["./ui"], always_run, watchdog_max_dt=(5 if not PC else None)),