Compare commits

...

43 Commits

Author SHA1 Message Date
firestar5683 d81713bb4e Update carcontroller.py 2025-06-04 20:52:07 -05:00
firestar5683 f043766864 Guard Can Disconnects 2025-06-04 14:12:32 -05:00
firestar5683 a0002ff3e4 Update carcontroller.py 2025-06-04 10:51:09 -05:00
firestar5683 a44a05fe7c Update carcontroller.py 2025-06-03 22:05:06 -05:00
firestar5683 24f122b21e Update carcontroller.py 2025-06-03 00:18:35 -05:00
firestar5683 cf2d09c511 overflow 20ms 2025-06-02 11:13:48 -05:00
firestar5683 98d6ee1d8f 15ms 2025-06-01 00:15:36 -05:00
firestar5683 3b12d08f82 Update carcontroller.py 2025-05-31 13:27:26 -05:00
firestar5683 f3327c9038 Update tinygrad_modeld.py 2025-05-31 12:59:28 -05:00
firestar5683 3a3c8bba99 Drop accumulator 2025-05-30 18:48:27 -05:00
firestar5683 415ceb9da0 Update carcontroller.py 2025-05-30 18:46:33 -05:00
firestar5683 51ca51039e Revert "Update launch_chffrplus.sh"
This reverts commit b4d070fbd58fe245be2ae7e1814589e0cfec1ad3.
2025-05-30 16:54:16 -05:00
firestar5683 40b5fbcd37 Update launch_chffrplus.sh 2025-05-30 16:54:16 -05:00
firestar5683 30d5a96f08 Delete not_vetted 2025-05-30 16:54:16 -05:00
firestar5683 d7a487ad6a 9ms 2025-05-30 16:54:16 -05:00
firestar5683 6b4a2e69ea 8ms guard 2025-05-30 16:54:16 -05:00
firestar5683 7d8187e508 Remove blending 2025-05-30 16:54:16 -05:00
firestar5683 d710061a72 Update carcontroller.py 2025-05-30 16:54:16 -05:00
firestar5683 9252b1589c fix blending 2025-05-30 16:54:16 -05:00
firestar5683 8912469fb0 Paddle Refactor and Smoothing
Paddle Smoothing
Takeoff safety

Co-Authored-By: jc01rho <rkjnice@gmail.com>
2025-05-30 16:54:16 -05:00
firestar5683 bf7d305d0f Update carcontroller.py 2025-05-30 16:54:16 -05:00
FrogAi 2e08378857 agnos 10.1.1 2025-05-30 16:54:16 -05:00
firestar5683 327e08410f Update carcontroller.py 2025-05-30 16:54:16 -05:00
firestar5683 99c3632070 Update carcontroller.py 2025-05-30 16:54:15 -05:00
firestar5683 3e35c2fb7d refactor? 2025-05-30 16:54:15 -05:00
firestar5683 f4d181e08a Update carcontroller.py 2025-05-30 16:54:15 -05:00
firestar5683 d4b422921c More consistent activation/deactivation 2025-05-30 16:54:15 -05:00
firestar5683 deb5322c40 guard off frames. 2025-05-30 16:54:15 -05:00
firestar5683 729ee3cc99 Send more off frames 2025-05-30 16:54:15 -05:00
firestar5683 95ec31a7f9 Madman Logic
Match OEM timing scheme
2025-05-30 16:54:15 -05:00
firestar5683 7db60a6c43 Update carcontroller.py 2025-05-30 16:54:15 -05:00
firestar5683 040220506f Vikander Model
Vikander Model
2025-05-30 16:54:14 -05:00
firestar5683 d732fe98ff Update carcontroller.py 2025-05-20 12:04:24 -05:00
firestar5683 153758c88b Restore Sanity
AHHHHHHH
2025-05-18 13:23:49 -05:00
firestar5683 c065aeba58 Update interface.py 2025-05-12 15:11:34 -05:00
firestar5683 888bff27e5 Update interface.py 2025-05-12 14:21:38 -05:00
firestar5683 a84adbbeac Revert "Update interface.py"
This reverts commit 6b82ff72e6.
2025-05-12 13:24:14 -05:00
firestar5683 6b82ff72e6 Update interface.py
Update interface.py

Update interface.py
2025-05-12 13:24:08 -05:00
firestar5683 2072bd521e Update carcontroller.py 2025-05-08 10:04:39 -05:00
firestar5683 e263bffd80 Revert "Update process_config.py"
This reverts commit c40a3d972c.
2025-05-07 11:16:44 -05:00
firestar5683 48b33a3f9d Update carcontroller.py 2025-05-07 08:20:52 -05:00
firestar5683 c40a3d972c Update process_config.py 2025-05-06 17:18:09 -05:00
firestar5683 d7a3930d42 Paddle
Paddle
2025-05-06 17:04:23 -05:00
32 changed files with 363 additions and 126 deletions
+2
View File
@@ -1048,6 +1048,8 @@ struct ModelDataV2 {
struct Action {
desiredCurvature @0 :Float32;
desiredAcceleration @1 :Float32;
shouldStop @2 :Bool;
}
}
+1 -1
View File
@@ -13,7 +13,7 @@ from openpilot.frogpilot.assets.download_functions import GITLAB_URL, download_f
from openpilot.frogpilot.common.frogpilot_utilities import delete_file
from openpilot.frogpilot.common.frogpilot_variables import DEFAULT_CLASSIC_MODEL, DEFAULT_MODEL, DEFAULT_TINYGRAD_MODEL, MODELS_PATH, params, params_default, params_memory
VERSION = "v14"
VERSION = "v15"
CANCEL_DOWNLOAD_PARAM = "CancelModelDownload"
DOWNLOAD_PROGRESS_PARAM = "ModelDownloadProgress"
Binary file not shown.

Before

Width:  |  Height:  |  Size: 371 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 503 KiB

After

Width:  |  Height:  |  Size: 661 KiB

