parent
							
								
									2d69a5a335
								
							
						
					
					
						commit
						b26d45ce04
					
				
				 2 changed files with 0 additions and 290 deletions
			
			
		@ -1,206 +0,0 @@ | 
				
			|||||||
#!/usr/bin/env python3 | 
					 | 
				
			||||||
import os | 
					 | 
				
			||||||
import zmq | 
					 | 
				
			||||||
import math | 
					 | 
				
			||||||
import json | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import numpy as np | 
					 | 
				
			||||||
from bisect import bisect_right | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
from cereal import car | 
					 | 
				
			||||||
from common.params import Params | 
					 | 
				
			||||||
from common.numpy_fast import clip | 
					 | 
				
			||||||
import cereal.messaging as messaging | 
					 | 
				
			||||||
from selfdrive.swaglog import cloudlog | 
					 | 
				
			||||||
from selfdrive.controls.lib.vehicle_model import VehicleModel | 
					 | 
				
			||||||
from cereal.services import service_list | 
					 | 
				
			||||||
from selfdrive.locationd.kalman.loc_local_kf import LocLocalKalman | 
					 | 
				
			||||||
from selfdrive.locationd.kalman.kalman_helpers import ObservationKind | 
					 | 
				
			||||||
from selfdrive.locationd.params_learner import ParamsLearner | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
DEBUG = False | 
					 | 
				
			||||||
kf = LocLocalKalman()  # Make sure that model is generated on import time | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
LEARNING_RATE = 3 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class Localizer(): | 
					 | 
				
			||||||
  def __init__(self, disabled_logs=None, dog=None): | 
					 | 
				
			||||||
    self.kf = LocLocalKalman() | 
					 | 
				
			||||||
    self.reset_kalman() | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    self.sensor_data_t = 0.0 | 
					 | 
				
			||||||
    self.max_age = .2  # seconds | 
					 | 
				
			||||||
    self.calibration_valid = False | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if disabled_logs is None: | 
					 | 
				
			||||||
      self.disabled_logs = list() | 
					 | 
				
			||||||
    else: | 
					 | 
				
			||||||
      self.disabled_logs = disabled_logs | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  def reset_kalman(self): | 
					 | 
				
			||||||
    self.filter_time = None | 
					 | 
				
			||||||
    self.observation_buffer = [] | 
					 | 
				
			||||||
    self.converter = None | 
					 | 
				
			||||||
    self.speed_counter = 0 | 
					 | 
				
			||||||
    self.sensor_counter = 0 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  def liveLocationMsg(self, time): | 
					 | 
				
			||||||
    fix = messaging.log.KalmanOdometry.new_message() | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    predicted_state = self.kf.x | 
					 | 
				
			||||||
    fix.trans = [float(predicted_state[0]), float(predicted_state[1]), float(predicted_state[2])] | 
					 | 
				
			||||||
    fix.rot = [float(predicted_state[3]), float(predicted_state[4]), float(predicted_state[5])] | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    return fix | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  def update_kalman(self, time, kind, meas): | 
					 | 
				
			||||||
    idx = bisect_right([x[0] for x in self.observation_buffer], time) | 
					 | 
				
			||||||
    self.observation_buffer.insert(idx, (time, kind, meas)) | 
					 | 
				
			||||||
    while self.observation_buffer[-1][0] - self.observation_buffer[0][0] > self.max_age: | 
					 | 
				
			||||||
      self.kf.predict_and_observe(*self.observation_buffer.pop(0)) | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  def handle_cam_odo(self, log, current_time): | 
					 | 
				
			||||||
    self.update_kalman(current_time, ObservationKind.CAMERA_ODO_ROTATION, np.concatenate([log.cameraOdometry.rot, | 
					 | 
				
			||||||
                                                                                          log.cameraOdometry.rotStd])) | 
					 | 
				
			||||||
    self.update_kalman(current_time, ObservationKind.CAMERA_ODO_TRANSLATION, np.concatenate([log.cameraOdometry.trans, | 
					 | 
				
			||||||
                                                                                             log.cameraOdometry.transStd])) | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  def handle_controls_state(self, log, current_time): | 
					 | 
				
			||||||
    self.speed_counter += 1 | 
					 | 
				
			||||||
    if self.speed_counter % 5 == 0: | 
					 | 
				
			||||||
      self.update_kalman(current_time, ObservationKind.ODOMETRIC_SPEED, np.array([log.controlsState.vEgo])) | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  def handle_sensors(self, log, current_time): | 
					 | 
				
			||||||
    for sensor_reading in log.sensorEvents: | 
					 | 
				
			||||||
      # TODO does not yet account for double sensor readings in the log | 
					 | 
				
			||||||
      if sensor_reading.type == 4: | 
					 | 
				
			||||||
        self.sensor_counter += 1 | 
					 | 
				
			||||||
        if self.sensor_counter % LEARNING_RATE == 0: | 
					 | 
				
			||||||
          self.update_kalman(current_time, ObservationKind.PHONE_GYRO, [-sensor_reading.gyro.v[2], -sensor_reading.gyro.v[1], -sensor_reading.gyro.v[0]]) | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  def handle_log(self, log): | 
					 | 
				
			||||||
    current_time = 1e-9 * log.logMonoTime | 
					 | 
				
			||||||
    typ = log.which | 
					 | 
				
			||||||
    if typ in self.disabled_logs: | 
					 | 
				
			||||||
      return | 
					 | 
				
			||||||
    if typ == "sensorEvents": | 
					 | 
				
			||||||
      self.sensor_data_t = current_time | 
					 | 
				
			||||||
      self.handle_sensors(log, current_time) | 
					 | 
				
			||||||
    elif typ == "controlsState": | 
					 | 
				
			||||||
      self.handle_controls_state(log, current_time) | 
					 | 
				
			||||||
    elif typ == "cameraOdometry": | 
					 | 
				
			||||||
      self.handle_cam_odo(log, current_time) | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
