Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

card parses radar points #33443

Merged
merged 33 commits into from
Sep 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
bc9fc98
interfaces returns radarinterface
sshane Sep 3, 2024
8ff26f7
bump
sshane Sep 3, 2024
182ce8c
Merge remote-tracking branch 'upstream/master' into card-radar
sshane Sep 4, 2024
c6f0cfe
get RI from opendbc
sshane Sep 4, 2024
a5de6ef
stash so far
sshane Sep 4, 2024
2730c23
Merge remote-tracking branch 'upstream/master' into card-radar
sshane Sep 5, 2024
c91b87d
new liveTracks message (radard expects and needs RadarData)
sshane Sep 5, 2024
4939c9c
this should just work?
sshane Sep 5, 2024
8dba952
whoops
sshane Sep 5, 2024
02b66ee
fix that
sshane Sep 5, 2024
14f24e8
rm liveTracks from radard pm
sshane Sep 5, 2024
a56bf0e
fix proceess replay
sshane Sep 5, 2024
a57708e
lol fcw diff, something's not right
sshane Sep 5, 2024
13cd297
actually there's fcw in original route. it's pretty close
sshane Sep 5, 2024
5484ebb
no tracks!
sshane Sep 5, 2024
c6195d3
fix test_leads
sshane Sep 5, 2024
8f661f9
CPU moved across procs
sshane Sep 5, 2024
6eef520
fix not engageable from onroadEvents
sshane Sep 5, 2024
06bbf8e
Merge remote-tracking branch 'upstream/master' into card-radar
sshane Sep 5, 2024
fbbd44e
bump
sshane Sep 5, 2024
46a6f90
fixes
sshane Sep 5, 2024
099ade6
bump to master
sshane Sep 5, 2024
c3641a7
radard publishes w/ modelV2 now, so it will always be sent. check val…
sshane Sep 5, 2024
bf6c063
fix that (it works!)
sshane Sep 5, 2024
d47f7c8
combine
sshane Sep 5, 2024
9f6b8ae
bump
sshane Sep 5, 2024
93c01a9
bump
sshane Sep 5, 2024
945176a
deprecate
sshane Sep 5, 2024
ecf9661
why
sshane Sep 5, 2024
1aae8f6
fix incorrect args
sshane Sep 5, 2024
dc3558c
remove cumLagMs from process_replay
sshane Sep 6, 2024
2b842a2
Merge remote-tracking branch 'upstream/master' into card-radar
sshane Sep 6, 2024
2fbe215
update refs
sshane Sep 6, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions cereal/car.capnp
Original file line number Diff line number Diff line change
Expand Up @@ -508,6 +508,7 @@ struct CarParams {
carFw @44 :List(CarFw);

radarTimeStep @45: Float32 = 0.05; # time delta between radar updates, 20Hz is very standard
radarDelay @74 :Float32;
fingerprintSource @49: FingerprintSource;
networkLocation @50 :NetworkLocation; # Where Panda/C2 is integrated into the car's CAN network

Expand Down
7 changes: 4 additions & 3 deletions cereal/log.capnp
Original file line number Diff line number Diff line change
Expand Up @@ -611,7 +611,6 @@ struct RadarState @0x9a185389d6fdd05f {

leadOne @3 :LeadData;
leadTwo @4 :LeadData;
cumLagMs @5 :Float32;

struct LeadData {
dRel @0 :Float32;
Expand Down Expand Up @@ -641,6 +640,7 @@ struct RadarState @0x9a185389d6fdd05f {
calCycleDEPRECATED @8 :Int32;
calPercDEPRECATED @9 :Int8;
canMonoTimesDEPRECATED @10 :List(UInt64);
cumLagMsDEPRECATED @5 :Float32;
}

struct LiveCalibrationData {
Expand Down Expand Up @@ -671,7 +671,7 @@ struct LiveCalibrationData {
}
}

struct LiveTracks {
struct LiveTracksDEPRECATED {
trackId @0 :Int32;
dRel @1 :Float32;
yRel @2 :Float32;
Expand Down Expand Up @@ -2335,7 +2335,7 @@ struct Event {
pandaStates @81 :List(PandaState);
peripheralState @80 :PeripheralState;
radarState @13 :RadarState;
liveTracks @16 :List(LiveTracks);
liveTracks @131 :Car.RadarData;
sendcan @17 :List(CanData);
liveCalibration @19 :LiveCalibrationData;
carState @22 :Car.CarState;
Expand Down Expand Up @@ -2465,5 +2465,6 @@ struct Event {
navModelDEPRECATED @104 :NavModelData;
uiPlanDEPRECATED @106 :UiPlan;
liveLocationKalmanDEPRECATED @72 :LiveLocationKalman;
liveTracksDEPRECATED @16 :List(LiveTracksDEPRECATED);
}
}
37 changes: 26 additions & 11 deletions selfdrive/car/card.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@
from opendbc.car import DT_CTRL, carlog, structs
from opendbc.car.can_definitions import CanData, CanRecvCallable, CanSendCallable
from opendbc.car.fw_versions import ObdCallback
from opendbc.car.car_helpers import get_car
from opendbc.car.interfaces import CarInterfaceBase
from opendbc.car.car_helpers import get_car, get_radar_interface
from opendbc.car.interfaces import CarInterfaceBase, RadarInterfaceBase
from openpilot.selfdrive.pandad import can_capnp_to_list, can_list_to_can_capnp
from openpilot.selfdrive.car.cruise import VCruiseHelper
from openpilot.selfdrive.car.car_specific import CarSpecificEvents, MockCarState
Expand Down Expand Up @@ -63,13 +63,14 @@ def can_send(msgs: list[CanData]) -> None:

class Car:
CI: CarInterfaceBase
RI: RadarInterfaceBase
CP: structs.CarParams
CP_capnp: car.CarParams

def __init__(self, CI=None) -> None:
def __init__(self, CI=None, RI=None) -> None:
self.can_sock = messaging.sub_sock('can', timeout=20)
self.sm = messaging.SubMaster(['pandaStates', 'carControl', 'onroadEvents'])
self.pm = messaging.PubMaster(['sendcan', 'carState', 'carParams', 'carOutput'])
self.pm = messaging.PubMaster(['sendcan', 'carState', 'carParams', 'carOutput', 'liveTracks'])

self.can_rcv_cum_timeout_counter = 0

Expand Down Expand Up @@ -101,12 +102,14 @@ def __init__(self, CI=None) -> None:
cached_params = structs.CarParams(carName=_cached_params.carName, carFw=_cached_params.carFw, carVin=_cached_params.carVin)

self.CI = get_car(*self.can_callbacks, obd_callback(self.params), experimental_long_allowed, num_pandas, cached_params)
self.RI = get_radar_interface(self.CI.CP)
self.CP = self.CI.CP

# continue onto next fingerprinting step in pandad
self.params.put_bool("FirmwareQueryDone", True)
else:
self.CI, self.CP = CI, CI.CP
self.RI = RI

# set alternative experiences from parameters
self.disengage_on_accelerator = self.params.get_bool("DisengageOnAccelerator")
Expand Down Expand Up @@ -149,7 +152,7 @@ def __init__(self, CI=None) -> None:
# card is driven by can recv, expected at 100Hz
self.rk = Ratekeeper(100, print_delay_threshold=None)

def state_update(self) -> car.CarState:
def state_update(self) -> tuple[car.CarState, structs.RadarData | None]:
"""carState update loop, driven by can"""

# Update carState from CAN
Expand All @@ -159,6 +162,9 @@ def state_update(self) -> car.CarState:
if self.CP.carName == 'mock':
CS = self.mock_carstate.update(CS)

# Update radar tracks from CAN
RD: structs.RadarData | None = self.RI.update(can_capnp_to_list(can_strs))

self.sm.update(0)

can_rcv_valid = len(can_strs) > 0
Expand All @@ -175,9 +181,9 @@ def state_update(self) -> car.CarState:
CS.vCruise = float(self.v_cruise_helper.v_cruise_kph)
CS.vCruiseCluster = float(self.v_cruise_helper.v_cruise_cluster_kph)

return CS
return CS, RD

def update_events(self, CS: car.CarState):
def update_events(self, CS: car.CarState, RD: structs.RadarData | None):
self.events.clear()

CS.events = self.car_events.update(self.CI.CS, self.CS_prev, self.CI.CC, self.CC_prev).to_msg()
Expand All @@ -196,9 +202,12 @@ def update_events(self, CS: car.CarState):
(CS.regenBraking and (not self.CS_prev.regenBraking or not CS.standstill)):
self.events.add(EventName.pedalPressed)

if RD is not None and len(RD.errors):
self.events.add(EventName.radarFault)

CS.events = self.events.to_msg()

def state_publish(self, CS: car.CarState):
def state_publish(self, CS: car.CarState, RD: structs.RadarData | None):
"""carState and carParams publish loop"""

# carParams - logged every 50 seconds (> 1 per segment)
Expand All @@ -222,6 +231,12 @@ def state_publish(self, CS: car.CarState):
cs_send.carState.cumLagMs = -self.rk.remaining * 1000.
self.pm.send('carState', cs_send)

if RD is not None:
tracks_msg = messaging.new_message('liveTracks')
tracks_msg.valid = CS.canValid and len(RD.errors) == 0
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why is the carState canValid relevant here? if the radar bus is good, then the tracks should be valid

tracks_msg.liveTracks = convert_to_capnp(RD)
self.pm.send('liveTracks', tracks_msg)

def controls_update(self, CS: car.CarState, CC: car.CarControl):
"""control update loop, driven by carControl"""

Expand All @@ -241,14 +256,14 @@ def controls_update(self, CS: car.CarState, CC: car.CarControl):
self.CC_prev = CC

def step(self):
CS = self.state_update()
CS, RD = self.state_update()

self.update_events(CS)
self.update_events(CS, RD)

if not self.sm['carControl'].enabled and self.events.contains(ET.ENABLE):
self.v_cruise_helper.initialize_v_cruise(CS, self.experimental_mode)

self.state_publish(CS)
self.state_publish(CS, RD)

initialized = (not any(e.name == EventName.controlsInitializing for e in self.sm['onroadEvents']) and
self.sm.seen['onroadEvents'])
Expand Down
4 changes: 3 additions & 1 deletion selfdrive/car/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ def asdictref(obj) -> dict[str, Any]:
return _asdictref_inner(obj)


def convert_to_capnp(struct: structs.CarParams | structs.CarState | structs.CarControl.Actuators) -> capnp.lib.capnp._DynamicStructBuilder:
def convert_to_capnp(struct: structs.CarParams | structs.CarState | structs.CarControl.Actuators | structs.RadarData) -> capnp.lib.capnp._DynamicStructBuilder:
struct_dict = asdictref(struct)

if isinstance(struct, structs.CarParams):
Expand All @@ -51,6 +51,8 @@ def convert_to_capnp(struct: structs.CarParams | structs.CarState | structs.CarC
struct_capnp = car.CarState.new_message(**struct_dict)
elif isinstance(struct, structs.CarControl.Actuators):
struct_capnp = car.CarControl.Actuators.new_message(**struct_dict)
elif isinstance(struct, structs.RadarData):
struct_capnp = car.RadarData.new_message(**struct_dict)
else:
raise ValueError(f"Unsupported struct type: {type(struct)}")

Expand Down
2 changes: 1 addition & 1 deletion selfdrive/car/tests/test_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -448,7 +448,7 @@ def test_panda_safety_carstate(self):
checks['cruiseState'] += CS.cruiseState.enabled != self.safety.get_cruise_engaged_prev()
else:
# Check for enable events on rising edge of controls allowed
card.update_events(CS)
card.update_events(CS, None)
card.CS_prev = CS
button_enable = (any(evt.enable for evt in CS.events) and
not any(evt == EventName.pedalPressed for evt in card.events.names))
Expand Down
4 changes: 2 additions & 2 deletions selfdrive/controls/controlsd.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ def __init__(self, CI=None):
'carOutput', 'driverMonitoringState', 'longitudinalPlan', 'livePose',
'managerState', 'liveParameters', 'radarState', 'liveTorqueParameters',
'testJoystick'] + self.camera_packets + self.sensor_packets + self.gps_packets,
ignore_alive=ignore, ignore_avg_freq=ignore+['radarState', 'testJoystick'], ignore_valid=['testJoystick', ],
ignore_alive=ignore, ignore_avg_freq=ignore+['testJoystick'], ignore_valid=['testJoystick', ],
frequency=int(1/DT_CTRL))

self.joystick_mode = self.params.get_bool("JoystickDebugMode")
Expand Down Expand Up @@ -301,7 +301,7 @@ def update_events(self, CS):
self.events.add(EventName.cameraFrameRate)
if not REPLAY and self.rk.lagging:
self.events.add(EventName.controlsdLagging)
if len(self.sm['radarState'].radarErrors) or ((not self.rk.lagging or REPLAY) and not self.sm.all_checks(['radarState'])):
if not self.sm.valid['radarState']:
self.events.add(EventName.radarFault)
if not self.sm.valid['pandaStates']:
self.events.add(EventName.usbError)
Expand Down
54 changes: 12 additions & 42 deletions selfdrive/controls/radard.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,15 @@
#!/usr/bin/env python3
import importlib
import math
from collections import deque
from typing import Any

import capnp
from cereal import messaging, log, car
from opendbc.car import structs
from openpilot.common.numpy_fast import interp
from openpilot.common.params import Params
from openpilot.common.realtime import DT_CTRL, Ratekeeper, Priority, config_realtime_process
from openpilot.common.realtime import DT_MDL, Priority, config_realtime_process
from openpilot.common.swaglog import cloudlog
from openpilot.common.simple_kalman import KF1D
from openpilot.selfdrive.pandad import can_capnp_to_list


# Default lead acceleration decay set to 50% at 1s
Expand Down Expand Up @@ -194,22 +191,22 @@ def get_lead(v_ego: float, ready: bool, tracks: dict[int, Track], lead_msg: capn


class RadarD:
def __init__(self, radar_ts: float, delay: int = 0):
def __init__(self, delay: float = 0.0):
self.current_time = 0.0

self.tracks: dict[int, Track] = {}
self.kalman_params = KalmanParams(radar_ts)
self.kalman_params = KalmanParams(DT_MDL)

self.v_ego = 0.0
self.v_ego_hist = deque([0.0], maxlen=delay+1)
self.v_ego_hist = deque([0.0], maxlen=int(round(delay / DT_MDL))+1)
self.last_v_ego_frame = -1

self.radar_state: capnp._DynamicStructBuilder | None = None
self.radar_state_valid = False

self.ready = False

def update(self, sm: messaging.SubMaster, rr: structs.RadarData):
def update(self, sm: messaging.SubMaster, rr: car.RadarData):
self.ready = sm.seen['modelV2']
self.current_time = 1e-9*max(sm.logMonoTime.values())

Expand Down Expand Up @@ -255,27 +252,14 @@ def update(self, sm: messaging.SubMaster, rr: structs.RadarData):
self.radar_state.leadOne = get_lead(self.v_ego, self.ready, self.tracks, leads_v3[0], model_v_ego, low_speed_override=True)
self.radar_state.leadTwo = get_lead(self.v_ego, self.ready, self.tracks, leads_v3[1], model_v_ego, low_speed_override=False)

def publish(self, pm: messaging.PubMaster, lag_ms: float):
def publish(self, pm: messaging.PubMaster):
assert self.radar_state is not None

radar_msg = messaging.new_message("radarState")
radar_msg.valid = self.radar_state_valid
radar_msg.radarState = self.radar_state
radar_msg.radarState.cumLagMs = lag_ms
pm.send("radarState", radar_msg)

# publish tracks for UI debugging (keep last)
tracks_msg = messaging.new_message('liveTracks', len(self.tracks))
tracks_msg.valid = self.radar_state_valid
for index, tid in enumerate(sorted(self.tracks.keys())):
tracks_msg.liveTracks[index] = {
"trackId": tid,
"dRel": float(self.tracks[tid].dRel),
"yRel": float(self.tracks[tid].yRel),
"vRel": float(self.tracks[tid].vRel),
}
pm.send('liveTracks', tracks_msg)


# fuses camera and radar data for best lead detection
def main() -> None:
Expand All @@ -286,31 +270,17 @@ def main() -> None:
CP = messaging.log_from_bytes(Params().get("CarParams", block=True), car.CarParams)
cloudlog.info("radard got CarParams")

# import the radar from the fingerprint
cloudlog.info("radard is importing %s", CP.carName)
RadarInterface = importlib.import_module(f'opendbc.car.{CP.carName}.radar_interface').RadarInterface

# *** setup messaging
can_sock = messaging.sub_sock('can')
sm = messaging.SubMaster(['modelV2', 'carState'], frequency=int(1./DT_CTRL))
pm = messaging.PubMaster(['radarState', 'liveTracks'])
sm = messaging.SubMaster(['modelV2', 'carState', 'liveTracks'], poll='modelV2')
pm = messaging.PubMaster(['radarState'])

RI = RadarInterface(CP)

rk = Ratekeeper(1.0 / CP.radarTimeStep, print_delay_threshold=None)
RD = RadarD(CP.radarTimeStep, RI.delay)
RD = RadarD(CP.radarDelay)

while 1:
can_strings = messaging.drain_sock_raw(can_sock, wait_for_one=True)
rr: structs.RadarData | None = RI.update(can_capnp_to_list(can_strings))
sm.update(0)
if rr is None:
continue

RD.update(sm, rr)
RD.publish(pm, -rk.remaining*1000.0)
sm.update()

rk.monitor_time()
RD.update(sm, sm['liveTracks'])
RD.publish(pm)


if __name__ == "__main__":
Expand Down
10 changes: 6 additions & 4 deletions selfdrive/controls/tests/test_leads.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,19 +11,21 @@ def test_radar_fault(self):
def single_iter_pkg():
# single iter package, with meaningless cans and empty carState/modelV2
msgs = []
for _ in range(5):
for _ in range(500):
can = messaging.new_message("can", 1)
cs = messaging.new_message("carState")
cp = messaging.new_message("carParams")
msgs.append(can.as_reader())
msgs.append(cs.as_reader())
msgs.append(cp.as_reader())
model = messaging.new_message("modelV2")
msgs.append(model.as_reader())

return msgs

msgs = [m for _ in range(3) for m in single_iter_pkg()]
out = replay_process_with_name("radard", msgs, fingerprint=TOYOTA.TOYOTA_COROLLA_TSS2)
states = [m for m in out if m.which() == "radarState"]
failures = [not state.valid and len(state.radarState.radarErrors) for state in states]
out = replay_process_with_name("card", msgs, fingerprint=TOYOTA.TOYOTA_COROLLA_TSS2)
states = [m for m in out if m.which() == "liveTracks"]
failures = [not state.valid and len(state.liveTracks.errors) for state in states]

assert len(states) == 0 or all(failures)
Loading
Loading