+2 -2
View File
@@ -58,8 +58,8 @@ DEFAULT_MODEL = "national-public-radio"
DEFAULT_MODEL_NAME = "National Public Radio 👀📡"
DEFAULT_MODEL_VERSION = "v6"
DEFAULT_TINYGRAD_MODEL = "filet-o-fish"
DEFAULT_TINYGRAD_MODEL_NAME = "Filet-O-Fish 👀📡"
DEFAULT_TINYGRAD_MODEL = "tomb-raider"
DEFAULT_TINYGRAD_MODEL_NAME = "Vikander 👀📡"
DEFAULT_TINYGRAD_MODEL_VERSION = "v8"
EXCLUDED_KEYS = {
+10 -9
View File
@@ -56,7 +56,7 @@ def fill_lane_line_meta(builder, lane_lines, lane_line_probs):
builder.rightProb = lane_line_probs[2]
def fill_model_msg(base_msg: capnp._DynamicStructBuilder, extended_msg: capnp._DynamicStructBuilder,
net_output_data: dict[str, np.ndarray], v_ego: float, delay: float,
net_output_data: dict[str, np.ndarray], action: log.ModelDataV2.Action,
publish_state: PublishState, vipc_frame_id: int, vipc_frame_id_extra: int,
frame_id: int, frame_drop: float, timestamp_eof: int, model_execution_time: float,
valid: bool) -> None:
@@ -71,7 +71,8 @@ def fill_model_msg(base_msg: capnp._DynamicStructBuilder, extended_msg: capnp._D
driving_model_data.frameIdExtra = vipc_frame_id_extra
driving_model_data.frameDropPerc = frame_drop_perc
driving_model_data.modelExecutionTime = model_execution_time
driving_model_data.action.desiredCurvature = float(net_output_data['desired_curvature'][0,0])
driving_model_data.action = action
modelV2 = extended_msg.modelV2
modelV2.frameId = vipc_frame_id
@@ -89,17 +90,17 @@ def fill_model_msg(base_msg: capnp._DynamicStructBuilder, extended_msg: capnp._D
fill_xyzt(modelV2.orientationRate, ModelConstants.T_IDXS, *net_output_data['plan'][0,:,Plan.ORIENTATION_RATE].T)
# temporal pose
temporal_pose = modelV2.temporalPose
temporal_pose.trans = net_output_data['sim_pose'][0,:ModelConstants.POSE_WIDTH//2].tolist()
temporal_pose.transStd = net_output_data['sim_pose_stds'][0,:ModelConstants.POSE_WIDTH//2].tolist()
temporal_pose.rot = net_output_data['sim_pose'][0,ModelConstants.POSE_WIDTH//2:].tolist()
temporal_pose.rotStd = net_output_data['sim_pose_stds'][0,ModelConstants.POSE_WIDTH//2:].tolist()
#temporal_pose = modelV2.temporalPose
#temporal_pose.trans = net_output_data['sim_pose'][0,:ModelConstants.POSE_WIDTH//2].tolist()
#temporal_pose.transStd = net_output_data['sim_pose_stds'][0,:ModelConstants.POSE_WIDTH//2].tolist()
#temporal_pose.rot = net_output_data['sim_pose'][0,ModelConstants.POSE_WIDTH//2:].tolist()
#temporal_pose.rotStd = net_output_data['sim_pose_stds'][0,ModelConstants.POSE_WIDTH//2:].tolist()
# poly path
fill_xyz_poly(driving_model_data.path, ModelConstants.POLY_PATH_DEGREE, *net_output_data['plan'][0,:,Plan.POSITION].T)
# lateral planning
modelV2.action.desiredCurvature = float(net_output_data['desired_curvature'][0,0])
# action
modelV2.action = action
# times at X_IDXS of edges and lines aren't used
LINE_T_IDXS: list[float] = []
@@ -88,6 +88,12 @@ class Parser:
self.parse_mdn('pose', outs, in_N=0, out_N=0, out_shape=(ModelConstants.POSE_WIDTH,))
self.parse_mdn('wide_from_device_euler', outs, in_N=0, out_N=0, out_shape=(ModelConstants.WIDE_FROM_DEVICE_WIDTH,))
self.parse_mdn('road_transform', outs, in_N=0, out_N=0, out_shape=(ModelConstants.POSE_WIDTH,))
self.parse_mdn('lane_lines', outs, in_N=0, out_N=0, out_shape=(ModelConstants.NUM_LANE_LINES,ModelConstants.IDX_N,ModelConstants.LANE_LINES_WIDTH))
self.parse_mdn('road_edges', outs, in_N=0, out_N=0, out_shape=(ModelConstants.NUM_ROAD_EDGES,ModelConstants.IDX_N,ModelConstants.LANE_LINES_WIDTH))
self.parse_mdn('lead', outs, in_N=ModelConstants.LEAD_MHP_N, out_N=ModelConstants.LEAD_MHP_SELECTION,
out_shape=(ModelConstants.LEAD_TRAJ_LEN,ModelConstants.LEAD_WIDTH))
for k in ['lead_prob', 'lane_lines_prob']:
self.parse_binary_crossentropy(k, outs)
self.parse_categorical_crossentropy('desire_pred', outs, out_shape=(ModelConstants.DESIRE_PRED_LEN,ModelConstants.DESIRE_PRED_WIDTH))
self.parse_binary_crossentropy('meta', outs)
return outs
@@ -95,17 +101,10 @@ class Parser:
def parse_policy_outputs(self, outs: dict[str, np.ndarray]) -> dict[str, np.ndarray]:
self.parse_mdn('plan', outs, in_N=ModelConstants.PLAN_MHP_N, out_N=ModelConstants.PLAN_MHP_SELECTION,
out_shape=(ModelConstants.IDX_N,ModelConstants.PLAN_WIDTH))
self.parse_mdn('lane_lines', outs, in_N=0, out_N=0, out_shape=(ModelConstants.NUM_LANE_LINES,ModelConstants.IDX_N,ModelConstants.LANE_LINES_WIDTH))
self.parse_mdn('road_edges', outs, in_N=0, out_N=0, out_shape=(ModelConstants.NUM_ROAD_EDGES,ModelConstants.IDX_N,ModelConstants.LANE_LINES_WIDTH))
self.parse_mdn('sim_pose', outs, in_N=0, out_N=0, out_shape=(ModelConstants.POSE_WIDTH,))
self.parse_mdn('lead', outs, in_N=ModelConstants.LEAD_MHP_N, out_N=ModelConstants.LEAD_MHP_SELECTION,
out_shape=(ModelConstants.LEAD_TRAJ_LEN,ModelConstants.LEAD_WIDTH))
if 'lat_planner_solution' in outs:
self.parse_mdn('lat_planner_solution', outs, in_N=0, out_N=0, out_shape=(ModelConstants.IDX_N,ModelConstants.LAT_PLANNER_SOLUTION_WIDTH))
if 'desired_curvature' in outs:
self.parse_mdn('desired_curvature', outs, in_N=0, out_N=0, out_shape=(ModelConstants.DESIRED_CURV_WIDTH,))
for k in ['lead_prob', 'lane_lines_prob']:
self.parse_binary_crossentropy(k, outs)
self.parse_categorical_crossentropy('desire_state', outs, out_shape=(ModelConstants.DESIRE_PRED_WIDTH,))
return outs
+38 -6
View File
@@ -20,19 +20,21 @@ from msgq.visionipc import VisionIpcClient, VisionStreamType, VisionBuf
from openpilot.common.swaglog import cloudlog
from openpilot.common.params import Params
from openpilot.common.filter_simple import FirstOrderFilter
from openpilot.common.realtime import config_realtime_process
from openpilot.common.realtime import config_realtime_process, DT_MDL
from openpilot.common.transformations.camera import DEVICE_CAMERAS
from openpilot.common.transformations.model import get_warp_matrix
from openpilot.system import sentry
from openpilot.selfdrive.car.car_helpers import get_demo_car_params
from openpilot.selfdrive.controls.lib.desire_helper import DesireHelper
from openpilot.selfdrive.controls.lib.drive_helpers import get_accel_from_plan_tomb_raider, smooth_value, get_curvature_from_plan
from openpilot.frogpilot.tinygrad_modeld.parse_model_outputs import Parser
from openpilot.frogpilot.tinygrad_modeld.fill_model_msg import fill_model_msg, fill_pose_msg, PublishState
from openpilot.frogpilot.tinygrad_modeld.constants import ModelConstants
from openpilot.frogpilot.tinygrad_modeld.constants import ModelConstants, Plan
from openpilot.frogpilot.tinygrad_modeld.models.commonmodel_pyx import DrivingModelFrame, CLContext
from openpilot.frogpilot.common.frogpilot_variables import get_frogpilot_toggles
PROCESS_NAME = "frogpilot.tinygrad_modeld.tinygrad_modeld"
SEND_RAW_PRED = os.getenv('SEND_RAW_PRED')
@@ -41,6 +43,30 @@ POLICY_PKL_PATH = Path(__file__).parent / 'models/driving_policy_tinygrad.pkl'
VISION_METADATA_PATH = Path(__file__).parent / 'models/driving_vision_metadata.pkl'
POLICY_METADATA_PATH = Path(__file__).parent / 'models/driving_policy_metadata.pkl'
LAT_SMOOTH_SECONDS = 0.2
LONG_SMOOTH_SECONDS = 0.2
MIN_LAT_CONTROL_SPEED = 0.3
def get_action_from_model(model_output: dict[str, np.ndarray], prev_action: log.ModelDataV2.Action,
lat_action_t: float, long_action_t: float, v_ego: float) -> log.ModelDataV2.Action:
plan = model_output['plan'][0]
desired_accel, should_stop = get_accel_from_plan_tomb_raider(plan[:,Plan.VELOCITY][:,0],
plan[:,Plan.ACCELERATION][:,0],
ModelConstants.T_IDXS,
action_t=long_action_t)
desired_accel = smooth_value(desired_accel, prev_action.desiredAcceleration, LONG_SMOOTH_SECONDS)
desired_curvature = model_output['desired_curvature'][0, 0]
if v_ego > MIN_LAT_CONTROL_SPEED:
desired_curvature = smooth_value(desired_curvature, prev_action.desiredCurvature, LAT_SMOOTH_SECONDS)
else:
desired_curvature = prev_action.desiredCurvature
return log.ModelDataV2.Action(desiredCurvature=float(desired_curvature),
desiredAcceleration=float(desired_accel),
shouldStop=bool(should_stop))
class FrameMeta:
frame_id: int = 0
timestamp_sof: int = 0
@@ -148,7 +174,7 @@ class ModelState:
# TODO model only uses last value now
self.full_prev_desired_curv[0,:-1] = self.full_prev_desired_curv[0,1:]
self.full_prev_desired_curv[0,-1,:] = policy_outputs_dict['desired_curvature'][0, :]
self.numpy_inputs['prev_desired_curv'][:] = self.full_prev_desired_curv[0, self.temporal_idxs]
self.numpy_inputs['prev_desired_curv'][:] = 0*self.full_prev_desired_curv[0, self.temporal_idxs]
combined_outputs_dict = {**vision_outputs_dict, **policy_outputs_dict}
if SEND_RAW_PRED:
@@ -223,7 +249,10 @@ def main(demo=False):
cloudlog.info("tinygrad_modeld got CarParams: %s", CP.carName)
# TODO this needs more thought, use .2s extra for now to estimate other delays
steer_delay = CP.steerActuatorDelay + .2
# TODO Move smooth seconds to action function
lat_delay = CP.steerActuatorDelay + .2 + LAT_SMOOTH_SECONDS
long_delay = CP.longitudinalActuatorDelay + LONG_SMOOTH_SECONDS
prev_action = log.ModelDataV2.Action()
DH = DesireHelper()
@@ -268,7 +297,7 @@ def main(demo=False):
is_rhd = sm["driverMonitoringState"].isRHD
frame_id = sm["roadCameraState"].frameId
v_ego = max(sm["carState"].vEgo, 0.)
lateral_control_params = np.array([v_ego, steer_delay], dtype=np.float32)
lateral_control_params = np.array([v_ego, lat_delay], dtype=np.float32)
if sm.updated["liveCalibration"] and sm.seen['roadCameraState'] and sm.seen['deviceState']:
device_from_calib_euler = np.array(sm["liveCalibration"].rpyCalib, dtype=np.float32)
dc = DEVICE_CAMERAS[(str(sm['deviceState'].deviceType), str(sm['roadCameraState'].sensor))]
@@ -311,7 +340,10 @@ def main(demo=False):
modelv2_send = messaging.new_message('modelV2')
drivingdata_send = messaging.new_message('drivingModelData')
posenet_send = messaging.new_message('cameraOdometry')
fill_model_msg(drivingdata_send, modelv2_send, model_output, v_ego, steer_delay,
action = get_action_from_model(model_output, prev_action, lat_delay + DT_MDL, long_delay + DT_MDL, v_ego)
prev_action = action
fill_model_msg(drivingdata_send, modelv2_send, model_output, action,
publish_state, meta_main.frame_id, meta_extra.frame_id, frame_id,
frame_drop_ratio, meta_main.timestamp_eof, model_execution_time, live_calib_seen)
-22
View File
@@ -1,13 +1,9 @@
#!/usr/bin/bash
if [ -z "$BASEDIR" ]; then
BASEDIR="/data/openpilot"
fi
# One-time setup flag (reset on each overlay install)
ONCE_FLAG_FILE="/data/openpilot/.one_time_setup_done"
source "$BASEDIR/launch_env.sh"
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )"
@@ -35,22 +31,6 @@ function agnos_init {
fi
}
function one_time_setup {
if [ ! -f "$ONCE_FLAG_FILE" ]; then
echo "Performing one-time setup tasks..."
# Run once:
echo "Wiping old params..."
rm /data/params/d/DongleId
rm /data/params/d/StockDongleId
echo "Old params wiped."
touch "$ONCE_FLAG_FILE"
else
echo "One-time setup already completed. Skipping."
fi
}
function launch {
# Remove orphaned git lock if it exists on boot
[ -f "$DIR/.git/index.lock" ] && rm -f $DIR/.git/index.lock
@@ -112,6 +92,4 @@ function launch {
while true; do sleep 1; done
}
one_time_setup
launch
+1 -1
View File
@@ -7,7 +7,7 @@ export OPENBLAS_NUM_THREADS=1
export VECLIB_MAXIMUM_THREADS=1
if [ -z "$AGNOS_VERSION" ]; then
export AGNOS_VERSION="10.1"
export AGNOS_VERSION="10.1.1"
fi
export STAGING_ROOT="/data/safe_staging"
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 418 KiB

After

Width:  |  Height:  |  Size: 671 KiB

+2 -1
View File
@@ -209,7 +209,8 @@ def get_car(logcan, sendcan, experimental_long_allowed, params, num_pandas=1, fr
CP.fingerprintSource = source
CP.fuzzyFingerprint = not exact_match
return get_car_interface(CP, FPCP), CP, FPCP
interface_instance = get_car_interface(CP, None)
return interface_instance, CP, FPCP
def write_car_param(platform=MOCK.MOCK):
params = Params()
+174 -28
View File
@@ -1,3 +1,4 @@
from typing import Tuple
from cereal import car
from openpilot.common.conversions import Conversions as CV
from openpilot.common.filter_simple import FirstOrderFilter
@@ -7,7 +8,7 @@ from openpilot.common.params_pyx import Params
from opendbc.can.packer import CANPacker
from openpilot.selfdrive.car import apply_driver_steer_torque_limits, create_gas_interceptor_command
from openpilot.selfdrive.car.gm import gmcan
from openpilot.selfdrive.car.gm.values import DBC, CanBus, CarControllerParams, CruiseButtons, GMFlags, CC_ONLY_CAR, SDGM_CAR, EV_CAR, AccState
from openpilot.selfdrive.car.gm.values import DBC, CanBus, CarControllerParams, CruiseButtons, GMFlags, CC_ONLY_CAR, SDGM_CAR, EV_CAR, AccState, CC_REGEN_PADDLE_CAR
from openpilot.selfdrive.car.interfaces import CarControllerBase
from openpilot.selfdrive.controls.lib.drive_helpers import apply_deadzone
from openpilot.selfdrive.controls.lib.vehicle_model import ACCELERATION_DUE_TO_GRAVITY
@@ -21,8 +22,7 @@ TransmissionType = car.CarParams.TransmissionType
# Camera cancels up to 0.1s after brake is pressed, ECM allows 0.5s
CAMERA_CANCEL_DELAY_FRAMES = 10
# Enforce a minimum interval between steering messages to avoid a fault
MIN_STEER_MSG_INTERVAL_MS = 25
MIN_STEER_MSG_INTERVAL_MS = 15
# Constants for pitch compensation
PITCH_DEADZONE = 0.01 # [radians] 0.01 ≈ 1% grade
BRAKE_PITCH_FACTOR_BP = [5., 10.] # [m/s] smoothly revert to planned accel at low speeds
@@ -38,6 +38,10 @@ class CarController(CarControllerBase):
self.apply_speed = 0
self.frame = 0
self.last_steer_frame = 0
self.last_steer_ts_ns = 0
self.last_regen_active = False
self.prev_steer_ts_ns = 0
self.last_spoof_ts_ns = 0
self.last_button_frame = 0
self.cancel_counter = 0
self.pedal_steady = 0.
@@ -55,24 +59,64 @@ class CarController(CarControllerBase):
# FrogPilot variables
self.pitch = FirstOrderFilter(0., 0.09 * 4, DT_CTRL * 4) # runs at 25 Hz
self.accel_g = 0.0
self.regen_paddle_pressed = False
self.aego = 0.0
self.regen_paddle_timer = 0
@staticmethod
def calc_pedal_command(accel: float, long_active: bool, car_velocity) -> float:
if not long_active: return 0.
# Midpoint + overflow spoof accumulator and flags
self.spoof_accum = 0.0
self.spoof_mid_sent = False
self.spoof_over_sent = False
self.last_interval_ns = 0
if accel < -0.5:
pedal_gas = 0
else:
def calc_pedal_command(self, accel: float, long_active: bool, car_velocity) -> Tuple[float, bool]:
if not long_active:
return 0., False
pedaloffset = interp(car_velocity, [0., 3, 6, 30], [0.10, 0.175, 0.240, 0.240])
pedal_gas = clip((pedaloffset + accel * 0.6), 0.0, 1.0)
return pedal_gas
# Regen paddle hysteresis (frame-based): hold 30 frames, with decrement dead-zone
if not hasattr(self, 'regen_paddle_timer'):
self.regen_paddle_timer = 0 # frames
# Regen paddle hysteresis (framebased): count frames when decelerating hard, decrement only when truly released
if self.aego < -0.7:
self.regen_paddle_timer += 1
elif self.aego > -0.3:
self.regen_paddle_timer = max(self.regen_paddle_timer - 1, 0)
# else: hold timer between -0.7 and -0.3
# Base paddle press hysteresis
self.regen_paddle_pressed = self.regen_paddle_timer >= 20 # 30 frames
press_regen_paddle = self.regen_paddle_pressed
# Regen gain ratios from bin-averaged 600 deceleration sweep; Calculates stronger decel from paddle
speed_mps = [0.559, 1.678, 2.797, 3.916, 5.035, 6.154, 7.273, 8.392, 9.511, 10.63,
11.749, 12.868, 13.987, 15.106, 16.225, 17.344, 18.463, 19.582, 20.701, 21.820,
22.939, 24.058, 25.177, 26.296]
regen_gain_ratio = [1.01, 1.01, 1.02, 1.05, 1.08, 1.345979, 1.369975,
1.376302, 1.388052, 1.370367, 1.388498, 1.386030, 1.405950, 1.387555,
1.390392, 1.394946, 1.414915, 1.428535, 1.439611, 1.440106, 1.441438,
1.439395, 1.446909, 1.445738]
gain = interp(car_velocity, speed_mps, regen_gain_ratio)
pedaloffset = interp(car_velocity, [0., 3, 6, 30], [0.10, 0.175, 0.240, 0.240])
# Compute raw pedal gas
raw_pedal_gas = clip((pedaloffset + (accel / gain) * 0.6), 0.0, 1.0) if press_regen_paddle else clip((pedaloffset + accel * 0.6), 0.0, 1.0)
# --- Immediate application of raw pedal gas, no blending ---
pedal_gas = raw_pedal_gas
# Safety cap: ramp from 22% at 0 m/s to 37.25% at 10 mph (4.47 m/s), then allow full throttleAdd commentMore actions
pedal_gas_max = interp(car_velocity, [0.0, 4.47, 4.48], [0.22, 0.3725, 1.0])
pedal_gas = clip(pedal_gas, 0.0, pedal_gas_max)
return pedal_gas, press_regen_paddle
def update(self, CC, CS, now_nanos, frogpilot_toggles):
self.CS = CS
self.aego = CS.out.aEgo
actuators = CC.actuators
accel = brake_accel = actuators.accel
press_regen_paddle = False
hud_control = CC.hudControl
hud_alert = hud_control.visualAlert
hud_v_cruise = hud_control.setSpeed
@@ -81,6 +125,98 @@ class CarController(CarControllerBase):
# Send CAN commands.
can_sends = []
paddle_sends = []
raw_regen_active = (
self.CP.carFingerprint in CC_REGEN_PADDLE_CAR and
self.CP.openpilotLongitudinalControl and
CC.longActive and
self.CP.enableGasInterceptor and
self.regen_paddle_timer >= 20 # raw hysteresis-only
)
regen_active = raw_regen_active
# === Spoof scheduling: midpoint + overflow (~40Hz) ===
# Implement 1s hold-off after CAN reconnect
# Detect reconnect: loopback_lka_steering_cmd_ts_nanos == 0 and self.last_steer_ts_ns != 0
if CS.loopback_lka_steering_cmd_ts_nanos == 0 and self.last_steer_ts_ns != 0:
self.loopback_holdoff_until = now_nanos + 1_000_000_000
# Remove holdoff if expired
if hasattr(self, "loopback_holdoff_until") and now_nanos >= self.loopback_holdoff_until:
del self.loopback_holdoff_until
# Rising-edge reset on regen start
if raw_regen_active and not self.last_regen_active:
self.prev_steer_ts_ns = self.last_steer_ts_ns
self.last_spoof_ts_ns = 0
self.spoof_accum = 0.0
self.spoof_mid_sent = False
self.spoof_over_sent = False
if raw_regen_active:
# Interval between last two bus-0 steer sends
interval_ns = self.last_steer_ts_ns - self.prev_steer_ts_ns
# New steer interval? clear per-interval flags
if interval_ns != self.last_interval_ns:
self.spoof_mid_sent = False
self.spoof_over_sent = False
self.last_interval_ns = interval_ns
# Accumulate extra spoofs needed above 33Hz base to reach 40Hz
self.spoof_accum += (40.0/33.0 - 1.0)
# 500ms holdoff after reconnect: skip spoof scheduling if active
skip_spoof = hasattr(self, "loopback_holdoff_until") and now_nanos < self.loopback_holdoff_until
# Midpoint spoof: one per interval; avoid sending if loopback stale
if not skip_spoof and not self.spoof_mid_sent and interval_ns > 0:
midpoint_ns = self.prev_steer_ts_ns + interval_ns // 2
if (CS.loopback_lka_steering_cmd_ts_nanos != 0 and
now_nanos - CS.loopback_lka_steering_cmd_ts_nanos < 10_000_000 and
now_nanos >= midpoint_ns and
now_nanos - self.last_steer_ts_ns >= 26_000_000):
paddle_sends.append(gmcan.create_prndl2_command(self.packer_pt, CanBus.POWERTRAIN, True))
paddle_sends.append(gmcan.create_regen_paddle_command(self.packer_pt, CanBus.POWERTRAIN, True))
self.last_spoof_ts_ns = now_nanos
self.spoof_mid_sent = True
# Overflow spoof: insert extra when accumulator allows
if not skip_spoof and self.spoof_accum >= 0.5 and not self.spoof_over_sent and interval_ns > 0:
slot2_ns = self.prev_steer_ts_ns + (interval_ns * 2) // 3
if (CS.loopback_lka_steering_cmd_ts_nanos != 0 and
now_nanos - CS.loopback_lka_steering_cmd_ts_nanos < 10_000_000 and
now_nanos >= slot2_ns and
now_nanos - self.last_steer_ts_ns >= 28_000_000):
paddle_sends.append(gmcan.create_prndl2_command(self.packer_pt, CanBus.POWERTRAIN, True))
paddle_sends.append(gmcan.create_regen_paddle_command(self.packer_pt, CanBus.POWERTRAIN, True))
self.last_spoof_ts_ns = now_nanos
self.spoof_over_sent = True
self.spoof_accum -= 0.5
# === End Spoof scheduling ===
# === Off-pulse scheduling on regen release ===
if not raw_regen_active and self.last_regen_active:
# schedule two off-slots at 1/3 and 2/3 of the last steer interval
if self.prev_steer_ts_ns and self.last_steer_ts_ns:
intv = self.last_steer_ts_ns - self.prev_steer_ts_ns
self.off_schedule_ns = [
self.prev_steer_ts_ns + intv // 3,
self.prev_steer_ts_ns + (2 * intv) // 3
]
self.off_sent = [False, False]
if hasattr(self, "off_schedule_ns"):
for i, t_ns in enumerate(self.off_schedule_ns):
if not self.off_sent[i] and now_nanos >= t_ns and now_nanos - self.last_steer_ts_ns >= 18_000_000:
paddle_sends.append(gmcan.create_prndl2_command(self.packer_pt, CanBus.POWERTRAIN, False))
paddle_sends.append(gmcan.create_regen_paddle_command(self.packer_pt, CanBus.POWERTRAIN, False))
self.off_sent[i] = True
# clean up once both off pulses are sent
if hasattr(self, "off_sent") and all(self.off_sent):
del self.off_schedule_ns
del self.off_sent
# === End off-pulse scheduling ===
# Steering (Active: 50Hz, inactive: 10Hz)
steer_step = self.params.STEER_STEP if CC.latActive else self.params.INACTIVE_STEER_STEP
@@ -110,11 +246,24 @@ class CarController(CarControllerBase):
else:
apply_steer = 0
# shift previous steer timestamp
self.prev_steer_ts_ns = self.last_steer_ts_ns
self.last_steer_ts_ns = now_nanos
self.last_steer_frame = self.frame
self.apply_steer_last = apply_steer
idx = self.lka_steering_cmd_counter % 4
can_sends.append(gmcan.create_steering_control(self.packer_pt, CanBus.POWERTRAIN, apply_steer, idx, CC.latActive))
# Update regen_active state and last_regen_paddle_pressed for next loop
self.last_regen_active = regen_active
self.last_regen_paddle_pressed = self.regen_paddle_pressed
# Merge paddle spoof CAN frames, time-guarded only
if paddle_sends:
# wait at least 15 ms after the last bus0 steer send
if now_nanos - self.last_steer_ts_ns >= 15_000_000:
can_sends.extend(paddle_sends)
if self.CP.openpilotLongitudinalControl:
# Gas/regen, brakes, and UI commands - all at 25Hz
if self.frame % 4 == 0:
@@ -141,12 +290,11 @@ class CarController(CarControllerBase):
else:
# Normal operation
if self.CP.carFingerprint in EV_CAR:
self.params.update_ev_gas_brake_threshold(CS.out.vEgo)
if frogpilot_toggles.sport_plus:
self.apply_gas = int(round(interp(accel, self.params.EV_GAS_LOOKUP_BP_PLUS, self.params.GAS_LOOKUP_V_PLUS)))
self.apply_gas = int(round(interp(accel, self.params.GAS_LOOKUP_BP_PLUS, self.params.GAS_LOOKUP_V_PLUS)))
else:
self.apply_gas = int(round(interp(accel, self.params.EV_GAS_LOOKUP_BP, self.params.GAS_LOOKUP_V)))
self.apply_brake = int(round(interp(brake_accel, self.params.EV_BRAKE_LOOKUP_BP, self.params.BRAKE_LOOKUP_V)))
self.apply_gas = int(round(interp(accel, self.params.GAS_LOOKUP_BP, self.params.GAS_LOOKUP_V)))
self.apply_brake = int(round(interp(brake_accel, self.params.BRAKE_LOOKUP_BP, self.params.BRAKE_LOOKUP_V)))
else:
if frogpilot_toggles.sport_plus:
self.apply_gas = int(round(interp(accel, self.params.GAS_LOOKUP_BP_PLUS, self.params.GAS_LOOKUP_V_PLUS)))
@@ -159,12 +307,13 @@ class CarController(CarControllerBase):
self.apply_gas = self.params.INACTIVE_REGEN
if self.CP.carFingerprint in CC_ONLY_CAR:
# gas interceptor only used for full long control on cars without ACC
interceptor_gas_cmd = self.calc_pedal_command(actuators.accel, CC.longActive, CS.out.vEgo)
interceptor_gas_cmd, press_regen_paddle = self.calc_pedal_command(actuators.accel, CC.longActive, CS.out.vEgo)
if self.CP.enableGasInterceptor and self.apply_gas > self.params.INACTIVE_REGEN and CS.out.cruiseState.standstill:
# "Tap" the accelerator pedal to re-engage ACC
interceptor_gas_cmd = self.params.SNG_INTERCEPTOR_GAS
self.apply_brake = 0
press_regen_paddle = False
self.apply_gas = self.params.INACTIVE_REGEN
idx = (self.frame // 4) % 4
@@ -195,33 +344,30 @@ class CarController(CarControllerBase):
# GasRegenCmdActive needs to be 1 to avoid cruise faults. It describes the ACC state, not actuation
can_sends.append(gmcan.create_gas_regen_command(self.packer_pt, CanBus.POWERTRAIN, self.apply_gas, idx, acc_engaged, at_full_stop))
can_sends.append(gmcan.create_friction_brake_command(self.packer_ch, friction_brake_bus, self.apply_brake,
idx, CC.enabled, near_stop, at_full_stop, self.CP))
idx, CC.enabled, near_stop, at_full_stop, self.CP))
# Send dashboard UI commands (ACC status)
send_fcw = hud_alert == VisualAlert.fcw
can_sends.append(gmcan.create_acc_dashboard_command(self.packer_pt, CanBus.POWERTRAIN, CC.enabled,
if self.frame % 10 == 5:
can_sends.append(gmcan.create_acc_dashboard_command(self.packer_pt, CanBus.POWERTRAIN, CC.enabled,
hud_v_cruise * CV.MS_TO_KPH, hud_control, send_fcw))
else:
# to keep accel steady for logs when not sending gas
accel += self.accel_g
# Radar needs to know current speed and yaw rate (50hz),
# and that ADAS is alive (10hz)
# and that ADAS is alive (5hz, previously 10hz)
if not self.CP.radarUnavailable:
tt = self.frame * DT_CTRL
time_and_headlights_step = 10
time_and_headlights_step = 20
if self.frame % time_and_headlights_step == 0:
idx = (self.frame // time_and_headlights_step) % 4
can_sends.append(gmcan.create_adas_time_status(CanBus.OBSTACLE, int((tt - self.start_time) * 60), idx))
can_sends.append(gmcan.create_adas_headlights_status(self.packer_obj, CanBus.OBSTACLE))
speed_and_accelerometer_step = 2
if self.frame % speed_and_accelerometer_step == 0:
idx = (self.frame // speed_and_accelerometer_step) % 4
can_sends.append(gmcan.create_adas_steering_status(CanBus.OBSTACLE, idx))
can_sends.append(gmcan.create_adas_accelerometer_speed_status(CanBus.OBSTACLE, CS.out.vEgo, idx))
if self.CP.networkLocation == NetworkLocation.gateway and self.frame % self.params.ADAS_KEEPALIVE_STEP == 0:
if self.CP.networkLocation == NetworkLocation.gateway and self.frame % (self.params.ADAS_KEEPALIVE_STEP * 2) == 0:
can_sends += gmcan.create_adas_keepalive(CanBus.POWERTRAIN)
# TODO: integrate this with the code block below?
@@ -249,7 +395,7 @@ class CarController(CarControllerBase):
if self.CP.networkLocation == NetworkLocation.fwdCamera:
# Silence "Take Steering" alert sent by camera, forward PSCMStatus with HandsOffSWlDetectionStatus=1
if self.frame % 10 == 0:
if self.frame % 20 == 0:
can_sends.append(gmcan.create_pscm_status(self.packer_pt, CanBus.CAMERA, CS.pscm_status))
new_actuators = actuators.as_builder()
+7 -3
View File
@@ -56,6 +56,10 @@ class CarState(CarStateBase):
self.loopback_lka_steering_cmd_updated = len(loopback_cp.vl_all["ASCMLKASteeringCmd"]["RollingCounter"]) > 0
if self.loopback_lka_steering_cmd_updated:
self.loopback_lka_steering_cmd_ts_nanos = loopback_cp.ts_nanos["ASCMLKASteeringCmd"]["RollingCounter"]
# Track timestamps for OEM PRNDL2 and Regen Paddle messages (used to sync spoofing timing)
self.prndl2_ts_nanos = pt_cp.ts_nanos["ECMPRDNL2"]["PRNDL2"]
self.regen_paddle_ts_nanos = pt_cp.ts_nanos["EBCMRegenPaddle"]["RegenPaddle"]
if self.CP.networkLocation == NetworkLocation.fwdCamera and not self.CP.flags & GMFlags.NO_CAMERA.value:
self.pt_lka_steering_cmd_counter = pt_cp.vl["ASCMLKASteeringCmd"]["RollingCounter"]
self.cam_lka_steering_cmd_counter = cam_cp.vl["ASCMLKASteeringCmd"]["RollingCounter"]
@@ -101,7 +105,7 @@ class CarState(CarStateBase):
if self.CP.enableGasInterceptor:
ret.gas = (pt_cp.vl["GAS_SENSOR"]["INTERCEPTOR_GAS"] + pt_cp.vl["GAS_SENSOR"]["INTERCEPTOR_GAS2"]) / 2.
threshold = 11 if self.CP.carFingerprint in CAMERA_ACC_CAR else 4 # Panda 515 threshold = 10.88. Set lower to avoid panda blocking messages and GasInterceptor faulting.
threshold = 12 if self.CP.carFingerprint in CAMERA_ACC_CAR else 4 # Panda 595 threshold = 10.88. Set lower to avoid panda blocking messages and GasInterceptor faulting.
ret.gasPressed = ret.gas > threshold
else:
ret.gas = pt_cp.vl["AcceleratorPedal2"]["AcceleratorPedal2"] / 254.
@@ -235,7 +239,7 @@ class CarState(CarStateBase):
]
else:
messages += [
("ECMPRDNL2", 10),
("ECMPRDNL2", 40),
("AcceleratorPedal2", 33),
("ECMEngineStatus", 100),
("BCMTurnSignals", 1),
@@ -257,7 +261,7 @@ class CarState(CarStateBase):
if CP.transmissionType == TransmissionType.direct:
messages += [
("EBCMRegenPaddle", 50),
("EBCMRegenPaddle", 40),
("EVDriveMode", 0),
]
+27
View File
@@ -177,6 +177,33 @@ def create_lka_icon_command(bus, active, critical, steer):
dat = b"\x00\x00\x00"
return make_can_msg(0x104c006c, dat, bus)
def create_prndl2_command(packer, bus, press_regen_paddle):
prndl2_value = 7 if press_regen_paddle else 6
manual_mode = 1 if press_regen_paddle else 0
values = {
"Byte0": 0x0C,
"Byte1": 0x0C,
"Byte2": 0x00,
"PRNDL2": prndl2_value,
"Byte4": 0x00,
"ManualMode": manual_mode,
"TransmissionState": 1,
"Byte7": 0x00
}
return packer.make_can_msg("ECMPRDNL2", bus, values)
def create_regen_paddle_command(packer, bus, press_regen_paddle):
regen_paddle_value = 2 if press_regen_paddle else 0
values = {
"RegenPaddle": regen_paddle_value,
"Byte1": 0,
"Byte2": 0,
"Byte3": 0,
"Byte4": 0,
"Byte5": 0,
"Byte6": 0
}
return packer.make_can_msg("EBCMRegenPaddle", bus, values)
def create_gm_cc_spam_command(packer, controller, CS, actuators):
if controller.params_.get_bool("IsMetric"):
+7 -5
View File
@@ -12,8 +12,8 @@ from openpilot.selfdrive.car.gm.values import CAR, CruiseButtons, CarControllerP
from openpilot.selfdrive.car.interfaces import CarInterfaceBase, TorqueFromLateralAccelCallbackType, FRICTION_THRESHOLD, LatControlInputs, NanoFFModel
from openpilot.selfdrive.controls.lib.drive_helpers import get_friction
from openpilot.frogpilot.common.frogpilot_variables import params
from openpilot.common.params import Params
params = Params()
ButtonType = car.CarState.ButtonEvent.Type
FrogPilotButtonType = custom.FrogPilotCarState.ButtonEvent.Type
EventName = car.CarEvent.EventName
@@ -102,6 +102,8 @@ class CarInterface(CarInterfaceBase):
if PEDAL_MSG in fingerprint[0]:
ret.enableGasInterceptor = True
ret.safetyConfigs[0].safetyParam |= Panda.FLAG_GM_GAS_INTERCEPTOR
# When a pedal interceptor is present, always use normal longitudinal (block stock cruise)
experimental_long = False
if candidate in EV_CAR:
ret.transmissionType = TransmissionType.direct
@@ -144,7 +146,7 @@ class CarInterface(CarInterfaceBase):
ret.safetyConfigs[0].safetyParam |= Panda.FLAG_GM_HW_SDGM
else: # ASCM, OBD-II harness
ret.openpilotLongitudinalControl = not frogpilot_toggles.disable_openpilot_long
ret.openpilotLongitudinalControl = not params.get_bool("DisableOpenpilotLongitudinal")
ret.networkLocation = NetworkLocation.gateway
ret.radarUnavailable = RADAR_HEADER_MSG not in fingerprint[CanBus.OBSTACLE] and not docs
ret.pcmCruise = False # stock non-adaptive cruise control is kept off
@@ -266,7 +268,7 @@ class CarInterface(CarInterfaceBase):
ret.safetyConfigs[0].safetyParam |= Panda.FLAG_GM_HW_CAM
ret.minEnableSpeed = -1
ret.pcmCruise = False
ret.openpilotLongitudinalControl = not frogpilot_toggles.disable_openpilot_long
ret.openpilotLongitudinalControl = not params.get_bool("DisableOpenpilotLongitudinal")
ret.stoppingControl = True
ret.autoResumeSng = True
@@ -290,7 +292,7 @@ class CarInterface(CarInterfaceBase):
ret.radarUnavailable = True
ret.experimentalLongitudinalAvailable = False
ret.minEnableSpeed = 24 * CV.MPH_TO_MS
ret.openpilotLongitudinalControl = not frogpilot_toggles.disable_openpilot_long
ret.openpilotLongitudinalControl = not params.get_bool("DisableOpenpilotLongitudinal")
ret.pcmCruise = False
if not ret.enableGasInterceptor and candidate in CC_ONLY_CAR:
+5 -15
View File
@@ -41,7 +41,7 @@ class CarControllerParams:
self.ZERO_GAS = 6144 # Coasting
self.MAX_BRAKE = 400 # ~ -4.0 m/s^2 with regen
if CP.carFingerprint in CAMERA_ACC_CAR and CP.carFingerprint not in CC_ONLY_CAR:
if CP.carFingerprint in CAMERA_ACC_CAR and CP.carFingerprint not in CC_ONLY_CAR and CP.carFingerprint != CAR.CHEVROLET_BOLT_EUV:
self.MAX_GAS = 7496
self.MAX_GAS_PLUS = 8848
self.MAX_ACC_REGEN = 5610
@@ -53,18 +53,18 @@ class CarControllerParams:
elif CP.carFingerprint in SDGM_CAR:
self.MAX_GAS = 7496
self.MAX_GAS_PLUS = 7496
self.MAX_ACC_REGEN = 5610
self.MAX_ACC_REGEN = 7110
self.INACTIVE_REGEN = 5650
self.max_regen_acceleration = 0.
else:
self.MAX_GAS = 7168 # Safety limit, not ACC max. Stock ACC >8192 from standstill.
self.MAX_GAS_PLUS = 8191 # 8292 uses new bit, possible but not tested. Matches Twilsonco tw-main max
self.MAX_ACC_REGEN = 5500 # Max ACC regen is slightly less than max paddle regen
self.MAX_ACC_REGEN = 7110 # Increased for stronger regen braking
self.INACTIVE_REGEN = 5500
# ICE has much less engine braking force compared to regen in EVs,
# lower threshold removes some braking deadzone
self.max_regen_acceleration = -1. if CP.carFingerprint in EV_CAR else -0.1
self.max_regen_acceleration = -3. if CP.carFingerprint in EV_CAR else -0.1 # More aggressive regen for EVs
self.GAS_LOOKUP_BP = [self.max_regen_acceleration, 0., self.ACCEL_MAX]
self.GAS_LOOKUP_BP_PLUS = [self.max_regen_acceleration, 0., self.ACCEL_MAX_PLUS]
@@ -74,18 +74,7 @@ class CarControllerParams:
self.BRAKE_LOOKUP_BP = [self.ACCEL_MIN, self.max_regen_acceleration]
self.BRAKE_LOOKUP_V = [self.MAX_BRAKE, 0.]
# determined by letting Volt regen to a stop in L gear from 89mph,
# and by letting off gas and allowing car to creep, for determining
# the positive threshold values at very low speed
EV_GAS_BRAKE_THRESHOLD_BP = [1.29, 1.52, 1.55, 1.6, 1.7, 1.8, 2.0, 2.2, 2.5, 5.52, 9.6, 20.5, 23.5, 35.0] # [m/s]
EV_GAS_BRAKE_THRESHOLD_V = [0.0, -0.14, -0.16, -0.18, -0.215, -0.255, -0.32, -0.41, -0.5, -0.72, -0.895, -1.125, -1.145, -1.16] # [m/s^s]
def update_ev_gas_brake_threshold(self, v_ego):
gas_brake_threshold = interp(v_ego, self.EV_GAS_BRAKE_THRESHOLD_BP, self.EV_GAS_BRAKE_THRESHOLD_V)
self.GAS_LOOKUP_BP_PLUS = [self.max_regen_acceleration, 0., self.ACCEL_MAX_PLUS]
self.EV_GAS_LOOKUP_BP = [gas_brake_threshold, max(0., gas_brake_threshold), self.ACCEL_MAX]
self.EV_GAS_LOOKUP_BP_PLUS = [gas_brake_threshold, max(0., gas_brake_threshold), self.ACCEL_MAX_PLUS]
self.EV_BRAKE_LOOKUP_BP = [self.ACCEL_MIN, gas_brake_threshold]
@dataclass
class GMCarDocs(CarDocs):
@@ -322,6 +311,7 @@ FW_QUERY_CONFIG = FwQueryConfig(
EV_CAR = {CAR.CHEVROLET_VOLT, CAR.CHEVROLET_BOLT_EUV, CAR.CHEVROLET_VOLT_CC, CAR.CHEVROLET_BOLT_CC}
CC_ONLY_CAR = {CAR.CHEVROLET_VOLT_CC, CAR.CHEVROLET_BOLT_CC, CAR.CHEVROLET_EQUINOX_CC, CAR.CHEVROLET_SUBURBAN_CC, CAR.GMC_YUKON_CC, CAR.CADILLAC_CT6_CC, CAR.CHEVROLET_TRAILBLAZER_CC, CAR.CADILLAC_XT5_CC, CAR.CHEVROLET_MALIBU_CC}
CC_REGEN_PADDLE_CAR = {CAR.CHEVROLET_BOLT_CC, CAR.CHEVROLET_BOLT_EUV}
# CC_ONLY_CAR = set(c for c in CAR if str(c).endswith('_CC'))
# We're integrated at the Safety Data Gateway Module on these cars
+4 -3
View File
@@ -218,9 +218,10 @@ class CarInterfaceBase(ABC):
self.silent_steer_warning = True
self.v_ego_cluster_seen = False
self.CS = CarState(CP, FPCP)
self.cp = self.CS.get_can_parser(CP, FPCP)
self.cp_cam = self.CS.get_cam_can_parser(CP, FPCP)
self.CS = CarState(CP, None)
fp = FPCP if FPCP is not None else getattr(self, "FPCP", None)
self.cp = self.CS.get_can_parser(CP, fp)
self.cp_cam = self.CS.get_cam_can_parser(CP, fp)
self.cp_adas = self.CS.get_adas_can_parser(CP)
self.cp_body = self.CS.get_body_can_parser(CP)
self.cp_loopback = self.CS.get_loopback_can_parser(CP)
+31 -1
View File
@@ -1,9 +1,10 @@
import math
import numpy as np
from cereal import car, log
from openpilot.common.conversions import Conversions as CV
from openpilot.common.numpy_fast import clip, interp
from openpilot.common.realtime import DT_CTRL
from openpilot.common.realtime import DT_CTRL, DT_MDL
# WARNING: this value was determined based on the model's training distribution,
# model predictions above this speed can be unpredictable
@@ -178,6 +179,9 @@ def apply_center_deadzone(error, deadzone):
def rate_limit(new_value, last_value, dw_step, up_step):
return clip(new_value, last_value + dw_step, last_value + up_step)
def smooth_value(val, prev_val, tau, dt=DT_MDL):
alpha = 1 - np.exp(-dt/tau) if tau > 0 else 1
return alpha * val + (1 - alpha) * prev_val
def clip_curvature(v_ego, prev_curvature, new_curvature, planner_curves):
if planner_curves:
@@ -208,3 +212,29 @@ def get_speed_error(modelV2: log.ModelDataV2, v_ego: float) -> float:
vel_err = clip(modelV2.temporalPose.trans[0] - v_ego, -MAX_VEL_ERR, MAX_VEL_ERR)
return float(vel_err)
return 0.0
def get_accel_from_plan_tomb_raider(speeds, accels, t_idxs, action_t=DT_MDL, vEgoStopping=0.05):
if len(speeds) == len(t_idxs):
v_now = speeds[0]
a_now = accels[0]
v_target = np.interp(action_t, t_idxs, speeds)
a_target = 2 * (v_target - v_now) / (action_t) - a_now
v_target_1sec = np.interp(action_t + 1.0, t_idxs, speeds)
else:
v_target = 0.0
v_target_1sec = 0.0
a_target = 0.0
should_stop = (v_target < vEgoStopping and
v_target_1sec < vEgoStopping)
return a_target, should_stop
def curv_from_psis(psi_target, psi_rate, vego, action_t):
vego = np.clip(vego, MIN_SPEED, np.inf)
curv_from_psi = psi_target / (vego * action_t)
return 2*curv_from_psi - psi_rate / vego
def get_curvature_from_plan(yaws, yaw_rates, t_idxs, vego, action_t):
psi_target = np.interp(action_t, t_idxs, yaws)
psi_rate = yaw_rates[0]
return curv_from_psis(psi_target, psi_rate, vego, action_t)
+23 -6
View File
@@ -13,7 +13,7 @@ from openpilot.selfdrive.car.interfaces import ACCEL_MIN, ACCEL_MAX
from openpilot.selfdrive.controls.lib.longcontrol import LongCtrlState
from openpilot.selfdrive.controls.lib.longitudinal_mpc_lib.long_mpc import LongitudinalMpc
from openpilot.selfdrive.controls.lib.longitudinal_mpc_lib.long_mpc import T_IDXS as T_IDXS_MPC, LEAD_ACCEL_TAU
from openpilot.selfdrive.controls.lib.drive_helpers import V_CRUISE_MAX, V_CRUISE_UNSET, CONTROL_N, get_speed_error
from openpilot.selfdrive.controls.lib.drive_helpers import V_CRUISE_MAX, V_CRUISE_UNSET, CONTROL_N, get_speed_error, get_accel_from_plan_tomb_raider
from openpilot.common.swaglog import cloudlog
LON_MPC_STEP = 0.2 # first step is 0.2s
@@ -203,8 +203,12 @@ class LongitudinalPlanner:
throttle_prob = 1.0
return x, v, a, j, throttle_prob
def update(self, radarless_model, sm, frogpilot_toggles):
self.mpc.mode = 'blended' if sm['controlsState'].experimentalMode else 'acc'
def update(self, radarless_model, tomb_raider, sm, frogpilot_toggles):
if tomb_raider:
self.mpc.mode = 'acc'
self.mode = 'blended' if sm['controlsState'].experimentalMode else 'acc'
else:
self.mpc.mode = 'blended' if sm['controlsState'].experimentalMode else 'acc'
if len(sm['carControl'].orientationNED) == 3:
accel_coast = get_coast_accel(sm['carControl'].orientationNED[1])
@@ -294,7 +298,7 @@ class LongitudinalPlanner:
self.a_desired = float(interp(self.dt, CONTROL_N_T_IDX, self.a_desired_trajectory))
self.v_desired_filter.x = self.v_desired_filter.x + self.dt * (self.a_desired + a_prev) / 2.0
def publish(self, classic_model, sm, pm, frogpilot_toggles):
def publish(self, classic_model, tomb_raider, sm, pm, frogpilot_toggles):
plan_send = messaging.new_message('longitudinalPlan')
plan_send.valid = sm.all_checks(service_list=['carState', 'controlsState'])
@@ -314,12 +318,25 @@ class LongitudinalPlanner:
if classic_model:
a_target, should_stop = get_accel_from_plan_classic(self.CP, longitudinalPlan.speeds, longitudinalPlan.accels, vEgoStopping=frogpilot_toggles.vEgoStopping)
elif tomb_raider:
action_t = self.CP.longitudinalActuatorDelay + DT_MDL
output_a_target_mpc, output_should_stop_mpc = get_accel_from_plan_tomb_raider(self.v_desired_trajectory, self.a_desired_trajectory, CONTROL_N_T_IDX,
action_t=action_t, vEgoStopping=frogpilot_toggles.vEgoStopping)
output_a_target_e2e = sm['modelV2'].action.desiredAcceleration
output_should_stop_e2e = sm['modelV2'].action.shouldStop
if self.mode == 'acc':
a_target = output_a_target_mpc
should_stop = output_should_stop_mpc
else:
a_target = min(output_a_target_mpc, output_a_target_e2e)
should_stop = output_should_stop_e2e or output_should_stop_mpc
else:
action_t = self.CP.longitudinalActuatorDelay + DT_MDL
a_target, should_stop = get_accel_from_plan(longitudinalPlan.speeds, longitudinalPlan.accels,
action_t=action_t, vEgoStopping=frogpilot_toggles.vEgoStopping)
longitudinalPlan.aTarget = a_target
longitudinalPlan.shouldStop = should_stop
longitudinalPlan.aTarget = float(a_target)
longitudinalPlan.shouldStop = bool(should_stop)
longitudinalPlan.allowBrake = True
longitudinalPlan.allowThrottle = self.allow_throttle
+3 -2
View File
@@ -39,12 +39,13 @@ def plannerd_thread():
classic_model = frogpilot_toggles.classic_model
radarless_model = frogpilot_toggles.radarless_model
tomb_raider = frogpilot_toggles.model == "tomb-raider"
while True:
sm.update()
if sm.updated['modelV2']:
longitudinal_planner.update(radarless_model, sm, frogpilot_toggles)
longitudinal_planner.publish(classic_model, sm, pm, frogpilot_toggles)
longitudinal_planner.update(radarless_model, tomb_raider, sm, frogpilot_toggles)
longitudinal_planner.publish(classic_model, tomb_raider, sm, pm, frogpilot_toggles)
publish_ui_plan(sm, pm, longitudinal_planner)
# Update FrogPilot parameters
Binary file not shown.
Binary file not shown.
BIN
View File
Binary file not shown.
+7 -7
View File
@@ -1,9 +1,9 @@
[
{
"name": "boot",
"url": "https://commadist.azureedge.net/agnosupdate/boot-5674ea6767e7198cf1e7def3de66a57061f001ed76d43dc4b4f84de545c53c6f.img.xz",
"hash": "5674ea6767e7198cf1e7def3de66a57061f001ed76d43dc4b4f84de545c53c6f",
"hash_raw": "5674ea6767e7198cf1e7def3de66a57061f001ed76d43dc4b4f84de545c53c6f",
"url": "https://boot.frogpilot.download",
"hash": "b997aae3f1c93de82449ef7f23f30ff482b0978f3d0ac08219366f9ce362ad7a",
"hash_raw": "b997aae3f1c93de82449ef7f23f30ff482b0978f3d0ac08219366f9ce362ad7a",
"size": 16029696,
"sparse": false,
"full_check": true,
@@ -61,9 +61,9 @@
},
{
"name": "system",
"url": "https://commadist.azureedge.net/agnosupdate/system-1badfe72851628d6cf9200a53a6151bb4e797b49c717141409fc57138eae388a.img.xz",
"hash": "328e90c62068222dfd98f71dd3f6251fcb962f082b49c6be66ab2699f5db6f4f",
"hash_raw": "1badfe72851628d6cf9200a53a6151bb4e797b49c717141409fc57138eae388a",
"url": "https://system.frogpilot.download",
"hash": "be1c6bb9ee5e06779087b1b81e09b6df61d942566b0f8d4539c452179c661782",
"hash_raw": "a5f84e68d199466fda5c9aead760b90a4cd2d2ef9a418708b9794d95bb03ec5b",
"size": 10737418240,
"sparse": true,
"full_check": false,
@@ -74,4 +74,4 @@
"size": 4548070000
}
}
]
]
+13 -7
View File
@@ -168,18 +168,24 @@ def extract_compressed_image(target_slot_number: int, partition: dict, cloudlog)
last_p = p
print(f"Installing {partition['name']}: {p}", flush=True)
if raw_hash.hexdigest().lower() != partition['hash_raw'].lower():
raise Exception(f"Raw hash mismatch '{raw_hash.hexdigest().lower()}'")
written_size = out.tell()
expected_size = partition['size']
actual_raw_hash = raw_hash.hexdigest().lower()
expected_raw_hash = partition['hash_raw'].lower()
actual_final_hash = downloader.sha256.hexdigest().lower()
expected_final_hash = partition['hash'].lower()
if downloader.sha256.hexdigest().lower() != partition['hash'].lower():
raise Exception("Uncompressed hash mismatch")
if actual_raw_hash != expected_raw_hash:
raise Exception(f"Raw hash mismatch: got {actual_raw_hash}, expected {expected_raw_hash}")
if out.tell() != partition['size']:
raise Exception("Uncompressed size mismatch")
if actual_final_hash != expected_final_hash:
raise Exception(f"Uncompressed hash mismatch: got {actual_final_hash}, expected {expected_final_hash}")
if written_size != expected_size:
raise Exception(f"Uncompressed size mismatch: wrote {written_size} bytes, expected {expected_size} bytes")
os.sync()
def extract_casync_image(target_slot_number: int, partition: dict, cloudlog):
path = get_partition_path(target_slot_number, partition)
seed_path = path[:-1] + ('b' if path[-1] == 'a' else 'a')