def locationd_thread(gctx, addr, disabled_logs): | 
					 | 
				
			||||||
  poller = zmq.Poller() | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  controls_state_socket = messaging.sub_sock('controlsState', poller, addr=addr, conflate=True) | 
					 | 
				
			||||||
  sensor_events_socket = messaging.sub_sock('sensorEvents', poller, addr=addr, conflate=True) | 
					 | 
				
			||||||
  camera_odometry_socket = messaging.sub_sock('cameraOdometry', poller, addr=addr, conflate=True) | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  kalman_odometry_socket = messaging.pub_sock('kalmanOdometry') | 
					 | 
				
			||||||
  live_parameters_socket = messaging.pub_sock('liveParameters') | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  params_reader = Params() | 
					 | 
				
			||||||
  cloudlog.info("Parameter learner is waiting for CarParams") | 
					 | 
				
			||||||
  CP = car.CarParams.from_bytes(params_reader.get("CarParams", block=True)) | 
					 | 
				
			||||||
  VM = VehicleModel(CP) | 
					 | 
				
			||||||
  cloudlog.info("Parameter learner got CarParams: %s" % CP.carFingerprint) | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  params = params_reader.get("LiveParameters") | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  # Check if car model matches | 
					 | 
				
			||||||
  if params is not None: | 
					 | 
				
			||||||
    params = json.loads(params) | 
					 | 
				
			||||||
    if (params.get('carFingerprint', None) != CP.carFingerprint) or (params.get('carVin', CP.carVin) != CP.carVin): | 
					 | 
				
			||||||
      cloudlog.info("Parameter learner found parameters for wrong car.") | 
					 | 
				
			||||||
      params = None | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  if params is None: | 
					 | 
				
			||||||
    params = { | 
					 | 
				
			||||||
      'carFingerprint': CP.carFingerprint, | 
					 | 
				
			||||||
      'carVin': CP.carVin, | 
					 | 
				
			||||||
      'angleOffsetAverage': 0.0, | 
					 | 
				
			||||||
      'stiffnessFactor': 1.0, | 
					 | 
				
			||||||
      'steerRatio': VM.sR, | 
					 | 
				
			||||||
    } | 
					 | 
				
			||||||
    params_reader.put("LiveParameters", json.dumps(params)) | 
					 | 
				
			||||||
    cloudlog.info("Parameter learner resetting to default values") | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  cloudlog.info("Parameter starting with: %s" % str(params)) | 
					 | 
				
			||||||
  localizer = Localizer(disabled_logs=disabled_logs) | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  learner = ParamsLearner(VM, | 
					 | 
				
			||||||
                          angle_offset=params['angleOffsetAverage'], | 
					 | 
				
			||||||
                          stiffness_factor=params['stiffnessFactor'], | 
					 | 
				
			||||||
                          steer_ratio=params['steerRatio'], | 
					 | 
				
			||||||
                          learning_rate=LEARNING_RATE) | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  i = 1 | 
					 | 
				
			||||||
  while True: | 
					 | 
				
			||||||
    for socket, event in poller.poll(timeout=1000): | 
					 | 
				
			||||||
      log = messaging.recv_one(socket) | 
					 | 
				
			||||||
      localizer.handle_log(log) | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      if socket is controls_state_socket: | 
					 | 
				
			||||||
        if not localizer.kf.t: | 
					 | 
				
			||||||
          continue | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if i % LEARNING_RATE == 0: | 
					 | 
				
			||||||
          # controlsState is not updating the Kalman Filter, so update KF manually | 
					 | 
				
			||||||
          localizer.kf.predict(1e-9 * log.logMonoTime) | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
          predicted_state = localizer.kf.x | 
					 | 
				
			||||||
          yaw_rate = -float(predicted_state[5]) | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
          steering_angle = math.radians(log.controlsState.angleSteers) | 
					 | 
				
			||||||
          params_valid = learner.update(yaw_rate, log.controlsState.vEgo, steering_angle) | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
          log_t = 1e-9 * log.logMonoTime | 
					 | 
				
			||||||
          sensor_data_age = log_t - localizer.sensor_data_t | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
          params = messaging.new_message() | 
					 | 
				
			||||||
          params.init('liveParameters') | 
					 | 
				
			||||||
          params.liveParameters.valid = bool(params_valid) | 
					 | 
				
			||||||
          params.liveParameters.sensorValid = bool(sensor_data_age < 5.0) | 
					 | 
				
			||||||
          params.liveParameters.angleOffset = float(math.degrees(learner.ao)) | 
					 | 
				
			||||||
          params.liveParameters.angleOffsetAverage = float(math.degrees(learner.slow_ao)) | 
					 | 
				
			||||||
          params.liveParameters.stiffnessFactor = float(learner.x) | 
					 | 
				
			||||||
          params.liveParameters.steerRatio = float(learner.sR) | 
					 | 
				
			||||||
          live_parameters_socket.send(params.to_bytes()) | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if i % 6000 == 0:   # once a minute | 
					 | 
				
			||||||
          params = learner.get_values() | 
					 | 
				
			||||||
          params['carFingerprint'] = CP.carFingerprint | 
					 | 
				
			||||||
          params['carVin'] = CP.carVin | 
					 | 
				
			||||||
          params_reader.put("LiveParameters", json.dumps(params)) | 
					 | 
				
			||||||
          params_reader.put("ControlsParams", json.dumps({'angle_model_bias': log.controlsState.angleModelBias})) | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        i += 1 | 
					 | 
				
			||||||
      elif socket is camera_odometry_socket: | 
					 | 
				
			||||||
        msg = messaging.new_message() | 
					 | 
				
			||||||
        msg.init('kalmanOdometry') | 
					 | 
				
			||||||
        msg.logMonoTime = log.logMonoTime | 
					 | 
				
			||||||
        msg.kalmanOdometry = localizer.liveLocationMsg(log.logMonoTime * 1e-9) | 
					 | 
				
			||||||
        kalman_odometry_socket.send(msg.to_bytes()) | 
					 | 
				
			||||||
      elif socket is sensor_events_socket: | 
					 | 
				
			||||||
        pass | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
