This commit is contained in:
firestar5683
2026-05-11 15:34:33 -05:00
parent 4370e9c7a3
commit 013ecbed97
3 changed files with 54 additions and 31 deletions
@@ -54,9 +54,8 @@ IONIQ_6_STOP_RELEASE_JERK_V = [3.6 * IONIQ_6_RESPONSE_MULTIPLIER,
4.2 * IONIQ_6_RESPONSE_MULTIPLIER,
4.8 * IONIQ_6_RESPONSE_MULTIPLIER]
IONIQ_6_IPEDAL_PRESS_SEND_COUNT = 6
IONIQ_6_IPEDAL_LATCH_PRESS_SEND_COUNT = 10
IONIQ_6_IPEDAL_LATCH_PRESS_SEND_COUNT = 6
IONIQ_6_IPEDAL_PADDLE_BURST_COUNT = 3
IONIQ_6_IPEDAL_NEXT_COUNTER_BURST_COUNT = 1
IONIQ_6_MAX_REGEN_STATE = 0x3C
IONIQ_6_MAX_REGEN_STATE_2 = 0x01
IONIQ_6_IPEDAL_REGEN_STATE = 0x50
@@ -246,6 +245,7 @@ class CarController(CarControllerBase):
self._ioniq_6_last_buttons_counter = 0
self._ioniq_6_last_regen_control_counter = -1
self._ioniq_6_regen_request_sent = False
self._ioniq_6_ipedal_latch_counter = -1
self._ioniq_6_last_gear = structs.CarState.GearShifter.unknown
self._genesis_g90_long_tuning = GenesisG90LongitudinalTuningState()
@@ -254,12 +254,14 @@ class CarController(CarControllerBase):
self._ioniq_6_always_ipedal_press_remaining = 0
self._ioniq_6_always_ipedal_retry_frame = 0
self._ioniq_6_regen_request_sent = False
self._ioniq_6_ipedal_latch_counter = -1
def _arm_ioniq_6_always_ipedal(self) -> None:
self._ioniq_6_always_ipedal_pending = True
self._ioniq_6_always_ipedal_press_remaining = 0
self._ioniq_6_always_ipedal_retry_frame = self.frame
self._ioniq_6_regen_request_sent = False
self._ioniq_6_ipedal_latch_counter = -1
def _update_ioniq_6_always_ipedal(self, CC, CS, starpilot_toggles):
can_sends = []
@@ -286,6 +288,12 @@ class CarController(CarControllerBase):
regen_control_counter_changed = regen_control_counter != self._ioniq_6_last_regen_control_counter
max_regen_state = regen_state == IONIQ_6_MAX_REGEN_STATE and regen_state_2 == IONIQ_6_MAX_REGEN_STATE_2
ipedal_latch_pending = regen_state == IONIQ_6_IPEDAL_REGEN_STATE and regen_state_2 == IONIQ_6_IPEDAL_REGEN_STATE_2_PENDING
was_max_regen_state = self._ioniq_6_last_ipedal_regen_state == IONIQ_6_MAX_REGEN_STATE and \
self._ioniq_6_last_ipedal_regen_state_2 == IONIQ_6_MAX_REGEN_STATE_2
was_ipedal_latch_pending = self._ioniq_6_last_ipedal_regen_state == IONIQ_6_IPEDAL_REGEN_STATE and \
self._ioniq_6_last_ipedal_regen_state_2 == IONIQ_6_IPEDAL_REGEN_STATE_2_PENDING
entered_latch_stage = (max_regen_state and not was_max_regen_state) or \
(ipedal_latch_pending and not was_ipedal_latch_pending)
drive = gear == structs.CarState.GearShifter.drive
park = gear == structs.CarState.GearShifter.park
drive_edge = drive and self._ioniq_6_last_gear != structs.CarState.GearShifter.drive
@@ -306,29 +314,46 @@ class CarController(CarControllerBase):
self._ioniq_6_always_ipedal_pending = False
self._ioniq_6_always_ipedal_press_remaining = 0
if entered_latch_stage:
self._ioniq_6_always_ipedal_press_remaining = 0
self._ioniq_6_always_ipedal_retry_frame = self.frame
self._ioniq_6_regen_request_sent = False
self._ioniq_6_ipedal_latch_counter = -1
if target_gear and self._ioniq_6_always_ipedal_pending and not CS.ipedal_active and not CC.enabled:
if self._ioniq_6_always_ipedal_press_remaining == 0 and self.frame >= self._ioniq_6_always_ipedal_retry_frame:
self._ioniq_6_always_ipedal_press_remaining = IONIQ_6_IPEDAL_LATCH_PRESS_SEND_COUNT if (max_regen_state or ipedal_latch_pending) \
else IONIQ_6_IPEDAL_PRESS_SEND_COUNT
self._ioniq_6_regen_request_sent = False
# Mirror the current stock counter after seeing the real CRUISE_BUTTONS frame land on the bus.
# A single duplicate was enough to move the cluster-facing state, but not enough to reliably beat
# the stock no-paddle frame for the actual drivetrain latch path.
if self._ioniq_6_always_ipedal_press_remaining > 0 and buttons_counter_changed and \
0 <= buttons_counter < hyundaicanfd.IONIQ_6_CRUISE_BUTTONS_COUNTER_MAX:
paddle_msg = hyundaicanfd.create_ioniq_6_paddle_buttons(self.packer, self.CP, self.CAN,
buttons_counter, left_paddle=True)
can_sends.extend([paddle_msg] * IONIQ_6_IPEDAL_PADDLE_BURST_COUNT)
if max_regen_state or ipedal_latch_pending:
next_counter = hyundaicanfd.get_ioniq_6_cruise_buttons_next_counter(buttons_counter)
next_paddle_msg = hyundaicanfd.create_ioniq_6_paddle_buttons(self.packer, self.CP, self.CAN,
next_counter, left_paddle=True)
can_sends.extend([next_paddle_msg] * IONIQ_6_IPEDAL_NEXT_COUNTER_BURST_COUNT)
self._ioniq_6_always_ipedal_press_remaining -= 1
self._ioniq_6_ipedal_latch_counter = hyundaicanfd.get_ioniq_6_cruise_buttons_next_counter(buttons_counter) \
if 0 <= buttons_counter < hyundaicanfd.IONIQ_6_CRUISE_BUTTONS_COUNTER_MAX else -1
else:
self._ioniq_6_ipedal_latch_counter = -1
if self._ioniq_6_always_ipedal_press_remaining > 0:
if max_regen_state or ipedal_latch_pending:
if self._ioniq_6_ipedal_latch_counter < 0 and 0 <= buttons_counter < hyundaicanfd.IONIQ_6_CRUISE_BUTTONS_COUNTER_MAX:
self._ioniq_6_ipedal_latch_counter = hyundaicanfd.get_ioniq_6_cruise_buttons_next_counter(buttons_counter)
if 0 <= self._ioniq_6_ipedal_latch_counter < hyundaicanfd.IONIQ_6_CRUISE_BUTTONS_COUNTER_MAX:
# A real successful i-Pedal pull is a short held press followed by release, not a continuous hold.
# Mirror that with a stock-like six-counter sequence, then stop transmitting to give the car a release window.
paddle_msg = hyundaicanfd.create_ioniq_6_paddle_buttons(self.packer, self.CP, self.CAN,
self._ioniq_6_ipedal_latch_counter, left_paddle=True)
can_sends.append(paddle_msg)
self._ioniq_6_ipedal_latch_counter = hyundaicanfd.get_ioniq_6_cruise_buttons_next_counter(self._ioniq_6_ipedal_latch_counter)
self._ioniq_6_always_ipedal_press_remaining -= 1
elif buttons_counter_changed and 0 <= buttons_counter < hyundaicanfd.IONIQ_6_CRUISE_BUTTONS_COUNTER_MAX:
paddle_msg = hyundaicanfd.create_ioniq_6_paddle_buttons(self.packer, self.CP, self.CAN,
buttons_counter, left_paddle=True)
can_sends.extend([paddle_msg] * IONIQ_6_IPEDAL_PADDLE_BURST_COUNT)
self._ioniq_6_always_ipedal_press_remaining -= 1
if self._ioniq_6_always_ipedal_press_remaining == 0:
retry_wait_frames = IONIQ_6_IPEDAL_PROGRESS_RETRY_WAIT_FRAMES if regen_state_changed else IONIQ_6_IPEDAL_RETRY_WAIT_FRAMES
self._ioniq_6_always_ipedal_retry_frame = self.frame + retry_wait_frames
self._ioniq_6_ipedal_latch_counter = -1
# The drivetrain latch uses a second HKG CAN-FD request path in addition to the left paddle bit.
# Mirror the next stock 0x25A frame once per retry burst instead of spamming it continuously.
@@ -8,7 +8,7 @@ from opendbc.car import Bus, ButtonType, gen_empty_fingerprint, structs
from opendbc.car.structs import CarControl, CarParams
from opendbc.car.fw_versions import build_fw_dict, match_fw_to_car
from opendbc.car.hyundai.carcontroller import CarController, Ioniq6LongitudinalTuningState, GenesisG90LongitudinalTuningState, \
IONIQ_6_IPEDAL_PADDLE_BURST_COUNT, IONIQ_6_IPEDAL_NEXT_COUNTER_BURST_COUNT, \
IONIQ_6_IPEDAL_PADDLE_BURST_COUNT, \
update_ioniq_6_longitudinal_tuning, \
update_genesis_g90_longitudinal_tuning
from opendbc.car.hyundai.carstate import CarState, decode_ioniq_6_blindspot_radar_state, decode_ioniq_6_ipedal_intermediate_state, \
@@ -484,7 +484,8 @@ class TestHyundaiFingerprint:
sends = controller._update_ioniq_6_always_ipedal(cc, cs, toggles)
assert sends
assert controller._ioniq_6_always_ipedal_press_remaining == 9
assert [msg[1].hex() for msg in sends if msg[0] == 0x1CF] == ["9060002800000000"]
assert controller._ioniq_6_always_ipedal_press_remaining == 5
def test_ioniq_6_always_ipedal_sends_regen_control_companion_once_max_regen_is_shown(self):
toggles = get_test_toggles()
@@ -550,13 +551,12 @@ class TestHyundaiFingerprint:
sends = controller._update_ioniq_6_always_ipedal(cc, cs, toggles)
paddle_msgs = [msg for msg in sends if msg[0] == 0x1CF]
assert len(paddle_msgs) == IONIQ_6_IPEDAL_PADDLE_BURST_COUNT + IONIQ_6_IPEDAL_NEXT_COUNTER_BURST_COUNT
assert [msg[1].hex() for msg in paddle_msgs[:IONIQ_6_IPEDAL_PADDLE_BURST_COUNT]] == ["4650002800000000"] * IONIQ_6_IPEDAL_PADDLE_BURST_COUNT
assert [msg[1].hex() for msg in paddle_msgs[IONIQ_6_IPEDAL_PADDLE_BURST_COUNT:]] == ["9060002800000000"] * IONIQ_6_IPEDAL_NEXT_COUNTER_BURST_COUNT
assert [msg[1].hex() for msg in paddle_msgs] == ["9060002800000000"]
regen_cmd = next(msg for msg in sends if msg[0] == 0x25A)
assert regen_cmd[1][24:28] == bytes.fromhex("c00c1200")
checksum = hyundaicanfd.hkg_can_fd_checksum(regen_cmd[0], None, bytearray(regen_cmd[1]))
assert regen_cmd[1][0] | (regen_cmd[1][1] << 8) == checksum
assert controller._ioniq_6_always_ipedal_press_remaining == 5
controller.frame = 1
cs.buttons_counter = 6
@@ -564,9 +564,7 @@ class TestHyundaiFingerprint:
sends = controller._update_ioniq_6_always_ipedal(cc, cs, toggles)
paddle_msgs = [msg for msg in sends if msg[0] == 0x1CF]
assert len(paddle_msgs) == IONIQ_6_IPEDAL_PADDLE_BURST_COUNT + IONIQ_6_IPEDAL_NEXT_COUNTER_BURST_COUNT
assert [msg[1].hex() for msg in paddle_msgs[:IONIQ_6_IPEDAL_PADDLE_BURST_COUNT]] == ["9060002800000000"] * IONIQ_6_IPEDAL_PADDLE_BURST_COUNT
assert [msg[1].hex() for msg in paddle_msgs[IONIQ_6_IPEDAL_PADDLE_BURST_COUNT:]] == ["2970002800000000"] * IONIQ_6_IPEDAL_NEXT_COUNTER_BURST_COUNT
assert [msg[1].hex() for msg in paddle_msgs] == ["2970002800000000"]
assert not any(msg[0] == 0x25A for msg in sends)
def test_ioniq_6_longitudinal_params_match_canfd_tune(self):
+7 -7
View File
@@ -284,18 +284,18 @@ IONIQ_6_TRANSITION_SPEED = 10.0
IONIQ_6_PHASE_SCALE = 0.10
IONIQ_6_TURN_IN_BOOST_LEFT = 1.58
IONIQ_6_TURN_IN_BOOST_RIGHT = 1.82
IONIQ_6_UNWIND_TAPER_LEFT = 2.84
IONIQ_6_UNWIND_TAPER_LEFT = 3.05
IONIQ_6_UNWIND_TAPER_RIGHT = 6.35
IONIQ_6_FRICTION_MULT = 0.928
IONIQ_6_FRICTION_LAT_RISE = 0.20
IONIQ_6_FRICTION_JERK_RISE = 0.24
IONIQ_6_TURN_IN_THRESHOLD_REDUCTION_LEFT = 0.74
IONIQ_6_TURN_IN_THRESHOLD_REDUCTION_RIGHT = 1.18
IONIQ_6_UNWIND_THRESHOLD_INCREASE_LEFT = 3.35
IONIQ_6_UNWIND_THRESHOLD_INCREASE_LEFT = 3.70
IONIQ_6_UNWIND_THRESHOLD_INCREASE_RIGHT = 7.85
IONIQ_6_TURN_IN_FRICTION_BOOST_LEFT = 0.40
IONIQ_6_TURN_IN_FRICTION_BOOST_RIGHT = 0.72
IONIQ_6_UNWIND_FRICTION_REDUCTION_LEFT = 3.10
IONIQ_6_UNWIND_FRICTION_REDUCTION_LEFT = 3.35
IONIQ_6_UNWIND_FRICTION_REDUCTION_RIGHT = 7.30
IONIQ_6_CENTER_TAPER_MAX = 0.082
IONIQ_6_CENTER_TAPER_LAT = 0.24
@@ -311,9 +311,9 @@ IONIQ_6_LOW_MID_CENTER_TAPER_SPEED_WIDTH = 1.5
IONIQ_6_DIRECTIONAL_TAPER_LAT_START = 0.19
IONIQ_6_DIRECTIONAL_TAPER_LAT_END = 0.90
IONIQ_6_DIRECTIONAL_TAPER_LAT_WIDTH = 0.06
IONIQ_6_DIRECTIONAL_TAPER_BASE_LEFT = 0.14
IONIQ_6_DIRECTIONAL_TAPER_BASE_RIGHT = 0.48
IONIQ_6_DIRECTIONAL_TAPER_UNWIND_LEFT = 1.64
IONIQ_6_DIRECTIONAL_TAPER_BASE_LEFT = 0.13
IONIQ_6_DIRECTIONAL_TAPER_BASE_RIGHT = 0.45
IONIQ_6_DIRECTIONAL_TAPER_UNWIND_LEFT = 1.82
IONIQ_6_DIRECTIONAL_TAPER_UNWIND_RIGHT = 3.28
IONIQ_6_DIRECTIONAL_TAPER_FLOOR_LEFT = 0.48
IONIQ_6_DIRECTIONAL_TAPER_FLOOR_RIGHT = 0.52
@@ -325,7 +325,7 @@ IONIQ_6_HEAVY_DIRECTIONAL_TAPER_LAT_START = 0.82
IONIQ_6_HEAVY_DIRECTIONAL_TAPER_LAT_WIDTH = 0.12
IONIQ_6_HEAVY_DIRECTIONAL_TAPER_BASE_LEFT = 0.10
IONIQ_6_HEAVY_DIRECTIONAL_TAPER_BASE_RIGHT = 0.17
IONIQ_6_HEAVY_DIRECTIONAL_TAPER_UNWIND_LEFT = 0.56
IONIQ_6_HEAVY_DIRECTIONAL_TAPER_UNWIND_LEFT = 0.62
IONIQ_6_HEAVY_DIRECTIONAL_TAPER_UNWIND_RIGHT = 0.94
IONIQ_6_OUTPUT_TAPER_SPEED = 8.5
IONIQ_6_OUTPUT_TAPER_SPEED_WIDTH = 2.5