You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							230 lines
						
					
					
						
							7.3 KiB
						
					
					
				
			
		
		
	
	
							230 lines
						
					
					
						
							7.3 KiB
						
					
					
				#!/usr/bin/env python
 | 
						|
import numpy as np
 | 
						|
import numpy.matlib
 | 
						|
import importlib
 | 
						|
from collections import defaultdict, deque
 | 
						|
 | 
						|
import selfdrive.messaging as messaging
 | 
						|
from selfdrive.services import service_list
 | 
						|
from selfdrive.controls.lib.radar_helpers import Track, Cluster
 | 
						|
from selfdrive.config import RADAR_TO_CENTER
 | 
						|
from selfdrive.controls.lib.cluster.fastcluster_py import cluster_points_centroid
 | 
						|
from selfdrive.swaglog import cloudlog
 | 
						|
from cereal import car
 | 
						|
from common.params import Params
 | 
						|
from common.realtime import set_realtime_priority, Ratekeeper, DT_MDL
 | 
						|
 | 
						|
DEBUG = False
 | 
						|
 | 
						|
#vision point
 | 
						|
DIMSV = 2
 | 
						|
XV, SPEEDV = 0, 1
 | 
						|
VISION_POINT = -1
 | 
						|
 | 
						|
# Time-alignment
 | 
						|
rate = 1. / DT_MDL  # model and radar are both at 20Hz
 | 
						|
v_len = 20   # how many speed data points to remember for t alignment with rdr data
 | 
						|
 | 
						|
 | 
						|
def laplacian_cdf(x, mu, b):
 | 
						|
  b = np.max([b, 1e-4])
 | 
						|
  return np.exp(-abs(x-mu)/b)
 | 
						|
 | 
						|
 | 
						|
def match_vision_to_cluster(v_ego, lead, clusters):
 | 
						|
  # match vision point to best statistical cluster match
 | 
						|
  probs = []
 | 
						|
  offset_vision_dist = lead.dist - RADAR_TO_CENTER
 | 
						|
  for c in clusters:
 | 
						|
    prob_d = laplacian_cdf(c.dRel, offset_vision_dist, lead.std)
 | 
						|
    prob_y = laplacian_cdf(c.yRel, lead.relY, lead.relYStd)
 | 
						|
    prob_v = laplacian_cdf(c.vRel, lead.relVel, lead.relVelStd)
 | 
						|
    # This is isn't exactly right, but good heuristic
 | 
						|
    combined_prob = prob_d * prob_y * prob_v
 | 
						|
    probs.append(combined_prob)
 | 
						|
  idx = np.argmax(probs)
 | 
						|
  # if no 'sane' match is found return -1
 | 
						|
  # stationary radar points can be false positives
 | 
						|
  dist_sane = abs(clusters[idx].dRel - offset_vision_dist) < max([(offset_vision_dist)*.25, 5.0])
 | 
						|
  vel_sane = (abs(clusters[idx].vRel - lead.relVel) < 10) or (v_ego + clusters[idx].vRel > 2)
 | 
						|
  if dist_sane and vel_sane:
 | 
						|
    return idx
 | 
						|
  else:
 | 
						|
    return None
 | 
						|
 | 
						|
 | 
						|
def get_lead(v_ego, ready, clusters, lead_msg, low_speed_override=True):
 | 
						|
  # Determine leads, this is where the essential logic happens
 | 
						|
  if len(clusters) > 0 and ready and lead_msg.prob > .5:
 | 
						|
    lead_idx = match_vision_to_cluster(v_ego, lead_msg, clusters)
 | 
						|
  else:
 | 
						|
    lead_idx = None
 | 
						|
 | 
						|
  lead_dict = {'status': False}
 | 
						|
  if lead_idx is not None:
 | 
						|
    lead_dict = clusters[lead_idx].get_RadarState(lead_msg.prob)
 | 
						|
  elif (lead_idx is None) and ready and (lead_msg.prob > .5):
 | 
						|
    lead_dict = Cluster().get_RadarState_from_vision(lead_msg, v_ego)
 | 
						|
 | 
						|
  if low_speed_override:
 | 
						|
    low_speed_clusters = [c for c in clusters if c.potential_low_speed_lead(v_ego)]
 | 
						|
    if len(low_speed_clusters) > 0:
 | 
						|
      lead_idx = np.argmin([c.dRel for c in low_speed_clusters])
 | 
						|
      if (not lead_dict['status']) or (low_speed_clusters[lead_idx].dRel < lead_dict['dRel']):
 | 
						|
        lead_dict = low_speed_clusters[lead_idx].get_RadarState()
 | 
						|
 | 
						|
  return lead_dict
 | 
						|
 | 
						|
 | 
						|