def main(gctx=None, addr="127.0.0.1"): | 
					 | 
				
			||||||
  IN_CAR = os.getenv("IN_CAR", False) | 
					 | 
				
			||||||
  disabled_logs = os.getenv("DISABLED_LOGS", "").split(",") | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  # No speed for now | 
					 | 
				
			||||||
  disabled_logs.append('controlsState') | 
					 | 
				
			||||||
  if IN_CAR: | 
					 | 
				
			||||||
    addr = "192.168.5.11" | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  locationd_thread(gctx, addr, disabled_logs) | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
if __name__ == "__main__": | 
					 | 
				
			||||||
  main() | 
					 | 
				
			||||||
@ -1,84 +0,0 @@ | 
				
			|||||||
import math | 
					 | 
				
			||||||
from common.numpy_fast import clip | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
MAX_ANGLE_OFFSET = math.radians(10.) | 
					 | 
				
			||||||
MAX_ANGLE_OFFSET_TH = math.radians(9.) | 
					 | 
				
			||||||
MIN_STIFFNESS = 0.5 | 
					 | 
				
			||||||
MAX_STIFFNESS = 2.0 | 
					 | 
				
			||||||
MIN_SR = 0.5 | 
					 | 
				
			||||||
MAX_SR = 2.0 | 
					 | 
				
			||||||
