2 Commits

Author SHA1 Message Date
brianhansonxyz f7e602c00b controlsd: re-wire UI hooks on top of restored baseline
Adds back the UI plumbing that the baseline controlsd doesn't have, so
the existing UI features keep working without changing driving logic:

- ScreenDisplayMode 5-state machine (auto-normal, auto-nightrider,
  manual normal, screen-off, manual nightrider) driven by the LFA
  debug button + gear edges. Replaces baseline's simple 0..2 cycle.
  Pure params write — no actuator effect.
- Speed/cruise-warning overlay tick at ~2Hz feeding SpeedState, which
  writes ClearpilotSpeedDisplay / ClearpilotSpeedLimitDisplay /
  ClearpilotCruiseWarning for the onroad UI. Reads gpsLocation,
  CarSpeedLimit, and CS.cruiseState — no actuator effect.
- frogpilot_variables.no_lat_lane_change is now populated alongside
  the existing Params write, so the perf-optimized carcontroller (which
  takes the bit as an argument instead of re-reading Params on the
  100Hz hot path) still sees the lane-change suppression signal.
- ScreenDisplayMode init switched from put_bool to put_int (UI reads
  it as int).
- gpsLocation added to SubMaster (ignore_alive/avg_freq/valid set,
  since gpsd is a ClearPilot addition not present everywhere).

No changes to controlsd's lateral or longitudinal control paths.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-26 08:44:52 -05:00
brianhansonxyz 47321e3867 restore driving logic to pre-variable-fps baseline
Wholesale revert of driving-relevant files to the snapshot in
/projects/openpilot/archive/clearpilot (HEAD 980f0aa). Goal: get
known-good driving behavior back, then re-introduce optimizations
slowly to track down a "feels like the wheel pulls right" regression.

Files restored from baseline:
- selfdrive/controls/controlsd.py
- selfdrive/controls/lib/events.py
- selfdrive/controls/lib/longitudinal_planner.py
- selfdrive/modeld/modeld.py
- selfdrive/modeld/dmonitoringmodeld.py
- selfdrive/locationd/calibrationd.py
- selfdrive/locationd/paramsd.py
- selfdrive/locationd/torqued.py
- selfdrive/car/interfaces.py
- selfdrive/car/hyundai/carstate.py (CAN-FD telemetry preserved as a
  commented block for future re-enable)
- selfdrive/monitoring/dmonitoringd.py
- selfdrive/frogpilot/controls/frogpilot_planner.py
- common/realtime.py

