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.interfaces import CarControllerBase
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
ButtonType = structs.CarState.ButtonEvent.Type
@@ -30,6 +30,11 @@ class CarController(CarControllerBase):
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):
# car control running in 100Hz
actuators = CC.actuators
@@ -75,11 +80,12 @@ class CarController(CarControllerBase):
lkas_request_prepare, lkas_active,
lkas_mode, CS.eps_activated)
can_sends.append(pack)
pack = self.can.create_steering_torque(CS.eps_steering_torque_msg,
CS.mpc_lkas_output,
CS.mpc_lkas_request_prepare,
CS.mpc_lkas_active)
can_sends.append(pack)
if self.send_steering_feedback:
pack = self.can.create_steering_torque(CS.eps_steering_torque_msg,
CS.mpc_lkas_output,
CS.mpc_lkas_request_prepare,
CS.mpc_lkas_active)
can_sends.append(pack)
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.car.common.conversions import Conversions as CV
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
GearShifter = structs.CarState.GearShifter
@@ -15,6 +15,13 @@ class CarState(CarStateBase, MadsCarState):
CarStateBase.__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.eps_steering_torque_msg = None
self.eps_prepared = False
@@ -45,12 +52,12 @@ class CarState(CarStateBase, MadsCarState):
# Steering wheel
ret.steeringAngleDeg = cp.vl["STEER_MODULE"]["STEER_ANGLE"]
ret.steeringRateDeg = cp.vl["STEER_MODULE"]["STEERING_RATE"]
ret.steeringTorque = cp.vl["STEERING_TORQUE"]["Steer_Torque_Sensor"]
ret.steeringTorqueEps = cp.vl["STEERING_TORQUE"]["MAIN_TORQUE"]
ret.steeringTorque = cp.vl[self.steer_msg]["Steer_Torque_Sensor"]
ret.steeringTorqueEps = cp.vl[self.steer_msg]["MAIN_TORQUE"]
# TODO: looks like STEER_THRESHOLD is too small
ret.steeringPressed = self.update_steering_pressed(abs(ret.steeringTorque) > STEER_THRESHOLD, 5)
ret.steerFaultPermanent = cp.vl["STEERING_TORQUE"]["TORQUE_FAILED"] != 0
ret.steerFaultTemporary = cp.vl["STEERING_TORQUE"]["TORQUE_FAILED"] != 0
ret.steerFaultPermanent = cp.vl[self.steer_msg]["TORQUE_FAILED"] != 0
ret.steerFaultTemporary = cp.vl[self.steer_msg]["TORQUE_FAILED"] != 0
ret.stockAeb = cp_cam.vl["ACC_HUD_ADAS"]["AEB"] == 1
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
# for generate 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_activated = cp.vl["STEERING_TORQUE"]["Cruise_Activated"] != 0
# for generate STEERING_TORQUE
self.eps_steering_torque_msg = copy.copy(cp.vl["STEERING_TORQUE"])
self.eps_prepared = cp.vl[self.steer_msg]["LKSPrepare"] != 0
self.eps_activated = cp.vl[self.steer_msg]["Cruise_Activated"] != 0
# for generate steering feedback message
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_active = cp_cam.vl["MPC_LKAS_CMD"]["LKAS_ACTIVE"] != 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),
("PEDAL", 10),
("STEER_MODULE", 10),
("STEERING_TORQUE", 10),
("DRIVE_STATE", 10),
("STALKS", float('nan')),
("METER_CLUSTER", float('nan')),
@@ -117,6 +123,15 @@ class CarState(CarStateBase, MadsCarState):
("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 = [
("ACC_HUD_ADAS", 10),
# 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
#define BYD_GET_LKAS_OUTPUT(msg) (to_signed((((msg)->data[3] & 0x7U) << 8U) | (msg)->data[2], 11))
#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_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)
#define BYD_LKAS_ON_BTN_BIT 14U
@@ -73,7 +76,7 @@ static void byd_rx_hook(const CANPacket_t *msg) {
if (msg->addr == 289U) {
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) {
int torque_meas_new = BYD_GET_MAIN_TORQUE(msg);
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);
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
if (msg->addr == 944U) {
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 = {{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 = {{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 = {{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