MIN_SR_TH = 0.55 | 
					 | 
				
			||||||
MAX_SR_TH = 1.9 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
DEBUG = False | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class ParamsLearner(): | 
					 | 
				
			||||||
  def __init__(self, VM, angle_offset=0., stiffness_factor=1.0, steer_ratio=None, learning_rate=1.0): | 
					 | 
				
			||||||
    self.VM = VM | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    self.ao = math.radians(angle_offset) | 
					 | 
				
			||||||
    self.slow_ao = math.radians(angle_offset) | 
					 | 
				
			||||||
    self.x = stiffness_factor | 
					 | 
				
			||||||
    self.sR = VM.sR if steer_ratio is None else steer_ratio | 
					 | 
				
			||||||
    self.MIN_SR = MIN_SR * self.VM.sR | 
					 | 
				
			||||||
    self.MAX_SR = MAX_SR * self.VM.sR | 
					 | 
				
			||||||
    self.MIN_SR_TH = MIN_SR_TH * self.VM.sR | 
					 | 
				
			||||||
    self.MAX_SR_TH = MAX_SR_TH * self.VM.sR | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    self.alpha1 = 0.01 * learning_rate | 
					 | 
				
			||||||
    self.alpha2 = 0.0005 * learning_rate | 
					 | 
				
			||||||
    self.alpha3 = 0.1 * learning_rate | 
					 | 
				
			||||||
    self.alpha4 = 1.0 * learning_rate | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  def get_values(self): | 
					 | 
				
			||||||
    return { | 
					 | 
				
			||||||
      'angleOffsetAverage': math.degrees(self.slow_ao), | 
					 | 
				
			||||||
      'stiffnessFactor': self.x, | 
					 | 
				
			||||||
      'steerRatio': self.sR, | 
					 | 
				
			||||||
    } | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  def update(self, psi, u, sa): | 
					 | 
				
			||||||
    cF0 = self.VM.cF | 
					 | 
				
			||||||
    cR0 = self.VM.cR | 
					 | 
				
			||||||
    aR = self.VM.aR | 
					 | 
				
			||||||
    aF = self.VM.aF | 
					 | 
				
			||||||
    l = self.VM.l | 
					 | 
				
			||||||
    m = self.VM.m | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    x = self.x | 
					 | 
				
			||||||
    ao = self.ao | 
					 | 
				
			||||||
    sR = self.sR | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # Gradient descent:  learn angle offset, tire stiffness and steer ratio. | 
					 | 
				
			||||||
    if u > 10.0 and abs(math.degrees(sa)) < 15.: | 
					 | 
				
			||||||
      self.ao -= self.alpha1 * 2.0*cF0*cR0*l*u*x*(1.0*cF0*cR0*l*u*x*(ao - sa) + psi*sR*(cF0*cR0*l**2*x - m*u**2*(aF*cF0 - aR*cR0)))/(sR**2*(cF0*cR0*l**2*x - m*u**2*(aF*cF0 - aR*cR0))**2) | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      ao = self.slow_ao | 
					 | 
				
			||||||
      self.slow_ao -= self.alpha2 * 2.0*cF0*cR0*l*u*x*(1.0*cF0*cR0*l*u*x*(ao - sa) + psi*sR*(cF0*cR0*l**2*x - m*u**2*(aF*cF0 - aR*cR0)))/(sR**2*(cF0*cR0*l**2*x - m*u**2*(aF*cF0 - aR*cR0))**2) | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      self.x -= self.alpha3 * -2.0*cF0*cR0*l*m*u**3*(ao - sa)*(aF*cF0 - aR*cR0)*(1.0*cF0*cR0*l*u*x*(ao - sa) + psi*sR*(cF0*cR0*l**2*x - m*u**2*(aF*cF0 - aR*cR0)))/(sR**2*(cF0*cR0*l**2*x - m*u**2*(aF*cF0 - aR*cR0))**3) | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      self.sR -= self.alpha4 * -2.0*cF0*cR0*l*u*x*(ao - sa)*(1.0*cF0*cR0*l*u*x*(ao - sa) + psi*sR*(cF0*cR0*l**2*x - m*u**2*(aF*cF0 - aR*cR0)))/(sR**3*(cF0*cR0*l**2*x - m*u**2*(aF*cF0 - aR*cR0))**2) | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if DEBUG: | 
					 | 
				
			||||||
      # s1 = "Measured yaw rate % .6f" % psi | 
					 | 
				
			||||||
      # ao = 0. | 
					 | 
				
			||||||
      # s2 = "Uncompensated yaw % .6f" % (1.0*u*(-ao + sa)/(l*sR*(1 - m*u**2*(aF*cF0*x - aR*cR0*x)/(cF0*cR0*l**2*x**2)))) | 
					 | 
				
			||||||
      # instant_ao = aF*m*psi*sR*u/(cR0*l*x) - aR*m*psi*sR*u/(cF0*l*x) - l*psi*sR/u + sa | 
					 | 
				
			||||||
      s4 = "Instant AO: % .6f Avg. AO % .6f" % (math.degrees(self.ao), math.degrees(self.slow_ao)) | 
					 | 
				
			||||||
      s5 = "Stiffnes: % .6f x" % self.x | 
					 | 
				
			||||||
      s6 = "sR: %.4f" % self.sR | 
					 | 
				
			||||||
      print("{0} {1} {2}".format(s4, s5, s6)) | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    self.ao = clip(self.ao, -MAX_ANGLE_OFFSET, MAX_ANGLE_OFFSET) | 
					 | 
				
			||||||
    self.slow_ao = clip(self.slow_ao, -MAX_ANGLE_OFFSET, MAX_ANGLE_OFFSET) | 
					 | 
				
			||||||
    self.x = clip(self.x, MIN_STIFFNESS, MAX_STIFFNESS) | 
					 | 
				
			||||||
    self.sR = clip(self.sR, self.MIN_SR, self.MAX_SR) | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # don't check stiffness for validity, as it can change quickly if sR is off | 
					 | 
				
			||||||
    valid = abs(self.slow_ao) < MAX_ANGLE_OFFSET_TH and \ | 
					 | 
				
			||||||
      self.sR > self.MIN_SR_TH and self.sR < self.MAX_SR_TH | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    return valid | 
					 | 
				
			||||||
					Loading…
					
					
				
		Reference in new issue