class RadarD(object):
 | 
						|
  def __init__(self, mocked):
 | 
						|
    self.current_time = 0
 | 
						|
    self.mocked = mocked
 | 
						|
 | 
						|
    self.tracks = defaultdict(dict)
 | 
						|
 | 
						|
    self.last_md_ts = 0
 | 
						|
    self.last_controls_state_ts = 0
 | 
						|
 | 
						|
    self.active = 0
 | 
						|
 | 
						|
    # v_ego
 | 
						|
    self.v_ego = 0.
 | 
						|
    self.v_ego_hist_t = deque([0], maxlen=v_len)
 | 
						|
    self.v_ego_hist_v = deque([0], maxlen=v_len)
 | 
						|
    self.v_ego_t_aligned = 0.
 | 
						|
    self.ready = False
 | 
						|
 | 
						|
  def update(self, frame, delay, sm, rr, has_radar):
 | 
						|
    self.current_time = 1e-9*max([sm.logMonoTime[key] for key in sm.logMonoTime.keys()])
 | 
						|
 | 
						|
    if sm.updated['controlsState']:
 | 
						|
      self.active = sm['controlsState'].active
 | 
						|
      self.v_ego = sm['controlsState'].vEgo
 | 
						|
      self.v_ego_hist_v.append(self.v_ego)
 | 
						|
      self.v_ego_hist_t.append(float(frame)/rate)
 | 
						|
    if sm.updated['model']:
 | 
						|
      self.ready = True
 | 
						|
 | 
						|
    ar_pts = {}
 | 
						|
    for pt in rr.points:
 | 
						|
      ar_pts[pt.trackId] = [pt.dRel, pt.yRel, pt.vRel, pt.measured]
 | 
						|
 | 
						|
    # *** remove missing points from meta data ***
 | 
						|
    for ids in self.tracks.keys():
 | 
						|
      if ids not in ar_pts:
 | 
						|
        self.tracks.pop(ids, None)
 | 
						|
 | 
						|
    # *** compute the tracks ***
 | 
						|
    for ids in ar_pts:
 | 
						|
      rpt = ar_pts[ids]
 | 
						|
 | 
						|
      # align v_ego by a fixed time to align it with the radar measurement
 | 
						|
      cur_time = float(frame)/rate
 | 
						|
      self.v_ego_t_aligned = np.interp(cur_time - delay, self.v_ego_hist_t, self.v_ego_hist_v)
 | 
						|
 | 
						|
      # create the track if it doesn't exist or it's a new track
 | 
						|
      if ids not in self.tracks:
 | 
						|
        self.tracks[ids] = Track()
 | 
						|
      self.tracks[ids].update(rpt[0], rpt[1], rpt[2], self.v_ego_t_aligned, rpt[3])
 | 
						|
 | 
						|
    idens = list(self.tracks.keys())
 | 
						|
    track_pts = np.array([self.tracks[iden].get_key_for_cluster() for iden in idens])
 | 
						|
 | 
						|
 | 
						|
    # If we have multiple points, cluster them
 | 
						|
    if len(track_pts) > 1:
 | 
						|
      cluster_idxs = cluster_points_centroid(track_pts, 2.5)
 | 
						|
      clusters = [None] * (max(cluster_idxs) + 1)
 | 
						|
 | 
						|
      for idx in xrange(len(track_pts)):
 | 
						|
        cluster_i = cluster_idxs[idx]
 | 
						|
        if clusters[cluster_i] is None:
 | 
						|
          clusters[cluster_i] = Cluster()
 | 
						|
        clusters[cluster_i].add(self.tracks[idens[idx]])
 | 
						|
    elif len(track_pts) == 1:
 | 
						|
      clusters = [Cluster()]
 | 
						|
      clusters[0].add(self.tracks[idens[0]])
 | 
						|
    else:
 | 
						|
      clusters = []
 | 
						|
 | 
						|
    # *** publish radarState ***
 | 
						|
    dat = messaging.new_message()
 | 
						|
    dat.init('radarState')
 | 
						|
    dat.valid = sm.all_alive_and_valid(service_list=['controlsState'])
 | 
						|
    dat.radarState.mdMonoTime = self.last_md_ts
 | 
						|
    dat.radarState.canMonoTimes = list(rr.canMonoTimes)
 | 
						|
    dat.radarState.radarErrors = list(rr.errors)
 | 
						|
    dat.radarState.controlsStateMonoTime = self.last_controls_state_ts
 | 
						|
 | 
						|
    if has_radar:
 | 
						|
      dat.radarState.leadOne = get_lead(self.v_ego, self.ready, clusters, sm['model'].lead, low_speed_override=True)
 | 
						|
      dat.radarState.leadTwo = get_lead(self.v_ego, self.ready, clusters, sm['model'].leadFuture, low_speed_override=False)
 | 
						|
    return dat
 | 
						|
 | 
						|
 | 
						|