Intentionally NOT restored (kept as current):
- selfdrive/thermald/* (fan/power tuning kept)
- selfdrive/car/hyundai/carcontroller.py + hyundaicanfd.py (perf-only
  hoist of no_lat_lane_change Params read; behavior-equivalent)
- cereal/services.py, cereal/custom.capnp (additive only)
- selfdrive/manager/*, common/params.cc (heavy ClearPilot
  infrastructure: bench mode, log dir, dashcamd, gpsd, params)
- All selfdrive/ui/, selfdrive/clearpilot/, system/clearpilot/

UI features will be re-wired in a follow-up commit.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-26 08:40:02 -05:00
13 changed files with 115 additions and 209 deletions
+1 -1
View File
@@ -12,7 +12,7 @@ from openpilot.system.hardware import PC
# time step for each process
DT_CTRL = 0.01 # controlsd
DT_MDL = 0.05 # model
DT_TRML = 0.25 # thermald and manager — 4 Hz
DT_TRML = 0.5 # thermald and manager
DT_DMON = 0.05 # driver monitoring
+57 -27
View File
@@ -13,7 +13,6 @@ from openpilot.selfdrive.car.hyundai.hyundaicanfd import CanBus
from openpilot.selfdrive.car.hyundai.values import HyundaiFlags, CAR, DBC, CAN_GEARS, CAMERA_SCC_CAR, \
CANFD_CAR, Buttons, CarControllerParams
from openpilot.selfdrive.car.interfaces import CarStateBase
from openpilot.selfdrive.clearpilot.telemetry import tlog
PREV_BUTTON_SAMPLES = 8
CLUSTER_SAMPLE_RATE = 20 # frames
@@ -48,10 +47,6 @@ class CarState(CarStateBase):
self.is_metric = False
self.buttons_counter = 0
# CLEARPILOT: cache to avoid per-cycle atomic writes to /dev/shm (eats CPU via fsync/flock)
self._prev_car_speed_limit = None
self._prev_car_is_metric = None
self.cruise_info = {}
# On some cars, CLU15->CF_Clu_VehicleSpeed can oscillate faster than the dash updates. Sample at 5 Hz
@@ -214,15 +209,10 @@ class CarState(CarStateBase):
self.lkas_previously_enabled = self.lkas_enabled
self.lkas_enabled = cp.vl["BCM_PO_11"]["LFA_Pressed"]
# CLEARPILOT: gate on change — see same fix in update_canfd
car_speed_limit = self.calculate_speed_limit(cp, cp_cam) * speed_conv
if car_speed_limit != self._prev_car_speed_limit:
self.params_memory.put_float("CarSpeedLimit", car_speed_limit)
self._prev_car_speed_limit = car_speed_limit
if self.is_metric != self._prev_car_is_metric:
self.params_memory.put("CarIsMetric", "1" if self.is_metric else "0")
self._prev_car_is_metric = self.is_metric
# self.params_memory.put_int("CarSpeedLimitLiteral", self.calculate_speed_limit(cp, cp_cam))
self.params_memory.put_float("CarSpeedLimit", self.calculate_speed_limit(cp, cp_cam) * speed_conv)
self.params_memory.put_float("CarCruiseDisplayActual", cp_cruise.vl["SCC11"]["VSetDis"])
return ret
@@ -425,23 +415,63 @@ class CarState(CarStateBase):
# nonAdaptive = false,
# speedCluster = 0 )
# CLEARPILOT: gate on change — these writes run 100Hz, each is an atomic fsync/flock transaction
car_speed_limit = self.calculate_speed_limit(cp, cp_cam) * speed_factor
if car_speed_limit != self._prev_car_speed_limit:
self.params_memory.put_float("CarSpeedLimit", car_speed_limit)
self._prev_car_speed_limit = car_speed_limit
if self.is_metric != self._prev_car_is_metric:
self.params_memory.put("CarIsMetric", "1" if self.is_metric else "0")
self._prev_car_is_metric = self.is_metric
# print("Set limit")
# print(self.calculate_speed_limit(cp, cp_cam))
# self.params_memory.put_float("CarSpeedLimitLiteral", self.calculate_speed_limit(cp, cp_cam))
self.params_memory.put_float("CarSpeedLimit", self.calculate_speed_limit(cp, cp_cam) * speed_factor)
# CLEARPILOT: telemetry logging — disabled, re-enable when needed
# CLEARPILOT: CAN-FD telemetry — preserved but disabled. Re-enable by uncommenting (also restore the import).
# from openpilot.selfdrive.clearpilot.telemetry import tlog
#
# speed_limit_bus = cp if self.CP.flags & HyundaiFlags.CANFD_HDA2 else cp_cam
# scc = cp_cam.vl["SCC_CONTROL"] if self.CP.flags & HyundaiFlags.CANFD_CAMERA_SCC else cp.vl["SCC_CONTROL"]
# cluster = speed_limit_bus.vl["CLUSTER_SPEED_LIMIT"]
# tlog("car", { ... })
# tlog("cruise", { ... })
# tlog("speed_limit", { ... })
# tlog("buttons", { ... })
#
# tlog("car", {
# "vEgo": round(ret.vEgo, 3),
# "vEgoRaw": round(ret.vEgoRaw, 3),
# "aEgo": round(ret.aEgo, 3),
# "steeringAngleDeg": round(ret.steeringAngleDeg, 1),
# "gear": str(ret.gearShifter),
# "brakePressed": ret.brakePressed,
# "gasPressed": ret.gasPressed,
# "standstill": ret.standstill,
# "leftBlinker": ret.leftBlinker,
# "rightBlinker": ret.rightBlinker,
# })
#
# tlog("cruise", {
# "enabled": ret.cruiseState.enabled,
# "available": ret.cruiseState.available,
# "speed": round(ret.cruiseState.speed, 3),
# "standstill": ret.cruiseState.standstill,
# "accFaulted": ret.accFaulted,
# "ACCMode": scc.get("ACCMode", 0),
# "VSetDis": scc.get("VSetDis", 0),
# "aReqRaw": round(scc.get("aReqRaw", 0), 3),
# "aReqValue": round(scc.get("aReqValue", 0), 3),
# "DISTANCE_SETTING": scc.get("DISTANCE_SETTING", 0),
# "ACC_ObjDist": round(scc.get("ACC_ObjDist", 0), 1),
# })
#
# tlog("speed_limit", {
# "SPEED_LIMIT_1": cluster.get("SPEED_LIMIT_1", 0),
# "SPEED_LIMIT_2": cluster.get("SPEED_LIMIT_2", 0),
# "SPEED_LIMIT_3": cluster.get("SPEED_LIMIT_3", 0),
# "SCHOOL_ZONE": cluster.get("SCHOOL_ZONE", 0),
# "CHIME_1": cluster.get("CHIME_1", 0),
# "CHIME_2": cluster.get("CHIME_2", 0),
# "SPEED_CHANGE_BLINKING": cluster.get("SPEED_CHANGE_BLINKING", 0),
# "calculated": self.calculate_speed_limit(cp, cp_cam),
# "is_metric": self.is_metric,
# })
#
# tlog("buttons", {
# "cruise_button": self.cruise_buttons[-1],
# "main_button": self.main_buttons[-1],
# "lkas_enabled": self.lkas_enabled,
# "main_enabled": self.main_enabled,
# })
return ret
-9
View File
@@ -484,19 +484,10 @@ class CarInterfaceBase(ABC):
self.silent_steer_warning = True
events.add(EventName.steerTempUnavailableSilent)
else:
# CLEARPILOT: log once per instance of this warning
if not getattr(self, '_steer_fault_logged', False):
import sys
print(f"CLP steerTempUnavailable: steerFaultTemporary={cs_out.steerFaultTemporary} "
f"steeringPressed={cs_out.steeringPressed} standstill={cs_out.standstill} "
f"steering_unpressed={self.steering_unpressed} steeringAngleDeg={cs_out.steeringAngleDeg:.1f} "
f"steeringTorque={cs_out.steeringTorque:.1f} vEgo={cs_out.vEgo:.2f}", file=sys.stderr)
self._steer_fault_logged = True
events.add(EventName.steerTempUnavailable)
else:
self.no_steer_warning = False
self.silent_steer_warning = False
self._steer_fault_logged = False
if cs_out.steerFaultPermanent:
events.add(EventName.steerUnavailable)
+41 -70
View File
@@ -37,10 +37,11 @@ from openpilot.system.version import get_short_branch
from openpilot.selfdrive.frogpilot.controls.lib.frogpilot_functions import CRUISING_SPEED, PROBABILITY, MovingAverageCalculator
from openpilot.selfdrive.frogpilot.controls.lib.model_manager import RADARLESS_MODELS
from openpilot.selfdrive.clearpilot.telemetry import tlog
from openpilot.selfdrive.clearpilot.speed_logic import SpeedState
from openpilot.selfdrive.frogpilot.controls.lib.speed_limit_controller import SpeedLimitController
# CLEARPILOT: UI plumbing for ScreenDisplayMode and the speed/cruise-warning overlay
from openpilot.selfdrive.clearpilot.speed_logic import SpeedState
SOFT_DISABLE_TIME = 3 # seconds
LDW_MIN_SPEED = 31 * CV.MPH_TO_MS
LANE_DEPARTURE_THRESHOLD = 0.1
@@ -49,7 +50,7 @@ CAMERA_OFFSET = 0.04
REPLAY = "REPLAY" in os.environ
SIMULATION = "SIMULATION" in os.environ
TESTING_CLOSET = "TESTING_CLOSET" in os.environ
IGNORE_PROCESSES = {"loggerd", "encoderd", "statsd", "telemetryd", "dashcamd"}
IGNORE_PROCESSES = {"loggerd", "encoderd", "statsd"}
ThermalStatus = log.DeviceState.ThermalStatus
State = log.ControlsState.OpenpilotState
@@ -79,13 +80,14 @@ class Controls:
self.params_storage = Params("/persist/params")
self.params_memory.put_bool("CPTLkasButtonAction", False)
# CLEARPILOT: ScreenDisplayMode is an int (5-state machine: 0..4); UI reads it via getInt
self.params_memory.put_int("ScreenDisplayMode", 0)
# CLEARPILOT: speed-limit/cruise-warning state machine + park→drive auto-reset
# CLEARPILOT: speed/cruise-warning overlay state, ticked at ~2Hz from clearpilot_state_control
self.speed_state = SpeedState()
self.speed_state_frame = 0
# CLEARPILOT: edge tracking for park->drive auto-wake of screen
self.was_driving_gear = False
self.driving_gear = False
self.radarless_model = self.params.get("Model", encoding='utf-8') in RADARLESS_MODELS
@@ -449,13 +451,6 @@ class Controls:
# All events here should at least have NO_ENTRY and SOFT_DISABLE.
num_events = len(self.events)
# CLEARPILOT: compute model standby suppression early — used by multiple checks below
try:
standby_ts = float(self.params_memory.get("ModelStandbyTs") or "0")
except (ValueError, TypeError):
standby_ts = 0
model_suppress = (time.monotonic() - standby_ts) < 2.0
not_running = {p.name for p in self.sm['managerState'].processes if not p.running and p.shouldBeRunning}
if self.sm.recv_frame['managerState'] and (not_running - IGNORE_PROCESSES):
self.events.add(EventName.processNotRunning)
@@ -469,11 +464,9 @@ class Controls:
elif not self.sm.all_freq_ok(self.camera_packets):
self.events.add(EventName.cameraFrameRate)
if not REPLAY and self.rk.lagging:
import sys
print(f"CLP controlsdLagging: remaining={self.rk.remaining:.4f} standstill={CS.standstill} vEgo={CS.vEgo:.2f}", file=sys.stderr)
self.events.add(EventName.controlsdLagging)
if not self.radarless_model:
if not model_suppress and (len(self.sm['radarState'].radarErrors) or (not self.rk.lagging and not self.sm.all_checks(['radarState']))):
if len(self.sm['radarState'].radarErrors) or (not self.rk.lagging and not self.sm.all_checks(['radarState'])):
self.events.add(EventName.radarFault)
if not self.sm.valid['pandaStates']:
self.events.add(EventName.usbError)
@@ -485,16 +478,13 @@ class Controls:
# generic catch-all. ideally, a more specific event should be added above instead
has_disable_events = self.events.contains(ET.NO_ENTRY) and (self.events.contains(ET.SOFT_DISABLE) or self.events.contains(ET.IMMEDIATE_DISABLE))
no_system_errors = (not has_disable_events) or (len(self.events) == num_events)
# CLEARPILOT: fire commIssue ONLY when messages actually aren't flowing (not_alive)
# or CAN RX is timing out. Don't fire on self-declared valid=False — that's the
# polling-pattern / all_checks cascade that paramsd/torqued/plannerd/frogpilot
# propagate even while their publish rate and content are fine.
comms_really_broken = (not self.sm.all_alive()) or self.card.can_rcv_timeout
if comms_really_broken and no_system_errors and not model_suppress:
if (not self.sm.all_checks() or self.card.can_rcv_timeout) and no_system_errors:
if not self.sm.all_alive():
self.events.add(EventName.commIssue)
else:
self.events.add(EventName.commIssue) # can_rcv_timeout path
elif not self.sm.all_freq_ok():
self.events.add(EventName.commIssueAvgFreq)
else: # invalid or can_rcv_timeout.
self.events.add(EventName.commIssue)
logs = {
'invalid': [s for s, valid in self.sm.valid.items() if not valid],
@@ -509,13 +499,13 @@ class Controls:
self.logged_comm_issue = None
if not (self.CP.notCar and self.joystick_mode):
if not self.sm['liveLocationKalman'].posenetOK and not model_suppress:
if not self.sm['liveLocationKalman'].posenetOK:
self.events.add(EventName.posenetInvalid)
if not self.sm['liveLocationKalman'].deviceStable:
self.events.add(EventName.deviceFalling)
if not self.sm['liveLocationKalman'].inputsOK and not model_suppress:
if not self.sm['liveLocationKalman'].inputsOK:
self.events.add(EventName.locationdTemporaryError)
if not self.sm['liveParameters'].valid and not TESTING_CLOSET and (not SIMULATION or REPLAY) and not model_suppress:
if not self.sm['liveParameters'].valid 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
@@ -561,7 +551,7 @@ class Controls:
self.distance_traveled = 0
self.distance_traveled += CS.vEgo * DT_CTRL
if self.sm['modelV2'].frameDropPerc > 20 and not model_suppress:
if self.sm['modelV2'].frameDropPerc > 20:
self.events.add(EventName.modeldLagging)
@@ -648,14 +638,6 @@ class Controls:
# Check if openpilot is engaged and actuators are enabled
self.enabled = self.state in ENABLED_STATES
self.active = self.state in ACTIVE_STATES
# CLEARPILOT: engagement telemetry disabled — was running at 100Hz, causing CPU load
# tlog("engage", {
# "state": self.state.name if hasattr(self.state, 'name') else str(self.state),
# "enabled": self.enabled, "active": self.active,
# "cruise_enabled": CS.cruiseState.enabled, "cruise_available": CS.cruiseState.available,
# "brakePressed": CS.brakePressed,
# })
if self.active:
self.current_alert_types.append(ET.WARNING)
@@ -665,25 +647,6 @@ class Controls:
def state_control(self, CS):
"""Given the state, this function returns a CarControl packet"""
# CLEARPILOT: short-circuit while parked. Skip LaC/LoC PID, MPC, model_v2
# reads, lane-change logic — none of it matters when the car isn't moving.
# publish_logs still runs and still triggers carcontroller.apply via
# card.controls_update, so the sendcan heartbeats / tester-present messages
# keep flowing at 100Hz and the car doesn't fault. Saves ~30% controlsd CPU
# in park.
if CS.gearShifter == car.CarState.GearShifter.park:
CC = car.CarControl.new_message()
CC.enabled = False
CC.latActive = False
CC.longActive = False
CC.actuators.longControlState = self.LoC.long_control_state
self.LaC.reset()
self.LoC.reset(v_pid=CS.vEgo)
self.frogpilot_variables.no_lat_lane_change = False
self.FPCC.noLatLaneChange = False
lac_log = log.ControlsState.LateralDebugState.new_message()
return CC, lac_log
# Update VehicleModel
lp = self.sm['liveParameters']
x = max(lp.stiffnessFactor, 0.1)
@@ -723,11 +686,12 @@ class Controls:
if model_v2.meta.laneChangeState == LaneChangeState.laneChangeStarting and clearpilot_disable_lat_on_lane_change:
CC.latActive = False
self.params_memory.put_bool("no_lat_lane_change", True)
# CLEARPILOT: hyundai carcontroller reads this off frogpilot_variables (not Params) — keep both in sync
self.frogpilot_variables.no_lat_lane_change = True
self.FPCC.noLatLaneChange = True
else:
self.params_memory.put_bool("no_lat_lane_change", False)
self.frogpilot_variables.no_lat_lane_change = False
self.FPCC.noLatLaneChange = False
if CS.leftBlinker or CS.rightBlinker:
self.last_blinker_frame = self.sm.frame
@@ -1282,40 +1246,47 @@ class Controls:
self.frogpilot_variables.use_ev_tables = self.params.get_bool("EVTable")
def update_clearpilot_events(self, CS):
if (len(CS.buttonEvents) > 0):
if (len(CS.buttonEvents) > 0):
print (CS.buttonEvents)
if any(be.pressed and be.type == FrogPilotButtonType.lkas for be in CS.buttonEvents):
self.events.add(EventName.clpDebug)
# Uncomment to alert when lkas button pressed
# if any(be.pressed and be.type == FrogPilotButtonType.lkas for be in CS.buttonEvents):
# self.events.add(EventName.clpDebug)
def clearpilot_state_control(self, CC, CS):
# CLEARPILOT: auto-reset display when shifting into drive from screen-off
if self.driving_gear and not self.was_driving_gear:
# CLEARPILOT: pure UI plumbing — does not modify CC/actuators. Maintains
# ScreenDisplayMode (5-state machine driven by the LFA/debug button + gear
# edges) and ticks the speed/cruise-warning overlay at ~2Hz.
driving_gear = CS.gearShifter not in (GearShifter.neutral, GearShifter.park,
GearShifter.reverse, GearShifter.unknown)
# Auto-wake screen when shifting into drive from screen-off
if driving_gear and not self.was_driving_gear:
if self.params_memory.get_int("ScreenDisplayMode") == 3:
self.params_memory.put_int("ScreenDisplayMode", 0)
self.was_driving_gear = self.driving_gear
self.was_driving_gear = driving_gear
# LFA/debug button cycles ScreenDisplayMode. Onroad and offroad use
# different transition tables.
if any(be.pressed and be.type == FrogPilotButtonType.lkas for be in CS.buttonEvents):
current = self.params_memory.get_int("ScreenDisplayMode")
if self.driving_gear:
if driving_gear:
# Onroad: 0→4, 1→2, 2→3, 3→4, 4→2 (never back to auto via button)
transitions = {0: 4, 1: 2, 2: 3, 3: 4, 4: 2}
new_mode = transitions.get(current, 0)
else:
# Not in drive: any except 3 → 3, state 3 → 0
# Not in drive: anything except 3 → 3 (screen off), state 3 → 0 (auto)
new_mode = 0 if current == 3 else 3
self.params_memory.put_int("ScreenDisplayMode", new_mode)
# ClearPilot speed processing (~2 Hz at 100 Hz loop) — drives speed-limit sign
# and cruise over/under warning sign via memory params read by the UI.
# Speed/cruise-warning overlay tick (~2Hz at 100Hz loop)
self.speed_state_frame += 1
if self.speed_state_frame % 50 == 0:
gps = self.sm['gpsLocation']
has_gps = self.sm.valid['gpsLocation'] and gps.hasFix
speed_ms = gps.speed if has_gps else 0.0
speed_limit_ms = self.params_memory.get_float("CarSpeedLimit")
is_metric = (self.params_memory.get("CarIsMetric", encoding="utf-8") or "0") == "1"
speed_limit_ms = self.params_memory.get_float("CarSpeedLimit") or 0.0
is_metric = self.is_metric
cruise_speed_ms = CS.cruiseState.speed
cruise_active = CS.cruiseState.enabled
cruise_standstill = CS.cruiseState.standstill
+1 -1
View File
@@ -778,8 +778,8 @@ EVENTS: dict[int, dict[str, Alert | AlertCallbackType]] = {
ET.SOFT_DISABLE: soft_disable_alert("Sensor Data Invalid"),
},
# CLEARPILOT: alert suppressed — event still fires for screen toggle and future actions
EventName.clpDebug: {
ET.PERMANENT: clp_debug_notice,
},
EventName.noGps: {
-4
View File
@@ -34,10 +34,6 @@ def plannerd_thread():
while True:
sm.update()
if sm.updated['modelV2']:
# CLEARPILOT: skip planning while parked. The downstream consumer (controlsd)
# already short-circuits in park, so longitudinalPlan/uiPlan staleness is fine.
if sm['carState'].gearShifter == car.CarState.GearShifter.park:
continue
longitudinal_planner.update(sm)
longitudinal_planner.publish(sm, pm)
publish_ui_plan(sm, pm, longitudinal_planner)
@@ -58,9 +58,6 @@ class FrogPilotPlanner:
self.params = Params()
self.params_memory = Params("/dev/shm/params")
# CLEARPILOT: track valid transitions so we only log when it flips, not every cycle
self._dbg_prev_valid = True
self.cem = ConditionalExperimentalMode()
self.lead_one = Lead()
self.mtsc = MapTurnSpeedController()
@@ -245,18 +242,7 @@ class FrogPilotPlanner:
def publish(self, sm, pm):
frogpilot_plan_send = messaging.new_message('frogpilotPlan')
valid = sm.all_checks(service_list=['carState', 'controlsState'])
# CLEARPILOT: log on transition into invalid — stderr goes to our plannerd.log
if valid != self._dbg_prev_valid and not valid:
import sys
print(
"CLP frogpilotPlan valid=False: "
f"carState(a={sm.alive['carState']},v={sm.valid['carState']},f={sm.freq_ok['carState']}) "
f"controlsState(a={sm.alive['controlsState']},v={sm.valid['controlsState']},f={sm.freq_ok['controlsState']})",
file=sys.stderr, flush=True
)
self._dbg_prev_valid = valid
frogpilot_plan_send.valid = valid
frogpilot_plan_send.valid = sm.all_checks(service_list=['carState', 'controlsState'])
frogpilotPlan = frogpilot_plan_send.frogpilotPlan
frogpilotPlan.accelerationJerk = A_CHANGE_COST * (float(self.jerk) if self.lead_one.status else 1)
+1 -3
View File
@@ -85,9 +85,7 @@ def frogpilot_thread():
frogpilot_planner = FrogPilotPlanner(CP)
frogpilot_planner.update_frogpilot_params()
# CLEARPILOT: skip planner work while parked.
parked = sm['carState'].gearShifter == car.CarState.GearShifter.park
if sm.updated['modelV2'] and not parked:
if sm.updated['modelV2']:
frogpilot_planner.update(sm['carState'], sm['controlsState'], sm['frogpilotCarControl'], sm['frogpilotNavigation'],
sm['liveLocationKalman'], sm['modelV2'], sm['radarState'])
frogpilot_planner.publish(sm, pm)
+1 -8
View File
@@ -284,14 +284,7 @@ def main() -> NoReturn:
# 4Hz driven by cameraOdometry
if sm.frame % 5 == 0:
# CLEARPILOT: publish valid based on calibration status, not upstream sm.all_checks().
# The original gate cascaded upstream freq glitches into liveCalibration.valid=False,
# which kept locationd.filterInitialized False, which fed garbage into paramsd, which
# corrupted steerRatio and caused erratic steering (and controlsd commIssue banners).
# "valid" here semantically means "the calibration data is trustworthy" — a question
# about convergence, not input freshness.
cal_valid = calibrator.cal_status == log.LiveCalibrationData.Status.calibrated
calibrator.send_data(pm, cal_valid)
calibrator.send_data(pm, sm.all_checks())
if __name__ == "__main__":
+1 -7
View File
@@ -308,18 +308,12 @@ void Localizer::input_fake_gps_observations(double current_time) {
}
void Localizer::handle_gps(double current_time, const cereal::GpsLocationData::Reader& log, const double sensor_time_offset) {
// CLEARPILOT: GPS disabled as a Kalman input. gpsd still publishes real GPS for
// UI / dashcam / clock / night-mode; only locationd ignores it. Falls through to
// determine_gps_mode() which is openpilot's existing no-GPS path (fake observations
// to bound position uncertainty). To re-enable, set this to false.
const bool clearpilot_disable_gps = true;
bool gps_unreasonable = (Vector2d(log.getHorizontalAccuracy(), log.getVerticalAccuracy()).norm() >= SANE_GPS_UNCERTAINTY);
bool gps_accuracy_insane = ((log.getVerticalAccuracy() <= 0) || (log.getSpeedAccuracy() <= 0) || (log.getBearingAccuracyDeg() <= 0));
bool gps_lat_lng_alt_insane = ((std::abs(log.getLatitude()) > 90) || (std::abs(log.getLongitude()) > 180) || (std::abs(log.getAltitude()) > ALTITUDE_SANITY_CHECK));
bool gps_vel_insane = (floatlist2vector(log.getVNED()).norm() > TRANS_SANITY_CHECK);
if (clearpilot_disable_gps || !log.getHasFix() || gps_unreasonable || gps_accuracy_insane || gps_lat_lng_alt_insane || gps_vel_insane) {
if (!log.getHasFix() || gps_unreasonable || gps_accuracy_insane || gps_lat_lng_alt_insane || gps_vel_insane) {
//this->gps_valid = false;
this->determine_gps_mode(current_time);
return;
+3 -13
View File
@@ -7,7 +7,7 @@ import ctypes
import numpy as np
from pathlib import Path
from cereal import car, messaging
from cereal import messaging
from cereal.messaging import PubMaster, SubMaster
from cereal.visionipc import VisionIpcClient, VisionStreamType, VisionBuf
from openpilot.common.swaglog import cloudlog
@@ -128,15 +128,10 @@ def main():
assert vipc_client.is_connected()
cloudlog.warning(f"connected with buffer size: {vipc_client.buffer_len}")
sm = SubMaster(["liveCalibration", "carState"])
sm = SubMaster(["liveCalibration"])
pm = PubMaster(["driverStateV2"])
calib = np.zeros(CALIB_LEN, dtype=np.float32)
# CLEARPILOT: cache last model output to serve while gear is in park —
# mirrors the same trick modeld uses. Skips DSP inference on the driver
# camera when the car is stationary; downstream dmonitoringd still gets
# a fresh publish each frame.
last_model_output = None
# last = 0
while True:
@@ -148,13 +143,8 @@ def main():
if sm.updated["liveCalibration"]:
calib[:] = np.array(sm["liveCalibration"].rpyCalib)
parked = sm["carState"].gearShifter == car.CarState.GearShifter.park
t1 = time.perf_counter()
if parked and last_model_output is not None:
model_output, dsp_execution_time = last_model_output
else:
model_output, dsp_execution_time = model.run(buf, calib)
last_model_output = (model_output, dsp_execution_time)
model_output, dsp_execution_time = model.run(buf, calib)
t2 = time.perf_counter()
pm.send("driverStateV2", get_driverstate_packet(model_output, vipc_client.frame_id, vipc_client.timestamp_sof, t2 - t1, dsp_execution_time))
+6 -29
View File
@@ -134,15 +134,11 @@ def main(demo=False):
setproctitle(PROCESS_NAME)
config_realtime_process(7, 54)
import time as _time
cloudlog.warning("setting up CL context")
_t0 = _time.monotonic()
cl_context = CLContext()
_t1 = _time.monotonic()
cloudlog.warning("CL context ready in %.3fs; loading model", _t1 - _t0)
cloudlog.warning("CL context ready; loading model")
model = ModelState(cl_context)
_t2 = _time.monotonic()
cloudlog.warning("model loaded in %.3fs (total init %.3fs), modeld starting", _t2 - _t1, _t2 - _t0)
cloudlog.warning("models loaded, modeld starting")
# visionipc clients
while True:
@@ -183,10 +179,6 @@ def main(demo=False):
model_transform_main = np.zeros((3, 3), dtype=np.float32)
model_transform_extra = np.zeros((3, 3), dtype=np.float32)
live_calib_seen = False
# CLEARPILOT: cache last model output to serve while gear is in park — saves
# GPU inference cost while still giving downstream a constant publish rate so
# freq_ok / valid checks don't cascade.
last_model_output = None
nav_features = np.zeros(ModelConstants.NAV_FEATURE_LEN, dtype=np.float32)
nav_instructions = np.zeros(ModelConstants.NAV_INSTRUCTION_LEN, dtype=np.float32)
buf_main, buf_extra = None, None
@@ -241,12 +233,6 @@ def main(demo=False):
meta_extra = meta_main
sm.update(0)
# CLEARPILOT: constant 20fps. Variable-rate + standby logic removed — the
# variable-rate path caused freq_ok cascades in downstream consumers
# (calibrationd/locationd/paramsd). Running at the camera's native rate is
# simpler and keeps the full-stack localization chain happy.
desire = DH.desire
is_rhd = sm["driverMonitoringState"].isRHD
frame_id = sm["roadCameraState"].frameId
@@ -318,21 +304,12 @@ def main(demo=False):
**({'radar_tracks': radar_tracks,} if DISABLE_RADAR else {}),
}
# CLEARPILOT: in park, serve the cached last model output instead of running
# GPU inference. First cycle (no cache yet) still runs once so we have
# something to serve. Out-of-park resumes fresh inference every frame.
parked = sm['carState'].gearShifter == car.CarState.GearShifter.park
if parked and last_model_output is not None:
model_output = last_model_output
model_execution_time = 0.0
else:
mt1 = time.perf_counter()
model_output = model.run(buf_main, buf_extra, model_transform_main, model_transform_extra, inputs, prepare_only)
mt2 = time.perf_counter()
model_execution_time = mt2 - mt1
mt1 = time.perf_counter()
model_output = model.run(buf_main, buf_extra, model_transform_main, model_transform_extra, inputs, prepare_only)
mt2 = time.perf_counter()
model_execution_time = mt2 - mt1
if model_output is not None:
last_model_output = model_output
modelv2_send = messaging.new_message('modelV2')
posenet_send = messaging.new_message('cameraOdometry')
fill_model_msg(modelv2_send, model_output, publish_state, meta_main.frame_id, meta_extra.frame_id, frame_id, frame_drop_ratio,
+2 -22
View File
@@ -21,7 +21,6 @@ def dmonitoringd_thread():
v_cruise_last = 0
driver_engaged = False
dbg_prev_valid = True # CLEARPILOT: track valid transitions
# 10Hz <- dmonitoringmodeld
while True:
@@ -44,18 +43,7 @@ def dmonitoringd_thread():
# Get data from dmonitoringmodeld
events = Events()
# CLEARPILOT: narrow update_states gate. The original sm.all_checks() also
# required modelV2 fresh (stops at standstill in two-state modeld) and
# liveCalibration.valid (calibrationd cascades its own freq_ok to valid, which
# flaps). Both made DM freeze pose → face_detected stuck False → awareness
# decayed to 0 within 6s of engagement. Narrow the gate to the subs
# update_states actually reads, and only to alive+valid (skip freq_ok and
# skip liveCalibration.valid). rpyCalib presence is sufficient to know
# calibration has produced output.
if (sm.alive['driverStateV2'] and sm.valid['driverStateV2'] and
sm.alive['carState'] and sm.valid['carState'] and
sm.alive['controlsState'] and sm.valid['controlsState'] and
sm.alive['liveCalibration'] and len(sm['liveCalibration'].rpyCalib) > 0):
if sm.all_checks() and len(sm['liveCalibration'].rpyCalib):
driver_status.update_states(sm['driverStateV2'], sm['liveCalibration'].rpyCalib, sm['carState'].vEgo, sm['controlsState'].enabled)
# Block engaging after max number of distrations
@@ -66,16 +54,8 @@ def dmonitoringd_thread():
# Update events from driver state
driver_status.update_events(events, driver_engaged, sm['controlsState'].enabled, sm['carState'].standstill)
# CLEARPILOT: log on transition to invalid so we can see which sub caused the cascade
dm_valid = sm.all_checks()
if dm_valid != dbg_prev_valid and not dm_valid:
import sys
bad = [s for s in sm.alive if not (sm.alive[s] and sm.valid[s] and sm.freq_ok.get(s, True))]
details = [f"{s}(a={sm.alive[s]},v={sm.valid[s]},f={sm.freq_ok[s]})" for s in bad]
print(f"CLP driverMonitoringState valid=False: {' '.join(details)}", file=sys.stderr, flush=True)
dbg_prev_valid = dm_valid
# build driverMonitoringState packet
dat = messaging.new_message('driverMonitoringState', valid=dm_valid)
dat = messaging.new_message('driverMonitoringState', valid=sm.all_checks())
dat.driverMonitoringState = {
"events": events.to_msg(),
"faceDetected": driver_status.face_detected,