BYD ATTO3: new brand port (lateral) - #3677
Open
qzwf wants to merge 2 commits into
Open
Conversation
Contributor
Car behavior reportReplays driving segments through this PR and compares the behavior to master. ✅ No changes detected |
Adds BYD as a brand with the ATTO3 (2022-24) as its first platform, at community support level. Lateral only; longitudinal stays with the stock ACC. The EPS takes an absolute steering wheel angle target in STEERING_MODULE_ADAS (0x1E2), so this is an angle-control port. Two things about the car shaped the design: - The camera does not tolerate having its steering frame synthesised from scratch. Everything in 0x1E2 other than the angle is held at the constant the camera itself transmits while steering; varying those fields made the car drop its entire ADAS mid-drive. - The EPS must never see two sources. openpilot transmits 0x1E2 and 0x316 only while it is actually steering, and the safety mode blocks the camera's copies per-frame while that is happening, handing both back ~150 ms after openpilot goes quiet. There is no window with neither source driving the EPS. The command is anchored to the measured angle and clamped to a fixed window around it, so the limiter cannot ratchet away from the wheel and saturate. Safety mode SAFETY_BYD uses the standard angle checks: speed-dependent rate limits, inactive-angle tracking, and driver override on the column torque sensor. ACC_CMD is deliberately absent from the TX allowlist.
gadhadar
force-pushed
the
byd-atto3-upstream
branch
from
August 13, 2026 09:05
4b6233d to
05652c2
Compare
The ATTO3 now has an uploaded route, so it moves out of non_tested_cars. Two things the route surfaced: - ANGLE_DEG_TO_CAN needed a byd entry (10, from the 0.1 deg DBC factor) for test_panda_safety_carstate to check the measured angle. - test_panda_safety_tx_cases asserts every car transmits >50 messages, but all of its cases are inactive. This port sends nothing unless it is actively steering: the safety mode blocks the camera's steering command per-frame only while openpilot is transmitting, so transmitting while inactive would lock the camera out of the EPS permanently. The message count check is skipped for that case and an active case is added instead, so the transmitting path stays covered.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds BYD as a brand with the ATTO3 (2022-24) as its first platform, at community support level. Submission for the BYD port bounty, #2017. Harness work and connector pinout are in #2065.
Lateral is done and driven daily on a comma 3X. Longitudinal stays with the stock ACC.
What the car does
The EPS takes an absolute steering wheel angle target in
STEERING_MODULE_ADAS(0x1E2), so this is an angle-control port, not a torque one. Two properties of the car shaped the design and are worth calling out, because both were found the hard way:The camera's steering frame cannot be synthesised. Everything in 0x1E2 other than the angle is held at the constant the camera itself transmits while steering (bytes 0-2 =
2b 55 eb, at every speed). Treating those bytes as a sequence and walking them made the car drop its entire ADAS mid-drive — LKAS, ACC and the cluster indicators all went away until a power cycle. The controller now copies the camera's own latched frame when it has seen the camera steer, and falls back to the captured constant otherwise (a camera that never gets a lock never emits one).The EPS must never see two sources. openpilot transmits 0x1E2 and
LKAS_HUD_ADAS(0x316) only while it is actually steering. The safety mode blocks the camera's copies per-frame while that is happening and hands both back ~150 ms after openpilot goes quiet, so the stock LKAS resumes on disengage and there is never a window with neither source driving the EPS. Static blocking is disabled for exactly this reason;check_relayis kept so stuck-relay detection still works.The angle command is anchored to the measured angle and clamped to a fixed ±12° window around it. Without that anchor a limiter that tracks only its own previous output can ratchet away from the wheel, saturate at the clamp, and get every frame rejected by panda — which is what happened before this was in place.
Safety
SAFETY_BYDuses the standard angle checks: speed-dependent rate limits (matchingCarControllerParams.ANGLE_LIMITS), inactive-angle tracking, and driver override onDRIVER_EPS_TORQUE, the column torque sensor.MAIN_TORQUEfromSTEERING_TORQUEis EPS motor output, not driver input, and is deliberately not used for override.ACC_CMDis deliberately absent from the TX allowlist, so panda blocks longitudinal outright.opendbc/safety/tests/test_byd.py: 34 tests, including coverage for the frame-template behaviour aboveruff/ty/codespell/cpplint: cleanSignal notes
A few signals on this platform are not what their DBC names suggest, all verified against a controlled pedal capture and ~3.6 h of driving:
DRIVE_STATE.BRAKE_PRESSEDis dead — byte 4 is a constant0x0CPEDAL_PRESSED_ACTIVE_LOWis the brake-light switch; 86% of its on-road assertions happened while the camera's ACC was commanding decel, not the driver.brakePressedusesPEDAL.BRAKE_PEDALinstead, the only signal that tracks the driver's foot.ACC_HUD_ADAS.ACC_ON1/ON2are main-switch/standby state, not engagement; they stay set while the driver brakes and drives manually. Engagement comes fromACC_CMD(CMD_REQ_ACTIVE_LOW).WHEELSPEED_BR's high byte is a status byte, not speed; wheel speed sums the three trustworthy wheels.STEER_REQ_ACTIVE_LOWis not the inverse ofSTEER_REQ— the camera holds it at 0.Known gaps
pcmCruise = True, stock ACC only. TheACC_CMDscaffolding incarcontroller.pyis gated behindopenpilotLongitudinalControland unreachable today; it needsACCEL_CMDscaling calibrated on the car before it can be enabled. Happy to strip it if you'd rather not carry it.FW_QUERY_CONFIGstill queries bus 0 so versions keep being collected.routes.pyyet.b8b2d1a1df1b3aad|000000a0--7c51639c22is driven and public, but only qlogs have been uploaded so far andtest_modelsneeds rlogs.BYD_ATTO3is innon_tested_carsuntil those finish uploading, at which point it's a one-line change.CarHarness.custompending a name. The camera is a Veoneer MVS4; the car-side connector is TE1-1355211-2(mating1-1670459-2). Full pinout in BYD Atto 3 Harness #2065.Bus layout
Port by QZWF.