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
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];
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.