# fuses camera and radar data for best lead detection
 | 
						|
def radard_thread(gctx=None):
 | 
						|
  set_realtime_priority(2)
 | 
						|
 | 
						|
  # wait for stats about the car to come in from controls
 | 
						|
  cloudlog.info("radard is waiting for CarParams")
 | 
						|
  CP = car.CarParams.from_bytes(Params().get("CarParams", block=True))
 | 
						|
  mocked = CP.carName == "mock"
 | 
						|
  cloudlog.info("radard got CarParams")
 | 
						|
 | 
						|
  # import the radar from the fingerprint
 | 
						|
  cloudlog.info("radard is importing %s", CP.carName)
 | 
						|
  RadarInterface = importlib.import_module('selfdrive.car.%s.radar_interface' % CP.carName).RadarInterface
 | 
						|
 | 
						|
  can_sock = messaging.sub_sock(service_list['can'].port)
 | 
						|
  sm = messaging.SubMaster(['model', 'controlsState', 'liveParameters'])
 | 
						|
 | 
						|
  RI = RadarInterface(CP)
 | 
						|
 | 
						|
  # *** publish radarState and liveTracks
 | 
						|
  radarState = messaging.pub_sock(service_list['radarState'].port)
 | 
						|
  liveTracks = messaging.pub_sock(service_list['liveTracks'].port)
 | 
						|
 | 
						|
  rk = Ratekeeper(rate, print_delay_threshold=None)
 | 
						|
  RD = RadarD(mocked)
 | 
						|
 | 
						|
  has_radar = not CP.radarOffCan
 | 
						|
 | 
						|
  while 1:
 | 
						|
    can_strings = messaging.drain_sock_raw(can_sock, wait_for_one=True)
 | 
						|
    rr = RI.update(can_strings)
 | 
						|
 | 
						|
    if rr is None:
 | 
						|
      continue
 | 
						|
 | 
						|
    sm.update(0)
 | 
						|
 | 
						|
    dat = RD.update(rk.frame, RI.delay, sm, rr, has_radar)
 | 
						|
    dat.radarState.cumLagMs = -rk.remaining*1000.
 | 
						|
 | 
						|
    radarState.send(dat.to_bytes())
 | 
						|
 | 
						|
    # *** publish tracks for UI debugging (keep last) ***
 | 
						|
    tracks = RD.tracks
 | 
						|
    dat = messaging.new_message()
 | 
						|
    dat.init('liveTracks', len(tracks))
 | 
						|
 | 
						|
    for cnt, ids in enumerate(tracks.keys()):
 | 
						|
      dat.liveTracks[cnt] = {
 | 
						|
        "trackId": ids,
 | 
						|
        "dRel": float(tracks[ids].dRel),
 | 
						|
        "yRel": float(tracks[ids].yRel),
 | 
						|
        "vRel": float(tracks[ids].vRel),
 | 
						|
      }
 | 
						|
    liveTracks.send(dat.to_bytes())
 | 
						|
 | 
						|
    rk.monitor_time()
 | 
						|
 | 
						|
 | 
						|
def main(gctx=None):
 | 
						|
  radard_thread(gctx)
 | 
						|
 | 
						|
 | 
						|
if __name__ == "__main__":
 | 
						|
  main()
 | 
						|
 |