Conversation
Car behavior reportReplays driving segments through this PR and compares the behavior to master. Testing 130 segments for: VOLKSWAGEN_ARTEON_MK1, VOLKSWAGEN_ATLAS_MK1, VOLKSWAGEN_CRAFTER_MK2, VOLKSWAGEN_GOLF_MK7, VOLKSWAGEN_JETTA_MK7, VOLKSWAGEN_PASSAT_MK8, VOLKSWAGEN_PASSAT_NMS, VOLKSWAGEN_POLO_MK6, VOLKSWAGEN_TAOS_MK1, VOLKSWAGEN_TIGUAN_MK2, VOLKSWAGEN_TOURAN_MK2, VOLKSWAGEN_TRANSPORTER_T61, VOLKSWAGEN_TROC_MK1 ✅ 0 changed, 130 passed, 0 errors |
There was a problem hiding this comment.
I would prefer we merge just lateral and without longitudinal and do that in a follow up PR, the diff for lateral is already big enough. Just longitudinal looks like a lot to review, and this PR doesn't put ID4 in release yet, not sure why? You can weigh in @adeebshihadeh.
I also want us to add the new curvature safety and use it for Ford first so using it here is a drop in small change, and we can validate it independently without worrying about anything else. We can bring back curvature steering mode that Cameron added if it's needed, just undeprecate it.
There's a few PRs that can be split out here so we can validate smaller changes quicker.
| # Add extra tolerance for average banked road since safety doesn't have the roll | ||
| AVERAGE_ROAD_ROLL = 0.06 # ~3.4 degrees, 6% superelevation. higher actual roll lowers lateral acceleration | ||
| MAX_LATERAL_ACCEL = ISO_LATERAL_ACCEL + (ACCELERATION_DUE_TO_GRAVITY * AVERAGE_ROAD_ROLL) # ~3.6 m/s^2 |
| def apply_std_curvature_limits(apply_curvature: float, apply_curvature_last: float, v_ego: float, curvature: float, | ||
| steer_step: int, lat_active: bool, limits: CurvatureSteeringLimits) -> float: | ||
| new_apply_curvature = apply_curvature | ||
|
|
||
| # Lateral jerk | ||
| max_jerk = get_max_curvature_jerk(v_ego, steer_step) | ||
| curvature_up = apply_curvature_last + max_jerk | ||
| curvature_down = apply_curvature_last - max_jerk | ||
|
|
||
| new_apply_curvature = float(np.clip(new_apply_curvature, curvature_down, curvature_up)) | ||
|
|
||
| # Lateral acceleration | ||
| min_curvature, max_curvature = get_max_curvature_average(v_ego) | ||
| new_apply_curvature = float(np.clip(new_apply_curvature, min_curvature, max_curvature)) | ||
|
|
||
| # set output curvature as current curvature (if otherwise set to 0 in car controller) | ||
| if not lat_active: | ||
| new_apply_curvature = curvature | ||
|
|
||
| return float(np.clip(new_apply_curvature, -limits.CURVATURE_MAX, limits.CURVATURE_MAX)) | ||
|
|
||
|
|
| else: | ||
| # Logic to avoid HCA state 4 "refused": | ||
| # * Don't steer unless HCA is in state 3 "ready" or 5 "active" | ||
| # * Don't steer at standstill | ||
| # * Don't send > 3.00 Newton-meters torque | ||
| # * Don't send the same torque for > 6 seconds | ||
| # * Don't send uninterrupted steering for > 360 seconds | ||
| # MQB racks reset the uninterrupted steering timer after a single frame | ||
| # of HCA disabled; this is done whenever output happens to be zero. | ||
| if CC.latActive: | ||
| new_torque = int(round(actuators.torque * self.CCP.STEER_MAX)) | ||
| apply_torque = apply_driver_steer_torque_limits(new_torque, self.apply_torque_last, CS.out.steeringTorque, self.CCP) | ||
|
|
||
| apply_torque = self.hca_mitigation.update(apply_torque, self.apply_torque_last) | ||
| hca_enabled = apply_torque != 0 | ||
| self.apply_torque_last = apply_torque | ||
| can_sends.append(self.CCS.create_steering_control(self.packer_pt, self.CAN.pt, apply_torque, hca_enabled)) |
There was a problem hiding this comment.
Is this the old torque control path? These comments look new. Move to new PR?
| if self.frame % self.CCP.ACC_CONTROL_STEP == 0 and self.CP.openpilotLongitudinalControl: | ||
| stopping = actuators.longControlState == LongCtrlState.stopping | ||
|
|
||
| if self.CP.flags & VolkswagenFlags.MEB: |
There was a problem hiding this comment.
one global MEB check and group everything there? easier to read
| else: | ||
| lead_distance = 0 | ||
| if hud_control.leadVisible and self.frame * DT_CTRL > 1.0: # Don't display lead until we know the scaling factor | ||
| lead_distance = 512 if CS.upscale_lead_car_signal else 8 | ||
| if self.CP.flags & VolkswagenFlags.PQ: | ||
| long_ccs = pqcan | ||
| elif self.CP.flags & VolkswagenFlags.MLB: | ||
| long_ccs = mlbcan | ||
| else: | ||
| long_ccs = mqbcan | ||
| acc_hud_status = long_ccs.acc_hud_status_value(CS.out.cruiseState.available, CS.out.accFaulted, CC.longActive) | ||
| # FIXME: PQ may need to use the on-the-wire mph/kmh toggle to fix rounding errors | ||
| # FIXME: Detect clusters with vEgoCluster offsets and apply an identical vCruiseCluster offset | ||
| set_speed = hud_control.setSpeed * CV.MS_TO_KPH | ||
| can_sends.append(long_ccs.create_acc_hud_control(self.packer_pt, self.CAN.pt, acc_hud_status, set_speed, | ||
| lead_distance, hud_control.leadDistanceBars)) |
There was a problem hiding this comment.
why does this look different than the deleted code?
|
|
||
| cam_messages = [] | ||
| if CP.networkLocation == NetworkLocation.gateway: | ||
| cam_messages.append(("AWV_03", 1)) # Front Collision Detection (1 Hz when inactive, 50 Hz when active) |
There was a problem hiding this comment.
| cam_messages.append(("AWV_03", 1)) # Front Collision Detection (1 Hz when inactive, 50 Hz when active) | |
| cam_messages.append(("AWV_03", 1)) # Front Collision Detection (1 Hz when inactive, 50 Hz when active) | |
| if 0x3DC in fingerprint[0]: # Gateway_73 | ||
| ret.flags |= VolkswagenFlags.ALT_GEAR.value | ||
|
|
||
| ret.radarUnavailable = 0x24F not in fingerprint[0] # Strukturen_01 |
There was a problem hiding this comment.
| ret.radarUnavailable = 0x24F not in fingerprint[0] # Strukturen_01 | |
| ret.radarUnavailable = 0x24F not in fingerprint[0] # Strukturen_01 |
| # only allow gateway harness for now | ||
| ret.dashcamOnly = is_release or ret.networkLocation == NetworkLocation.fwdCamera |
| if ret.flags & VolkswagenFlags.MEB: | ||
| ret.startingState = True | ||
| ret.startAccel = 0.8 | ||
| ret.vEgoStarting = 0.5 | ||
| ret.vEgoStopping = 0.1 | ||
| ret.stopAccel = -0.55 | ||
| else: | ||
| ret.stopAccel = -0.55 | ||
| ret.vEgoStarting = 0.1 | ||
| ret.vEgoStopping = 0.5 |
There was a problem hiding this comment.
stopAccel is the same, and why is starting and stopping exactly flipped? Also why is startAccel needed? That's generally for laggy cars and isn't a great and smooth experience.








First car on the Volkswagen MEB platform.
Only gateway Harness support for now to prevent disabling AEB and support stock Emergency Assist escalation.
MEB EPS uses curvature target. New native helpers added in lateral.py.
Set to Dashcam mode until EA escalation is validated, will be done in separate PR.
Validation
Checklist
Test car
2022 Volkswagen ID.4 Pro S, USA
Thanks to the community:
Carport: @infiniteCable / DaHansi
Harness: @lukasloetkolben