byd: support SEAL steering feedback on 508 STEERING_TORQUE_ANGLE

The Seal EPS reports steering torque/prepare/activated state on
508 STEERING_TORQUE_ANGLE (different bit layout; driver torque factor
0.1), unlike the Han which uses 792 STEERING_TORQUE. The Seal's
fingerprint contains 508 but not 792, so alive-checking 792 tripped
canError ("Unknown Vehicle Variant") the moment the interface started.

- carstate: per-platform steering message selection (508 strict-checked
  on Seal, 792 on Han; the other parsed opportunistically)
- carcontroller: don't send 792-layout camera feedback on the Seal;
  stock 508 forwards unmodified for now (camera-feedback spoofing for
  the Seal's 508 to be evaluated after a bus dump + road test)
- panda byd.h: rx check accepts 792 or 508 (first-seen-wins), torque
  sampling handles both layouts, driver torque rescaled for 508's 0.1x
  factor
- panda firmware rebuilt (panda_h7.bin.signed)
- tests: Seal(508) + Han(792) both verified canValid with correct
  torque values; 120 safety tests + MISRA clean
This commit is contained in:
Quintine committed 2026-07-25 18:32:42 +10:00
1 parent 264ebf956e
commit b598ba5dcb
21 files changed
+57 -21

No files matched your search

+12 -6
View File
@@ -4,7 +4,7 @@ from opendbc.car import Bus, structs
from opendbc.car.lateral import apply_meas_steer_torque_limits from opendbc.car.lateral import apply_meas_steer_torque_limits
from opendbc.car.interfaces import CarControllerBase from opendbc.car.interfaces import CarControllerBase
from opendbc.car.byd.bydcan import BydCAN from opendbc.car.byd.bydcan import BydCAN
from opendbc.car.byd.values import CarControllerParams from opendbc.car.byd.values import CAR, CarControllerParams
VisualAlert = structs.CarControl.HUDControl.VisualAlert VisualAlert = structs.CarControl.HUDControl.VisualAlert
ButtonType = structs.CarState.ButtonEvent.Type ButtonType = structs.CarState.ButtonEvent.Type
@@ -30,6 +30,11 @@ class CarController(CarControllerBase):
self.apply_accel_last = 0 self.apply_accel_last = 0
# EPS steering feedback forwarding (792 STEERING_TORQUE) only applies to
# platforms whose EPS reports on 792 (HAN). The SEAL EPS reports on
# 508 STEERING_TORQUE_ANGLE which is forwarded stock for now.
self.send_steering_feedback = CP.carFingerprint != CAR.BYD_SEAL_PERFORMANCE_25
def update(self, CC, CC_SP, CS, now_nanos): def update(self, CC, CC_SP, CS, now_nanos):
# car control running in 100Hz # car control running in 100Hz
actuators = CC.actuators actuators = CC.actuators
@@ -75,11 +80,12 @@ class CarController(CarControllerBase):
lkas_request_prepare, lkas_active, lkas_request_prepare, lkas_active,
lkas_mode, CS.eps_activated) lkas_mode, CS.eps_activated)
can_sends.append(pack) can_sends.append(pack)
pack = self.can.create_steering_torque(CS.eps_steering_torque_msg, if self.send_steering_feedback:
CS.mpc_lkas_output, pack = self.can.create_steering_torque(CS.eps_steering_torque_msg,
CS.mpc_lkas_request_prepare, CS.mpc_lkas_output,
CS.mpc_lkas_active) CS.mpc_lkas_request_prepare,
can_sends.append(pack) CS.mpc_lkas_active)
can_sends.append(pack)
self.apply_torque_last = apply_torque self.apply_torque_last = apply_torque
+25 -10
View File
@@ -3,7 +3,7 @@ from opendbc.car import Bus, create_button_events, structs
from opendbc.can.parser import CANParser from opendbc.can.parser import CANParser
from opendbc.car.common.conversions import Conversions as CV from opendbc.car.common.conversions import Conversions as CV
from opendbc.car.interfaces import CarStateBase from opendbc.car.interfaces import CarStateBase
from opendbc.car.byd.values import DBC, CANBUS, STEER_THRESHOLD, GEAR_MAP from opendbc.car.byd.values import CAR, DBC, CANBUS, STEER_THRESHOLD, GEAR_MAP
from opendbc.sunnypilot.car.byd.mads import MadsCarState from opendbc.sunnypilot.car.byd.mads import MadsCarState
GearShifter = structs.CarState.GearShifter GearShifter = structs.CarState.GearShifter
@@ -15,6 +15,13 @@ class CarState(CarStateBase, MadsCarState):
CarStateBase.__init__(self, CP, CP_SP) CarStateBase.__init__(self, CP, CP_SP)
MadsCarState.__init__(self, CP, CP_SP) MadsCarState.__init__(self, CP, CP_SP)
# EPS steering feedback message differs by platform:
# HAN uses 792 STEERING_TORQUE, SEAL uses 508 STEERING_TORQUE_ANGLE
if CP.carFingerprint == CAR.BYD_SEAL_PERFORMANCE_25:
self.steer_msg = "STEERING_TORQUE_ANGLE"
else:
self.steer_msg = "STEERING_TORQUE"
self.mpc_lkas_cmd_msg = None self.mpc_lkas_cmd_msg = None
self.eps_steering_torque_msg = None self.eps_steering_torque_msg = None
self.eps_prepared = False self.eps_prepared = False
@@ -45,12 +52,12 @@ class CarState(CarStateBase, MadsCarState):
# Steering wheel # Steering wheel
ret.steeringAngleDeg = cp.vl["STEER_MODULE"]["STEER_ANGLE"] ret.steeringAngleDeg = cp.vl["STEER_MODULE"]["STEER_ANGLE"]
ret.steeringRateDeg = cp.vl["STEER_MODULE"]["STEERING_RATE"] ret.steeringRateDeg = cp.vl["STEER_MODULE"]["STEERING_RATE"]
ret.steeringTorque = cp.vl["STEERING_TORQUE"]["Steer_Torque_Sensor"] ret.steeringTorque = cp.vl[self.steer_msg]["Steer_Torque_Sensor"]
ret.steeringTorqueEps = cp.vl["STEERING_TORQUE"]["MAIN_TORQUE"] ret.steeringTorqueEps = cp.vl[self.steer_msg]["MAIN_TORQUE"]
# TODO: looks like STEER_THRESHOLD is too small # TODO: looks like STEER_THRESHOLD is too small
ret.steeringPressed = self.update_steering_pressed(abs(ret.steeringTorque) > STEER_THRESHOLD, 5) ret.steeringPressed = self.update_steering_pressed(abs(ret.steeringTorque) > STEER_THRESHOLD, 5)
ret.steerFaultPermanent = cp.vl["STEERING_TORQUE"]["TORQUE_FAILED"] != 0 ret.steerFaultPermanent = cp.vl[self.steer_msg]["TORQUE_FAILED"] != 0
ret.steerFaultTemporary = cp.vl["STEERING_TORQUE"]["TORQUE_FAILED"] != 0 ret.steerFaultTemporary = cp.vl[self.steer_msg]["TORQUE_FAILED"] != 0
ret.stockAeb = cp_cam.vl["ACC_HUD_ADAS"]["AEB"] == 1 ret.stockAeb = cp_cam.vl["ACC_HUD_ADAS"]["AEB"] == 1
ret.stockFcw = cp_cam.vl["ACC_HUD_ADAS"]["FCW"] == 1 # Forward Collision Warning ret.stockFcw = cp_cam.vl["ACC_HUD_ADAS"]["FCW"] == 1 # Forward Collision Warning
@@ -87,10 +94,10 @@ class CarState(CarStateBase, MadsCarState):
# Messages needed by carcontroller # Messages needed by carcontroller
# for generate MPC_LKAS_CMD # for generate MPC_LKAS_CMD
self.mpc_lkas_cmd_msg = copy.copy(cp_cam.vl["MPC_LKAS_CMD"]) self.mpc_lkas_cmd_msg = copy.copy(cp_cam.vl["MPC_LKAS_CMD"])
self.eps_prepared = cp.vl["STEERING_TORQUE"]["LKSPrepare"] != 0 self.eps_prepared = cp.vl[self.steer_msg]["LKSPrepare"] != 0
self.eps_activated = cp.vl["STEERING_TORQUE"]["Cruise_Activated"] != 0 self.eps_activated = cp.vl[self.steer_msg]["Cruise_Activated"] != 0
# for generate STEERING_TORQUE # for generate steering feedback message
self.eps_steering_torque_msg = copy.copy(cp.vl["STEERING_TORQUE"]) self.eps_steering_torque_msg = copy.copy(cp.vl[self.steer_msg])
self.mpc_lkas_output = cp_cam.vl["MPC_LKAS_CMD"]["LKAS_Output"] self.mpc_lkas_output = cp_cam.vl["MPC_LKAS_CMD"]["LKAS_Output"]
self.mpc_lkas_active = cp_cam.vl["MPC_LKAS_CMD"]["LKAS_ACTIVE"] != 0 self.mpc_lkas_active = cp_cam.vl["MPC_LKAS_CMD"]["LKAS_ACTIVE"] != 0
self.mpc_lkas_request_prepare = cp_cam.vl["MPC_LKAS_CMD"]["LKASPrepare"] != 0 self.mpc_lkas_request_prepare = cp_cam.vl["MPC_LKAS_CMD"]["LKASPrepare"] != 0
@@ -109,7 +116,6 @@ class CarState(CarStateBase, MadsCarState):
("BRAKE_APPLIED", 10), ("BRAKE_APPLIED", 10),
("PEDAL", 10), ("PEDAL", 10),
("STEER_MODULE", 10), ("STEER_MODULE", 10),
("STEERING_TORQUE", 10),
("DRIVE_STATE", 10), ("DRIVE_STATE", 10),
("STALKS", float('nan')), ("STALKS", float('nan')),
("METER_CLUSTER", float('nan')), ("METER_CLUSTER", float('nan')),
@@ -117,6 +123,15 @@ class CarState(CarStateBase, MadsCarState):
("PCM_BUTTONS", float('nan')), ("PCM_BUTTONS", float('nan')),
] ]
# EPS steering feedback message differs by platform; alive-check only
# the one this car has, parse the other opportunistically
if CP.carFingerprint == CAR.BYD_SEAL_PERFORMANCE_25:
pt_messages.append(("STEERING_TORQUE_ANGLE", 10))
pt_messages.append(("STEERING_TORQUE", float('nan')))
else:
pt_messages.append(("STEERING_TORQUE", 10))
pt_messages.append(("STEERING_TORQUE_ANGLE", float('nan')))
cam_messages = [ cam_messages = [
("ACC_HUD_ADAS", 10), ("ACC_HUD_ADAS", 10),
# stock camera command echoes, only present when stock ADAS is active # stock camera command echoes, only present when stock ADAS is active
+18 -3
View File
@@ -3,9 +3,12 @@
// MPC_LKAS_CMD (790), little endian // MPC_LKAS_CMD (790), little endian
#define BYD_GET_LKAS_OUTPUT(msg) (to_signed((((msg)->data[3] & 0x7U) << 8U) | (msg)->data[2], 11)) #define BYD_GET_LKAS_OUTPUT(msg) (to_signed((((msg)->data[3] & 0x7U) << 8U) | (msg)->data[2], 11))
#define BYD_LKAS_ACTIVE_BIT 28U #define BYD_LKAS_ACTIVE_BIT 28U
// STEERING_TORQUE (792), little endian // STEERING_TORQUE (792, HAN), little endian
#define BYD_GET_MAIN_TORQUE(msg) (to_signed((((msg)->data[2] & 0xFU) << 8U) | (msg)->data[1], 12)) #define BYD_GET_MAIN_TORQUE(msg) (to_signed((((msg)->data[2] & 0xFU) << 8U) | (msg)->data[1], 12))
#define BYD_GET_DRIVER_TORQUE(msg) (to_signed((((msg)->data[4] & 0xFU) << 8U) | (msg)->data[3], 12)) #define BYD_GET_DRIVER_TORQUE(msg) (to_signed((((msg)->data[4] & 0xFU) << 8U) | (msg)->data[3], 12))
// STEERING_TORQUE_ANGLE (508, SEAL), little endian
#define BYD_GET_MAIN_TORQUE_ANGLE(msg) (to_signed((((msg)->data[5] & 0xFU) << 8U) | (msg)->data[4], 12))
#define BYD_GET_DRIVER_TORQUE_ANGLE(msg) (to_signed((((msg)->data[1] & 0xFU) << 4U) | ((msg)->data[0] >> 4U), 12))
// PCM_BUTTONS (944) // PCM_BUTTONS (944)
#define BYD_LKAS_ON_BTN_BIT 14U #define BYD_LKAS_ON_BTN_BIT 14U
@@ -73,7 +76,7 @@ static void byd_rx_hook(const CANPacket_t *msg) {
if (msg->addr == 289U) { if (msg->addr == 289U) {
vehicle_moving = (((msg->data[1] & 0x0FU) << 8U) | msg->data[0]) != 0U; vehicle_moving = (((msg->data[1] & 0x0FU) << 8U) | msg->data[0]) != 0U;
} }
// STEERING_TORQUE: sample EPS output torque and driver torque // STEERING_TORQUE (HAN): sample EPS output torque and driver torque
if (msg->addr == 792U) { if (msg->addr == 792U) {
int torque_meas_new = BYD_GET_MAIN_TORQUE(msg); int torque_meas_new = BYD_GET_MAIN_TORQUE(msg);
update_sample(&torque_meas, torque_meas_new); update_sample(&torque_meas, torque_meas_new);
@@ -83,6 +86,17 @@ static void byd_rx_hook(const CANPacket_t *msg) {
int torque_driver_new = BYD_GET_DRIVER_TORQUE(msg); int torque_driver_new = BYD_GET_DRIVER_TORQUE(msg);
update_sample(&torque_driver, torque_driver_new); update_sample(&torque_driver, torque_driver_new);
} }
// STEERING_TORQUE_ANGLE (SEAL): same feedback on a different message/layout
if (msg->addr == 508U) {
int torque_meas_new = BYD_GET_MAIN_TORQUE_ANGLE(msg);
update_sample(&torque_meas, torque_meas_new);
torque_meas.min--;
torque_meas.max++;
// driver torque factor is 0.1 on this message
int torque_driver_new = (BYD_GET_DRIVER_TORQUE_ANGLE(msg) + 5) / 10;
update_sample(&torque_driver, torque_driver_new);
}
// PCM_BUTTONS: dedicated LKAS steering wheel button for MADS // PCM_BUTTONS: dedicated LKAS steering wheel button for MADS
if (msg->addr == 944U) { if (msg->addr == 944U) {
mads_button_press = GET_BIT(msg, BYD_LKAS_ON_BTN_BIT) ? MADS_BUTTON_PRESSED : MADS_BUTTON_NOT_PRESSED; mads_button_press = GET_BIT(msg, BYD_LKAS_ON_BTN_BIT) ? MADS_BUTTON_PRESSED : MADS_BUTTON_NOT_PRESSED;
@@ -118,7 +132,8 @@ static safety_config byd_init(uint16_t param) {
{.msg = {{834, 0, 8, .ignore_checksum = true, .ignore_counter = true, .ignore_quality_flag = true}, { 0 }, { 0 }}}, // PEDAL {.msg = {{834, 0, 8, .ignore_checksum = true, .ignore_counter = true, .ignore_quality_flag = true}, { 0 }, { 0 }}}, // PEDAL
{.msg = {{289, 0, 8, .ignore_checksum = true, .ignore_counter = true, .ignore_quality_flag = true}, { 0 }, { 0 }}}, // ESC {.msg = {{289, 0, 8, .ignore_checksum = true, .ignore_counter = true, .ignore_quality_flag = true}, { 0 }, { 0 }}}, // ESC
{.msg = {{287, 0, 5, .ignore_checksum = true, .ignore_counter = true, .ignore_quality_flag = true}, { 0 }, { 0 }}}, // STEER_MODULE {.msg = {{287, 0, 5, .ignore_checksum = true, .ignore_counter = true, .ignore_quality_flag = true}, { 0 }, { 0 }}}, // STEER_MODULE
{.msg = {{792, 0, 8, .ignore_checksum = true, .ignore_counter = true, .ignore_quality_flag = true}, { 0 }, { 0 }}}, // STEERING_TORQUE {.msg = {{792, 0, 8, .ignore_checksum = true, .ignore_counter = true, .ignore_quality_flag = true}, // STEERING_TORQUE (HAN)
{508, 0, 8, .ignore_checksum = true, .ignore_counter = true, .ignore_quality_flag = true}, { 0 }}}, // STEERING_TORQUE_ANGLE (SEAL)
{.msg = {{944, 0, 8, .ignore_checksum = true, .ignore_counter = true, .ignore_quality_flag = true}, { 0 }, { 0 }}}, // PCM_BUTTONS {.msg = {{944, 0, 8, .ignore_checksum = true, .ignore_counter = true, .ignore_quality_flag = true}, { 0 }, { 0 }}}, // PCM_BUTTONS
{.msg = {{813, 2, 8, .ignore_checksum = true, .ignore_counter = true, .ignore_quality_flag = true}, { 0 }, { 0 }}}, // ACC_HUD_ADAS {.msg = {{813, 2, 8, .ignore_checksum = true, .ignore_counter = true, .ignore_quality_flag = true}, { 0 }, { 0 }}}, // ACC_HUD_ADAS
{.msg = {{307, 0, 8, .ignore_checksum = true, .ignore_counter = true, .ignore_quality_flag = true}, { 0 }, { 0 }}}, // STALKS {.msg = {{307, 0, 8, .ignore_checksum = true, .ignore_counter = true, .ignore_quality_flag = true}, { 0 }, { 0 }}}, // STALKS
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+1 -1
View File
@@ -1,2 +1,2 @@
extern const uint8_t gitversion[19]; extern const uint8_t gitversion[19];
const uint8_t gitversion[19] = "DEV-de77c89c-DEBUG"; const uint8_t gitversion[19] = "DEV-264ebf95-DEBUG";
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+1 -1
View File
@@ -1 +1 @@
DEV-de77c89c-DEBUG DEV-264ebf95-DEBUG
Binary file not shown.